]> jfr.im git - irc/quakenet/newserv.git/blob - nterface/esockets.c
Whoops, forgot to remove a printf
[irc/quakenet/newserv.git] / nterface / esockets.c
1 /*
2 Easy async socket library with HELIX encryption and authentication
3 Copyright (C) 2004-2005 Chris Porter.
4
5 v1.02
6 - added some \n stripping in crypto code
7 v1.01
8 - noticed small problem in _read, if ret == -1 && errno != EAGAIN then it would mess up
9 - now disconnects on write buffer filling up
10 - increased buffer size and maximum queue size
11 */
12
13 #include "esockets.h"
14 #include <stdio.h>
15 #include <sys/poll.h>
16 #include <errno.h>
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <netinet/in.h>
25
26 #include "../lib/sha1.h"
27 #include "../core/events.h"
28
29 struct esocket *socklist = NULL;
30 char signal_set = 0;
31 unsigned short token = 1;
32
33 void sigpipe_handler(int moo) { }
34
35 unsigned short esocket_token(void) {
36 return token++;
37 }
38
39 struct esocket *esocket_add(int fd, char socket_type, struct esocket_events *events, unsigned short token) {
40 int flags;
41 struct esocket *newsock = (struct esocket *)malloc(sizeof(struct esocket));
42 if(!newsock)
43 return NULL;
44
45 memcpy(&newsock->events, events, sizeof(newsock->events));
46
47 newsock->socket_type = socket_type;
48
49 switch(socket_type) {
50 case ESOCKET_LISTENING:
51 /* listening sockets require POLLIN, which is triggered on connect */
52 flags = POLLIN;
53 newsock->socket_status = ST_LISTENING;
54 break;
55 case ESOCKET_OUTGOING:
56 /* outgoing connections require POLLOUT (triggered on connect) */
57 flags = POLLOUT;
58 newsock->socket_status = ST_CONNECTING;
59 break;
60 case ESOCKET_OUTGOING_CONNECTED:
61 /* outgoing connections that have connected (think localhost) require just POLLIN */
62 flags = POLLIN;
63 newsock->socket_status = ST_CONNECTED;
64 break;
65 case ESOCKET_INCOMING:
66 /* sockets that have just connected to us require (initially) POLLIN */
67 flags = POLLIN;
68 newsock->socket_status = ST_CONNECTED;
69 break;
70 default:
71 return NULL;
72 }
73
74 registerhandler(fd, flags | POLLERR | POLLHUP, esocket_poll_event);
75 newsock->fd = fd;
76 newsock->next = socklist;
77
78 newsock->in.on_parse = buffer_parse_ascii;
79 newsock->in.startpos = newsock->in.curpos = newsock->in.writepos = newsock->in.data;
80 newsock->in.buffer_size = MAX_ASCII_LINE_SIZE;
81 newsock->in.packet_length = 0;
82 newsock->out.head = NULL;
83 newsock->out.end = NULL;
84 newsock->out.count = 0;
85 newsock->token = token;
86 newsock->tag = NULL;
87
88 if(!signal_set) {
89 signal(SIGPIPE, sigpipe_handler);
90 signal_set = 1;
91 }
92
93 socklist = newsock;
94
95 return newsock;
96 }
97
98 struct esocket *find_esocket_from_fd(int fd) {
99 struct esocket *p = socklist;
100
101 for(;p;p=p->next)
102 if(p->fd == fd)
103 return p;
104
105 return NULL;
106 }
107
108 void esocket_clean_by_token(unsigned short token) {
109 struct esocket *cp = socklist, *np;
110
111 /* not efficient but boohoo */
112 for(;cp;) {
113 if(cp->token == token) {
114 np = cp->next;
115 esocket_disconnect(cp);
116 cp = np;
117 } else {
118 cp = cp->next;
119 }
120 }
121 }
122
123 void esocket_poll_event(int fd, short events) {
124 struct esocket *active = find_esocket_from_fd(fd);
125
126 if(!active)
127 return;
128
129 switch(active->socket_status) {
130 case ST_LISTENING:
131 active->events.on_accept(active);
132 break;
133 case ST_CONNECTING:
134 if(events & (POLLERR | POLLHUP)) {
135 esocket_disconnect(active);
136 return;
137 }
138 if(events & POLLOUT) { /* connected if connecting! */
139 deregisterhandler(active->fd, 0);
140 registerhandler(active->fd, POLLIN | POLLERR | POLLHUP, esocket_poll_event);
141 active->socket_status = ST_CONNECTED;
142 active->events.on_connect(active);
143 }
144 break;
145 case ST_CONNECTED:
146 case ST_BLANK:
147 if(events & (POLLERR | POLLHUP)) {
148 esocket_disconnect(active);
149 return;
150 }
151 if(events & POLLOUT) { /* flush buffer */
152 if(esocket_raw_write(active, NULL, 0)) {
153 esocket_disconnect(active);
154 return;
155 }
156 return;
157 }
158 if(events & POLLIN) { /* read buffer */
159 int ret = esocket_read(active);
160 if(ret) {
161 if(ret != BUF_ERROR)
162 esocket_disconnect(active);
163 return;
164 }
165 }
166
167 break;
168 }
169 }
170
171 void esocket_disconnect(struct esocket *active) {
172 struct esocket *l = NULL, *p = socklist;
173
174 if(active->events.on_disconnect)
175 active->events.on_disconnect(active);
176
177 for(;p;l=p,p=p->next)
178 if(p == active) {
179 struct esocket_packet *pkt = p->out.head, *npkt;
180 if(l) {
181 l->next = p->next;
182 } else {
183 socklist = p->next;
184 }
185
186 deregisterhandler(p->fd, 1);
187
188 for(;pkt;) {
189 npkt = pkt->next;
190 free(pkt->line);
191 free(pkt);
192 pkt = npkt;
193 }
194
195 free(p);
196 break;
197 }
198 }
199
200 void esocket_disconnect_when_complete(struct esocket *active) {
201 if(active->out.count) {
202 active->socket_status = ST_BLANK;
203 } else {
204 esocket_disconnect(active);
205 }
206 }
207
208 int esocket_read(struct esocket *sock) {
209 struct esocket_in_buffer *buf = &sock->in;
210 int ret, bytesread = read(sock->fd, buf->writepos, buf->buffer_size - (buf->writepos - buf->data));
211
212 if(!bytesread || ((bytesread == -1) && (errno != EAGAIN)))
213 return 1;
214
215 if((bytesread == -1) && (errno == EAGAIN))
216 return 0;
217
218 buf->writepos+=bytesread;
219
220 do {
221 ret = buf->on_parse(sock);
222 if((ret != BUF_CONT) && ret)
223 return ret;
224 } while(ret);
225
226 if((buf->curpos == (buf->data + buf->buffer_size)) && (buf->startpos == buf->data))
227 return 1;
228
229 if(buf->startpos != buf->curpos) {
230 int moveback = buf->writepos - buf->startpos;
231 memmove(buf->data, buf->startpos, moveback);
232 buf->writepos = buf->data + moveback;
233 } else {
234 buf->writepos = buf->data;
235 }
236
237 buf->curpos = buf->writepos;
238 buf->startpos = buf->data;
239
240 return 0;
241 }
242
243 struct esocket_packet *esocket_new_packet(struct esocket_out_buffer *buf, char *buffer, int bytes) {
244 struct esocket_packet *nw;
245
246 if(buf->count == MAX_OUT_QUEUE_SIZE)
247 return NULL;
248
249 nw = malloc(sizeof(struct esocket_packet));
250 if(!nw)
251 return NULL;
252
253 nw->line = malloc(bytes);
254 if(!nw->line) {
255 free(nw);
256 return NULL;
257 }
258
259 if(!buf->head) {
260 buf->head = nw;
261 } else {
262 buf->end->next = nw;
263 }
264
265 buf->end = nw;
266
267 nw->next = NULL;
268 nw->size = bytes;
269
270 memcpy(nw->line, buffer, bytes);
271
272 buf->count++;
273
274 return nw;
275 }
276
277 int esocket_raw_write(struct esocket *sock, char *buffer, int bytes) {
278 struct esocket_out_buffer *buf = &sock->out;
279
280 if((bytes > USHRT_MAX) || (!buffer && bytes) || (buffer && !bytes))
281 return 1;
282
283 if(bytes) { /* if we're not flushing */
284 if(buf->count) { /* if we're currently blocked */
285 if(!esocket_new_packet(buf, buffer, bytes))
286 return 1;
287 return 0;
288 } else { /* not currently blocked */
289 int ret;
290 for(;;) {
291 ret = write(sock->fd, buffer, bytes);
292 if(ret == bytes) {
293 return 0;
294 } else if(ret == -1) { /* something bad happened */
295 switch(errno) {
296 case EAGAIN: /* was going to block, store the data in the buffer */
297 if(!esocket_new_packet(buf, buffer, bytes))
298 return 1;
299
300 deregisterhandler(sock->fd, 0); /* now we need to ignore the socket until it's unblocked */
301 registerhandler(sock->fd, POLLOUT | POLLERR | POLLHUP, esocket_poll_event);
302
303 return 0;
304 break;
305 case EPIPE: /* if there's some weird code disconnect the socket */
306 default:
307 return 1;
308 break;
309 }
310 } else { /* less than total was written, try again */
311 buffer+=ret;
312 bytes-=ret;
313 }
314 }
315 }
316 } else if(buf->count) { /* if we're just flushing the buffer */
317 int ret;
318 for(;;) {
319 ret = write(sock->fd, buf->head->line, buf->head->size);
320 if(!ret) {
321 return 0;
322 } else if(ret == buf->head->size) {
323 struct esocket_packet *p = buf->head;
324 buf->head = buf->head->next;
325
326 free(p->line);
327 free(p);
328
329 buf->count--;
330 if(!buf->head) { /* if we've exhausted the buffer */
331 buf->end = NULL;
332
333 if(sock->socket_status == ST_BLANK) {
334 esocket_disconnect(sock);
335 } else {
336 deregisterhandler(sock->fd, 0);
337
338 registerhandler(sock->fd, POLLIN | POLLERR | POLLHUP, esocket_poll_event);
339 }
340 return 0;
341 }
342 } else if(ret == -1) {
343 switch(errno) {
344 case EAGAIN: /* was going to block, wait until we're called again */
345 return 0;
346 break;
347
348 case EPIPE: /* if there's some weird code disconnect the socket */
349 default:
350 return 1;
351 break;
352 }
353 } else {
354 buf->head->line+=ret;
355 buf->head->size-=ret;
356 }
357 }
358 return 0;
359 }
360
361 /* something went wrong */
362 return 1;
363 }
364
365 int esocket_write(struct esocket *sock, char *buffer, int bytes) {
366 int ret;
367 if(sock->in.on_parse == buffer_parse_ascii) {
368 ret = esocket_raw_write(sock, buffer, bytes);
369 } else {
370 unsigned char mac[MAC_LEN];
371 char newbuf[MAX_BUFSIZE + USED_MAC_LEN + sizeof(packet_t)];
372 packet_t packetlength;
373 if(bytes > MAX_BUFSIZE)
374 return 1;
375 packetlength = htons(bytes + USED_MAC_LEN);
376
377 memcpy(newbuf, &packetlength, sizeof(packet_t));
378 h_encrypt(&sock->keysend, (unsigned char *)buffer, bytes, mac);
379
380 memcpy(newbuf + sizeof(packet_t), buffer, bytes);
381 memcpy(newbuf + sizeof(packet_t) + bytes, mac, USED_MAC_LEN);
382
383 ret = esocket_raw_write(sock, newbuf, bytes + sizeof(packet_t) + USED_MAC_LEN);
384 }
385
386 /* AWOOGA!! */
387 if(ret)
388 esocket_disconnect(sock);
389
390 return ret;
391 }
392
393 int esocket_write_line(struct esocket *sock, char *format, ...) {
394 char nbuf[MAX_ASCII_LINE_SIZE];
395 va_list va;
396 int len;
397
398 va_start(va, format);
399
400 if(sock->in.on_parse == buffer_parse_ascii) {
401 vsnprintf(nbuf, sizeof(nbuf) - 1, format, va); /* snprintf() and vsnprintf() will write at most size-1 of the characters, one for \n */
402 } else {
403 vsnprintf(nbuf, sizeof(nbuf), format, va);
404 }
405 va_end(va);
406
407 len = strlen(nbuf);
408
409 if(sock->in.on_parse == buffer_parse_ascii)
410 nbuf[len++] = '\n';
411
412 nbuf[len] = '\0';
413
414 return esocket_write(sock, nbuf, len);
415 }
416
417 int buffer_parse_ascii(struct esocket *sock) {
418 struct esocket_in_buffer *buf = &sock->in;
419
420 for(;buf->curpos<buf->writepos;buf->curpos++) {
421 if((*buf->curpos == '\n') || !*buf->curpos) {
422 int ret;
423 char *newline = buf->startpos;
424
425 *buf->curpos = '\0';
426
427 buf->startpos = buf->curpos + 1;
428
429 ret = sock->events.on_line(sock, newline);
430 if(ret)
431 return ret;
432
433 if(buf->curpos + 1 < buf->writepos) {
434 buf->curpos++;
435 return BUF_CONT;
436 }
437 }
438 }
439
440 return 0;
441 }
442
443 int buffer_parse_crypt(struct esocket *sock) {
444 struct esocket_in_buffer *buf = &sock->in;
445 unsigned char mac[MAC_LEN];
446
447 if(!buf->packet_length) {
448 if(buf->writepos - buf->startpos > sizeof(packet_t)) {
449 memcpy(&buf->packet_length, buf->startpos, sizeof(packet_t));
450 buf->startpos = buf->startpos + sizeof(packet_t);
451 buf->curpos = buf->startpos;
452
453 buf->packet_length = ntohs(buf->packet_length);
454 if((buf->packet_length > buf->buffer_size - sizeof(packet_t)) || (buf->packet_length <= 0))
455 return 1;
456
457 } else {
458 buf->curpos = buf->writepos;
459 return 0;
460 }
461 }
462
463 if(buf->packet_length <= buf->writepos - buf->startpos) {
464 int ret;
465 char *newline, *p;
466 h_decrypt(&sock->keyreceive, (unsigned char *)buf->startpos, buf->packet_length - USED_MAC_LEN, mac);
467
468 if(memcmp(mac, buf->startpos + buf->packet_length - USED_MAC_LEN, USED_MAC_LEN))
469 return 1;
470
471 p = newline = buf->startpos;
472 newline[buf->packet_length - USED_MAC_LEN] = '\0';
473
474 for(;*p;p++) /* shouldn't happen */
475 if(*p == '\n')
476 *p = '\0';
477
478 buf->startpos = buf->startpos + buf->packet_length;
479 buf->packet_length = 0;
480
481 ret = sock->events.on_line(sock, newline);
482 if(ret)
483 return ret;
484
485 return BUF_CONT;
486 }
487
488 buf->curpos = buf->writepos;
489 return 0;
490 }
491
492 void switch_buffer_mode(struct esocket *sock, char *key, unsigned char *ournonce, unsigned char *theirnonce) {
493 unsigned char ukey[20];
494 SHA1_CTX context;
495
496 SHA1Init(&context);
497 SHA1Update(&context, (unsigned char *)key, strlen(key));
498 SHA1Update(&context, (unsigned char *)" ", 1);
499 /* not sure if this is cryptographically secure! */
500 SHA1Update(&context, (unsigned char *)ournonce, NONCE_LEN);
501 SHA1Final(ukey, &context);
502
503 sock->in.on_parse = buffer_parse_crypt;
504 sock->in.buffer_size = MAX_BINARY_LINE_SIZE;
505
506
507 h_key(&sock->keysend, ukey, sizeof(ukey));
508 h_nonce(&sock->keysend, ournonce);
509
510 SHA1Init(&context);
511 SHA1Update(&context, (unsigned char *)key, strlen(key));
512 SHA1Update(&context, (unsigned char *)" ", 1);
513 SHA1Update(&context, (unsigned char *)theirnonce, NONCE_LEN);
514 SHA1Final(ukey, &context);
515
516 h_key(&sock->keyreceive, ukey, sizeof(ukey));
517 h_nonce(&sock->keyreceive, theirnonce);
518 }
519