]> jfr.im git - solanum.git/blame - wsockd/wsockd.c
Merge branch 'master' of github.com:charybdis-ircd/charybdis
[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
37052804
AC
159#ifdef _WIN32
160char *
2d89c9ff 161strcasestr(const char *s, const char *find)
37052804
AC
162{
163 char c, sc;
164 size_t len;
165
166 if ((c = *find++) != 0) {
167 c = tolower((unsigned char)c);
168 len = strlen(find);
169 do {
170 do {
171 if ((sc = *s++) == 0)
172 return (NULL);
173 } while ((char)tolower((unsigned char)sc) != c);
174 } while (strnicmp(s, find, len) != 0);
175 s--;
176 }
177 return ((char *)s);
178}
179#endif
180
1160f6c9
AC
181static void close_conn(conn_t * conn, int wait_plain, const char *fmt, ...);
182static void conn_mod_read_cb(rb_fde_t *fd, void *data);
183static void conn_plain_read_cb(rb_fde_t *fd, void *data);
184
caebeeca
AC
185#define FLAG_CORK 0x01
186#define FLAG_DEAD 0x02
187#define FLAG_WSOCK 0x04
1160f6c9 188#define FLAG_KEYED 0x08
caebeeca
AC
189
190#define IsCork(x) ((x)->flags & FLAG_CORK)
191#define IsDead(x) ((x)->flags & FLAG_DEAD)
192#define IsWS(x) ((x)->flags & FLAG_WSOCK)
1160f6c9 193#define IsKeyed(x) ((x)->flags & FLAG_KEYED)
caebeeca
AC
194
195#define SetCork(x) ((x)->flags |= FLAG_CORK)
196#define SetDead(x) ((x)->flags |= FLAG_DEAD)
197#define SetWS(x) ((x)->flags |= FLAG_WSOCK)
1160f6c9 198#define SetKeyed(x) ((x)->flags |= FLAG_KEYED)
caebeeca
AC
199
200#define ClearCork(x) ((x)->flags &= ~FLAG_CORK)
201#define ClearDead(x) ((x)->flags &= ~FLAG_DEAD)
202#define ClearWS(x) ((x)->flags &= ~FLAG_WSOCK)
1160f6c9 203#define ClearKeyed(x) ((x)->flags &= ~FLAG_KEYED)
caebeeca
AC
204
205#define NO_WAIT 0x0
206#define WAIT_PLAIN 0x1
207
208#define HASH_WALK_SAFE(i, max, ptr, next, table) for(i = 0; i < max; i++) { RB_DLINK_FOREACH_SAFE(ptr, next, table[i].head)
209#define HASH_WALK_END }
210#define CONN_HASH_SIZE 2000
211#define connid_hash(x) (&connid_hash_table[(x % CONN_HASH_SIZE)])
212
1160f6c9
AC
213static const char *remote_closed = "Remote host closed the connection";
214
caebeeca
AC
215static rb_dlink_list connid_hash_table[CONN_HASH_SIZE];
216static rb_dlink_list dead_list;
217
05e0aa9a
AC
218static void conn_plain_read_shutdown_cb(rb_fde_t *fd, void *data);
219
caebeeca
AC
220#ifndef _WIN32
221static void
222dummy_handler(int sig)
223{
224 return;
225}
226#endif
227
228static void
229setup_signals()
230{
231#ifndef _WIN32
232 struct sigaction act;
233
234 act.sa_flags = 0;
235 act.sa_handler = SIG_IGN;
236 sigemptyset(&act.sa_mask);
237 sigaddset(&act.sa_mask, SIGPIPE);
238 sigaddset(&act.sa_mask, SIGALRM);
239#ifdef SIGTRAP
240 sigaddset(&act.sa_mask, SIGTRAP);
241#endif
242
243#ifdef SIGWINCH
244 sigaddset(&act.sa_mask, SIGWINCH);
245 sigaction(SIGWINCH, &act, 0);
246#endif
247 sigaction(SIGPIPE, &act, 0);
248#ifdef SIGTRAP
249 sigaction(SIGTRAP, &act, 0);
250#endif
251
252 act.sa_handler = dummy_handler;
253 sigaction(SIGALRM, &act, 0);
254#endif
255}
256
257static int
258maxconn(void)
259{
260#if defined(RLIMIT_NOFILE) && defined(HAVE_SYS_RESOURCE_H)
261 struct rlimit limit;
262
263 if(!getrlimit(RLIMIT_NOFILE, &limit))
264 {
265 return limit.rlim_cur;
266 }
267#endif /* RLIMIT_FD_MAX */
268 return MAXCONNECTIONS;
269}
270
271static conn_t *
272conn_find_by_id(uint32_t id)
273{
274 rb_dlink_node *ptr;
275 conn_t *conn;
276
277 RB_DLINK_FOREACH(ptr, (connid_hash(id))->head)
278 {
279 conn = ptr->data;
280 if(conn->id == id && !IsDead(conn))
281 return conn;
282 }
283 return NULL;
284}
285
286static void
287conn_add_id_hash(conn_t * conn, uint32_t id)
288{
289 conn->id = id;
290 rb_dlinkAdd(conn, &conn->node, connid_hash(id));
291}
292
293static void
294free_conn(conn_t * conn)
295{
1c8c63cb
AC
296 rb_linebuf_donebuf(&conn->plainbuf_in);
297 rb_linebuf_donebuf(&conn->plainbuf_out);
298
1160f6c9
AC
299 rb_free_rawbuffer(conn->modbuf_in);
300 rb_free_rawbuffer(conn->modbuf_out);
1c8c63cb 301
caebeeca
AC
302 rb_free(conn);
303}
304
305static void
306clean_dead_conns(void *unused)
307{
308 conn_t *conn;
309 rb_dlink_node *ptr, *next;
310
311 RB_DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
312 {
313 conn = ptr->data;
314 free_conn(conn);
315 }
316
317 dead_list.tail = dead_list.head = NULL;
318}
319
1160f6c9
AC
320static void
321conn_mod_write_sendq(rb_fde_t *fd, void *data)
322{
323 conn_t *conn = data;
324 const char *err;
325 int retlen;
326
327 if(IsDead(conn))
328 return;
329
330 while((retlen = rb_rawbuf_flush(conn->modbuf_out, fd)) > 0)
331 conn->mod_out += retlen;
332
333 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
334 {
335 if(retlen == 0)
336 close_conn(conn, WAIT_PLAIN, "%s", remote_closed);
337 err = strerror(errno);
338 close_conn(conn, WAIT_PLAIN, "Write error: %s", err);
339 return;
340 }
341
342 if(rb_rawbuf_length(conn->modbuf_out) > 0)
343 rb_setselect(conn->mod_fd, RB_SELECT_WRITE, conn_mod_write_sendq, conn);
344 else
345 rb_setselect(conn->mod_fd, RB_SELECT_WRITE, NULL, NULL);
346
347 if(IsCork(conn) && rb_rawbuf_length(conn->modbuf_out) == 0)
348 {
349 ClearCork(conn);
350 conn_plain_read_cb(conn->plain_fd, conn);
351 }
352}
353
354static void
355conn_mod_write(conn_t * conn, void *data, size_t len)
356{
357 if(IsDead(conn)) /* no point in queueing to a dead man */
358 return;
359 rb_rawbuf_append(conn->modbuf_out, data, len);
360}
361
f297042b
AC
362static void
363conn_mod_write_frame(conn_t * conn, void *data, size_t len)
364{
e688bcbd
AC
365 ws_frame_ext_t hdr;
366
f297042b
AC
367 if(IsDead(conn)) /* no point in queueing to a dead man */
368 return;
e688bcbd
AC
369
370 ws_frame_set_opcode(&hdr.header, WEBSOCKET_OPCODE_BINARY_FRAME);
371 hdr.header.payload_length_mask = 127;
372 hdr.payload_length_extended = htons(len + 7);
373
374 conn_mod_write(conn, &hdr, sizeof(hdr));
375 conn_mod_write(conn, data, len);
376 conn_mod_write(conn, "\r\n\0", 3);
f297042b
AC
377}
378
1160f6c9
AC
379static void
380conn_plain_write(conn_t * conn, void *data, size_t len)
381{
382 if(IsDead(conn)) /* again no point in queueing to dead men */
383 return;
384 rb_linebuf_put(&conn->plainbuf_out, data, len);
385}
386
05e0aa9a
AC
387static void
388mod_write_ctl(rb_fde_t *F, void *data)
389{
390 mod_ctl_t *ctl = data;
391 mod_ctl_buf_t *ctl_buf;
392 rb_dlink_node *ptr, *next;
393 int retlen, x;
394
395 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
396 {
397 ctl_buf = ptr->data;
398 retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf,
399 ctl_buf->buflen, ppid);
400 if(retlen > 0)
401 {
402 rb_dlinkDelete(ptr, &ctl->writeq);
403 for(x = 0; x < ctl_buf->nfds; x++)
404 rb_close(ctl_buf->F[x]);
405 rb_free(ctl_buf->buf);
406 rb_free(ctl_buf);
407
408 }
409 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
410 exit(0);
411
412 }
413 if(rb_dlink_list_length(&ctl->writeq) > 0)
414 rb_setselect(ctl->F, RB_SELECT_WRITE, mod_write_ctl, ctl);
415}
416
417static void
418mod_cmd_write_queue(mod_ctl_t * ctl, const void *data, size_t len)
419{
420 mod_ctl_buf_t *ctl_buf;
421 ctl_buf = rb_malloc(sizeof(mod_ctl_buf_t));
422 ctl_buf->buf = rb_malloc(len);
423 ctl_buf->buflen = len;
424 memcpy(ctl_buf->buf, data, len);
425 ctl_buf->nfds = 0;
426 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->writeq);
427 mod_write_ctl(ctl->F, ctl);
428}
429
430static void
431close_conn(conn_t * conn, int wait_plain, const char *fmt, ...)
432{
433 va_list ap;
434 char reason[128]; /* must always be under 250 bytes */
435 uint8_t buf[256];
436 int len;
437 if(IsDead(conn))
438 return;
439
1160f6c9 440 rb_rawbuf_flush(conn->modbuf_out, conn->mod_fd);
1c8c63cb 441 rb_linebuf_flush(conn->plain_fd, &conn->plainbuf_out);
05e0aa9a
AC
442 rb_close(conn->mod_fd);
443 SetDead(conn);
444
445 rb_dlinkDelete(&conn->node, connid_hash(conn->id));
446
447 if(!wait_plain || fmt == NULL)
448 {
449 rb_close(conn->plain_fd);
450 rb_dlinkAdd(conn, &conn->node, &dead_list);
451 return;
452 }
453
454 rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_shutdown_cb, conn);
455 rb_setselect(conn->plain_fd, RB_SELECT_WRITE, NULL, NULL);
456
457 va_start(ap, fmt);
458 vsnprintf(reason, sizeof(reason), fmt, ap);
459 va_end(ap);
460
461 buf[0] = 'D';
462 uint32_to_buf(&buf[1], conn->id);
463 rb_strlcpy((char *) &buf[5], reason, sizeof(buf) - 5);
464 len = (strlen(reason) + 1) + 5;
465 mod_cmd_write_queue(conn->ctl, buf, len);
466}
467
caebeeca
AC
468static conn_t *
469make_conn(mod_ctl_t * ctl, rb_fde_t *mod_fd, rb_fde_t *plain_fd)
470{
471 conn_t *conn = rb_malloc(sizeof(conn_t));
472 conn->ctl = ctl;
caebeeca
AC
473 conn->mod_fd = mod_fd;
474 conn->plain_fd = plain_fd;
475 conn->id = -1;
caebeeca
AC
476 rb_set_nb(mod_fd);
477 rb_set_nb(plain_fd);
1c8c63cb
AC
478
479 rb_linebuf_newbuf(&conn->plainbuf_in);
480 rb_linebuf_newbuf(&conn->plainbuf_out);
481
1160f6c9
AC
482 conn->modbuf_in = rb_new_rawbuffer();
483 conn->modbuf_out = rb_new_rawbuffer();
1c8c63cb 484
caebeeca
AC
485 return conn;
486}
487
488static void
489cleanup_bad_message(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
490{
491 int i;
492
493 /* XXX should log this somehow */
494 for (i = 0; i < ctlb->nfds; i++)
495 rb_close(ctlb->F[i]);
496}
497
1c8c63cb
AC
498static void
499conn_mod_handshake_process(conn_t *conn)
500{
501 char inbuf[READBUF_SIZE];
502
e688bcbd
AC
503 memset(inbuf, 0, sizeof inbuf);
504
1c8c63cb
AC
505 while (1)
506 {
1160f6c9
AC
507 char *p = NULL;
508
e688bcbd 509 int dolen = rb_rawbuf_get(conn->modbuf_in, inbuf, sizeof inbuf);
1c8c63cb
AC
510 if (!dolen)
511 break;
1160f6c9
AC
512
513 if ((p = strcasestr(inbuf, "Sec-WebSocket-Key:")) != NULL)
514 {
515 char *start, *end;
516
517 start = p + strlen("Sec-WebSocket-Key:");
518
519 for (; start < (inbuf + READBUF_SIZE) && *start; start++)
520 {
521 if (*start != ' ' && *start != '\t')
522 break;
523 }
524
525 for (end = start; end < (inbuf + READBUF_SIZE) && *end; end++)
526 {
527 if (*end == '\r' || *end == '\n')
528 {
529 *end = '\0';
530 break;
531 }
532 }
533
534 rb_strlcpy(conn->client_key, start, sizeof(conn->client_key));
535 SetKeyed(conn);
536 }
537 }
538
539 if (IsKeyed(conn))
540 {
541 SHA1 sha1;
542 uint8_t digest[SHA1_DIGEST_LENGTH];
543 char *resp;
544
545 sha1_init(&sha1);
546 sha1_update(&sha1, (uint8_t *) conn->client_key, strlen(conn->client_key));
547 sha1_update(&sha1, (uint8_t *) WEBSOCKET_SERVER_KEY, strlen(WEBSOCKET_SERVER_KEY));
548 sha1_final(&sha1, digest);
549
550 resp = (char *) rb_base64_encode(digest, SHA1_DIGEST_LENGTH);
551
552 conn_mod_write(conn, WEBSOCKET_ANSWER_STRING_1, strlen(WEBSOCKET_ANSWER_STRING_1));
553 conn_mod_write(conn, resp, strlen(resp));
554 conn_mod_write(conn, WEBSOCKET_ANSWER_STRING_2, strlen(WEBSOCKET_ANSWER_STRING_2));
555
556 rb_free(resp);
1c8c63cb 557 }
1160f6c9
AC
558
559 conn_mod_write_sendq(conn->mod_fd, conn);
1c8c63cb
AC
560}
561
caebeeca 562static void
f297042b 563conn_mod_read_cb(rb_fde_t *fd, void *data)
05e0aa9a
AC
564{
565 char inbuf[READBUF_SIZE];
e688bcbd
AC
566
567 memset(inbuf, 0, sizeof inbuf);
568
05e0aa9a
AC
569 conn_t *conn = data;
570 int length = 0;
571 if (conn == NULL)
572 return;
573
574 if (IsDead(conn))
575 return;
576
577 while (1)
578 {
579 if (IsDead(conn))
580 return;
581
1c8c63cb
AC
582 length = rb_read(fd, inbuf, sizeof(inbuf));
583
584 if (length < 0)
585 {
586 if (rb_ignore_errno(errno))
f297042b 587 rb_setselect(fd, RB_SELECT_READ, conn_mod_read_cb, conn);
1c8c63cb
AC
588 else
589 close_conn(conn, NO_WAIT, "Connection closed");
590
591 return;
592 }
593 else if (length == 0)
05e0aa9a
AC
594 {
595 close_conn(conn, NO_WAIT, "Connection closed");
596 return;
597 }
1c8c63cb 598
1160f6c9 599 rb_rawbuf_append(conn->modbuf_in, inbuf, length);
f297042b
AC
600 if (!IsKeyed(conn))
601 conn_mod_handshake_process(conn);
1c8c63cb
AC
602
603 if (length < sizeof(inbuf))
604 {
f297042b 605 rb_setselect(fd, RB_SELECT_READ, conn_mod_read_cb, conn);
1c8c63cb
AC
606 return;
607 }
05e0aa9a
AC
608 }
609}
610
f297042b
AC
611static bool
612plain_check_cork(conn_t * conn)
613{
614 if(rb_rawbuf_length(conn->modbuf_out) >= 4096)
615 {
616 /* if we have over 4k pending outbound, don't read until
617 * we've cleared the queue */
618 SetCork(conn);
619 rb_setselect(conn->plain_fd, RB_SELECT_READ, NULL, NULL);
620 /* try to write */
e688bcbd
AC
621 if (IsKeyed(conn))
622 conn_mod_write_sendq(conn->mod_fd, conn);
f297042b
AC
623 return true;
624 }
625
626 return false;
627}
628
05e0aa9a 629static void
f297042b 630conn_plain_process_recvq(conn_t *conn)
05e0aa9a 631{
f297042b
AC
632 char inbuf[READBUF_SIZE];
633
e688bcbd
AC
634 memset(inbuf, 0, sizeof inbuf);
635
f297042b
AC
636 while (1)
637 {
e688bcbd 638 int dolen = rb_linebuf_get(&conn->plainbuf_in, inbuf, sizeof inbuf, LINEBUF_COMPLETE, LINEBUF_PARSED);
f297042b
AC
639 if (!dolen)
640 break;
641
642 conn_mod_write_frame(conn, inbuf, dolen);
643 }
e688bcbd
AC
644
645 if (IsKeyed(conn))
646 conn_mod_write_sendq(conn->mod_fd, conn);
05e0aa9a
AC
647}
648
649static void
650conn_plain_read_cb(rb_fde_t *fd, void *data)
651{
f297042b 652 char inbuf[READBUF_SIZE];
e688bcbd
AC
653
654 memset(inbuf, 0, sizeof inbuf);
655
f297042b
AC
656 conn_t *conn = data;
657 int length = 0;
658 if(conn == NULL)
659 return;
660
661 if(IsDead(conn))
662 return;
663
664 if(plain_check_cork(conn))
665 return;
666
667 while(1)
668 {
669 if(IsDead(conn))
670 return;
671
672 length = rb_read(conn->plain_fd, inbuf, sizeof(inbuf));
673
674 if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
675 {
676 close_conn(conn, NO_WAIT, NULL);
677 return;
678 }
679
680 if(length < 0)
681 {
682 rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_cb, conn);
e688bcbd
AC
683 if (IsKeyed(conn))
684 conn_plain_process_recvq(conn);
f297042b
AC
685 return;
686 }
687 conn->plain_in += length;
688
689 (void) rb_linebuf_parse(&conn->plainbuf_in, inbuf, sizeof(inbuf), 0);
690
691 if(IsDead(conn))
692 return;
693 if(plain_check_cork(conn))
694 return;
695 }
05e0aa9a
AC
696}
697
698static void
699conn_plain_read_shutdown_cb(rb_fde_t *fd, void *data)
700{
701 char inbuf[READBUF_SIZE];
702 conn_t *conn = data;
703 int length = 0;
704
705 if(conn == NULL)
706 return;
707
708 while(1)
709 {
710 length = rb_read(conn->plain_fd, inbuf, sizeof(inbuf));
711
712 if(length == 0 || (length < 0 && !rb_ignore_errno(errno)))
713 {
714 rb_close(conn->plain_fd);
715 rb_dlinkAdd(conn, &conn->node, &dead_list);
716 return;
717 }
718
719 if(length < 0)
720 {
721 rb_setselect(conn->plain_fd, RB_SELECT_READ, conn_plain_read_shutdown_cb, conn);
722 return;
723 }
724 }
725}
726
727static void
728wsock_process(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
caebeeca
AC
729{
730 conn_t *conn;
731 uint32_t id;
732
733 conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
734
735 id = buf_to_uint32(&ctlb->buf[1]);
736 conn_add_id_hash(conn, id);
737 SetWS(conn);
738
739 if(rb_get_type(conn->mod_fd) & RB_FD_UNKNOWN)
740 rb_set_type(conn->mod_fd, RB_FD_SOCKET);
741
742 if(rb_get_type(conn->plain_fd) == RB_FD_UNKNOWN)
743 rb_set_type(conn->plain_fd, RB_FD_SOCKET);
744
f297042b 745 conn_mod_read_cb(conn->mod_fd, conn);
e688bcbd 746 conn_plain_read_cb(conn->plain_fd, conn);
caebeeca
AC
747}
748
749static void
750mod_process_cmd_recv(mod_ctl_t * ctl)
751{
752 rb_dlink_node *ptr, *next;
753 mod_ctl_buf_t *ctl_buf;
754
755 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
756 {
757 ctl_buf = ptr->data;
758
759 switch (*ctl_buf->buf)
760 {
761 case 'A':
762 {
763 if (ctl_buf->nfds != 2 || ctl_buf->buflen != 5)
764 {
765 cleanup_bad_message(ctl, ctl_buf);
766 break;
767 }
05e0aa9a 768 wsock_process(ctl, ctl_buf);
caebeeca
AC
769 break;
770 }
771 default:
772 break;
773 /* Log unknown commands */
774 }
775 rb_dlinkDelete(ptr, &ctl->readq);
776 rb_free(ctl_buf->buf);
777 rb_free(ctl_buf);
778 }
779
780}
781
782static void
783mod_read_ctl(rb_fde_t *F, void *data)
784{
785 mod_ctl_buf_t *ctl_buf;
786 mod_ctl_t *ctl = data;
787 int retlen;
788 int i;
789
790 do
791 {
792 ctl_buf = rb_malloc(sizeof(mod_ctl_buf_t));
793 ctl_buf->buf = rb_malloc(READBUF_SIZE);
794 ctl_buf->buflen = READBUF_SIZE;
795 retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, ctl_buf->buflen, ctl_buf->F,
796 MAXPASSFD);
797 if(retlen <= 0)
798 {
799 rb_free(ctl_buf->buf);
800 rb_free(ctl_buf);
801 }
802 else
803 {
804 ctl_buf->buflen = retlen;
805 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
806 for (i = 0; i < MAXPASSFD && ctl_buf->F[i] != NULL; i++)
807 ;
808 ctl_buf->nfds = i;
809 }
810 }
811 while(retlen > 0);
812
813 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
814 exit(0);
815
816 mod_process_cmd_recv(ctl);
817 rb_setselect(ctl->F, RB_SELECT_READ, mod_read_ctl, ctl);
818}
819
caebeeca
AC
820static void
821read_pipe_ctl(rb_fde_t *F, void *data)
822{
823 char inbuf[READBUF_SIZE];
824 int retlen;
825 while((retlen = rb_read(F, inbuf, sizeof(inbuf))) > 0)
826 {
827 ;; /* we don't do anything with the pipe really, just care if the other process dies.. */
828 }
829 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
830 exit(0);
831 rb_setselect(F, RB_SELECT_READ, read_pipe_ctl, NULL);
832}
833
834int
835main(int argc, char **argv)
836{
837 const char *s_ctlfd, *s_pipe, *s_pid;
838 int ctlfd, pipefd, x, maxfd;
839 maxfd = maxconn();
840
841 s_ctlfd = getenv("CTL_FD");
842 s_pipe = getenv("CTL_PIPE");
843 s_pid = getenv("CTL_PPID");
844
845 if(s_ctlfd == NULL || s_pipe == NULL || s_pid == NULL)
846 {
847 fprintf(stderr,
848 "This is the charybdis wsockd for internal ircd use.\n");
849 fprintf(stderr,
850 "You aren't supposed to run me directly. Exiting.\n");
851 exit(1);
852 }
853
854 ctlfd = atoi(s_ctlfd);
855 pipefd = atoi(s_pipe);
856 ppid = atoi(s_pid);
857 x = 0;
858#ifndef _WIN32
859 for(x = 0; x < maxfd; x++)
860 {
861 if(x != ctlfd && x != pipefd && x > 2)
862 close(x);
863 }
864 x = open("/dev/null", O_RDWR);
865
866 if(x >= 0)
867 {
868 if(ctlfd != 0 && pipefd != 0)
869 dup2(x, 0);
870 if(ctlfd != 1 && pipefd != 1)
871 dup2(x, 1);
872 if(ctlfd != 2 && pipefd != 2)
873 dup2(x, 2);
874 if(x > 2)
875 close(x);
876 }
877#endif
878 setup_signals();
879 rb_lib_init(NULL, NULL, NULL, 0, maxfd, 1024, 4096);
1c8c63cb 880 rb_linebuf_init(4096);
1160f6c9 881 rb_init_rawbuffers(4096);
caebeeca
AC
882
883 mod_ctl = rb_malloc(sizeof(mod_ctl_t));
884 mod_ctl->F = rb_open(ctlfd, RB_FD_SOCKET, "ircd control socket");
885 mod_ctl->F_pipe = rb_open(pipefd, RB_FD_PIPE, "ircd pipe");
886 rb_set_nb(mod_ctl->F);
887 rb_set_nb(mod_ctl->F_pipe);
888 rb_event_addish("clean_dead_conns", clean_dead_conns, NULL, 10);
889 read_pipe_ctl(mod_ctl->F_pipe, NULL);
890 mod_read_ctl(mod_ctl->F, mod_ctl);
891
892 rb_lib_loop(0);
893 return 0;
894}