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