2 Easy async socket library
3 Copyright (C) 2004-2007 Chris Porter.
16 #include <netinet/in.h>
18 #include "../core/events.h"
20 struct esocket
*socklist
= NULL
;
22 unsigned short token
= 1;
24 void sigpipe_handler(int moo
) { }
26 unsigned short esocket_token(void) {
30 struct esocket
*esocket_add(int fd
, char socket_type
, struct esocket_events
*events
, unsigned short token
) {
32 struct esocket
*newsock
= (struct esocket
*)ntmalloc(sizeof(struct esocket
));
36 memcpy(&newsock
->events
, events
, sizeof(newsock
->events
));
38 newsock
->socket_type
= socket_type
;
41 case ESOCKET_LISTENING
:
42 /* listening sockets require POLLIN, which is triggered on connect */
44 newsock
->socket_status
= ST_LISTENING
;
46 case ESOCKET_OUTGOING
:
47 /* outgoing connections require POLLOUT (triggered on connect) */
49 newsock
->socket_status
= ST_CONNECTING
;
51 case ESOCKET_OUTGOING_CONNECTED
:
52 /* outgoing connections that have connected (think localhost) require just POLLIN */
54 newsock
->socket_status
= ST_CONNECTED
;
56 case ESOCKET_INCOMING
:
57 /* sockets that have just connected to us require (initially) POLLIN */
59 newsock
->socket_status
= ST_CONNECTED
;
65 registerhandler(fd
, flags
| POLLERR
| POLLHUP
, esocket_poll_event
);
67 newsock
->next
= socklist
;
69 newsock
->in
.mode
= PARSE_ASCII
;
70 newsock
->in
.data
= NULL
;
72 newsock
->in
.cryptobuf
= NULL
;
73 newsock
->in
.cryptobufsize
= 0;
76 newsock
->out
.head
= NULL
;
77 newsock
->out
.end
= NULL
;
78 newsock
->out
.count
= 0;
79 newsock
->token
= token
;
83 signal(SIGPIPE
, sigpipe_handler
);
92 struct esocket
*find_esocket_from_fd(int fd
) {
93 struct esocket
*p
= socklist
;
102 void esocket_clean_by_token(unsigned short token
) {
103 struct esocket
*cp
= socklist
, *np
;
105 /* not efficient but boohoo */
107 if(cp
->token
== token
) {
109 esocket_disconnect(cp
);
117 void esocket_poll_event(int fd
, short events
) {
118 struct esocket
*active
= find_esocket_from_fd(fd
);
123 switch(active
->socket_status
) {
125 active
->events
.on_accept(active
);
128 if(events
& (POLLERR
| POLLHUP
)) {
129 esocket_disconnect(active
);
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
);
141 if(events
& (POLLERR
| POLLHUP
)) {
142 esocket_disconnect(active
);
145 if(events
& POLLOUT
) { /* flush buffer */
146 if(esocket_raw_write(active
, NULL
, 0)) {
147 esocket_disconnect(active
);
152 if(events
& POLLIN
) { /* read buffer */
153 int ret
= esocket_read(active
);
156 esocket_disconnect(active
);
165 void esocket_disconnect(struct esocket
*active
) {
166 struct esocket
*l
= NULL
, *p
= socklist
;
168 if(active
->events
.on_disconnect
)
169 active
->events
.on_disconnect(active
);
171 for(;p
;l
=p
,p
=p
->next
)
173 struct esocket_packet
*pkt
= p
->out
.head
, *npkt
;
181 deregisterhandler(p
->fd
, 1);
193 ntfree(p
->in
.cryptobuf
);
200 void esocket_disconnect_when_complete(struct esocket
*active
) {
201 if(active
->out
.count
) {
202 active
->socket_status
= ST_BLANK
;
204 esocket_disconnect(active
);
208 int parse_ascii(struct esocket
*sock
, int *bytes_to_strip
) {
209 struct esocket_in_buffer
*buf
= &sock
->in
;
213 for(p
=buf
->data
,i
=0;i
<buf
->size
;i
++,p
++) {
214 if(*p
== '\0' || *p
== '\n') {
217 *bytes_to_strip
= i
+ 1;
218 ret
= sock
->events
.on_line(sock
, buf
->data
);
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);
235 int crypto_newblock(struct esocket
*sock
, unsigned char *block
) {
236 unsigned char *p
, *p2
;
238 struct esocket_in_buffer
*buf
= &sock
->in
;
241 unsigned char digest
[32];
243 hmacsha256_final(&sock
->clienthmac
, digest
);
245 if(memcmp(block
, digest
, 16)) /* mac error */
248 hmacsha256_init(&sock
->clienthmac
, sock
->clienthmackey
, 32);
249 seqno_update(&sock
->clienthmac
, sock
->clientseqno
);
252 ret
= sock
->events
.on_line(sock
, (char *)buf
->cryptobuf
);
253 ntfree(buf
->cryptobuf
);
254 buf
->cryptobuf
= NULL
;
255 buf
->cryptobufsize
= 0;
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') {
270 p2
= ntrealloc(buf
->cryptobuf
, buf
->cryptobufsize
+ 16);
272 Error("nterface", ERR_STOP
, "ntrealloc() failed in crypto_newblock (esockets.c)");
275 memcpy(p2
+ buf
->cryptobufsize
, p
, 16);
276 buf
->cryptobufsize
+=16;
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
;
289 ret
= crypto_newblock(sock
, (unsigned char *)buf
->data
);
290 *bytes_to_strip
= 16;
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
;
300 bytesread
= read(sock
->fd
, bufd
, sizeof(bufd
));
301 if(!bytesread
|| ((bytesread
== -1) && (errno
!= EAGAIN
)))
304 if((bytesread
== -1) && (errno
== EAGAIN
))
307 p
= ntrealloc(buf
->data
, buf
->size
+ bytesread
);
309 Error("nterface", ERR_STOP
, "ntrealloc() failed in esocket_read (esockets.c)");
312 memcpy(buf
->data
+ buf
->size
, bufd
, bytesread
);
313 buf
->size
+=bytesread
;
316 if(buf
->mode
== PARSE_ASCII
) {
317 ret
= parse_ascii(sock
, &strip
);
319 ret
= parse_crypt(sock
, &strip
);
323 if(buf
->size
- strip
== 0) {
327 memmove(buf
->data
, buf
->data
+ strip
, buf
->size
- strip
);
328 p
= ntrealloc(buf
->data
, buf
->size
- strip
);
330 Error("nterface", ERR_STOP
, "ntrealloc() failed in esocket_read (esockets.c)");
340 } while(strip
&& buf
->data
);
345 struct esocket_packet
*esocket_new_packet(struct esocket_out_buffer
*buf
, char *buffer
, int bytes
) {
346 struct esocket_packet
*nw
;
348 if(buf
->count
== MAX_OUT_QUEUE_SIZE
)
351 nw
= ntmalloc(sizeof(struct esocket_packet
));
355 nw
->line
= ntmalloc(bytes
);
373 memcpy(nw
->line
, buffer
, bytes
);
380 int esocket_raw_write(struct esocket
*sock
, char *buffer
, int bytes
) {
381 struct esocket_out_buffer
*buf
= &sock
->out
;
383 if((bytes
> USHRT_MAX
) || (!buffer
&& bytes
) || (buffer
&& !bytes
))
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
))
391 } else { /* not currently blocked */
394 ret
= write(sock
->fd
, buffer
, bytes
);
397 } else if(ret
== -1) { /* something bad happened */
399 case EAGAIN
: /* was going to block, store the data in the buffer */
400 if(!esocket_new_packet(buf
, buffer
, bytes
))
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
);
408 case EPIPE
: /* if there's some weird code disconnect the socket */
413 } else { /* less than total was written, try again */
419 } else if(buf
->count
) { /* if we're just flushing the buffer */
422 ret
= write(sock
->fd
, buf
->head
->line
+ buf
->head
->startpos
, buf
->head
->size
);
425 } else if(ret
== buf
->head
->size
) {
426 struct esocket_packet
*p
= buf
->head
;
427 buf
->head
= buf
->head
->next
;
433 if(!buf
->head
) { /* if we've exhausted the buffer */
436 if(sock
->socket_status
== ST_BLANK
) {
437 esocket_disconnect(sock
);
439 deregisterhandler(sock
->fd
, 0);
441 registerhandler(sock
->fd
, POLLIN
| POLLERR
| POLLHUP
, esocket_poll_event
);
445 } else if(ret
== -1) {
447 case EAGAIN
: /* was going to block, wait until we're called again */
451 case EPIPE
: /* if there's some weird code disconnect the socket */
457 buf
->head
->startpos
+=ret
;
458 buf
->head
->size
-=ret
;
464 /* something went wrong */
468 int esocket_write(struct esocket
*sock
, char *buffer
, int bytes
) {
470 if(sock
->in
.mode
== PARSE_ASCII
) {
471 ret
= esocket_raw_write(sock
, buffer
, bytes
);
473 unsigned char newbuf
[MAX_BUFSIZE
+ USED_MAC_LEN
], *p
= newbuf
, hmacdigest
[32];
475 int padding
= 16 - bytes
% 16, i
;
480 memcpy(newbuf
, buffer
, bytes
);
481 for(i
=0;i
<padding
;i
++)
482 newbuf
[bytes
+ i
] = i
;
485 hmacsha256_init(&hmac
, sock
->serverhmackey
, 32);
486 seqno_update(&hmac
, sock
->serverseqno
);
491 unsigned char *ct
= rijndaelcbc_encrypt(sock
->servercrypto
, p
);
492 hmacsha256_update(&hmac
, ct
, 16);
500 hmacsha256_final(&hmac
, hmacdigest
);
501 memcpy(p
, hmacdigest
, USED_MAC_LEN
);
503 ret
= esocket_raw_write(sock
, (char *)newbuf
, bytes
+ USED_MAC_LEN
);
508 esocket_disconnect(sock
);
513 int esocket_write_line(struct esocket
*sock
, char *format
, ...) {
514 char nbuf
[MAX_ASCII_LINE_SIZE
];
518 va_start(va
, format
);
520 vsnprintf(nbuf
, sizeof(nbuf
) - 1, format
, va
); /* snprintf() and vsnprintf() will write at most size-1 of the characters, one for \n */
528 return esocket_write(sock
, nbuf
, len
);
531 static void derive_key(unsigned char *out
, unsigned char *key
, u_int64_t seqno
, unsigned char *extra
, int extralen
) {
534 hmacsha256_init(&hmac
, key
, 32);
535 seqno_update(&hmac
, seqno
);
536 hmacsha256_update(&hmac
, extra
, extralen
);
538 hmacsha256_final(&hmac
, out
);
541 void rekey_esocket(struct esocket
*sock
, unsigned char *serveriv
, unsigned char *clientiv
) {
542 unsigned char key
[32];
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);
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);
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);
559 sock
->in
.mode
= PARSE_CRYPTO
;
561 sock
->clientseqno
= 0;
562 sock
->serverseqno
= 0;
564 sock
->clientkeyno
= 0;
565 sock
->serverkeyno
= 0;
567 rekey_esocket(sock
, serveriv
, clientiv
);
569 hmacsha256_init(&sock
->clienthmac
, sock
->clienthmackey
, 32);
570 seqno_update(&sock
->clienthmac
, sock
->clientseqno
);