]>
Commit | Line | Data |
---|---|---|
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 |