blob: 7bad622458461e1df3a309636cc3a5fc4baeaa77 [file] [log] [blame]
Sean Middleditch79376a02009-03-19 02:32:23 -04001/*
2 * Sean Middleditch
3 * sean@sourcemud.org
4 *
5 * The author or authors of this code dedicate any and all copyright interest
6 * in this code to the public domain. We make this dedication for the benefit
7 * of the public at large and to the detriment of our heirs and successors. We
8 * intend this dedication to be an overt act of relinquishment in perpetuity of
9 * all present and future rights to this code under copyright law.
10 */
11
12#include <sys/socket.h>
13#include <netinet/in.h>
14#include <arpa/inet.h>
15#include <poll.h>
16#include <errno.h>
17#include <stdio.h>
18#include <string.h>
19#include <stdlib.h>
20#include <ctype.h>
21#include <unistd.h>
22
23#ifdef HAVE_ZLIB
24#include "zlib.h"
25#endif
26
27#include "libtelnet.h"
28
29#define MAX_USERS 64
30#define LINEBUFFER_SIZE 256
31
Sean Middleditchbfc641e2009-03-22 16:26:06 -040032const telnet_telopt_t telopts[] = {
33 { TELNET_TELOPT_COMPRESS2, TELNET_WILL, TELNET_DONT },
34 { -1, 0, 0 }
35};
36
Sean Middleditch79376a02009-03-19 02:32:23 -040037struct user_t {
38 char *name;
39 int sock;
Sean Middleditchd2466a02009-09-19 14:35:48 -070040 telnet_t *telnet;
Sean Middleditch79376a02009-03-19 02:32:23 -040041 char linebuf[256];
42 int linepos;
43};
44
45static struct user_t users[MAX_USERS];
46
47static void linebuffer_push(char *buffer, size_t size, int *linepos,
48 char ch, void (*cb)(const char *line, int overflow, void *ud),
49 void *ud) {
50
51 /* CRLF -- line terminator */
52 if (ch == '\n' && *linepos > 0 && buffer[*linepos - 1] == '\r') {
53 /* NUL terminate (replaces \r in buffer), notify app, clear */
54 buffer[*linepos - 1] = 0;
55 cb(buffer, 0, ud);
56 *linepos = 0;
57
58 /* CRNUL -- just a CR */
59 } else if (ch == 0 && *linepos > 0 && buffer[*linepos - 1] == '\r') {
60 /* do nothing, the CR is already in the buffer */
61
62 /* anything else (including technically invalid CR followed by
63 * anything besides LF or NUL -- just buffer if we have room
64 * \r
65 */
66 } else if (*linepos != size) {
67 buffer[(*linepos)++] = ch;
68
69 /* buffer overflow */
70 } else {
71 /* terminate (NOTE: eats a byte), notify app, clear buffer */
72 buffer[size - 1] = 0;
73 cb(buffer, size - 1, ud);
74 *linepos = 0;
75 }
76}
77
78static void _message(const char *from, const char *msg) {
79 int i;
80 for (i = 0; i != MAX_USERS; ++i) {
81 if (users[i].sock != -1) {
Sean Middleditchd2466a02009-09-19 14:35:48 -070082 telnet_printf(users[i].telnet, "%s: %s\n", from, msg);
Sean Middleditch79376a02009-03-19 02:32:23 -040083 }
84 }
85}
86
87static void _send(int sock, const char *buffer, unsigned int size) {
88 int rs;
89
90 /* ignore on invalid socket */
91 if (sock == -1)
92 return;
93
94 /* send data */
95 while (size > 0) {
96 if ((rs = send(sock, buffer, size, 0)) == -1) {
97 if (errno != EINTR && errno != ECONNRESET) {
98 fprintf(stderr, "send() failed: %s\n", strerror(errno));
99 exit(1);
100 } else {
101 return;
102 }
103 } else if (rs == 0) {
104 fprintf(stderr, "send() unexpectedly returned 0\n");
105 exit(1);
106 }
107
108 /* update pointer and size to see if we've got more to send */
109 buffer += rs;
110 size -= rs;
111 }
112}
113
114/* process input line */
115static void _online(const char *line, int overflow, void *ud) {
116 struct user_t *user = (struct user_t*)ud;
117 int i;
118
119 /* if the user has no name, this is his "login" */
120 if (user->name == 0) {
121 /* must not be empty, must be at least 32 chars */
122 if (strlen(line) == 0 || strlen(line) > 32) {
Sean Middleditchd2466a02009-09-19 14:35:48 -0700123 telnet_printf(user->telnet, "Invalid name.\nEnter name: ");
Sean Middleditch79376a02009-03-19 02:32:23 -0400124 return;
125 }
126
127 /* must not already be in use */
128 for (i = 0; i != MAX_USERS; ++i) {
129 if (users[i].name != 0 && strcmp(users[i].name, line) == 0) {
Sean Middleditchd2466a02009-09-19 14:35:48 -0700130 telnet_printf(user->telnet, "Name in use.\nEnter name: ");
Sean Middleditch79376a02009-03-19 02:32:23 -0400131 return;
132 }
133 }
134
135 /* keep name */
136 user->name = strdup(line);
Sean Middleditchd2466a02009-09-19 14:35:48 -0700137 telnet_printf(user->telnet, "Welcome, %s!\n", line);
Sean Middleditch79376a02009-03-19 02:32:23 -0400138 return;
139 }
140
141 /* if line is "quit" then, well, quit */
142 if (strcmp(line, "quit") == 0) {
143 close(user->sock);
144 user->sock = -1;
145 _message(user->name, "** HAS QUIT **");
146 free(user->name);
147 user->name = 0;
148 return;
149 }
150
151 /* just a message -- send to all users */
152 _message(user->name, line);
153}
154
155static void _input(struct user_t *user, const char *buffer,
156 unsigned int size) {
157 unsigned int i;
158 for (i = 0; i != size; ++i)
159 linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos,
160 (char)buffer[i], _online, user);
161}
162
163static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
164 void *user_data) {
165 struct user_t *user = (struct user_t*)user_data;
166
167 switch (ev->type) {
168 /* data received */
169 case TELNET_EV_DATA:
170 _input(user, ev->buffer, ev->size);
171 break;
172 /* data must be sent */
173 case TELNET_EV_SEND:
174 _send(user->sock, ev->buffer, ev->size);
175 break;
Sean Middleditchc0e9fcd2009-03-19 15:16:32 -0400176 /* enable compress2 if accepted by client */
177 case TELNET_EV_DO:
178 if (ev->telopt == TELNET_TELOPT_COMPRESS2)
179 telnet_begin_compress2(telnet);
180 break;
Sean Middleditch79376a02009-03-19 02:32:23 -0400181 /* error */
182 case TELNET_EV_ERROR:
183 close(user->sock);
184 user->sock = -1;
185 if (user->name != 0) {
186 _message(user->name, "** HAS HAD AN ERROR **");
187 free(user->name);
188 user->name = 0;
189 }
Sean Middleditchd2466a02009-09-19 14:35:48 -0700190 telnet_free(user->telnet);
Sean Middleditch79376a02009-03-19 02:32:23 -0400191 break;
192 default:
193 /* ignore */
194 break;
195 }
196}
197
198int main(int argc, char **argv) {
199 char buffer[512];
200 short listen_port;
201 int listen_sock;
202 int rs;
203 int i;
204 struct sockaddr_in addr;
205 socklen_t addrlen;
206 struct pollfd pfd[MAX_USERS + 1];
207
208 /* check usage */
209 if (argc != 2) {
210 fprintf(stderr, "Usage:\n ./telnet-chatd <port>\n");
211 return 1;
212 }
213
214 /* initialize data structures */
215 memset(&pfd, 0, sizeof(pfd));
216 memset(users, 0, sizeof(users));
217 for (i = 0; i != MAX_USERS; ++i)
218 users[i].sock = -1;
219
220 /* parse listening port */
221 listen_port = strtol(argv[1], 0, 10);
222
223 /* create listening socket */
224 if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
225 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
226 return 1;
227 }
228
229 /* reuse address option */
230 rs = 1;
231 setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs));
232
233 /* bind to listening addr/port */
234 memset(&addr, 0, sizeof(addr));
235 addr.sin_family = AF_INET;
236 addr.sin_addr.s_addr = INADDR_ANY;
237 addr.sin_port = htons(listen_port);
238 if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
239 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
240 return 1;
241 }
242
243 /* listen for clients */
244 if (listen(listen_sock, 5) == -1) {
245 fprintf(stderr, "listen() failed: %s\n", strerror(errno));
246 return 1;
247 }
248
249 printf("LISTENING ON PORT %d\n", listen_port);
250
251 /* initialize listening descriptors */
252 pfd[MAX_USERS].fd = listen_sock;
253 pfd[MAX_USERS].events = POLLIN;
254
255 /* loop for ever */
256 for (;;) {
257 /* prepare for poll */
258 for (i = 0; i != MAX_USERS; ++i) {
259 if (users[i].sock != -1) {
260 pfd[i].fd = users[i].sock;
261 pfd[i].events = POLLIN;
262 } else {
263 pfd[i].fd = -1;
264 pfd[i].events = 0;
265 }
266 }
267
268 /* poll */
269 rs = poll(pfd, MAX_USERS + 1, -1);
270 if (rs == -1 && errno != EINTR) {
271 fprintf(stderr, "poll() failed: %s\n", strerror(errno));
272 return 1;
273 }
274
275 /* new connection */
276 if (pfd[MAX_USERS].revents & POLLIN) {
277 /* acept the sock */
278 addrlen = sizeof(addr);
279 if ((rs = accept(listen_sock, (struct sockaddr *)&addr,
280 &addrlen)) == -1) {
281 fprintf(stderr, "accept() failed: %s\n", strerror(errno));
282 return 1;
283 }
284
285 printf("Connection received.\n");
286
287 /* find a free user */
288 for (i = 0; i != MAX_USERS; ++i)
289 if (users[i].sock == -1)
290 break;
291 if (i == MAX_USERS) {
292 printf(" rejected (too many users)\n");
293 _send(rs, "Too many users.\r\n", 14);
294 close(rs);
295 }
296
297 /* init, welcome */
298 users[i].sock = rs;
Sean Middleditchd2466a02009-09-19 14:35:48 -0700299 users[i].telnet = telnet_init(telopts, _event_handler, 0,
Sean Middleditchbfc641e2009-03-22 16:26:06 -0400300 &users[i]);
Sean Middleditchd2466a02009-09-19 14:35:48 -0700301 telnet_negotiate(users[i].telnet, TELNET_WILL,
Sean Middleditchc0e9fcd2009-03-19 15:16:32 -0400302 TELNET_TELOPT_COMPRESS2);
Sean Middleditchd2466a02009-09-19 14:35:48 -0700303 telnet_printf(users[i].telnet, "Enter name: ");
Sean Middleditch79376a02009-03-19 02:32:23 -0400304 }
305
306 /* read from client */
307 for (i = 0; i != MAX_USERS; ++i) {
308 /* skip users that aren't actually connected */
309 if (users[i].sock == -1)
310 continue;
311
312 if (pfd[i].revents & POLLIN) {
313 if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) {
Sean Middleditchd2466a02009-09-19 14:35:48 -0700314 telnet_recv(users[i].telnet, buffer, rs);
Sean Middleditch79376a02009-03-19 02:32:23 -0400315 } else if (rs == 0) {
316 printf("Connection closed.\n");
317 close(users[i].sock);
318 if (users[i].name != 0) {
319 _message(users[i].name, "** HAS DISCONNECTED **");
320 free(users[i].name);
321 users[i].name = 0;
322 }
Sean Middleditchd2466a02009-09-19 14:35:48 -0700323 telnet_free(users[i].telnet);
Sean Middleditch79376a02009-03-19 02:32:23 -0400324 users[i].sock = -1;
325 break;
326 } else if (errno != EINTR) {
327 fprintf(stderr, "recv(client) failed: %s\n",
328 strerror(errno));
329 exit(1);
330 }
331 }
332 }
333 }
334
335 /* not that we can reach this, but GCC will cry if it's not here */
336 return 0;
337}