blob: 516bcd41072cb1bc5e4343a57752dca9f3c40585 [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;
171 /* error */
172 case TELNET_EV_ERROR:
173 close(user->sock);
174 user->sock = -1;
175 if (user->name != 0) {
176 _message(user->name, "** HAS HAD AN ERROR **");
177 free(user->name);
178 user->name = 0;
179 }
180 telnet_free(&user->telnet);
181 break;
182 default:
183 /* ignore */
184 break;
185 }
186}
187
188int main(int argc, char **argv) {
189 char buffer[512];
190 short listen_port;
191 int listen_sock;
192 int rs;
193 int i;
194 struct sockaddr_in addr;
195 socklen_t addrlen;
196 struct pollfd pfd[MAX_USERS + 1];
197
198 /* check usage */
199 if (argc != 2) {
200 fprintf(stderr, "Usage:\n ./telnet-chatd <port>\n");
201 return 1;
202 }
203
204 /* initialize data structures */
205 memset(&pfd, 0, sizeof(pfd));
206 memset(users, 0, sizeof(users));
207 for (i = 0; i != MAX_USERS; ++i)
208 users[i].sock = -1;
209
210 /* parse listening port */
211 listen_port = strtol(argv[1], 0, 10);
212
213 /* create listening socket */
214 if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
215 fprintf(stderr, "socket() failed: %s\n", strerror(errno));
216 return 1;
217 }
218
219 /* reuse address option */
220 rs = 1;
221 setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &rs, sizeof(rs));
222
223 /* bind to listening addr/port */
224 memset(&addr, 0, sizeof(addr));
225 addr.sin_family = AF_INET;
226 addr.sin_addr.s_addr = INADDR_ANY;
227 addr.sin_port = htons(listen_port);
228 if (bind(listen_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
229 fprintf(stderr, "bind() failed: %s\n", strerror(errno));
230 return 1;
231 }
232
233 /* listen for clients */
234 if (listen(listen_sock, 5) == -1) {
235 fprintf(stderr, "listen() failed: %s\n", strerror(errno));
236 return 1;
237 }
238
239 printf("LISTENING ON PORT %d\n", listen_port);
240
241 /* initialize listening descriptors */
242 pfd[MAX_USERS].fd = listen_sock;
243 pfd[MAX_USERS].events = POLLIN;
244
245 /* loop for ever */
246 for (;;) {
247 /* prepare for poll */
248 for (i = 0; i != MAX_USERS; ++i) {
249 if (users[i].sock != -1) {
250 pfd[i].fd = users[i].sock;
251 pfd[i].events = POLLIN;
252 } else {
253 pfd[i].fd = -1;
254 pfd[i].events = 0;
255 }
256 }
257
258 /* poll */
259 rs = poll(pfd, MAX_USERS + 1, -1);
260 if (rs == -1 && errno != EINTR) {
261 fprintf(stderr, "poll() failed: %s\n", strerror(errno));
262 return 1;
263 }
264
265 /* new connection */
266 if (pfd[MAX_USERS].revents & POLLIN) {
267 /* acept the sock */
268 addrlen = sizeof(addr);
269 if ((rs = accept(listen_sock, (struct sockaddr *)&addr,
270 &addrlen)) == -1) {
271 fprintf(stderr, "accept() failed: %s\n", strerror(errno));
272 return 1;
273 }
274
275 printf("Connection received.\n");
276
277 /* find a free user */
278 for (i = 0; i != MAX_USERS; ++i)
279 if (users[i].sock == -1)
280 break;
281 if (i == MAX_USERS) {
282 printf(" rejected (too many users)\n");
283 _send(rs, "Too many users.\r\n", 14);
284 close(rs);
285 }
286
287 /* init, welcome */
288 users[i].sock = rs;
289 telnet_init(&users[i].telnet, _event_handler, 0, &users[i]);
290 telnet_printf(&users[i].telnet, "Enter name: ");
291 }
292
293 /* read from client */
294 for (i = 0; i != MAX_USERS; ++i) {
295 /* skip users that aren't actually connected */
296 if (users[i].sock == -1)
297 continue;
298
299 if (pfd[i].revents & POLLIN) {
300 if ((rs = recv(users[i].sock, buffer, sizeof(buffer), 0)) > 0) {
301 telnet_push(&users[i].telnet, buffer, rs);
302 } else if (rs == 0) {
303 printf("Connection closed.\n");
304 close(users[i].sock);
305 if (users[i].name != 0) {
306 _message(users[i].name, "** HAS DISCONNECTED **");
307 free(users[i].name);
308 users[i].name = 0;
309 }
310 telnet_free(&users[i].telnet);
311 users[i].sock = -1;
312 break;
313 } else if (errno != EINTR) {
314 fprintf(stderr, "recv(client) failed: %s\n",
315 strerror(errno));
316 exit(1);
317 }
318 }
319 }
320 }
321
322 /* not that we can reach this, but GCC will cry if it's not here */
323 return 0;
324}