]> jfr.im git - irc/quakenet/newserv.git/blob - nterface/esockets.c
Merged in nterface NU.
[irc/quakenet/newserv.git] / nterface / esockets.c
1 /*
2 Easy async socket library with HELIX encryption and authentication
3 Copyright (C) 2004-2005 Chris Porter.
4
5 v1.02
6 - added some \n stripping in crypto code
7 v1.01
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
11 */
12
13 #include "esockets.h"
14 #include <stdio.h>
15 #include <sys/poll.h>
16 #include <errno.h>
17 #include <signal.h>
18 #include <stdlib.h>
19 #include <sys/types.h>
20 #include <unistd.h>
21 #include <string.h>
22 #include <limits.h>
23 #include <stdarg.h>
24 #include <netinet/in.h>
25
26 #include "../lib/sha1.h"
27 #include "../core/events.h"
28
29 struct esocket *socklist = NULL;
30 char signal_set = 0;
31 unsigned short token = 1;
32
33 void sigpipe_handler(int moo) { }
34
35 unsigned short esocket_token(void) {
36 return token++;
37 }
38
39 struct esocket *esocket_add(int fd, char socket_type, struct esocket_events *events, unsigned short token) {
40 int flags;
41 struct esocket *newsock = (struct esocket *)malloc(sizeof(struct esocket));
42 if(!newsock)
43 return NULL;
44
45 memcpy(&newsock->events, events, sizeof(newsock->events));
46
47 newsock->socket_type = socket_type;
48
49 switch(socket_type) {
50 case ESOCKET_LISTENING:
51 /* listening sockets require POLLIN, which is triggered on connect */
52 flags = POLLIN;
53 newsock->socket_status = ST_LISTENING;
54 break;
55 case ESOCKET_OUTGOING:
56 /* outgoing connections require POLLOUT (triggered on connect) */
57 flags = POLLOUT;
58 newsock->socket_status = ST_CONNECTING;
59 break;
60 case ESOCKET_OUTGOING_CONNECTED:
61 /* outgoing connections that have connected (think localhost) require just POLLIN */
62 flags = POLLIN;
63 newsock->socket_status = ST_CONNECTED;
64 break;
65 case ESOCKET_INCOMING:
66 /* sockets that have just connected to us require (initially) POLLIN */
67 flags = POLLIN;
68 newsock->socket_status = ST_CONNECTED;
69 break;
70 default:
71 return NULL;
72 }
73
74 registerhandler(fd, flags | POLLERR | POLLHUP, esocket_poll_event);
75 newsock->fd = fd;
76 newsock->next = socklist;
77
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;
86 newsock->tag = NULL;
87
88 if(!signal_set) {
89 signal(SIGPIPE, sigpipe_handler);
90 signal_set = 1;
91 }
92
93 socklist = newsock;
94
95 return newsock;
96 }
97
98 struct esocket *find_esocket_from_fd(int fd) {
99 struct esocket *p = socklist;
100
101 for(;p;p=p->next)
102 if(p->fd == fd)
103 return p;
104
105 return NULL;
106 }
107
108 void esocket_clean_by_token(unsigned short token) {
109 struct esocket *cp = socklist, *np;
110
111 /* not efficient but boohoo */
112 for(;cp;) {
113 if(cp->token == token) {
114 np = cp->next;
115 esocket_disconnect(cp);
116 cp = np;
117 } else {
118 cp = cp->next;
119 }
120 }
121 }
122
123 void esocket_poll_event(int fd, short events) {
124 struct esocket *active = find_esocket_from_fd(fd);
125
126 if(!active)
127 return;
128
129 switch(active->socket_status) {
130 case ST_LISTENING:
131 active->events.on_accept(active);
132 break;
133 case ST_CONNECTING:
134 if(events & (POLLERR | POLLHUP)) {
135 esocket_disconnect(active);
136 return;
137 }
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);
143 }
144 break;
145 case ST_CONNECTED:
146 case ST_BLANK:
147 if(events & (POLLERR | POLLHUP)) {
148 esocket_disconnect(active);
149 return;
150 }
151 if(events & POLLOUT) { /* flush buffer */
152 if(esocket_raw_write(active, NULL, 0)) {
153 esocket_disconnect(active);
154 return;
155 }
156 return;
157 }
158 if(events & POLLIN) { /* read buffer */
159 if(esocket_read(active)) {
160 esocket_disconnect(active);
161 return;
162 }
163 }
164
165 break;
166 }
167 }
168
169 void esocket_disconnect(struct esocket *active) {
170 struct esocket *l = NULL, *p = socklist;
171
172 if(active->events.on_disconnect)
173 active->events.on_disconnect(active);
174
175 for(;p;l=p,p=p->next)
176 if(p == active) {
177 struct esocket_packet *pkt = p->out.head, *npkt;
178 if(l) {
179 l->next = p->next;
180 } else {
181 socklist = p->next;
182 }
183
184 deregisterhandler(p->fd, 1);
185
186 for(;pkt;) {
187 npkt = pkt->next;
188 free(pkt->line);
189 free(pkt);
190 pkt = npkt;
191 }
192
193 free(p);
194 break;
195 }
196 }
197
198 void esocket_disconnect_when_complete(struct esocket *active) {
199 if(active->out.count) {
200 active->socket_status = ST_BLANK;
201 } else {
202 esocket_disconnect(active);
203 }
204 }
205
206 int esocket_read(struct esocket *sock) {
207 struct esocket_in_buffer *buf = &sock->in;
208 int ret, bytesread = read(sock->fd, buf->writepos, buf->buffer_size - (buf->writepos - buf->data));
209
210 if(!bytesread || ((bytesread == -1) && (errno != EAGAIN)))
211 return 1;
212
213 if((bytesread == -1) && (errno == EAGAIN))
214 return 0;
215
216 buf->writepos+=bytesread;
217
218 do {
219 ret = buf->on_parse(sock);
220 if((ret != BUF_CONT) && ret)
221 return ret;
222 } while(ret);
223
224 if((buf->curpos == (buf->data + buf->buffer_size)) && (buf->startpos == buf->data))
225 return 1;
226
227 if(buf->startpos != buf->curpos) {
228 int moveback = buf->writepos - buf->startpos;
229 memmove(buf->data, buf->startpos, moveback);
230 buf->writepos = buf->data + moveback;
231 } else {
232 buf->writepos = buf->data;
233 }
234
235 buf->curpos = buf->writepos;
236 buf->startpos = buf->data;
237
238 return 0;
239 }
240
241 struct esocket_packet *esocket_new_packet(struct esocket_out_buffer *buf, char *buffer, int bytes) {
242 struct esocket_packet *nw;
243
244 if(buf->count == MAX_OUT_QUEUE_SIZE)
245 return NULL;
246
247 nw = malloc(sizeof(struct esocket_packet));
248 if(!nw)
249 return NULL;
250
251 nw->line = malloc(bytes);
252 if(!nw->line) {
253 free(nw);
254 return NULL;
255 }
256
257 if(!buf->head) {
258 buf->head = nw;
259 } else {
260 buf->end->next = nw;
261 }
262
263 buf->end = nw;
264
265 nw->next = NULL;
266 nw->size = bytes;
267
268 memcpy(nw->line, buffer, bytes);
269
270 buf->count++;
271
272 return nw;
273 }
274
275 int esocket_raw_write(struct esocket *sock, char *buffer, int bytes) {
276 struct esocket_out_buffer *buf = &sock->out;
277
278 if((bytes > USHRT_MAX) || (!buffer && bytes) || (buffer && !bytes))
279 return 1;
280
281 if(bytes) { /* if we're not flushing */
282 if(buf->count) { /* if we're currently blocked */
283 if(!esocket_new_packet(buf, buffer, bytes))
284 return 1;
285 return 0;
286 } else { /* not currently blocked */
287 int ret;
288 for(;;) {
289 ret = write(sock->fd, buffer, bytes);
290 if(ret == bytes) {
291 return 0;
292 } else if(ret == -1) { /* something bad happened */
293 switch(errno) {
294 case EAGAIN: /* was going to block, store the data in the buffer */
295 if(!esocket_new_packet(buf, buffer, bytes))
296 return 1;
297
298 deregisterhandler(sock->fd, 0); /* now we need to ignore the socket until it's unblocked */
299 registerhandler(sock->fd, POLLOUT | POLLERR | POLLHUP, esocket_poll_event);
300
301 return 0;
302 break;
303 case EPIPE: /* if there's some weird code disconnect the socket */
304 default:
305 return 1;
306 break;
307 }
308 } else { /* less than total was written, try again */
309 buffer+=ret;
310 bytes-=ret;
311 }
312 }
313 }
314 } else if(buf->count) { /* if we're just flushing the buffer */
315 int ret;
316 for(;;) {
317 ret = write(sock->fd, buf->head->line, buf->head->size);
318 if(!ret) {
319 return 0;
320 } else if(ret == buf->head->size) {
321 struct esocket_packet *p = buf->head;
322 buf->head = buf->head->next;
323
324 free(p->line);
325 free(p);
326
327 buf->count--;
328 if(!buf->head) { /* if we've exhausted the buffer */
329 buf->end = NULL;
330
331 if(sock->socket_status == ST_BLANK) {
332 esocket_disconnect(sock);
333 } else {
334 deregisterhandler(sock->fd, 0);
335
336 registerhandler(sock->fd, POLLIN | POLLERR | POLLHUP, esocket_poll_event);
337 }
338 return 0;
339 }
340 } else if(ret == -1) {
341 switch(errno) {
342 case EAGAIN: /* was going to block, wait until we're called again */
343 return 0;
344 break;
345
346 case EPIPE: /* if there's some weird code disconnect the socket */
347 default:
348 return 1;
349 break;
350 }
351 } else {
352 buf->head->line+=ret;
353 buf->head->size-=ret;
354 }
355 }
356 return 0;
357 }
358
359 /* something went wrong */
360 return 1;
361 }
362
363 int esocket_write(struct esocket *sock, char *buffer, int bytes) {
364 int ret;
365 if(sock->in.on_parse == buffer_parse_ascii) {
366 ret = esocket_raw_write(sock, buffer, bytes);
367 } else {
368 unsigned char mac[MAC_LEN];
369 char newbuf[MAX_BUFSIZE + USED_MAC_LEN + sizeof(packet_t)];
370 packet_t packetlength;
371 if(bytes > MAX_BUFSIZE)
372 return 1;
373 packetlength = htons(bytes + USED_MAC_LEN);
374
375 memcpy(newbuf, &packetlength, sizeof(packet_t));
376 h_encrypt(&sock->keysend, (unsigned char *)buffer, bytes, mac);
377
378 memcpy(newbuf + sizeof(packet_t), buffer, bytes);
379 memcpy(newbuf + sizeof(packet_t) + bytes, mac, USED_MAC_LEN);
380
381 ret = esocket_raw_write(sock, newbuf, bytes + sizeof(packet_t) + USED_MAC_LEN);
382 }
383
384 /* AWOOGA!! */
385 if(ret)
386 esocket_disconnect(sock);
387
388 return ret;
389 }
390
391 int esocket_write_line(struct esocket *sock, char *format, ...) {
392 char nbuf[MAX_ASCII_LINE_SIZE];
393 va_list va;
394 int len;
395
396 va_start(va, format);
397
398 if(sock->in.on_parse == buffer_parse_ascii) {
399 vsnprintf(nbuf, sizeof(nbuf) - 1, format, va); /* snprintf() and vsnprintf() will write at most size-1 of the characters, one for \n */
400 } else {
401 vsnprintf(nbuf, sizeof(nbuf), format, va);
402 }
403 va_end(va);
404
405 len = strlen(nbuf);
406
407 if(sock->in.on_parse == buffer_parse_ascii)
408 nbuf[len++] = '\n';
409
410 nbuf[len] = '\0';
411
412 return esocket_write(sock, nbuf, len);
413 }
414
415 int buffer_parse_ascii(struct esocket *sock) {
416 struct esocket_in_buffer *buf = &sock->in;
417
418 for(;buf->curpos<buf->writepos;buf->curpos++) {
419 if((*buf->curpos == '\n') || !*buf->curpos) {
420 int ret;
421 char *newline = buf->startpos;
422
423 *buf->curpos = '\0';
424
425 buf->startpos = buf->curpos + 1;
426
427 ret = sock->events.on_line(sock, newline);
428 if(ret)
429 return ret;
430
431 if(buf->curpos + 1 < buf->writepos) {
432 buf->curpos++;
433 return BUF_CONT;
434 }
435 }
436 }
437
438 return 0;
439 }
440
441 int buffer_parse_crypt(struct esocket *sock) {
442 struct esocket_in_buffer *buf = &sock->in;
443 unsigned char mac[MAC_LEN];
444
445 if(!buf->packet_length) {
446 if(buf->writepos - buf->startpos > sizeof(packet_t)) {
447 memcpy(&buf->packet_length, buf->startpos, sizeof(packet_t));
448 buf->startpos = buf->startpos + sizeof(packet_t);
449 buf->curpos = buf->startpos;
450
451 buf->packet_length = ntohs(buf->packet_length);
452 if((buf->packet_length > buf->buffer_size - sizeof(packet_t)) || (buf->packet_length <= 0))
453 return 1;
454
455 } else {
456 buf->curpos = buf->writepos;
457 return 0;
458 }
459 }
460
461 if(buf->packet_length <= buf->writepos - buf->startpos) {
462 int ret;
463 char *newline, *p;
464 h_decrypt(&sock->keyreceive, (unsigned char *)buf->startpos, buf->packet_length - USED_MAC_LEN, mac);
465
466 if(memcmp(mac, buf->startpos + buf->packet_length - USED_MAC_LEN, USED_MAC_LEN))
467 return 1;
468
469 p = newline = buf->startpos;
470 newline[buf->packet_length - USED_MAC_LEN] = '\0';
471
472 for(;*p;p++) /* shouldn't happen */
473 if(*p == '\n')
474 *p = '\0';
475
476 buf->startpos = buf->startpos + buf->packet_length;
477 buf->packet_length = 0;
478
479 ret = sock->events.on_line(sock, newline);
480 if(ret)
481 return ret;
482
483 return BUF_CONT;
484 }
485
486 buf->curpos = buf->writepos;
487 return 0;
488 }
489
490 void switch_buffer_mode(struct esocket *sock, char *key, unsigned char *ournonce, unsigned char *theirnonce) {
491 unsigned char ukey[20];
492 SHA1_CTX context;
493
494 SHA1Init(&context);
495 SHA1Update(&context, (unsigned char *)key, strlen(key));
496 SHA1Update(&context, (unsigned char *)" ", 1);
497 /* not sure if this is cryptographically secure! */
498 SHA1Update(&context, (unsigned char *)ournonce, NONCE_LEN);
499 SHA1Final(ukey, &context);
500
501 sock->in.on_parse = buffer_parse_crypt;
502 sock->in.buffer_size = MAX_BINARY_LINE_SIZE;
503
504
505 h_key(&sock->keysend, ukey, sizeof(ukey));
506 h_nonce(&sock->keysend, ournonce);
507
508 SHA1Init(&context);
509 SHA1Update(&context, (unsigned char *)key, strlen(key));
510 SHA1Update(&context, (unsigned char *)" ", 1);
511 SHA1Update(&context, (unsigned char *)theirnonce, NONCE_LEN);
512 SHA1Final(ukey, &context);
513
514 h_key(&sock->keyreceive, ukey, sizeof(ukey));
515 h_nonce(&sock->keyreceive, theirnonce);
516 }
517