]> jfr.im git - solanum.git/blame - wsockd/wsockd.c
m_alias: fix build with --enable-assert
[solanum.git] / wsockd / wsockd.c
CommitLineData
caebeeca
AC
1/*
2 * wsockd.c: charybdis websockets helper
3 * Copyright (C) 2007 Aaron Sethman <androsyn@ratbox.org>
4 * Copyright (C) 2007 ircd-ratbox development team
5 * Copyright (C) 2016 William Pitcock <nenolod@dereferenced.org>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20 * USA
21 */
22
23#include "stdinc.h"
1160f6c9 24#include "sha1.h"
caebeeca
AC
25
26#define MAXPASSFD 4
27#ifndef READBUF_SIZE
28#define READBUF_SIZE 16384
29#endif
30
1160f6c9
AC
31#define WEBSOCKET_SERVER_KEY "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
32#define WEBSOCKET_ANSWER_STRING_1 "HTTP/1.1 101 Switching Protocols\r\nAccess-Control-Allow-Origin: *\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "
33#define WEBSOCKET_ANSWER_STRING_2 "\r\n\r\n"
34
caebeeca
AC
35static void setup_signals(void);
36static pid_t ppid;
37
38static inline uint32_t
39buf_to_uint32(uint8_t *buf)
40{
41 uint32_t x;
42 memcpy(&x, buf, sizeof(x));
43 return x;
44}
45
46static inline void
47uint32_to_buf(uint8_t *buf, uint32_t x)
48{
49 memcpy(buf, &x, sizeof(x));
50 return;
51}
52
53typedef struct _mod_ctl_buf
54{
55 rb_dlink_node node;
56 uint8_t *buf;
57 size_t buflen;
58 rb_fde_t *F[MAXPASSFD];
59 int nfds;
60} mod_ctl_buf_t;
61
62typedef struct _mod_ctl
63{
64 rb_dlink_node node;
65 int cli_count;
66 rb_fde_t *F;
67 rb_fde_t *F_pipe;
68 rb_dlink_list readq;
69 rb_dlink_list writeq;
70} mod_ctl_t;
71
72static mod_ctl_t *mod_ctl;
73
74typedef struct _conn
75{
76 rb_dlink_node node;
77 mod_ctl_t *ctl;
1c8c63cb 78
1160f6c9
AC
79 rawbuf_head_t *modbuf_out;
80 rawbuf_head_t *modbuf_in;
1c8c63cb
AC
81
82 buf_head_t plainbuf_out;
83 buf_head_t plainbuf_in;
caebeeca
AC
84
85 uint32_t id;
86
87 rb_fde_t *mod_fd;
88 rb_fde_t *plain_fd;
89 uint64_t mod_out;
90 uint64_t mod_in;
91 uint64_t plain_in;
92 uint64_t plain_out;
93 uint8_t flags;
1160f6c9
AC
94
95 char client_key[37]; /* maximum 36 bytes + nul */
caebeeca
AC
96} conn_t;
97
839f2fa2
AC
98#define WEBSOCKET_OPCODE_CONTINUATION_FRAME 0
99#define WEBSOCKET_OPCODE_TEXT_FRAME 1
100#define WEBSOCKET_OPCODE_BINARY_FRAME 2
101#define WEBSOCKET_OPCODE_CLOSE_FRAME 8
102#define WEBSOCKET_OPCODE_PING_FRAME 9
103#define WEBSOCKET_OPCODE_PONG_FRAME 10
104
105#define WEBSOCKET_MASK_LENGTH 4
106
107#define WEBSOCKET_MAX_UNEXTENDED_PAYLOAD_DATA_LENGTH 125
108
109typedef struct {
110 uint8_t opcode_rsv_fin; // opcode: 4, rsv1: 1, rsv2: 1, rsv3: 1, fin: 1
111 uint8_t payload_length_mask; // payload_length: 7, mask: 1
112} ws_frame_hdr_t;
113
114typedef struct {
115 ws_frame_hdr_t header;
116 uint8_t payload_data[WEBSOCKET_MAX_UNEXTENDED_PAYLOAD_DATA_LENGTH];
117} ws_frame_payload_t;
118
119typedef struct {
120 ws_frame_hdr_t header;
839f2fa2
AC
121} ws_frame_t;
122
123typedef struct {
124 ws_frame_hdr_t header;
125 uint16_t payload_length_extended;
839f2fa2
AC
126} ws_frame_ext_t;
127
128typedef struct {
129 ws_frame_hdr_t header;
130 uint64_t payload_length_extended;
839f2fa2
AC
131} ws_frame_ext2_t;
132
e688bcbd
AC
133static inline int
134ws_frame_get_opcode(ws_frame_hdr_t *header)
135{
136 return header->opcode_rsv_fin & 0xF;
137}
138
139static inline void
140ws_frame_set_opcode(ws_frame_hdr_t *header, int opcode)
141{
142 header->opcode_rsv_fin &= ~0xF;
143 header->opcode_rsv_fin |= opcode & 0xF;
144}
145
146static inline int
147ws_frame_get_fin(ws_frame_hdr_t *header)
148{
149 return (header->opcode_rsv_fin >> 7) & 0x1;
150}
151
152static inline void
153ws_frame_set_fin(ws_frame_hdr_t *header, int fin)
154{
155 header->opcode_rsv_fin &= ~(0x1 << 7);
156 header->opcode_rsv_fin |= (fin << 7) & (0x1 << 7);
157}
158
1160f6c9
AC
159static void close_conn(conn_t * conn, int wait_plain, const char *fmt, ...);
160static void conn_mod_read_cb(rb_fde_t *fd, void *data);
161static void conn_plain_read_cb(rb_fde_t *fd, void *data);
c3780ae2 162static void conn_plain_process_recvq(conn_t *conn);
1160f6c9 163
caebeeca
AC
164#define FLAG_CORK 0x01
165#define FLAG_DEAD 0x02
166#define FLAG_WSOCK 0x04
1160f6c9 167#define FLAG_KEYED 0x08
caebeeca
AC
168
169#define IsCork(x) ((x)->flags & FLAG_CORK)
170#define IsDead(x) ((x)->flags & FLAG_DEAD)
171#define IsWS(x) ((x)->flags & FLAG_WSOCK)
1160f6c9 172#define IsKeyed(x) ((x)->flags & FLAG_KEYED)
caebeeca
AC
173
174#define SetCork(x) ((x)->flags |= FLAG_CORK)
175#define SetDead(x) ((x)->flags |= FLAG_DEAD)
176#define SetWS(x) ((x)->flags |= FLAG_WSOCK)
1160f6c9 177#define SetKeyed(x) ((x)->flags |= FLAG_KEYED)
caebeeca
AC
178
179#define ClearCork(x) ((x)->flags &= ~FLAG_CORK)
180#define ClearDead(x) ((x)->flags &= ~FLAG_DEAD)
181#define ClearWS(x) ((x)->flags &= ~FLAG_WSOCK)
1160f6c9 182#define ClearKeyed(x) ((x)->flags &= ~FLAG_KEYED)
caebeeca
AC
183
184#define NO_WAIT 0x0
185#define WAIT_PLAIN 0x1
186
187#define HASH_WALK_SAFE(i, max, ptr, next, table) for(i = 0; i < max; i++) { RB_DLINK_FOREACH_SAFE(ptr, next, table[i].head)
188#define HASH_WALK_END }
189#define CONN_HASH_SIZE 2000
190#define connid_hash(x) (&connid_hash_table[(x % CONN_HASH_SIZE)])
191
1160f6c9
AC
192static const char *remote_closed = "Remote host closed the connection";
193
caebeeca
AC
194static rb_dlink_list connid_hash_table[CONN_HASH_SIZE];
195static rb_dlink_list dead_list;
196
05e0aa9a
AC
197static void conn_plain_read_shutdown_cb(rb_fde_t *fd, void *data);
198
caebeeca
AC
199#ifndef _WIN32
200static void
201dummy_handler(int sig)
202{
203 return;
204}
205#endif
206
207static void
208setup_signals()
209{
210#ifndef _WIN32
211 struct sigaction act;
212
213 act.sa_flags = 0;
214 act.sa_handler = SIG_IGN;
215 sigemptyset(&act.sa_mask);
216 sigaddset(&act.sa_mask, SIGPIPE);
217 sigaddset(&act.sa_mask, SIGALRM);
218#ifdef SIGTRAP
219 sigaddset(&act.sa_mask, SIGTRAP);
220#endif
221
222#ifdef SIGWINCH
223 sigaddset(&act.sa_mask, SIGWINCH);
224 sigaction(SIGWINCH, &act, 0);
225#endif
226 sigaction(SIGPIPE, &act, 0);
227#ifdef SIGTRAP
228 sigaction(SIGTRAP, &act, 0);
229#endif
230
231 act.sa_handler = dummy_handler;
232 sigaction(SIGALRM, &act, 0);
233#endif
234}
235
236static int
237maxconn(void)
238{
239#if defined(RLIMIT_NOFILE) && defined(HAVE_SYS_RESOURCE_H)
240 struct rlimit limit;
241
242 if(!getrlimit(RLIMIT_NOFILE, &limit))
243 {
244 return limit.rlim_cur;
245 }
246#endif /* RLIMIT_FD_MAX */
247 return MAXCONNECTIONS;
248}
249
250static conn_t *
251conn_find_by_id(uint32_t id)
252{
253 rb_dlink_node *ptr;
254 conn_t *conn;
255
256 RB_DLINK_FOREACH(ptr, (connid_hash(id))->head)
257 {
258 conn = ptr->data;
259 if(conn->id == id && !IsDead(conn))
260 return conn;
261 }
262 return NULL;
263}
264
265static void
266conn_add_id_hash(conn_t * conn, uint32_t id)
267{
268 conn->id = id;
269 rb_dlinkAdd(conn, &conn->node, connid_hash(id));
270}
271
272static void
273free_conn(conn_t * conn)
274{
1c8c63cb
AC
275 rb_linebuf_donebuf(&conn->plainbuf_in);
276 rb_linebuf_donebuf(&conn->plainbuf_out);
277
1160f6c9
AC
278 rb_free_rawbuffer(conn->modbuf_in);
279 rb_free_rawbuffer(conn->modbuf_out);
1c8c63cb 280
caebeeca
AC
281 rb_free(conn);
282}
283
284static void
285clean_dead_conns(void *unused)
286{
287 conn_t *conn;
288 rb_dlink_node *ptr, *next;
289
290 RB_DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
291 {
292 conn = ptr->data;
293 free_conn(conn);
294 }
295
296 dead_list.tail = dead_list.head = NULL;
297}
298
c90e5c08
AC
299static void
300conn_plain_write_sendq(rb_fde_t *fd, void *data)
301{
302 conn_t *conn = data;
303 int retlen;
304
305 if(IsDead(conn))
306 return;
307
308 while((retlen = rb_linebuf_flush(fd, &conn->plainbuf_out)) > 0)
309 conn->plain_out += retlen;
310
311 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
312 {
313 close_conn(data, NO_WAIT, NULL);
314 return;
315 }
316
317 if(rb_linebuf_alloclen(&conn->plainbuf_out) > 0)
318 rb_setselect(conn->plain_fd, RB_SELECT_WRITE, conn_plain_write_sendq, conn);
319 else
320 rb_setselect(conn->plain_fd, RB_SELECT_WRITE, NULL, NULL);
321}
322
1160f6c9
AC
323static void
324conn_mod_write_sendq(rb_fde_t *fd, void *data)
325{
326 conn_t *conn = data;
327 const char *err;
328 int retlen;
329
330 if(IsDead(conn))
331 return;
332
333 while((retlen = rb_rawbuf_flush(conn->modbuf_out, fd)) > 0)
334 conn->mod_out += retlen;
335
336 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
337 {
338 if(retlen == 0)
339 close_conn(conn, WAIT_PLAIN, "%s", remote_closed);
340 err = strerror(errno);
341 close_conn(conn, WAIT_PLAIN, "Write error: %s", err);
342 return;
343 }
344
345 if(rb_rawbuf_length(conn->modbuf_out) > 0)
346 rb_setselect(conn->mod_fd, RB_SELECT_WRITE, conn_mod_write_sendq, conn);
347 else
348 rb_setselect(conn->mod_fd, RB_SELECT_WRITE, NULL, NULL);
349
350 if(IsCork(conn) && rb_rawbuf_length(conn->modbuf_out) == 0)
351 {
352 ClearCork(conn);
353 conn_plain_read_cb(conn->plain_fd, conn);
354 }
355}
356
357static void
358conn_mod_write(conn_t * conn, void *data, size_t len)
359{
360 if(IsDead(conn)) /* no point in queueing to a dead man */
361 return;
362 rb_rawbuf_append(conn->modbuf_out, data, len);
363}
364
f297042b 365static void
7428c4e0 366conn_mod_write_short_frame(conn_t * conn, void *data, int len)
f297042b 367{
7428c4e0 368 ws_frame_hdr_t hdr;
e688bcbd 369
5902547a 370 ws_frame_set_opcode(&hdr, WEBSOCKET_OPCODE_TEXT_FRAME);
dab62367 371 ws_frame_set_fin(&hdr, 1);
7428c4e0
AC
372 hdr.payload_length_mask = (len + 2) & 0x7f;
373
374 conn_mod_write(conn, &hdr, sizeof(hdr));
375 conn_mod_write(conn, data, len);
376 conn_mod_write(conn, "\r\n", 2);
377}
378
379static void
380conn_mod_write_long_frame(conn_t * conn, void *data, int len)
381{
382 ws_frame_ext_t hdr;
e688bcbd 383
5902547a 384 ws_frame_set_opcode(&hdr.header, WEBSOCKET_OPCODE_TEXT_FRAME);
dab62367 385 ws_frame_set_fin(&hdr.header, 1);
7428c4e0
AC
386 hdr.header.payload_length_mask = 126;
387 hdr.payload_length_extended = htons(len + 2);
e688bcbd
AC
388
389 conn_mod_write(conn, &hdr, sizeof(hdr));
390 conn_mod_write(conn, data, len);
7428c4e0
AC
391 conn_mod_write(conn, "\r\n", 2);
392}
393
394static void
395conn_mod_write_frame(conn_t *conn, void *data, int len)
396{
397 if(IsDead(conn)) /* no point in queueing to a dead man */
398 return;
399
400 if (len < 123)
401 return conn_mod_write_short_frame(conn, data, len);
402
403 return conn_mod_write_long_frame(conn, data, len);
f297042b
AC
404}
405
1160f6c9
AC
406static void
407conn_plain_write(conn_t * conn, void *data, size_t len)
408{
409 if(IsDead(conn)) /* again no point in queueing to dead men */
410 return;
411 rb_linebuf_put(&conn->plainbuf_out, data, len);
412}
413
05e0aa9a
AC
414static void
415mod_write_ctl(rb_fde_t *F, void *data)
416{
417 mod_ctl_t *ctl = data;
418 mod_ctl_buf_t *ctl_buf;
419 rb_dlink_node *ptr, *next;
420 int retlen, x;
421
422 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
423 {
424 ctl_buf = ptr->data;
425 retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf,
426 ctl_buf->buflen, ppid);
427 if(retlen > 0)
428 {
429 rb_dlinkDelete(ptr, &ctl->writeq);
430 for(x = 0; x < ctl_buf->nfds; x++)
431 rb_close(ctl_buf->F[x]);
432 rb_free(ctl_buf->buf);
433 rb_free(ctl_buf);
434
435 }
436 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
437 exit(0);
438
439 }
440 if(rb_dlink_list_length(&ctl->writeq) > 0)
441 rb_setselect(ctl->F, RB_SELECT_WRITE, mod_write_ctl, ctl);
442}
443
444static void
445mod_cmd_write_queue(mod_ctl_t * ctl, const void *data, size_t len)
446{
447 mod_ctl_buf_t *ctl_buf;
448 ctl_buf = rb_malloc(sizeof(mod_ctl_buf_t));
449 ctl_buf->buf = rb_malloc(len);
450 ctl_buf->buflen = len;
451 memcpy(ctl_buf->buf, data, len);
452 ctl_buf->nfds = 0;
453 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->writeq);
454 mod_write_ctl(ctl->F, ctl);
455}
456
457static void
458close_conn(conn_t * conn, int wait_plain, const char *fmt, ...)
459{
460 va_list ap;
461 char reason[128]; /* must always be under 250 bytes */
462 uint8_t buf[256];
463 int len;
464 if(IsDead(conn))
465 return;
466
c3780ae2
AC
467 if (IsKeyed(conn))
468 conn_plain_process_recvq(conn);
469
1160f6c9 470 rb_rawbuf_flush(conn->modbuf_out, conn->mod_fd);
1c8c63cb 471 rb_linebuf_flush(conn->plain_fd, &conn->plainbuf_out);
05e0aa9a
AC
472 rb_close(conn->mod_fd);
473 SetDead(conn);
474
475 rb_dlinkDelete(&conn->node, connid_hash(conn->id));
476
477 if(!wait_plain || fmt == NULL)
478 {
479 rb_close(conn->plain_fd);
480 rb_dlinkAdd(conn, &conn->node, &dead_list);
481 return;
482 }
483
484 rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_shutdown_cb, conn);
485 rb_setselect(conn->plain_fd, RB_SELECT_WRITE, NULL, NULL);
486
487 va_start(ap, fmt);
488 vsnprintf(reason, sizeof(reason), fmt, ap);
489 va_end(ap);
490
491 buf[0] = 'D';
492 uint32_to_buf(&buf[1], conn->id);
493 rb_strlcpy((char *) &buf[5], reason, sizeof(buf) - 5);
494 len = (strlen(reason) + 1) + 5;
495 mod_cmd_write_queue(conn->ctl, buf, len);
496}
497
caebeeca
AC
498static conn_t *
499make_conn(mod_ctl_t * ctl, rb_fde_t *mod_fd, rb_fde_t *plain_fd)
500{
501 conn_t *conn = rb_malloc(sizeof(conn_t));
502 conn->ctl = ctl;
caebeeca
AC
503 conn->mod_fd = mod_fd;
504 conn->plain_fd = plain_fd;
505 conn->id = -1;
caebeeca
AC
506 rb_set_nb(mod_fd);
507 rb_set_nb(plain_fd);
1c8c63cb
AC
508
509 rb_linebuf_newbuf(&conn->plainbuf_in);
510 rb_linebuf_newbuf(&conn->plainbuf_out);
511
1160f6c9
AC
512 conn->modbuf_in = rb_new_rawbuffer();
513 conn->modbuf_out = rb_new_rawbuffer();
1c8c63cb 514
caebeeca
AC
515 return conn;
516}
517
518static void
519cleanup_bad_message(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
520{
521 int i;
522
523 /* XXX should log this somehow */
524 for (i = 0; i < ctlb->nfds; i++)
525 rb_close(ctlb->F[i]);
526}
527
d3f01ce7
AC
528static void
529ws_frame_unmask(char *msg, int length, uint8_t maskval[WEBSOCKET_MASK_LENGTH])
530{
531 int i;
532
533 for (i = 0; i < length; i++)
534 msg[i] = msg[i] ^ maskval[i % 4];
535}
536
537static void
538conn_mod_process_frame(conn_t *conn, ws_frame_hdr_t *hdr, int masked)
539{
540 char msg[WEBSOCKET_MAX_UNEXTENDED_PAYLOAD_DATA_LENGTH];
541 uint8_t maskval[WEBSOCKET_MASK_LENGTH];
542 int dolen;
543
544 /* if we're masked, we get to collect the masking key for this frame */
545 if (masked)
546 {
547 dolen = rb_rawbuf_get(conn->modbuf_in, maskval, sizeof(maskval));
548 if (!dolen)
549 {
550 close_conn(conn, WAIT_PLAIN, "websocket error: fault unpacking unmask key");
551 return;
552 }
553 }
554
555 dolen = rb_rawbuf_get(conn->modbuf_in, msg, hdr->payload_length_mask);
556 if (!dolen)
557 {
558 close_conn(conn, WAIT_PLAIN, "websocket error: fault unpacking message");
559 return;
560 }
561
562 if (masked)
563 ws_frame_unmask(msg, dolen, maskval);
564
565 rb_linebuf_parse(&conn->plainbuf_out, msg, dolen, 1);
566}
567
568static void
569conn_mod_process_large(conn_t *conn, ws_frame_hdr_t *hdr, int masked)
570{
571 char msg[READBUF_SIZE];
572 uint16_t msglen;
573 uint8_t maskval[WEBSOCKET_MASK_LENGTH];
574 int dolen;
575
576 memset(msg, 0, sizeof msg);
577
578 dolen = rb_rawbuf_get(conn->modbuf_in, &msglen, sizeof(msglen));
579 if (!dolen)
580 {
581 close_conn(conn, WAIT_PLAIN, "websocket error: fault unpacking message size");
582 return;
583 }
584
585 msglen = ntohs(msglen);
586
587 if (masked)
588 {
589 dolen = rb_rawbuf_get(conn->modbuf_in, maskval, sizeof(maskval));
590 if (!dolen)
591 {
592 close_conn(conn, WAIT_PLAIN, "websocket error: fault unpacking unmask key");
593 return;
594 }
595 }
596
597 dolen = rb_rawbuf_get(conn->modbuf_in, msg, msglen);
598 if (!dolen)
599 {
600 close_conn(conn, WAIT_PLAIN, "websocket error: fault unpacking message");
601 return;
602 }
603
604 if (masked)
605 ws_frame_unmask(msg, dolen, maskval);
606
607 rb_linebuf_parse(&conn->plainbuf_out, msg, dolen, 1);
608}
609
610static void
611conn_mod_process_huge(conn_t *conn, ws_frame_hdr_t *hdr, int masked)
612{
613 /* XXX implement me */
614}
615
616static void
617conn_mod_process(conn_t *conn)
618{
619 ws_frame_hdr_t hdr;
620
621 while (1)
622 {
623 int masked;
624 int dolen = rb_rawbuf_get(conn->modbuf_in, &hdr, sizeof(hdr));
625 if (dolen != sizeof(hdr))
626 break;
627
628 masked = (hdr.payload_length_mask >> 7) == 1;
629
630 hdr.payload_length_mask &= 0x7f;
631 switch (hdr.payload_length_mask)
632 {
633 case 126:
634 conn_mod_process_large(conn, &hdr, masked);
635 break;
636 case 127:
637 conn_mod_process_huge(conn, &hdr, masked);
638 break;
639 default:
640 conn_mod_process_frame(conn, &hdr, masked);
641 break;
642 }
643 }
c90e5c08
AC
644
645 conn_plain_write_sendq(conn->plain_fd, conn);
d3f01ce7
AC
646}
647
1c8c63cb
AC
648static void
649conn_mod_handshake_process(conn_t *conn)
650{
651 char inbuf[READBUF_SIZE];
652
e688bcbd
AC
653 memset(inbuf, 0, sizeof inbuf);
654
1c8c63cb
AC
655 while (1)
656 {
1160f6c9
AC
657 char *p = NULL;
658
e688bcbd 659 int dolen = rb_rawbuf_get(conn->modbuf_in, inbuf, sizeof inbuf);
1c8c63cb
AC
660 if (!dolen)
661 break;
1160f6c9 662
f956cb0f 663 if ((p = rb_strcasestr(inbuf, "Sec-WebSocket-Key:")) != NULL)
1160f6c9
AC
664 {
665 char *start, *end;
666
667 start = p + strlen("Sec-WebSocket-Key:");
668
669 for (; start < (inbuf + READBUF_SIZE) && *start; start++)
670 {
671 if (*start != ' ' && *start != '\t')
672 break;
673 }
674
675 for (end = start; end < (inbuf + READBUF_SIZE) && *end; end++)
676 {
677 if (*end == '\r' || *end == '\n')
678 {
679 *end = '\0';
680 break;
681 }
682 }
683
684 rb_strlcpy(conn->client_key, start, sizeof(conn->client_key));
685 SetKeyed(conn);
686 }
687 }
688
689 if (IsKeyed(conn))
690 {
691 SHA1 sha1;
692 uint8_t digest[SHA1_DIGEST_LENGTH];
693 char *resp;
694
695 sha1_init(&sha1);
696 sha1_update(&sha1, (uint8_t *) conn->client_key, strlen(conn->client_key));
697 sha1_update(&sha1, (uint8_t *) WEBSOCKET_SERVER_KEY, strlen(WEBSOCKET_SERVER_KEY));
698 sha1_final(&sha1, digest);
699
700 resp = (char *) rb_base64_encode(digest, SHA1_DIGEST_LENGTH);
701
702 conn_mod_write(conn, WEBSOCKET_ANSWER_STRING_1, strlen(WEBSOCKET_ANSWER_STRING_1));
703 conn_mod_write(conn, resp, strlen(resp));
704 conn_mod_write(conn, WEBSOCKET_ANSWER_STRING_2, strlen(WEBSOCKET_ANSWER_STRING_2));
705
706 rb_free(resp);
1c8c63cb 707 }
1160f6c9
AC
708
709 conn_mod_write_sendq(conn->mod_fd, conn);
1c8c63cb
AC
710}
711
caebeeca 712static void
f297042b 713conn_mod_read_cb(rb_fde_t *fd, void *data)
05e0aa9a
AC
714{
715 char inbuf[READBUF_SIZE];
e688bcbd
AC
716
717 memset(inbuf, 0, sizeof inbuf);
718
05e0aa9a
AC
719 conn_t *conn = data;
720 int length = 0;
721 if (conn == NULL)
722 return;
723
724 if (IsDead(conn))
725 return;
726
727 while (1)
728 {
729 if (IsDead(conn))
730 return;
731
1c8c63cb
AC
732 length = rb_read(fd, inbuf, sizeof(inbuf));
733
734 if (length < 0)
735 {
736 if (rb_ignore_errno(errno))
c90e5c08 737 {
f297042b 738 rb_setselect(fd, RB_SELECT_READ, conn_mod_read_cb, conn);
c90e5c08
AC
739 conn_plain_write_sendq(conn->plain_fd, conn);
740 }
1c8c63cb
AC
741 else
742 close_conn(conn, NO_WAIT, "Connection closed");
743
744 return;
745 }
746 else if (length == 0)
05e0aa9a
AC
747 {
748 close_conn(conn, NO_WAIT, "Connection closed");
749 return;
750 }
1c8c63cb 751
1160f6c9 752 rb_rawbuf_append(conn->modbuf_in, inbuf, length);
f297042b
AC
753 if (!IsKeyed(conn))
754 conn_mod_handshake_process(conn);
d3f01ce7
AC
755 else
756 conn_mod_process(conn);
1c8c63cb
AC
757
758 if (length < sizeof(inbuf))
759 {
f297042b 760 rb_setselect(fd, RB_SELECT_READ, conn_mod_read_cb, conn);
1c8c63cb
AC
761 return;
762 }
05e0aa9a
AC
763 }
764}
765
f297042b
AC
766static bool
767plain_check_cork(conn_t * conn)
768{
769 if(rb_rawbuf_length(conn->modbuf_out) >= 4096)
770 {
771 /* if we have over 4k pending outbound, don't read until
772 * we've cleared the queue */
773 SetCork(conn);
774 rb_setselect(conn->plain_fd, RB_SELECT_READ, NULL, NULL);
775 /* try to write */
e688bcbd
AC
776 if (IsKeyed(conn))
777 conn_mod_write_sendq(conn->mod_fd, conn);
f297042b
AC
778 return true;
779 }
780
781 return false;
782}
783
05e0aa9a 784static void
f297042b 785conn_plain_process_recvq(conn_t *conn)
05e0aa9a 786{
f297042b
AC
787 char inbuf[READBUF_SIZE];
788
e688bcbd
AC
789 memset(inbuf, 0, sizeof inbuf);
790
f297042b
AC
791 while (1)
792 {
e688bcbd 793 int dolen = rb_linebuf_get(&conn->plainbuf_in, inbuf, sizeof inbuf, LINEBUF_COMPLETE, LINEBUF_PARSED);
f297042b
AC
794 if (!dolen)
795 break;
796
797 conn_mod_write_frame(conn, inbuf, dolen);
798 }
e688bcbd
AC
799
800 if (IsKeyed(conn))
801 conn_mod_write_sendq(conn->mod_fd, conn);
05e0aa9a
AC
802}
803
804static void
805conn_plain_read_cb(rb_fde_t *fd, void *data)
806{
f297042b 807 char inbuf[READBUF_SIZE];
e688bcbd
AC
808
809 memset(inbuf, 0, sizeof inbuf);
810
f297042b
AC
811 conn_t *conn = data;
812 int length = 0;
813 if(conn == NULL)
814 return;
815
816 if(IsDead(conn))
817 return;
818
819 if(plain_check_cork(conn))
820 return;
821
822 while(1)
823 {
824 if(IsDead(conn))
825 return;
826
827 length = rb_read(conn->plain_fd, inbuf, sizeof(inbuf));
828
829 if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
830 {
831 close_conn(conn, NO_WAIT, NULL);
832 return;
833 }
834
835 if(length < 0)
836 {
837 rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_cb, conn);
e688bcbd
AC
838 if (IsKeyed(conn))
839 conn_plain_process_recvq(conn);
f297042b
AC
840 return;
841 }
842 conn->plain_in += length;
843
7428c4e0 844 (void) rb_linebuf_parse(&conn->plainbuf_in, inbuf, length, 0);
f297042b
AC
845
846 if(IsDead(conn))
847 return;
848 if(plain_check_cork(conn))
849 return;
850 }
05e0aa9a
AC
851}
852
853static void
854conn_plain_read_shutdown_cb(rb_fde_t *fd, void *data)
855{
856 char inbuf[READBUF_SIZE];
857 conn_t *conn = data;
858 int length = 0;
859
860 if(conn == NULL)
861 return;
862
863 while(1)
864 {
865 length = rb_read(conn->plain_fd, inbuf, sizeof(inbuf));
866
867 if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
868 {
869 rb_close(conn->plain_fd);
870 rb_dlinkAdd(conn, &conn->node, &dead_list);
871 return;
872 }
873
874 if(length < 0)
875 {
876 rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_shutdown_cb, conn);
877 return;
878 }
879 }
880}
881
882static void
883wsock_process(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
caebeeca
AC
884{
885 conn_t *conn;
886 uint32_t id;
887
888 conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
889
890 id = buf_to_uint32(&ctlb->buf[1]);
891 conn_add_id_hash(conn, id);
892 SetWS(conn);
893
894 if(rb_get_type(conn->mod_fd) & RB_FD_UNKNOWN)
895 rb_set_type(conn->mod_fd, RB_FD_SOCKET);
896
897 if(rb_get_type(conn->plain_fd) == RB_FD_UNKNOWN)
898 rb_set_type(conn->plain_fd, RB_FD_SOCKET);
899
f297042b 900 conn_mod_read_cb(conn->mod_fd, conn);
e688bcbd 901 conn_plain_read_cb(conn->plain_fd, conn);
caebeeca
AC
902}
903
904static void
905mod_process_cmd_recv(mod_ctl_t * ctl)
906{
907 rb_dlink_node *ptr, *next;
908 mod_ctl_buf_t *ctl_buf;
909
910 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
911 {
912 ctl_buf = ptr->data;
913
914 switch (*ctl_buf->buf)
915 {
916 case 'A':
917 {
918 if (ctl_buf->nfds != 2 || ctl_buf->buflen != 5)
919 {
920 cleanup_bad_message(ctl, ctl_buf);
921 break;
922 }
05e0aa9a 923 wsock_process(ctl, ctl_buf);
caebeeca
AC
924 break;
925 }
926 default:
927 break;
928 /* Log unknown commands */
929 }
930 rb_dlinkDelete(ptr, &ctl->readq);
931 rb_free(ctl_buf->buf);
932 rb_free(ctl_buf);
933 }
934
935}
936
937static void
938mod_read_ctl(rb_fde_t *F, void *data)
939{
940 mod_ctl_buf_t *ctl_buf;
941 mod_ctl_t *ctl = data;
942 int retlen;
943 int i;
944
945 do
946 {
947 ctl_buf = rb_malloc(sizeof(mod_ctl_buf_t));
948 ctl_buf->buf = rb_malloc(READBUF_SIZE);
949 ctl_buf->buflen = READBUF_SIZE;
950 retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, ctl_buf->buflen, ctl_buf->F,
951 MAXPASSFD);
952 if(retlen <= 0)
953 {
954 rb_free(ctl_buf->buf);
955 rb_free(ctl_buf);
956 }
957 else
958 {
959 ctl_buf->buflen = retlen;
960 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
961 for (i = 0; i < MAXPASSFD && ctl_buf->F[i] != NULL; i++)
962 ;
963 ctl_buf->nfds = i;
964 }
965 }
966 while(retlen > 0);
967
968 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
969 exit(0);
970
971 mod_process_cmd_recv(ctl);
972 rb_setselect(ctl->F, RB_SELECT_READ, mod_read_ctl, ctl);
973}
974
caebeeca
AC
975static void
976read_pipe_ctl(rb_fde_t *F, void *data)
977{
978 char inbuf[READBUF_SIZE];
979 int retlen;
980 while((retlen = rb_read(F, inbuf, sizeof(inbuf))) > 0)
981 {
982 ;; /* we don't do anything with the pipe really, just care if the other process dies.. */
983 }
984 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
985 exit(0);
986 rb_setselect(F, RB_SELECT_READ, read_pipe_ctl, NULL);
987}
988
989int
990main(int argc, char **argv)
991{
992 const char *s_ctlfd, *s_pipe, *s_pid;
993 int ctlfd, pipefd, x, maxfd;
994 maxfd = maxconn();
995
996 s_ctlfd = getenv("CTL_FD");
997 s_pipe = getenv("CTL_PIPE");
998 s_pid = getenv("CTL_PPID");
999
1000 if(s_ctlfd == NULL || s_pipe == NULL || s_pid == NULL)
1001 {
1002 fprintf(stderr,
1003 "This is the charybdis wsockd for internal ircd use.\n");
1004 fprintf(stderr,
1005 "You aren't supposed to run me directly. Exiting.\n");
1006 exit(1);
1007 }
1008
1009 ctlfd = atoi(s_ctlfd);
1010 pipefd = atoi(s_pipe);
1011 ppid = atoi(s_pid);
1012 x = 0;
1013#ifndef _WIN32
1014 for(x = 0; x < maxfd; x++)
1015 {
1016 if(x != ctlfd && x != pipefd && x > 2)
1017 close(x);
1018 }
1019 x = open("/dev/null", O_RDWR);
1020
1021 if(x >= 0)
1022 {
1023 if(ctlfd != 0 && pipefd != 0)
1024 dup2(x, 0);
1025 if(ctlfd != 1 && pipefd != 1)
1026 dup2(x, 1);
1027 if(ctlfd != 2 && pipefd != 2)
1028 dup2(x, 2);
1029 if(x > 2)
1030 close(x);
1031 }
1032#endif
1033 setup_signals();
1034 rb_lib_init(NULL, NULL, NULL, 0, maxfd, 1024, 4096);
1c8c63cb 1035 rb_linebuf_init(4096);
1160f6c9 1036 rb_init_rawbuffers(4096);
caebeeca
AC
1037
1038 mod_ctl = rb_malloc(sizeof(mod_ctl_t));
1039 mod_ctl->F = rb_open(ctlfd, RB_FD_SOCKET, "ircd control socket");
1040 mod_ctl->F_pipe = rb_open(pipefd, RB_FD_PIPE, "ircd pipe");
1041 rb_set_nb(mod_ctl->F);
1042 rb_set_nb(mod_ctl->F_pipe);
1043 rb_event_addish("clean_dead_conns", clean_dead_conns, NULL, 10);
1044 read_pipe_ctl(mod_ctl->F_pipe, NULL);
1045 mod_read_ctl(mod_ctl->F, mod_ctl);
1046
1047 rb_lib_loop(0);
1048 return 0;
1049}