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
21 #include <sys/types.h>
26 #include <netinet/in.h>
28 #include "../lib/sha1.h"
29 #include "../core/events.h"
31 struct esocket
*socklist
= NULL
;
33 unsigned short token
= 1;
35 void sigpipe_handler(int moo
) { }
37 unsigned short esocket_token(void) {
41 struct esocket
*esocket_add(int fd
, char socket_type
, struct esocket_events
*events
, unsigned short token
) {
43 struct esocket
*newsock
= (struct esocket
*)malloc(sizeof(struct esocket
));
47 memcpy(&newsock
->events
, events
, sizeof(newsock
->events
));
49 newsock
->socket_type
= socket_type
;
52 case ESOCKET_LISTENING
:
53 /* listening sockets require POLLIN, which is triggered on connect */
55 newsock
->socket_status
= ST_LISTENING
;
57 case ESOCKET_OUTGOING
:
58 /* outgoing connections require POLLOUT (triggered on connect) */
60 newsock
->socket_status
= ST_CONNECTING
;
62 case ESOCKET_OUTGOING_CONNECTED
:
63 /* outgoing connections that have connected (think localhost) require just POLLIN */
65 newsock
->socket_status
= ST_CONNECTED
;
67 case ESOCKET_INCOMING
:
68 /* sockets that have just connected to us require (initially) POLLIN */
70 newsock
->socket_status
= ST_CONNECTED
;
76 registerhandler(fd
, flags
| POLLERR
| POLLHUP
, esocket_poll_event
);
78 newsock
->next
= socklist
;
80 newsock
->in
.on_parse
= buffer_parse_ascii
;
81 newsock
->in
.startpos
= newsock
->in
.curpos
= newsock
->in
.writepos
= newsock
->in
.data
;
82 newsock
->in
.buffer_size
= MAX_ASCII_LINE_SIZE
;
83 newsock
->in
.packet_length
= 0;
84 newsock
->out
.head
= NULL
;
85 newsock
->out
.end
= NULL
;
86 newsock
->out
.count
= 0;
87 newsock
->token
= token
;
91 signal(SIGPIPE
, sigpipe_handler
);
100 struct esocket
*find_esocket_from_fd(int fd
) {
101 struct esocket
*p
= socklist
;
110 void esocket_clean_by_token(unsigned short token
) {
111 struct esocket
*cp
= socklist
, *np
;
113 /* not efficient but boohoo */
115 if(cp
->token
== token
) {
117 esocket_disconnect(cp
);
125 void esocket_poll_event(int fd
, short events
) {
126 struct esocket
*active
= find_esocket_from_fd(fd
);
131 switch(active
->socket_status
) {
133 active
->events
.on_accept(active
);
136 if(events
& (POLLERR
| POLLHUP
)) {
137 esocket_disconnect(active
);
140 if(events
& POLLOUT
) { /* connected if connecting! */
141 deregisterhandler(active
->fd
, 0);
142 registerhandler(active
->fd
, POLLIN
| POLLERR
| POLLHUP
, esocket_poll_event
);
143 active
->socket_status
= ST_CONNECTED
;
144 active
->events
.on_connect(active
);
149 if(events
& (POLLERR
| POLLHUP
)) {
150 esocket_disconnect(active
);
153 if(events
& POLLOUT
) { /* flush buffer */
154 if(esocket_raw_write(active
, NULL
, 0)) {
155 esocket_disconnect(active
);
160 if(events
& POLLIN
) { /* read buffer */
161 int ret
= esocket_read(active
);
164 esocket_disconnect(active
);
173 void esocket_disconnect(struct esocket
*active
) {
174 struct esocket
*l
= NULL
, *p
= socklist
;
176 if(active
->events
.on_disconnect
)
177 active
->events
.on_disconnect(active
);
179 for(;p
;l
=p
,p
=p
->next
)
181 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
);
272 memcpy(nw
->line
, buffer
, bytes
);
279 int esocket_raw_write(struct esocket
*sock
, char *buffer
, int bytes
) {
280 struct esocket_out_buffer
*buf
= &sock
->out
;
282 if((bytes
> USHRT_MAX
) || (!buffer
&& bytes
) || (buffer
&& !bytes
))
285 if(bytes
) { /* if we're not flushing */
286 if(buf
->count
) { /* if we're currently blocked */
287 if(!esocket_new_packet(buf
, buffer
, bytes
))
290 } else { /* not currently blocked */
293 ret
= write(sock
->fd
, buffer
, bytes
);
296 } else if(ret
== -1) { /* something bad happened */
298 case EAGAIN
: /* was going to block, store the data in the buffer */
299 if(!esocket_new_packet(buf
, buffer
, bytes
))
302 deregisterhandler(sock
->fd
, 0); /* now we need to ignore the socket until it's unblocked */
303 registerhandler(sock
->fd
, POLLOUT
| POLLERR
| POLLHUP
, esocket_poll_event
);
307 case EPIPE
: /* if there's some weird code disconnect the socket */
312 } else { /* less than total was written, try again */
318 } else if(buf
->count
) { /* if we're just flushing the buffer */
321 ret
= write(sock
->fd
, buf
->head
->line
, buf
->head
->size
);
324 } else if(ret
== buf
->head
->size
) {
325 struct esocket_packet
*p
= buf
->head
;
326 buf
->head
= buf
->head
->next
;
332 if(!buf
->head
) { /* if we've exhausted the buffer */
335 if(sock
->socket_status
== ST_BLANK
) {
336 esocket_disconnect(sock
);
338 deregisterhandler(sock
->fd
, 0);
340 registerhandler(sock
->fd
, POLLIN
| POLLERR
| POLLHUP
, esocket_poll_event
);
344 } else if(ret
== -1) {
346 case EAGAIN
: /* was going to block, wait until we're called again */
350 case EPIPE
: /* if there's some weird code disconnect the socket */
356 buf
->head
->line
+=ret
;
357 buf
->head
->size
-=ret
;
363 /* something went wrong */
367 unsigned char *increase_nonce(unsigned char *nonce
) {
368 u_int64_t
*inonce
= (u_int64_t
*)(nonce
+ 8);
369 *inonce
= htonq(ntohq(*inonce
) + 1);
373 int esocket_write(struct esocket
*sock
, char *buffer
, int bytes
) {
375 if(sock
->in
.on_parse
== buffer_parse_ascii
) {
376 ret
= esocket_raw_write(sock
, buffer
, bytes
);
378 unsigned char mac
[MAC_LEN
];
379 char newbuf
[MAX_BUFSIZE
+ USED_MAC_LEN
+ sizeof(packet_t
)];
380 packet_t packetlength
;
381 if(bytes
> MAX_BUFSIZE
)
383 packetlength
= htons(bytes
+ USED_MAC_LEN
);
385 memcpy(newbuf
, &packetlength
, sizeof(packet_t
));
386 h_nonce(&sock
->keysend
, increase_nonce(sock
->sendnonce
));
387 h_encrypt(&sock
->keysend
, (unsigned char *)buffer
, bytes
, mac
);
389 memcpy(newbuf
+ sizeof(packet_t
), buffer
, bytes
);
390 memcpy(newbuf
+ sizeof(packet_t
) + bytes
, mac
, USED_MAC_LEN
);
392 ret
= esocket_raw_write(sock
, newbuf
, bytes
+ sizeof(packet_t
) + USED_MAC_LEN
);
397 esocket_disconnect(sock
);
402 int esocket_write_line(struct esocket
*sock
, char *format
, ...) {
403 char nbuf
[MAX_ASCII_LINE_SIZE
];
407 va_start(va
, format
);
409 if(sock
->in
.on_parse
== buffer_parse_ascii
) {
410 vsnprintf(nbuf
, sizeof(nbuf
) - 1, format
, va
); /* snprintf() and vsnprintf() will write at most size-1 of the characters, one for \n */
412 vsnprintf(nbuf
, sizeof(nbuf
), format
, va
);
418 if(sock
->in
.on_parse
== buffer_parse_ascii
)
423 return esocket_write(sock
, nbuf
, len
);
426 int buffer_parse_ascii(struct esocket
*sock
) {
427 struct esocket_in_buffer
*buf
= &sock
->in
;
429 for(;buf
->curpos
<buf
->writepos
;buf
->curpos
++) {
430 if((*buf
->curpos
== '\n') || !*buf
->curpos
) {
432 char *newline
= buf
->startpos
;
436 buf
->startpos
= buf
->curpos
+ 1;
438 ret
= sock
->events
.on_line(sock
, newline
);
442 if(buf
->curpos
+ 1 < buf
->writepos
) {
452 int buffer_parse_crypt(struct esocket
*sock
) {
453 struct esocket_in_buffer
*buf
= &sock
->in
;
454 unsigned char mac
[MAC_LEN
];
456 if(!buf
->packet_length
) {
457 if(buf
->writepos
- buf
->startpos
> sizeof(packet_t
)) {
458 memcpy(&buf
->packet_length
, buf
->startpos
, sizeof(packet_t
));
459 buf
->startpos
= buf
->startpos
+ sizeof(packet_t
);
460 buf
->curpos
= buf
->startpos
;
462 buf
->packet_length
= ntohs(buf
->packet_length
);
463 if((buf
->packet_length
> buf
->buffer_size
- sizeof(packet_t
)) || (buf
->packet_length
<= 0))
467 buf
->curpos
= buf
->writepos
;
472 if(buf
->packet_length
<= buf
->writepos
- buf
->startpos
) {
475 h_nonce(&sock
->keyreceive
, increase_nonce(sock
->recvnonce
));
476 h_decrypt(&sock
->keyreceive
, (unsigned char *)buf
->startpos
, buf
->packet_length
- USED_MAC_LEN
, mac
);
478 if(memcmp(mac
, buf
->startpos
+ buf
->packet_length
- USED_MAC_LEN
, USED_MAC_LEN
))
481 p
= newline
= buf
->startpos
;
482 newline
[buf
->packet_length
- USED_MAC_LEN
] = '\0';
484 for(;*p
;p
++) /* shouldn't happen */
488 buf
->startpos
= buf
->startpos
+ buf
->packet_length
;
489 buf
->packet_length
= 0;
491 ret
= sock
->events
.on_line(sock
, newline
);
498 buf
->curpos
= buf
->writepos
;
502 void switch_buffer_mode(struct esocket
*sock
, char *key
, unsigned char *ournonce
, unsigned char *theirnonce
) {
503 unsigned char ukey
[20];
506 memcpy(sock
->sendnonce
, ournonce
, sizeof(sock
->sendnonce
));
507 memcpy(sock
->recvnonce
, theirnonce
, sizeof(sock
->recvnonce
));
510 SHA1Update(&context
, (unsigned char *)key
, strlen(key
));
511 SHA1Update(&context
, (unsigned char *)" ", 1);
512 /* not sure if this is cryptographically secure! */
513 SHA1Update(&context
, (unsigned char *)ournonce
, NONCE_LEN
);
514 SHA1Final(ukey
, &context
);
516 sock
->in
.on_parse
= buffer_parse_crypt
;
517 sock
->in
.buffer_size
= MAX_BINARY_LINE_SIZE
;
519 h_key(&sock
->keysend
, ukey
, sizeof(ukey
));
522 SHA1Update(&context
, (unsigned char *)key
, strlen(key
));
523 SHA1Update(&context
, (unsigned char *)" ", 1);
524 SHA1Update(&context
, (unsigned char *)theirnonce
, NONCE_LEN
);
525 SHA1Final(ukey
, &context
);
527 h_key(&sock
->keyreceive
, ukey
, sizeof(ukey
));