blob: c4981f700fca20d11855099da9a83845d05e9a2b [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
32struct user_t {
33 char *name;
34 int sock;
35 telnet_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 telnet_printf(&users[i].telnet, "%s: %s\n", from, msg);
78 }
79 }
80}
81
82static void _send(int sock, const char *buffer, unsigned int size) {
83 int rs;
84
85 /* ignore on invalid socket */
86 if (sock == -1)
87 return;
88
89 /* send data */
90 while (size > 0) {
91 if ((rs = send(sock, buffer, size, 0)) == -1) {
92 if (errno != EINTR && errno != ECONNRESET) {
93 fprintf(stderr, "send() failed: %s\n", strerror(errno));
94 exit(1);
95 } else {
96 return;
97 }
98 } else if (rs == 0) {
99 fprintf(stderr, "send() unexpectedly returned 0\n");
100 exit(1);
101 }
102
103 /* update pointer and size to see if we've got more to send */
104 buffer += rs;
105 size -= rs;
106 }
107}
108
109/* process input line */
110static void _online(const char *line, int overflow, void *ud) {
111 struct user_t *user = (struct user_t*)ud;
112 int i;
113
114 /* if the user has no name, this is his "login" */
115 if (user->name == 0) {
116 /* must not be empty, must be at least 32 chars */
117 if (strlen(line) == 0 || strlen(line) > 32) {
118 telnet_printf(&user->telnet, "Invalid name.\nEnter name: ");
119 return;
120 }
121
122 /* must not already be in use */
123 for (i = 0; i != MAX_USERS; ++i) {
124 if (users[i].name != 0 && strcmp(users[i].name, line) == 0) {
125 telnet_printf(&user->telnet, "Name in use.\nEnter name: ");
126 return;
127 }
128 }
129
130 /* keep name */
131 user->name = strdup(line);
132 telnet_printf(&user->telnet, "Welcome, %s!\n", line);
133 return;
134 }
135
136 /* if line is "quit" then, well, quit */
137 if (strcmp(line, "quit") == 0) {
138 close(user->sock);
139 user->sock = -1;
140 _message(user->name, "** HAS QUIT **");
141 free(user->name);
142 user->name = 0;
143 return;
144 }
145
146 /* just a message -- send to all users */
147 _message(user->name, line);
148}
149
150static void _input(struct user_t *user, const char *buffer,
151 unsigned int size) {
152 unsigned int i;
153 for (i = 0; i != size; ++i)
154 linebuffer_push(user->linebuf, sizeof(user->linebuf), &user->linepos,
155 (char)buffer[i], _online, user);
156}
157
158static void _event_handler(telnet_t *telnet, telnet_event_t *ev,
159 void *user_data) {
160 struct user_t *user = (struct user_t*)user_data;
161
162 switch (ev->type) {
163 /* data received */
164 case TELNET_EV_DATA:
165 _input(user, ev->buffer, ev->size);
166 break;
167 /* data must be sent */
168 case TELNET_EV_SEND:
169 _send(user->sock, ev->buffer, ev->size);
170 break;
Sean Middleditchc0e9fcd2009-03-19 15:16:32 -0400171 /* enable compress2 if accepted by client */
172 case TELNET_EV_DO:
173 if (ev->telopt == TELNET_TELOPT_COMPRESS2)
174 telnet_begin_compress2(telnet);
175 break;
Sean Middleditch79376a02009-03-19 02:32:23 -0400176 /* error */
177 case TELNET_EV_ERROR:
178 close(user->sock);
179 user->sock = -1;
180 if (user->name != 0) {
181 _message(user->name, "** HAS HAD AN ERROR **");
182 free(user->name);
183 user->name = 0;
184 }
185 telnet_free(&user->telnet);
186 break;
187 default:
188 /* ignore */
189 break;
190 }
191}
192
193int main(int argc, char **argv) {
194 char buffer[512];
195 short listen_port;
196 int listen_sock;
197 int rs;
198 int i;
199 struct sockaddr_in addr;
200 socklen_t addrlen;
201 struct pollfd pfd[MAX_USERS + 1];
202
203 /* check usage */
204 if (argc != 2) {
205 fprintf(stderr, "Usage:\n ./telnet-chatd <port>\n");
206 return 1;
207 }
208
209 /* initialize data structures */
210 memset(&pfd, 0, sizeof(pfd));
211 memset(users, 0, sizeof(users));
212 for (i = 0; i != MAX_USERS; ++i)
213 users[i].sock = -1;
214
215 /* parse listening port */
216 listen_port = strtol(argv[1], 0, 10);
217
218 /* create listening socket */
219 if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
220 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
221 return 1;
222 }
223
224 /* reuse address option */
225 rs = 1;
226 setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs));
227
228 /* bind to listening addr/port */
229 memset(&addr, 0, sizeof(addr));
230 addr.sin_family = AF_INET;
231 addr.sin_addr.s_addr = INADDR_ANY;
232 addr.sin_port = htons(listen_port);
233 if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
234 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
235 return 1;
236 }
237
238 /* listen for clients */
239 if (listen(listen_sock, 5) == -1) {
240 fprintf(stderr, "listen() failed: %s\n", strerror(errno));
241 return 1;
242 }
243
244 printf("LISTENING ON PORT %d\n", listen_port);
245
246 /* initialize listening descriptors */
247 pfd[MAX_USERS].fd = listen_sock;
248 pfd[MAX_USERS].events = POLLIN;
249
250 /* loop for ever */
251 for (;;) {
252 /* prepare for poll */
253 for (i = 0; i != MAX_USERS; ++i) {
254 if (users[i].sock != -1) {
255 pfd[i].fd = users[i].sock;
256 pfd[i].events = POLLIN;
257 } else {
258 pfd[i].fd = -1;
259 pfd[i].events = 0;
260 }
261 }
262
263 /* poll */
264 rs = poll(pfd, MAX_USERS + 1, -1);
265 if (rs == -1 && errno != EINTR) {
266 fprintf(stderr, "poll() failed: %s\n", strerror(errno));
267 return 1;
268 }
269
270 /* new connection */
271 if (pfd[MAX_USERS].revents & POLLIN) {
272 /* acept the sock */
273 addrlen = sizeof(addr);
274 if ((rs = accept(listen_sock, (struct sockaddr *)&addr,
275 &addrlen)) == -1) {
276 fprintf(stderr, "accept() failed: %s\n", strerror(errno));
277 return 1;
278 }
279
280 printf("Connection received.\n");
281
282 /* find a free user */
283 for (i = 0; i != MAX_USERS; ++i)
284 if (users[i].sock == -1)
285 break;
286 if (i == MAX_USERS) {
287 printf(" rejected (too many users)\n");
288 _send(rs, "Too many users.\r\n", 14);
289 close(rs);
290 }
291
292 /* init, welcome */
293 users[i].sock = rs;
294 telnet_init(&users[i].telnet, _event_handler, 0, &users[i]);
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400295 telnet_negotiate(&users[i].telnet, TELNET_WILL,
Sean Middleditchc0e9fcd2009-03-19 15:16:32 -0400296 TELNET_TELOPT_COMPRESS2);
Sean Middleditch79376a02009-03-19 02:32:23 -0400297 telnet_printf(&users[i].telnet, "Enter name: ");
298 }
299
300 /* read from client */
301 for (i = 0; i != MAX_USERS; ++i) {
302 /* skip users that aren't actually connected */
303 if (users[i].sock == -1)
304 continue;
305
306 if (pfd[i].revents & POLLIN) {
307 if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) {
Sean Middleditch4f0c37f2009-03-20 23:08:55 -0400308 telnet_recv(&users[i].telnet, buffer, rs);
Sean Middleditch79376a02009-03-19 02:32:23 -0400309 } else if (rs == 0) {
310 printf("Connection closed.\n");
311 close(users[i].sock);
312 if (users[i].name != 0) {
313 _message(users[i].name, "** HAS DISCONNECTED **");
314 free(users[i].name);
315 users[i].name = 0;
316 }
317 telnet_free(&users[i].telnet);
318 users[i].sock = -1;
319 break;
320 } else if (errno != EINTR) {
321 fprintf(stderr, "recv(client) failed: %s\n",
322 strerror(errno));
323 exit(1);
324 }
325 }
326 }
327 }
328
329 /* not that we can reach this, but GCC will cry if it's not here */
330 return 0;
331}