blob: 67cbe8dd729cebae93b29759d13cd8243b56c928 [file] [log] [blame]
Sean Middleditcha7a6b312009-03-16 12:49:50 -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
32struct user_t {
33 char *name;
34 int sock;
35 libtelnet_t telnet;
36 char linebuf[256];
37 int linepos;
38};
39
40static struct user_t users[MAX_USERS];
41
42static void linebuffer_push(char *buffer, size_t size, int *linepos,
43 char ch, void (*cb)(const char *line, int overflow, void *ud),
44 void *ud) {
45
46 /* CRLF -- line terminator */
47 if (ch == '\n' && *linepos > 0 && buffer[*linepos - 1] == '\r') {
48 /* NUL terminate (replaces \r in buffer), notify app, clear */
49 buffer[*linepos - 1] = 0;
50 cb(buffer, 0, ud);
51 *linepos = 0;
52
53 /* CRNUL -- just a CR */
54 } else if (ch == 0 && *linepos > 0 && buffer[*linepos - 1] == '\r') {
55 /* do nothing, the CR is already in the buffer */
56
57 /* anything else (including technically invalid CR followed by
58 * anything besides LF or NUL -- just buffer if we have room
59 * \r
60 */
61 } else if (*linepos != size) {
62 buffer[(*linepos)++] = ch;
63
64 /* buffer overflow */
65 } else {
66 /* terminate (NOTE: eats a byte), notify app, clear buffer */
67 buffer[size - 1] = 0;
68 cb(buffer, size - 1, ud);
69 *linepos = 0;
70 }
71}
72
73static void _message(const char *from, const char *msg) {
74 int i;
75 for (i = 0; i != MAX_USERS; ++i) {
76 if (users[i].sock != -1) {
77 libtelnet_send_printf(&users[i].telnet, "%s: %s\r\n",
78 from, msg);
79 }
80 }
81}
82
83static void _send(int sock, unsigned char *buffer, unsigned int size) {
84 int rs;
85
86 /* ignore on invalid socket */
87 if (sock == -1)
88 return;
89
90 /* send data */
91 while (size > 0) {
92 if ((rs = send(sock, buffer, size, 0)) == -1) {
93 if (errno != EINTR && errno != ECONNRESET) {
94 fprintf(stderr, "send() failed: %s\n", strerror(errno));
95 exit(1);
96 } else {
97 return;
98 }
99 } else if (rs == 0) {
100 fprintf(stderr, "send() unexpectedly returned 0\n");
101 exit(1);
102 }
103
104 /* update pointer and size to see if we've got more to send */
105 buffer += rs;
106 size -= rs;
107 }
108}
109
110/* process input line */
111static void _online(const char *line, int overflow, void *ud) {
112 struct user_t *user = (struct user_t*)ud;
113 int i;
114
115 /* if the user has no name, this is his "login" */
116 if (user->name == 0) {
117 /* must not be empty, must be at least 32 chars */
118 if (strlen(line) == 0 || strlen(line) > 32) {
119 libtelnet_send_printf(&user->telnet,
120 "Invalid name.\r\nEnter name: ");
121 return;
122 }
123
124 /* must not already be in use */
125 for (i = 0; i != MAX_USERS; ++i) {
126 if (users[i].name != 0 && strcmp(users[i].name, line) == 0) {
127 libtelnet_send_printf(&user->telnet,
128 "Name in use.\r\nEnter name: ");
129 return;
130 }
131 }
132
133 /* keep name */
134 user->name = strdup(line);
135 libtelnet_send_printf(&user->telnet, "Welcome, %s!\r\n", line);
136 return;
137 }
138
139 /* if line is "quit" then, well, quit */
140 if (strcmp(line, "quit") == 0) {
141 close(user->sock);
142 user->sock = -1;
143 _message(user->name, "** HAS QUIT **");
144 free(user->name);
145 user->name = 0;
146 return;
147 }
148
149 /* just a message -- send to all users */
150 _message(user->name, line);
151}
152
153static void _input(struct user_t *user, unsigned char *buffer,
154 unsigned int size) {
155 unsigned int i;
156 for (i = 0; i != size; ++i)
157 linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos,
158 (char)buffer[i], _online, user);
159}
160
161static void _event_handler(libtelnet_t *telnet, libtelnet_event_t *ev,
162 void *user_data) {
163 struct user_t *user = (struct user_t*)user_data;
164
165 switch (ev->type) {
166 /* data received */
167 case LIBTELNET_EV_DATA:
168 _input(user, ev->buffer, ev->size);
169 break;
170 /* data must be sent */
171 case LIBTELNET_EV_SEND:
172 _send(user->sock, ev->buffer, ev->size);
173 break;
174 /* error */
175 case LIBTELNET_EV_ERROR:
176 close(user->sock);
177 user->sock = -1;
178 if (user->name != 0) {
179 _message(user->name, "** HAS HAD AN ERROR **");
180 free(user->name);
181 user->name = 0;
182 }
183 libtelnet_free(&user->telnet);
184 break;
185 default:
186 /* ignore */
187 break;
188 }
189}
190
191int main(int argc, char **argv) {
192 unsigned char buffer[512];
193 short listen_port;
194 int listen_sock;
195 int rs;
196 int i;
197 struct sockaddr_in addr;
198 socklen_t addrlen;
199 struct pollfd pfd[MAX_USERS + 1];
200
201 /* check usage */
202 if (argc != 2) {
203 fprintf(stderr, "Usage:\n ./telnet-chatd <port>\n");
204 return 1;
205 }
206
207 /* initialize data structures */
208 memset(&pfd, 0, sizeof(pfd));
209 memset(users, 0, sizeof(users));
210 for (i = 0; i != MAX_USERS; ++i)
211 users[i].sock = -1;
212
213 /* parse listening port */
214 listen_port = strtol(argv[1], 0, 10);
215
216 /* create listening socket */
217 if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
218 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
219 return 1;
220 }
221
222 /* reuse address option */
223 rs = 1;
224 setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs));
225
226 /* bind to listening addr/port */
227 memset(&addr, 0, sizeof(addr));
228 addr.sin_family = AF_INET;
229 addr.sin_addr.s_addr = INADDR_ANY;
230 addr.sin_port = htons(listen_port);
231 if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
232 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
233 return 1;
234 }
235
236 /* listen for clients */
237 if (listen(listen_sock, 5) == -1) {
238 fprintf(stderr, "listen() failed: %s\n", strerror(errno));
239 return 1;
240 }
241
242 printf("LISTENING ON PORT %d\n", listen_port);
243
244 /* initialize listening descriptors */
245 pfd[MAX_USERS].fd = listen_sock;
246 pfd[MAX_USERS].events = POLLIN;
247
248 /* loop for ever */
249 for (;;) {
250 /* prepare for poll */
251 for (i = 0; i != MAX_USERS; ++i) {
252 if (users[i].sock != -1) {
253 pfd[i].fd = users[i].sock;
254 pfd[i].events = POLLIN;
255 } else {
256 pfd[i].fd = -1;
257 pfd[i].events = 0;
258 }
259 }
260
261 /* poll */
262 rs = poll(pfd, MAX_USERS + 1, -1);
263 if (rs == -1 && errno != EINTR) {
264 fprintf(stderr, "poll() failed: %s\n", strerror(errno));
265 return 1;
266 }
267
268 /* new connection */
269 if (pfd[MAX_USERS].revents & POLLIN) {
270 /* acept the sock */
271 addrlen = sizeof(addr);
272 if ((rs = accept(listen_sock, (struct sockaddr *)&addr,
273 &addrlen)) == -1) {
274 fprintf(stderr, "accept() failed: %s\n", strerror(errno));
275 return 1;
276 }
277
278 printf("Connection received.\n");
279
280 /* find a free user */
281 for (i = 0; i != MAX_USERS; ++i)
282 if (users[i].sock == -1)
283 break;
284 if (i == MAX_USERS) {
285 printf(" rejected (too many users)\n");
286 _send(rs, (unsigned char *)"Too many users.\r\n", 14);
287 close(rs);
288 }
289
290 /* init, welcome */
291 users[i].sock = rs;
292 libtelnet_init(&users[i].telnet, _event_handler, 0, &users[i]);
293 libtelnet_send_printf(&users[i].telnet, "Enter name: ");
294 }
295
296 /* read from client */
297 for (i = 0; i != MAX_USERS; ++i) {
298 /* skip users that aren't actually connected */
299 if (users[i].sock == -1)
300 continue;
301
302 if (pfd[i].revents & POLLIN) {
303 if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) {
304 libtelnet_push(&users[i].telnet, buffer, rs);
305 } else if (rs == 0) {
306 printf("Connection closed.\n");
307 close(users[i].sock);
308 if (users[i].name != 0) {
309 _message(users[i].name, "** HAS DISCONNECTED **");
310 free(users[i].name);
311 users[i].name = 0;
312 }
313 libtelnet_free(&users[i].telnet);
314 users[i].sock = -1;
315 break;
316 } else if (errno != EINTR) {
317 fprintf(stderr, "recv(client) failed: %s\n",
318 strerror(errno));
319 exit(1);
320 }
321 }
322 }
323 }
324
325 /* not that we can reach this, but GCC will cry if it's not here */
326 return 0;
327}