]> jfr.im git - irc/quakenet/newserv.git/blob - nterfacer/esockets.c
BUILD: add require-all build mode
[irc/quakenet/newserv.git] / nterfacer / esockets.c
1 /*
2 Easy async socket library
3 Copyright (C) 2004-2007 Chris Porter.
4 */
5
6 #include "esockets.h"
7 #include <stdio.h>
8 #include <sys/poll.h>
9 #include <errno.h>
10 #include <signal.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <string.h>
14 #include <limits.h>
15 #include <stdarg.h>
16 #include <netinet/in.h>
17
18 #include "../core/events.h"
19
20 struct esocket *socklist = NULL;
21 char signal_set = 0;
22 unsigned short token = 1;
23
24 void sigpipe_handler(int moo) { }
25
26 unsigned short esocket_token(void) {
27 return token++;
28 }
29
30 struct esocket *esocket_add(int fd, char socket_type, struct esocket_events *events, unsigned short token) {
31 int flags;
32 struct esocket *newsock = (struct esocket *)ntmalloc(sizeof(struct esocket));
33 if(!newsock)
34 return NULL;
35
36 memcpy(&newsock->events, events, sizeof(newsock->events));
37
38 newsock->socket_type = socket_type;
39
40 switch(socket_type) {
41 case ESOCKET_LISTENING:
42 /* listening sockets require POLLIN, which is triggered on connect */
43 flags = POLLIN;
44 newsock->socket_status = ST_LISTENING;
45 break;
46 case ESOCKET_OUTGOING:
47 /* outgoing connections require POLLOUT (triggered on connect) */
48 flags = POLLOUT;
49 newsock->socket_status = ST_CONNECTING;
50 break;
51 case ESOCKET_OUTGOING_CONNECTED:
52 /* outgoing connections that have connected (think localhost) require just POLLIN */
53 flags = POLLIN;
54 newsock->socket_status = ST_CONNECTED;
55 break;
56 case ESOCKET_INCOMING:
57 /* sockets that have just connected to us require (initially) POLLIN */
58 flags = POLLIN;
59 newsock->socket_status = ST_CONNECTED;
60 break;
61 default:
62 return NULL;
63 }
64
65 registerhandler(fd, flags | POLLERR | POLLHUP, esocket_poll_event);
66 newsock->fd = fd;
67 newsock->next = socklist;
68
69 newsock->in.mode = PARSE_ASCII;
70 newsock->in.data = NULL;
71 newsock->in.size = 0;
72 newsock->in.cryptobuf = NULL;
73 newsock->in.cryptobufsize = 0;
74 newsock->in.mac = 0;
75
76 newsock->out.head = NULL;
77 newsock->out.end = NULL;
78 newsock->out.count = 0;
79 newsock->token = token;
80 newsock->tag = NULL;
81
82 if(!signal_set) {
83 signal(SIGPIPE, sigpipe_handler);
84 signal_set = 1;
85 }
86
87 socklist = newsock;
88
89 return newsock;
90 }
91
92 struct esocket *find_esocket_from_fd(int fd) {
93 struct esocket *p = socklist;
94
95 for(;p;p=p->next)
96 if(p->fd == fd)
97 return p;
98
99 return NULL;
100 }
101
102 void esocket_clean_by_token(unsigned short token) {
103 struct esocket *cp = socklist, *np;
104
105 /* not efficient but boohoo */
106 for(;cp;) {
107 if(cp->token == token) {
108 np = cp->next;
109 esocket_disconnect(cp);
110 cp = np;
111 } else {
112 cp = cp->next;
113 }
114 }
115 }
116
117 void esocket_poll_event(int fd, short events) {
118 struct esocket *active = find_esocket_from_fd(fd);
119
120 if(!active)
121 return;
122
123 switch(active->socket_status) {
124 case ST_LISTENING:
125 active->events.on_accept(active);
126 break;
127 case ST_CONNECTING:
128 if(events & (POLLERR | POLLHUP)) {
129 esocket_disconnect(active);
130 return;
131 }
132 if(events & POLLOUT) { /* connected if connecting! */
133 deregisterhandler(active->fd, 0);
134 registerhandler(active->fd, POLLIN | POLLERR | POLLHUP, esocket_poll_event);
135 active->socket_status = ST_CONNECTED;
136 active->events.on_connect(active);
137 }
138 break;
139 case ST_CONNECTED:
140 case ST_BLANK:
141 if(events & (POLLERR | POLLHUP)) {
142 esocket_disconnect(active);
143 return;
144 }
145 if(events & POLLOUT) { /* flush buffer */
146 if(esocket_raw_write(active, NULL, 0)) {
147 esocket_disconnect(active);
148 return;
149 }
150 return;
151 }
152 if(events & POLLIN) { /* read buffer */
153 int ret = esocket_read(active);
154 if(ret) {
155 if(ret != BUF_ERROR)
156 esocket_disconnect(active);
157 return;
158 }
159 }
160
161 break;
162 }
163 }
164
165 void esocket_disconnect(struct esocket *active) {
166 struct esocket *l = NULL, *p = socklist;
167
168 if(active->events.on_disconnect)
169 active->events.on_disconnect(active);
170
171 for(;p;l=p,p=p->next)
172 if(p == active) {
173 struct esocket_packet *pkt = p->out.head, *npkt;
174 if(l) {
175 l->next = p->next;
176 } else {
177 socklist = p->next;
178 }
179
180
181 deregisterhandler(p->fd, 1);
182
183 for(;pkt;) {
184 npkt = pkt->next;
185 ntfree(pkt->line);
186 ntfree(pkt);
187 pkt = npkt;
188 }
189
190 if(p->in.data)
191 ntfree(p->in.data);
192 if(p->in.cryptobuf)
193 ntfree(p->in.cryptobuf);
194
195 ntfree(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 parse_ascii(struct esocket *sock, int *bytes_to_strip) {
209 struct esocket_in_buffer *buf = &sock->in;
210 char *p;
211 int i, ret;
212
213 for(p=buf->data,i=0;i<buf->size;i++,p++) {
214 if(*p == '\0' || *p == '\n') {
215 *p = '\0';
216
217 *bytes_to_strip = i + 1;
218 ret = sock->events.on_line(sock, buf->data);
219 if(ret)
220 return ret;
221
222 return 0;
223 }
224 }
225
226 *bytes_to_strip = 0;
227 return 0;
228 }
229
230 void seqno_update(hmacsha256 *h, u_int64_t value) {
231 u_int64_t v2 = htonq(value);
232 hmacsha256_update(h, (unsigned char *)&v2, 8);
233 }
234
235 int crypto_newblock(struct esocket *sock, unsigned char *block) {
236 unsigned char *p, *p2;
237 int i;
238 struct esocket_in_buffer *buf = &sock->in;
239
240 if(buf->mac) {
241 unsigned char digest[32];
242 int ret;
243 hmacsha256_final(&sock->clienthmac, digest);
244
245 if(memcmp(block, digest, 16)) /* mac error */
246 return 1;
247
248 hmacsha256_init(&sock->clienthmac, sock->clienthmackey, 32);
249 seqno_update(&sock->clienthmac, sock->clientseqno);
250 sock->clientseqno++;
251
252 ret = sock->events.on_line(sock, (char *)buf->cryptobuf);
253 ntfree(buf->cryptobuf);
254 buf->cryptobuf = NULL;
255 buf->cryptobufsize = 0;
256 buf->mac = 0;
257
258 return ret;
259 }
260
261 hmacsha256_update(&sock->clienthmac, block, 16);
262 p = rijndaelcbc_decrypt(sock->clientcrypto, block);
263 for(p2=p,i=0;i<16;i++,p2++) { /* locate terminator */
264 if(*p2 == '\n' || *p2 == '\0') {
265 *p2 = '\0';
266 buf->mac = 1;
267 }
268 }
269
270 p2 = ntrealloc(buf->cryptobuf, buf->cryptobufsize + 16);
271 if(!p2)
272 Error("nterface", ERR_STOP, "ntrealloc() failed in crypto_newblock (esockets.c)");
273 buf->cryptobuf = p2;
274
275 memcpy(p2 + buf->cryptobufsize, p, 16);
276 buf->cryptobufsize+=16;
277
278 return 0;
279 }
280
281 int parse_crypt(struct esocket *sock, int *bytes_to_strip) {
282 struct esocket_in_buffer *buf = &sock->in;
283 int remaining = buf->size, ret;
284
285 *bytes_to_strip = 0;
286 if(remaining < 16)
287 return 0;
288
289 ret = crypto_newblock(sock, (unsigned char *)buf->data);
290 *bytes_to_strip = 16;
291
292 return ret;
293 }
294
295 int esocket_read(struct esocket *sock) {
296 struct esocket_in_buffer *buf = &sock->in;
297 char bufd[16384], *p;
298 int bytesread, ret, strip;
299
300 bytesread = read(sock->fd, bufd, sizeof(bufd));
301 if(!bytesread || ((bytesread == -1) && (errno != EAGAIN)))
302 return 1;
303
304 if((bytesread == -1) && (errno == EAGAIN))
305 return 0;
306
307 p = ntrealloc(buf->data, buf->size + bytesread);
308 if(!p)
309 Error("nterface", ERR_STOP, "ntrealloc() failed in esocket_read (esockets.c)");
310
311 buf->data = p;
312 memcpy(buf->data + buf->size, bufd, bytesread);
313 buf->size+=bytesread;
314
315 do {
316 if(buf->mode == PARSE_ASCII) {
317 ret = parse_ascii(sock, &strip);
318 } else {
319 ret = parse_crypt(sock, &strip);
320 }
321
322 if(strip > 0) {
323 if(buf->size - strip == 0) {
324 ntfree(buf->data);
325 buf->data = NULL;
326 } else {
327 memmove(buf->data, buf->data + strip, buf->size - strip);
328 p = ntrealloc(buf->data, buf->size - strip);
329 if(!p)
330 Error("nterface", ERR_STOP, "ntrealloc() failed in esocket_read (esockets.c)");
331
332 buf->data = p;
333 }
334
335 buf->size-=strip;
336 }
337
338 if(ret)
339 return ret;
340 } while(strip && buf->data);
341
342 return 0;
343 }
344
345 struct esocket_packet *esocket_new_packet(struct esocket_out_buffer *buf, char *buffer, int bytes) {
346 struct esocket_packet *nw;
347
348 if(buf->count == MAX_OUT_QUEUE_SIZE)
349 return NULL;
350
351 nw = ntmalloc(sizeof(struct esocket_packet));
352 if(!nw)
353 return NULL;
354
355 nw->line = ntmalloc(bytes);
356 if(!nw->line) {
357 ntfree(nw);
358 return NULL;
359 }
360
361 if(!buf->head) {
362 buf->head = nw;
363 } else {
364 buf->end->next = nw;
365 }
366
367 buf->end = nw;
368
369 nw->next = NULL;
370 nw->size = bytes;
371 nw->startpos = 0;
372
373 memcpy(nw->line, buffer, bytes);
374
375 buf->count++;
376
377 return nw;
378 }
379
380 int esocket_raw_write(struct esocket *sock, char *buffer, int bytes) {
381 struct esocket_out_buffer *buf = &sock->out;
382
383 if((bytes > USHRT_MAX) || (!buffer && bytes) || (buffer && !bytes))
384 return 1;
385
386 if(bytes) { /* if we're not flushing */
387 if(buf->count) { /* if we're currently blocked */
388 if(!esocket_new_packet(buf, buffer, bytes))
389 return 1;
390 return 0;
391 } else { /* not currently blocked */
392 int ret;
393 for(;;) {
394 ret = write(sock->fd, buffer, bytes);
395 if(ret == bytes) {
396 return 0;
397 } else if(ret == -1) { /* something bad happened */
398 switch(errno) {
399 case EAGAIN: /* was going to block, store the data in the buffer */
400 if(!esocket_new_packet(buf, buffer, bytes))
401 return 1;
402
403 deregisterhandler(sock->fd, 0); /* now we need to ignore the socket until it's unblocked */
404 registerhandler(sock->fd, POLLOUT | POLLERR | POLLHUP, esocket_poll_event);
405
406 return 0;
407 break;
408 case EPIPE: /* if there's some weird code disconnect the socket */
409 default:
410 return 1;
411 break;
412 }
413 } else { /* less than total was written, try again */
414 buffer+=ret;
415 bytes-=ret;
416 }
417 }
418 }
419 } else if(buf->count) { /* if we're just flushing the buffer */
420 int ret;
421 for(;;) {
422 ret = write(sock->fd, buf->head->line + buf->head->startpos, buf->head->size);
423 if(!ret) {
424 return 0;
425 } else if(ret == buf->head->size) {
426 struct esocket_packet *p = buf->head;
427 buf->head = buf->head->next;
428
429 ntfree(p->line);
430 ntfree(p);
431
432 buf->count--;
433 if(!buf->head) { /* if we've exhausted the buffer */
434 buf->end = NULL;
435
436 if(sock->socket_status == ST_BLANK) {
437 esocket_disconnect(sock);
438 } else {
439 deregisterhandler(sock->fd, 0);
440
441 registerhandler(sock->fd, POLLIN | POLLERR | POLLHUP, esocket_poll_event);
442 }
443 return 0;
444 }
445 } else if(ret == -1) {
446 switch(errno) {
447 case EAGAIN: /* was going to block, wait until we're called again */
448 return 0;
449 break;
450
451 case EPIPE: /* if there's some weird code disconnect the socket */
452 default:
453 return 1;
454 break;
455 }
456 } else {
457 buf->head->startpos+=ret;
458 buf->head->size-=ret;
459 }
460 }
461 return 0;
462 }
463
464 /* something went wrong */
465 return 1;
466 }
467
468 int esocket_write(struct esocket *sock, char *buffer, int bytes) {
469 int ret;
470 if(sock->in.mode == PARSE_ASCII) {
471 ret = esocket_raw_write(sock, buffer, bytes);
472 } else {
473 unsigned char newbuf[MAX_BUFSIZE + USED_MAC_LEN], *p = newbuf, hmacdigest[32];
474 hmacsha256 hmac;
475 int padding = 16 - bytes % 16, i;
476
477 if(padding == 16)
478 padding = 0;
479
480 memcpy(newbuf, buffer, bytes);
481 for(i=0;i<padding;i++)
482 newbuf[bytes + i] = i;
483 bytes+=padding;
484
485 hmacsha256_init(&hmac, sock->serverhmackey, 32);
486 seqno_update(&hmac, sock->serverseqno);
487 sock->serverseqno++;
488
489 i = bytes;
490 while(i) {
491 unsigned char *ct = rijndaelcbc_encrypt(sock->servercrypto, p);
492 hmacsha256_update(&hmac, ct, 16);
493 memcpy(p, ct, 16);
494
495 buffer+=16;
496 p+=16;
497 i-=16;
498 }
499
500 hmacsha256_final(&hmac, hmacdigest);
501 memcpy(p, hmacdigest, USED_MAC_LEN);
502
503 ret = esocket_raw_write(sock, (char *)newbuf, bytes + USED_MAC_LEN);
504 }
505
506 /* AWOOGA!! */
507 if(ret)
508 esocket_disconnect(sock);
509
510 return ret;
511 }
512
513 int esocket_write_line(struct esocket *sock, char *format, ...) {
514 char nbuf[MAX_ASCII_LINE_SIZE];
515 va_list va;
516 int len;
517
518 va_start(va, format);
519
520 vsnprintf(nbuf, sizeof(nbuf) - 1, format, va); /* snprintf() and vsnprintf() will write at most size-1 of the characters, one for \n */
521 va_end(va);
522
523 len = strlen(nbuf);
524
525 nbuf[len++] = '\n';
526 nbuf[len] = '\0';
527
528 return esocket_write(sock, nbuf, len);
529 }
530
531 static void derive_key(unsigned char *out, unsigned char *key, u_int64_t seqno, unsigned char *extra, int extralen) {
532 hmacsha256 hmac;
533
534 hmacsha256_init(&hmac, key, 32);
535 seqno_update(&hmac, seqno);
536 hmacsha256_update(&hmac, extra, extralen);
537
538 hmacsha256_final(&hmac, out);
539 }
540
541 void rekey_esocket(struct esocket *sock, unsigned char *serveriv, unsigned char *clientiv) {
542 unsigned char key[32];
543
544 derive_key(key, sock->serverrawkey, sock->serverkeyno, (unsigned char *)":SKEY", 5);
545 sock->servercrypto = rijndaelcbc_init(key, 256, serveriv, 0);
546 derive_key(sock->serverhmackey, sock->serverrawkey, sock->serverkeyno, (unsigned char *)":SHMAC", 6);
547 sock->serverkeyno++;
548
549 derive_key(key, sock->clientrawkey, sock->clientkeyno, (unsigned char *)":CKEY", 5);
550 sock->clientcrypto = rijndaelcbc_init(key, 256, clientiv, 1);
551 derive_key(sock->clienthmackey, sock->clientrawkey, sock->clientkeyno, (unsigned char *)":CHMAC", 6);
552 sock->clientkeyno++;
553 }
554
555 void switch_buffer_mode(struct esocket *sock, unsigned char *serverkey, unsigned char *serveriv, unsigned char *clientkey, unsigned char *clientiv) {
556 memcpy(sock->serverrawkey, serverkey, 32);
557 memcpy(sock->clientrawkey, clientkey, 32);
558
559 sock->in.mode = PARSE_CRYPTO;
560
561 sock->clientseqno = 0;
562 sock->serverseqno = 0;
563
564 sock->clientkeyno = 0;
565 sock->serverkeyno = 0;
566
567 rekey_esocket(sock, serveriv, clientiv);
568
569 hmacsha256_init(&sock->clienthmac, sock->clienthmackey, 32);
570 seqno_update(&sock->clienthmac, sock->clientseqno);
571
572 sock->clientseqno++;
573 }
574