2 Easy async socket library with HELIX encryption and authentication
3 Copyright (C) 2004-2005 Chris Porter.
8 - added some \n stripping in crypto code
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
25 #include <netinet/in.h>
27 #include "../lib/sha1.h"
28 #include "../core/events.h"
30 struct esocket
*socklist
= NULL
;
32 unsigned short token
= 1;
34 void sigpipe_handler(int moo
) { }
36 unsigned short esocket_token(void) {
40 struct esocket
*esocket_add(int fd
, char socket_type
, struct esocket_events
*events
, unsigned short token
) {
42 struct esocket
*newsock
= (struct esocket
*)malloc(sizeof(struct esocket
));
46 memcpy(&newsock
->events
, events
, sizeof(newsock
->events
));
48 newsock
->socket_type
= socket_type
;
51 case ESOCKET_LISTENING
:
52 /* listening sockets require POLLIN, which is triggered on connect */
54 newsock
->socket_status
= ST_LISTENING
;
56 case ESOCKET_OUTGOING
:
57 /* outgoing connections require POLLOUT (triggered on connect) */
59 newsock
->socket_status
= ST_CONNECTING
;
61 case ESOCKET_OUTGOING_CONNECTED
:
62 /* outgoing connections that have connected (think localhost) require just POLLIN */
64 newsock
->socket_status
= ST_CONNECTED
;
66 case ESOCKET_INCOMING
:
67 /* sockets that have just connected to us require (initially) POLLIN */
69 newsock
->socket_status
= ST_CONNECTED
;
75 registerhandler(fd
, flags
| POLLERR
| POLLHUP
, esocket_poll_event
);
77 newsock
->next
= socklist
;
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
;
90 signal(SIGPIPE
, sigpipe_handler
);
99 struct esocket
*find_esocket_from_fd(int fd
) {
100 struct esocket
*p
= socklist
;
109 void esocket_clean_by_token(unsigned short token
) {
110 struct esocket
*cp
= socklist
, *np
;
112 /* not efficient but boohoo */
114 if(cp
->token
== token
) {
116 esocket_disconnect(cp
);
124 void esocket_poll_event(int fd
, short events
) {
125 struct esocket
*active
= find_esocket_from_fd(fd
);
130 switch(active
->socket_status
) {
132 active
->events
.on_accept(active
);
135 if(events
& (POLLERR
| POLLHUP
)) {
136 esocket_disconnect(active
);
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
);
148 if(events
& (POLLERR
| POLLHUP
)) {
149 esocket_disconnect(active
);
152 if(events
& POLLOUT
) { /* flush buffer */
153 if(esocket_raw_write(active
, NULL
, 0)) {
154 esocket_disconnect(active
);
159 if(events
& POLLIN
) { /* read buffer */
160 int ret
= esocket_read(active
);
163 esocket_disconnect(active
);
172 void esocket_disconnect(struct esocket
*active
) {
173 struct esocket
*l
= NULL
, *p
= socklist
;
175 if(active
->events
.on_disconnect
)
176 active
->events
.on_disconnect(active
);
178 for(;p
;l
=p
,p
=p
->next
)
180 struct esocket_packet
*pkt
= p
->out
.head
, *npkt
;
188 deregisterhandler(p
->fd
, 1);
202 void esocket_disconnect_when_complete(struct esocket
*active
) {
203 if(active
->out
.count
) {
204 active
->socket_status
= ST_BLANK
;
206 esocket_disconnect(active
);
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
));
214 if(!bytesread
|| ((bytesread
== -1) && (errno
!= EAGAIN
)))
217 if((bytesread
== -1) && (errno
== EAGAIN
))
220 buf
->writepos
+=bytesread
;
223 ret
= buf
->on_parse(sock
);
224 if((ret
!= BUF_CONT
) && ret
)
228 if((buf
->curpos
== (buf
->data
+ buf
->buffer_size
)) && (buf
->startpos
== buf
->data
))
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
;
236 buf
->writepos
= buf
->data
;
239 buf
->curpos
= buf
->writepos
;
240 buf
->startpos
= buf
->data
;
245 struct esocket_packet
*esocket_new_packet(struct esocket_out_buffer
*buf
, char *buffer
, int bytes
) {
246 struct esocket_packet
*nw
;
248 if(buf
->count
== MAX_OUT_QUEUE_SIZE
)
251 nw
= malloc(sizeof(struct esocket_packet
));
255 nw
->line
= malloc(bytes
);
273 memcpy(nw
->line
, buffer
, bytes
);
280 int esocket_raw_write(struct esocket
*sock
, char *buffer
, int bytes
) {
281 struct esocket_out_buffer
*buf
= &sock
->out
;
283 if((bytes
> USHRT_MAX
) || (!buffer
&& bytes
) || (buffer
&& !bytes
))
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
))
291 } else { /* not currently blocked */
294 ret
= write(sock
->fd
, buffer
, bytes
);
297 } else if(ret
== -1) { /* something bad happened */
299 case EAGAIN
: /* was going to block, store the data in the buffer */
300 if(!esocket_new_packet(buf
, buffer
, bytes
))
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
);
308 case EPIPE
: /* if there's some weird code disconnect the socket */
313 } else { /* less than total was written, try again */
319 } else if(buf
->count
) { /* if we're just flushing the buffer */
322 ret
= write(sock
->fd
, buf
->head
->line
+ buf
->head
->startpos
, buf
->head
->size
);
325 } else if(ret
== buf
->head
->size
) {
326 struct esocket_packet
*p
= buf
->head
;
327 buf
->head
= buf
->head
->next
;
333 if(!buf
->head
) { /* if we've exhausted the buffer */
336 if(sock
->socket_status
== ST_BLANK
) {
337 esocket_disconnect(sock
);
339 deregisterhandler(sock
->fd
, 0);
341 registerhandler(sock
->fd
, POLLIN
| POLLERR
| POLLHUP
, esocket_poll_event
);
345 } else if(ret
== -1) {
347 case EAGAIN
: /* was going to block, wait until we're called again */
351 case EPIPE
: /* if there's some weird code disconnect the socket */
357 buf
->head
->startpos
+=ret
;
358 buf
->head
->size
-=ret
;
364 /* something went wrong */
368 unsigned char *increase_nonce(unsigned char *nonce
) {
369 u_int64_t
*inonce
= (u_int64_t
*)(nonce
+ 8);
370 *inonce
= htonq(ntohq(*inonce
) + 1);
374 int esocket_write(struct esocket
*sock
, char *buffer
, int bytes
) {
376 if(sock
->in
.on_parse
== buffer_parse_ascii
) {
377 ret
= esocket_raw_write(sock
, buffer
, bytes
);
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
)
384 packetlength
= htons(bytes
+ USED_MAC_LEN
);
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
);
390 memcpy(newbuf
+ sizeof(packet_t
), buffer
, bytes
);
391 memcpy(newbuf
+ sizeof(packet_t
) + bytes
, mac
, USED_MAC_LEN
);
393 ret
= esocket_raw_write(sock
, newbuf
, bytes
+ sizeof(packet_t
) + USED_MAC_LEN
);
398 esocket_disconnect(sock
);
403 int esocket_write_line(struct esocket
*sock
, char *format
, ...) {
404 char nbuf
[MAX_ASCII_LINE_SIZE
];
408 va_start(va
, format
);
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 */
413 vsnprintf(nbuf
, sizeof(nbuf
), format
, va
);
419 if(sock
->in
.on_parse
== buffer_parse_ascii
)
424 return esocket_write(sock
, nbuf
, len
);
427 int buffer_parse_ascii(struct esocket
*sock
) {
428 struct esocket_in_buffer
*buf
= &sock
->in
;
430 for(;buf
->curpos
<buf
->writepos
;buf
->curpos
++) {
431 if((*buf
->curpos
== '\n') || !*buf
->curpos
) {
433 char *newline
= buf
->startpos
;
437 buf
->startpos
= buf
->curpos
+ 1;
439 ret
= sock
->events
.on_line(sock
, newline
);
443 if(buf
->curpos
+ 1 < buf
->writepos
) {
453 int buffer_parse_crypt(struct esocket
*sock
) {
454 struct esocket_in_buffer
*buf
= &sock
->in
;
455 unsigned char mac
[MAC_LEN
];
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
;
463 buf
->packet_length
= ntohs(buf
->packet_length
);
464 if((buf
->packet_length
> buf
->buffer_size
- sizeof(packet_t
)) || (buf
->packet_length
<= 0))
468 buf
->curpos
= buf
->writepos
;
473 if(buf
->packet_length
<= buf
->writepos
- buf
->startpos
) {
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
);
479 if(memcmp(mac
, buf
->startpos
+ buf
->packet_length
- USED_MAC_LEN
, USED_MAC_LEN
))
482 p
= newline
= buf
->startpos
;
483 newline
[buf
->packet_length
- USED_MAC_LEN
] = '\0';
485 for(;*p
;p
++) /* shouldn't happen */
489 buf
->startpos
= buf
->startpos
+ buf
->packet_length
;
490 buf
->packet_length
= 0;
492 ret
= sock
->events
.on_line(sock
, newline
);
499 buf
->curpos
= buf
->writepos
;
503 void switch_buffer_mode(struct esocket
*sock
, char *key
, unsigned char *ournonce
, unsigned char *theirnonce
) {
504 unsigned char ukey
[20];
507 memcpy(sock
->sendnonce
, ournonce
, sizeof(sock
->sendnonce
));
508 memcpy(sock
->recvnonce
, theirnonce
, sizeof(sock
->recvnonce
));
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
);
517 sock
->in
.on_parse
= buffer_parse_crypt
;
518 sock
->in
.buffer_size
= MAX_BINARY_LINE_SIZE
;
520 h_key(&sock
->keysend
, ukey
, sizeof(ukey
));
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
);
528 h_key(&sock
->keyreceive
, ukey
, sizeof(ukey
));