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