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