2 Easy async socket library with HELIX encryption and authentication
3 Copyright (C) 2004-2005 Chris Porter.
6 - added some \n stripping in crypto code
8 - noticed small problem in _read, if ret == -1 && errno != EAGAIN then it would mess up
9 - now disconnects on write buffer filling up
10 - increased buffer size and maximum queue size
19 #include <sys/types.h>
24 #include <netinet/in.h>
26 #include "../lib/sha1.h"
27 #include "../core/events.h"
29 struct esocket
*socklist
= NULL
;
31 unsigned short token
= 1;
33 void sigpipe_handler(int moo
) { }
35 unsigned short esocket_token(void) {
39 struct esocket
*esocket_add(int fd
, char socket_type
, struct esocket_events
*events
, unsigned short token
) {
41 struct esocket
*newsock
= (struct esocket
*)malloc(sizeof(struct esocket
));
45 memcpy(&newsock
->events
, events
, sizeof(newsock
->events
));
47 newsock
->socket_type
= socket_type
;
50 case ESOCKET_LISTENING
:
51 /* listening sockets require POLLIN, which is triggered on connect */
53 newsock
->socket_status
= ST_LISTENING
;
55 case ESOCKET_OUTGOING
:
56 /* outgoing connections require POLLOUT (triggered on connect) */
58 newsock
->socket_status
= ST_CONNECTING
;
60 case ESOCKET_OUTGOING_CONNECTED
:
61 /* outgoing connections that have connected (think localhost) require just POLLIN */
63 newsock
->socket_status
= ST_CONNECTED
;
65 case ESOCKET_INCOMING
:
66 /* sockets that have just connected to us require (initially) POLLIN */
68 newsock
->socket_status
= ST_CONNECTED
;
74 registerhandler(fd
, flags
| POLLERR
| POLLHUP
, esocket_poll_event
);
76 newsock
->next
= socklist
;
78 newsock
->in
.on_parse
= buffer_parse_ascii
;
79 newsock
->in
.startpos
= newsock
->in
.curpos
= newsock
->in
.writepos
= newsock
->in
.data
;
80 newsock
->in
.buffer_size
= MAX_ASCII_LINE_SIZE
;
81 newsock
->in
.packet_length
= 0;
82 newsock
->out
.head
= NULL
;
83 newsock
->out
.end
= NULL
;
84 newsock
->out
.count
= 0;
85 newsock
->token
= token
;
89 signal(SIGPIPE
, sigpipe_handler
);
98 struct esocket
*find_esocket_from_fd(int fd
) {
99 struct esocket
*p
= socklist
;
108 void esocket_clean_by_token(unsigned short token
) {
109 struct esocket
*cp
= socklist
, *np
;
111 /* not efficient but boohoo */
113 if(cp
->token
== token
) {
115 esocket_disconnect(cp
);
123 void esocket_poll_event(int fd
, short events
) {
124 struct esocket
*active
= find_esocket_from_fd(fd
);
129 switch(active
->socket_status
) {
131 active
->events
.on_accept(active
);
134 if(events
& (POLLERR
| POLLHUP
)) {
135 esocket_disconnect(active
);
138 if(events
& POLLOUT
) { /* connected if connecting! */
139 deregisterhandler(active
->fd
, 0);
140 registerhandler(active
->fd
, POLLIN
| POLLERR
| POLLHUP
, esocket_poll_event
);
141 active
->socket_status
= ST_CONNECTED
;
142 active
->events
.on_connect(active
);
147 if(events
& (POLLERR
| POLLHUP
)) {
148 esocket_disconnect(active
);
151 if(events
& POLLOUT
) { /* flush buffer */
152 if(esocket_raw_write(active
, NULL
, 0)) {
153 esocket_disconnect(active
);
158 if(events
& POLLIN
) { /* read buffer */
159 int ret
= esocket_read(active
);
162 esocket_disconnect(active
);
171 void esocket_disconnect(struct esocket
*active
) {
172 struct esocket
*l
= NULL
, *p
= socklist
;
174 if(active
->events
.on_disconnect
)
175 active
->events
.on_disconnect(active
);
177 for(;p
;l
=p
,p
=p
->next
)
179 struct esocket_packet
*pkt
= p
->out
.head
, *npkt
;
186 deregisterhandler(p
->fd
, 1);
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 esocket_read(struct esocket
*sock
) {
209 struct esocket_in_buffer
*buf
= &sock
->in
;
210 int ret
, bytesread
= read(sock
->fd
, buf
->writepos
, buf
->buffer_size
- (buf
->writepos
- buf
->data
));
212 if(!bytesread
|| ((bytesread
== -1) && (errno
!= EAGAIN
)))
215 if((bytesread
== -1) && (errno
== EAGAIN
))
218 buf
->writepos
+=bytesread
;
221 ret
= buf
->on_parse(sock
);
222 if((ret
!= BUF_CONT
) && ret
)
226 if((buf
->curpos
== (buf
->data
+ buf
->buffer_size
)) && (buf
->startpos
== buf
->data
))
229 if(buf
->startpos
!= buf
->curpos
) {
230 int moveback
= buf
->writepos
- buf
->startpos
;
231 memmove(buf
->data
, buf
->startpos
, moveback
);
232 buf
->writepos
= buf
->data
+ moveback
;
234 buf
->writepos
= buf
->data
;
237 buf
->curpos
= buf
->writepos
;
238 buf
->startpos
= buf
->data
;
243 struct esocket_packet
*esocket_new_packet(struct esocket_out_buffer
*buf
, char *buffer
, int bytes
) {
244 struct esocket_packet
*nw
;
246 if(buf
->count
== MAX_OUT_QUEUE_SIZE
)
249 nw
= malloc(sizeof(struct esocket_packet
));
253 nw
->line
= malloc(bytes
);
270 memcpy(nw
->line
, buffer
, bytes
);
277 int esocket_raw_write(struct esocket
*sock
, char *buffer
, int bytes
) {
278 struct esocket_out_buffer
*buf
= &sock
->out
;
280 if((bytes
> USHRT_MAX
) || (!buffer
&& bytes
) || (buffer
&& !bytes
))
283 if(bytes
) { /* if we're not flushing */
284 if(buf
->count
) { /* if we're currently blocked */
285 if(!esocket_new_packet(buf
, buffer
, bytes
))
288 } else { /* not currently blocked */
291 ret
= write(sock
->fd
, buffer
, bytes
);
294 } else if(ret
== -1) { /* something bad happened */
296 case EAGAIN
: /* was going to block, store the data in the buffer */
297 if(!esocket_new_packet(buf
, buffer
, bytes
))
300 deregisterhandler(sock
->fd
, 0); /* now we need to ignore the socket until it's unblocked */
301 registerhandler(sock
->fd
, POLLOUT
| POLLERR
| POLLHUP
, esocket_poll_event
);
305 case EPIPE
: /* if there's some weird code disconnect the socket */
310 } else { /* less than total was written, try again */
316 } else if(buf
->count
) { /* if we're just flushing the buffer */
319 ret
= write(sock
->fd
, buf
->head
->line
, buf
->head
->size
);
322 } else if(ret
== buf
->head
->size
) {
323 struct esocket_packet
*p
= buf
->head
;
324 buf
->head
= buf
->head
->next
;
330 if(!buf
->head
) { /* if we've exhausted the buffer */
333 if(sock
->socket_status
== ST_BLANK
) {
334 esocket_disconnect(sock
);
336 deregisterhandler(sock
->fd
, 0);
338 registerhandler(sock
->fd
, POLLIN
| POLLERR
| POLLHUP
, esocket_poll_event
);
342 } else if(ret
== -1) {
344 case EAGAIN
: /* was going to block, wait until we're called again */
348 case EPIPE
: /* if there's some weird code disconnect the socket */
354 buf
->head
->line
+=ret
;
355 buf
->head
->size
-=ret
;
361 /* something went wrong */
365 int esocket_write(struct esocket
*sock
, char *buffer
, int bytes
) {
367 if(sock
->in
.on_parse
== buffer_parse_ascii
) {
368 ret
= esocket_raw_write(sock
, buffer
, bytes
);
370 unsigned char mac
[MAC_LEN
];
371 char newbuf
[MAX_BUFSIZE
+ USED_MAC_LEN
+ sizeof(packet_t
)];
372 packet_t packetlength
;
373 if(bytes
> MAX_BUFSIZE
)
375 packetlength
= htons(bytes
+ USED_MAC_LEN
);
377 memcpy(newbuf
, &packetlength
, sizeof(packet_t
));
378 h_encrypt(&sock
->keysend
, (unsigned char *)buffer
, bytes
, mac
);
380 memcpy(newbuf
+ sizeof(packet_t
), buffer
, bytes
);
381 memcpy(newbuf
+ sizeof(packet_t
) + bytes
, mac
, USED_MAC_LEN
);
383 ret
= esocket_raw_write(sock
, newbuf
, bytes
+ sizeof(packet_t
) + USED_MAC_LEN
);
388 esocket_disconnect(sock
);
393 int esocket_write_line(struct esocket
*sock
, char *format
, ...) {
394 char nbuf
[MAX_ASCII_LINE_SIZE
];
398 va_start(va
, format
);
400 if(sock
->in
.on_parse
== buffer_parse_ascii
) {
401 vsnprintf(nbuf
, sizeof(nbuf
) - 1, format
, va
); /* snprintf() and vsnprintf() will write at most size-1 of the characters, one for \n */
403 vsnprintf(nbuf
, sizeof(nbuf
), format
, va
);
409 if(sock
->in
.on_parse
== buffer_parse_ascii
)
414 return esocket_write(sock
, nbuf
, len
);
417 int buffer_parse_ascii(struct esocket
*sock
) {
418 struct esocket_in_buffer
*buf
= &sock
->in
;
420 for(;buf
->curpos
<buf
->writepos
;buf
->curpos
++) {
421 if((*buf
->curpos
== '\n') || !*buf
->curpos
) {
423 char *newline
= buf
->startpos
;
427 buf
->startpos
= buf
->curpos
+ 1;
429 ret
= sock
->events
.on_line(sock
, newline
);
433 if(buf
->curpos
+ 1 < buf
->writepos
) {
443 int buffer_parse_crypt(struct esocket
*sock
) {
444 struct esocket_in_buffer
*buf
= &sock
->in
;
445 unsigned char mac
[MAC_LEN
];
447 if(!buf
->packet_length
) {
448 if(buf
->writepos
- buf
->startpos
> sizeof(packet_t
)) {
449 memcpy(&buf
->packet_length
, buf
->startpos
, sizeof(packet_t
));
450 buf
->startpos
= buf
->startpos
+ sizeof(packet_t
);
451 buf
->curpos
= buf
->startpos
;
453 buf
->packet_length
= ntohs(buf
->packet_length
);
454 if((buf
->packet_length
> buf
->buffer_size
- sizeof(packet_t
)) || (buf
->packet_length
<= 0))
458 buf
->curpos
= buf
->writepos
;
463 if(buf
->packet_length
<= buf
->writepos
- buf
->startpos
) {
466 h_decrypt(&sock
->keyreceive
, (unsigned char *)buf
->startpos
, buf
->packet_length
- USED_MAC_LEN
, mac
);
468 if(memcmp(mac
, buf
->startpos
+ buf
->packet_length
- USED_MAC_LEN
, USED_MAC_LEN
))
471 p
= newline
= buf
->startpos
;
472 newline
[buf
->packet_length
- USED_MAC_LEN
] = '\0';
474 for(;*p
;p
++) /* shouldn't happen */
478 buf
->startpos
= buf
->startpos
+ buf
->packet_length
;
479 buf
->packet_length
= 0;
481 ret
= sock
->events
.on_line(sock
, newline
);
488 buf
->curpos
= buf
->writepos
;
492 void switch_buffer_mode(struct esocket
*sock
, char *key
, unsigned char *ournonce
, unsigned char *theirnonce
) {
493 unsigned char ukey
[20];
497 SHA1Update(&context
, (unsigned char *)key
, strlen(key
));
498 SHA1Update(&context
, (unsigned char *)" ", 1);
499 /* not sure if this is cryptographically secure! */
500 SHA1Update(&context
, (unsigned char *)ournonce
, NONCE_LEN
);
501 SHA1Final(ukey
, &context
);
503 sock
->in
.on_parse
= buffer_parse_crypt
;
504 sock
->in
.buffer_size
= MAX_BINARY_LINE_SIZE
;
507 h_key(&sock
->keysend
, ukey
, sizeof(ukey
));
508 h_nonce(&sock
->keysend
, ournonce
);
511 SHA1Update(&context
, (unsigned char *)key
, strlen(key
));
512 SHA1Update(&context
, (unsigned char *)" ", 1);
513 SHA1Update(&context
, (unsigned char *)theirnonce
, NONCE_LEN
);
514 SHA1Final(ukey
, &context
);
516 h_key(&sock
->keyreceive
, ukey
, sizeof(ukey
));
517 h_nonce(&sock
->keyreceive
, theirnonce
);