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>
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.
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.
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
28 #define READBUF_SIZE 16384
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"
35 static void setup_signals(void);
38 static inline uint32_t
39 buf_to_uint32(uint8_t *buf
)
42 memcpy(&x
, buf
, sizeof(x
));
47 uint32_to_buf(uint8_t *buf
, uint32_t x
)
49 memcpy(buf
, &x
, sizeof(x
));
53 typedef struct _mod_ctl_buf
58 rb_fde_t
*F
[MAXPASSFD
];
62 typedef struct _mod_ctl
72 static mod_ctl_t
*mod_ctl
;
79 rawbuf_head_t
*modbuf_out
;
80 rawbuf_head_t
*modbuf_in
;
82 buf_head_t plainbuf_out
;
83 buf_head_t plainbuf_in
;
95 char client_key
[37]; /* maximum 36 bytes + nul */
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
105 #define WEBSOCKET_MASK_LENGTH 4
107 #define WEBSOCKET_MAX_UNEXTENDED_PAYLOAD_DATA_LENGTH 125
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
115 ws_frame_hdr_t header
;
116 uint8_t payload_data
[WEBSOCKET_MAX_UNEXTENDED_PAYLOAD_DATA_LENGTH
];
117 } ws_frame_payload_t
;
120 ws_frame_hdr_t header
;
124 ws_frame_hdr_t header
;
125 uint16_t payload_length_extended
;
129 ws_frame_hdr_t header
;
130 uint64_t payload_length_extended
;
134 ws_frame_get_opcode(ws_frame_hdr_t
*header
)
136 return header
->opcode_rsv_fin
& 0xF;
140 ws_frame_set_opcode(ws_frame_hdr_t
*header
, int opcode
)
142 header
->opcode_rsv_fin
&= ~0xF;
143 header
->opcode_rsv_fin
|= opcode
& 0xF;
147 ws_frame_get_fin(ws_frame_hdr_t
*header
)
149 return (header
->opcode_rsv_fin
>> 7) & 0x1;
153 ws_frame_set_fin(ws_frame_hdr_t
*header
, int fin
)
155 header
->opcode_rsv_fin
&= ~(0x1 << 7);
156 header
->opcode_rsv_fin
|= (fin
<< 7) & (0x1 << 7);
159 static void close_conn(conn_t
* conn
, int wait_plain
, const char *fmt
, ...);
160 static void conn_mod_read_cb(rb_fde_t
*fd
, void *data
);
161 static void conn_plain_read_cb(rb_fde_t
*fd
, void *data
);
162 static void conn_plain_process_recvq(conn_t
*conn
);
164 #define FLAG_CORK 0x01
165 #define FLAG_DEAD 0x02
166 #define FLAG_WSOCK 0x04
167 #define FLAG_KEYED 0x08
169 #define IsCork(x) ((x)->flags & FLAG_CORK)
170 #define IsDead(x) ((x)->flags & FLAG_DEAD)
171 #define IsWS(x) ((x)->flags & FLAG_WSOCK)
172 #define IsKeyed(x) ((x)->flags & FLAG_KEYED)
174 #define SetCork(x) ((x)->flags |= FLAG_CORK)
175 #define SetDead(x) ((x)->flags |= FLAG_DEAD)
176 #define SetWS(x) ((x)->flags |= FLAG_WSOCK)
177 #define SetKeyed(x) ((x)->flags |= FLAG_KEYED)
179 #define ClearCork(x) ((x)->flags &= ~FLAG_CORK)
180 #define ClearDead(x) ((x)->flags &= ~FLAG_DEAD)
181 #define ClearWS(x) ((x)->flags &= ~FLAG_WSOCK)
182 #define ClearKeyed(x) ((x)->flags &= ~FLAG_KEYED)
185 #define WAIT_PLAIN 0x1
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)])
192 static const char *remote_closed
= "Remote host closed the connection";
194 static rb_dlink_list connid_hash_table
[CONN_HASH_SIZE
];
195 static rb_dlink_list dead_list
;
197 static void conn_plain_read_shutdown_cb(rb_fde_t
*fd
, void *data
);
201 dummy_handler(int sig
)
211 struct sigaction act
;
214 act
.sa_handler
= SIG_IGN
;
215 sigemptyset(&act
.sa_mask
);
216 sigaddset(&act
.sa_mask
, SIGPIPE
);
217 sigaddset(&act
.sa_mask
, SIGALRM
);
219 sigaddset(&act
.sa_mask
, SIGTRAP
);
223 sigaddset(&act
.sa_mask
, SIGWINCH
);
224 sigaction(SIGWINCH
, &act
, 0);
226 sigaction(SIGPIPE
, &act
, 0);
228 sigaction(SIGTRAP
, &act
, 0);
231 act
.sa_handler
= dummy_handler
;
232 sigaction(SIGALRM
, &act
, 0);
239 #if defined(RLIMIT_NOFILE) && defined(HAVE_SYS_RESOURCE_H)
242 if(!getrlimit(RLIMIT_NOFILE
, &limit
))
244 return limit
.rlim_cur
;
246 #endif /* RLIMIT_FD_MAX */
247 return MAXCONNECTIONS
;
251 conn_find_by_id(uint32_t id
)
256 RB_DLINK_FOREACH(ptr
, (connid_hash(id
))->head
)
259 if(conn
->id
== id
&& !IsDead(conn
))
266 conn_add_id_hash(conn_t
* conn
, uint32_t id
)
269 rb_dlinkAdd(conn
, &conn
->node
, connid_hash(id
));
273 free_conn(conn_t
* conn
)
275 rb_linebuf_donebuf(&conn
->plainbuf_in
);
276 rb_linebuf_donebuf(&conn
->plainbuf_out
);
278 rb_free_rawbuffer(conn
->modbuf_in
);
279 rb_free_rawbuffer(conn
->modbuf_out
);
285 clean_dead_conns(void *unused
)
288 rb_dlink_node
*ptr
, *next
;
290 RB_DLINK_FOREACH_SAFE(ptr
, next
, dead_list
.head
)
296 dead_list
.tail
= dead_list
.head
= NULL
;
300 conn_plain_write_sendq(rb_fde_t
*fd
, void *data
)
308 while((retlen
= rb_linebuf_flush(fd
, &conn
->plainbuf_out
)) > 0)
309 conn
->plain_out
+= retlen
;
311 if(retlen
== 0 || (retlen
< 0 && !rb_ignore_errno(errno
)))
313 close_conn(data
, NO_WAIT
, NULL
);
317 if(rb_linebuf_alloclen(&conn
->plainbuf_out
) > 0)
318 rb_setselect(conn
->plain_fd
, RB_SELECT_WRITE
, conn_plain_write_sendq
, conn
);
320 rb_setselect(conn
->plain_fd
, RB_SELECT_WRITE
, NULL
, NULL
);
324 conn_mod_write_sendq(rb_fde_t
*fd
, void *data
)
333 while((retlen
= rb_rawbuf_flush(conn
->modbuf_out
, fd
)) > 0)
334 conn
->mod_out
+= retlen
;
336 if(retlen
== 0 || (retlen
< 0 && !rb_ignore_errno(errno
)))
339 close_conn(conn
, WAIT_PLAIN
, "%s", remote_closed
);
340 err
= strerror(errno
);
341 close_conn(conn
, WAIT_PLAIN
, "Write error: %s", err
);
345 if(rb_rawbuf_length(conn
->modbuf_out
) > 0)
346 rb_setselect(conn
->mod_fd
, RB_SELECT_WRITE
, conn_mod_write_sendq
, conn
);
348 rb_setselect(conn
->mod_fd
, RB_SELECT_WRITE
, NULL
, NULL
);
350 if(IsCork(conn
) && rb_rawbuf_length(conn
->modbuf_out
) == 0)
353 conn_plain_read_cb(conn
->plain_fd
, conn
);
358 conn_mod_write(conn_t
* conn
, void *data
, size_t len
)
360 if(IsDead(conn
)) /* no point in queueing to a dead man */
362 rb_rawbuf_append(conn
->modbuf_out
, data
, len
);
366 conn_mod_write_short_frame(conn_t
* conn
, void *data
, int len
)
370 ws_frame_set_opcode(&hdr
, WEBSOCKET_OPCODE_TEXT_FRAME
);
371 ws_frame_set_fin(&hdr
, 1);
372 hdr
.payload_length_mask
= (len
+ 2) & 0x7f;
374 conn_mod_write(conn
, &hdr
, sizeof(hdr
));
375 conn_mod_write(conn
, data
, len
);
376 conn_mod_write(conn
, "\r\n", 2);
380 conn_mod_write_long_frame(conn_t
* conn
, void *data
, int len
)
384 ws_frame_set_opcode(&hdr
.header
, WEBSOCKET_OPCODE_TEXT_FRAME
);
385 ws_frame_set_fin(&hdr
.header
, 1);
386 hdr
.header
.payload_length_mask
= 126;
387 hdr
.payload_length_extended
= htons(len
+ 2);
389 conn_mod_write(conn
, &hdr
, sizeof(hdr
));
390 conn_mod_write(conn
, data
, len
);
391 conn_mod_write(conn
, "\r\n", 2);
395 conn_mod_write_frame(conn_t
*conn
, void *data
, int len
)
397 if(IsDead(conn
)) /* no point in queueing to a dead man */
401 return conn_mod_write_short_frame(conn
, data
, len
);
403 return conn_mod_write_long_frame(conn
, data
, len
);
407 conn_plain_write(conn_t
* conn
, void *data
, size_t len
)
409 if(IsDead(conn
)) /* again no point in queueing to dead men */
411 rb_linebuf_put(&conn
->plainbuf_out
, data
, len
);
415 mod_write_ctl(rb_fde_t
*F
, void *data
)
417 mod_ctl_t
*ctl
= data
;
418 mod_ctl_buf_t
*ctl_buf
;
419 rb_dlink_node
*ptr
, *next
;
422 RB_DLINK_FOREACH_SAFE(ptr
, next
, ctl
->writeq
.head
)
425 retlen
= rb_send_fd_buf(ctl
->F
, ctl_buf
->F
, ctl_buf
->nfds
, ctl_buf
->buf
,
426 ctl_buf
->buflen
, ppid
);
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
);
436 if(retlen
== 0 || (retlen
< 0 && !rb_ignore_errno(errno
)))
440 if(rb_dlink_list_length(&ctl
->writeq
) > 0)
441 rb_setselect(ctl
->F
, RB_SELECT_WRITE
, mod_write_ctl
, ctl
);
445 mod_cmd_write_queue(mod_ctl_t
* ctl
, const void *data
, size_t len
)
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
);
453 rb_dlinkAddTail(ctl_buf
, &ctl_buf
->node
, &ctl
->writeq
);
454 mod_write_ctl(ctl
->F
, ctl
);
458 close_conn(conn_t
* conn
, int wait_plain
, const char *fmt
, ...)
461 char reason
[128]; /* must always be under 250 bytes */
468 conn_plain_process_recvq(conn
);
470 rb_rawbuf_flush(conn
->modbuf_out
, conn
->mod_fd
);
471 rb_linebuf_flush(conn
->plain_fd
, &conn
->plainbuf_out
);
472 rb_close(conn
->mod_fd
);
475 rb_dlinkDelete(&conn
->node
, connid_hash(conn
->id
));
477 if(!wait_plain
|| fmt
== NULL
)
479 rb_close(conn
->plain_fd
);
480 rb_dlinkAdd(conn
, &conn
->node
, &dead_list
);
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
);
488 vsnprintf(reason
, sizeof(reason
), fmt
, ap
);
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
);
499 make_conn(mod_ctl_t
* ctl
, rb_fde_t
*mod_fd
, rb_fde_t
*plain_fd
)
501 conn_t
*conn
= rb_malloc(sizeof(conn_t
));
503 conn
->mod_fd
= mod_fd
;
504 conn
->plain_fd
= plain_fd
;
509 rb_linebuf_newbuf(&conn
->plainbuf_in
);
510 rb_linebuf_newbuf(&conn
->plainbuf_out
);
512 conn
->modbuf_in
= rb_new_rawbuffer();
513 conn
->modbuf_out
= rb_new_rawbuffer();
519 cleanup_bad_message(mod_ctl_t
* ctl
, mod_ctl_buf_t
* ctlb
)
523 /* XXX should log this somehow */
524 for (i
= 0; i
< ctlb
->nfds
; i
++)
525 rb_close(ctlb
->F
[i
]);
529 ws_frame_unmask(char *msg
, int length
, uint8_t maskval
[WEBSOCKET_MASK_LENGTH
])
533 for (i
= 0; i
< length
; i
++)
534 msg
[i
] = msg
[i
] ^ maskval
[i
% 4];
538 conn_mod_process_frame(conn_t
*conn
, ws_frame_hdr_t
*hdr
, int masked
)
540 char msg
[WEBSOCKET_MAX_UNEXTENDED_PAYLOAD_DATA_LENGTH
];
541 uint8_t maskval
[WEBSOCKET_MASK_LENGTH
];
544 /* if we're masked, we get to collect the masking key for this frame */
547 dolen
= rb_rawbuf_get(conn
->modbuf_in
, maskval
, sizeof(maskval
));
550 close_conn(conn
, WAIT_PLAIN
, "websocket error: fault unpacking unmask key");
555 dolen
= rb_rawbuf_get(conn
->modbuf_in
, msg
, hdr
->payload_length_mask
);
558 close_conn(conn
, WAIT_PLAIN
, "websocket error: fault unpacking message");
563 ws_frame_unmask(msg
, dolen
, maskval
);
565 rb_linebuf_parse(&conn
->plainbuf_out
, msg
, dolen
, 1);
569 conn_mod_process_large(conn_t
*conn
, ws_frame_hdr_t
*hdr
, int masked
)
571 char msg
[READBUF_SIZE
];
573 uint8_t maskval
[WEBSOCKET_MASK_LENGTH
];
576 memset(msg
, 0, sizeof msg
);
578 dolen
= rb_rawbuf_get(conn
->modbuf_in
, &msglen
, sizeof(msglen
));
581 close_conn(conn
, WAIT_PLAIN
, "websocket error: fault unpacking message size");
585 msglen
= ntohs(msglen
);
589 dolen
= rb_rawbuf_get(conn
->modbuf_in
, maskval
, sizeof(maskval
));
592 close_conn(conn
, WAIT_PLAIN
, "websocket error: fault unpacking unmask key");
597 dolen
= rb_rawbuf_get(conn
->modbuf_in
, msg
, msglen
);
600 close_conn(conn
, WAIT_PLAIN
, "websocket error: fault unpacking message");
605 ws_frame_unmask(msg
, dolen
, maskval
);
607 rb_linebuf_parse(&conn
->plainbuf_out
, msg
, dolen
, 1);
611 conn_mod_process_huge(conn_t
*conn
, ws_frame_hdr_t
*hdr
, int masked
)
613 /* XXX implement me */
617 conn_mod_process(conn_t
*conn
)
624 int dolen
= rb_rawbuf_get(conn
->modbuf_in
, &hdr
, sizeof(hdr
));
625 if (dolen
!= sizeof(hdr
))
628 masked
= (hdr
.payload_length_mask
>> 7) == 1;
630 hdr
.payload_length_mask
&= 0x7f;
631 switch (hdr
.payload_length_mask
)
634 conn_mod_process_large(conn
, &hdr
, masked
);
637 conn_mod_process_huge(conn
, &hdr
, masked
);
640 conn_mod_process_frame(conn
, &hdr
, masked
);
645 conn_plain_write_sendq(conn
->plain_fd
, conn
);
649 conn_mod_handshake_process(conn_t
*conn
)
651 char inbuf
[READBUF_SIZE
];
653 memset(inbuf
, 0, sizeof inbuf
);
659 int dolen
= rb_rawbuf_get(conn
->modbuf_in
, inbuf
, sizeof inbuf
);
663 if ((p
= rb_strcasestr(inbuf
, "Sec-WebSocket-Key:")) != NULL
)
667 start
= p
+ strlen("Sec-WebSocket-Key:");
669 for (; start
< (inbuf
+ READBUF_SIZE
) && *start
; start
++)
671 if (*start
!= ' ' && *start
!= '\t')
675 for (end
= start
; end
< (inbuf
+ READBUF_SIZE
) && *end
; end
++)
677 if (*end
== '\r' || *end
== '\n')
684 rb_strlcpy(conn
->client_key
, start
, sizeof(conn
->client_key
));
692 uint8_t digest
[SHA1_DIGEST_LENGTH
];
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
);
700 resp
= (char *) rb_base64_encode(digest
, SHA1_DIGEST_LENGTH
);
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
));
709 conn_mod_write_sendq(conn
->mod_fd
, conn
);
713 conn_mod_read_cb(rb_fde_t
*fd
, void *data
)
715 char inbuf
[READBUF_SIZE
];
717 memset(inbuf
, 0, sizeof inbuf
);
732 length
= rb_read(fd
, inbuf
, sizeof(inbuf
));
736 if (rb_ignore_errno(errno
))
738 rb_setselect(fd
, RB_SELECT_READ
, conn_mod_read_cb
, conn
);
739 conn_plain_write_sendq(conn
->plain_fd
, conn
);
742 close_conn(conn
, NO_WAIT
, "Connection closed");
746 else if (length
== 0)
748 close_conn(conn
, NO_WAIT
, "Connection closed");
752 rb_rawbuf_append(conn
->modbuf_in
, inbuf
, length
);
754 conn_mod_handshake_process(conn
);
756 conn_mod_process(conn
);
758 if (length
< sizeof(inbuf
))
760 rb_setselect(fd
, RB_SELECT_READ
, conn_mod_read_cb
, conn
);
767 plain_check_cork(conn_t
* conn
)
769 if(rb_rawbuf_length(conn
->modbuf_out
) >= 4096)
771 /* if we have over 4k pending outbound, don't read until
772 * we've cleared the queue */
774 rb_setselect(conn
->plain_fd
, RB_SELECT_READ
, NULL
, NULL
);
777 conn_mod_write_sendq(conn
->mod_fd
, conn
);
785 conn_plain_process_recvq(conn_t
*conn
)
787 char inbuf
[READBUF_SIZE
];
789 memset(inbuf
, 0, sizeof inbuf
);
793 int dolen
= rb_linebuf_get(&conn
->plainbuf_in
, inbuf
, sizeof inbuf
, LINEBUF_COMPLETE
, LINEBUF_PARSED
);
797 conn_mod_write_frame(conn
, inbuf
, dolen
);
801 conn_mod_write_sendq(conn
->mod_fd
, conn
);
805 conn_plain_read_cb(rb_fde_t
*fd
, void *data
)
807 char inbuf
[READBUF_SIZE
];
809 memset(inbuf
, 0, sizeof inbuf
);
819 if(plain_check_cork(conn
))
827 length
= rb_read(conn
->plain_fd
, inbuf
, sizeof(inbuf
));
829 if(length
== 0 || (length
< 0 && !rb_ignore_errno(errno
)))
831 close_conn(conn
, NO_WAIT
, NULL
);
837 rb_setselect(conn
->plain_fd
, RB_SELECT_READ
, conn_plain_read_cb
, conn
);
839 conn_plain_process_recvq(conn
);
842 conn
->plain_in
+= length
;
844 (void) rb_linebuf_parse(&conn
->plainbuf_in
, inbuf
, length
, 0);
848 if(plain_check_cork(conn
))
854 conn_plain_read_shutdown_cb(rb_fde_t
*fd
, void *data
)
856 char inbuf
[READBUF_SIZE
];
865 length
= rb_read(conn
->plain_fd
, inbuf
, sizeof(inbuf
));
867 if(length
== 0 || (length
< 0 && !rb_ignore_errno(errno
)))
869 rb_close(conn
->plain_fd
);
870 rb_dlinkAdd(conn
, &conn
->node
, &dead_list
);
876 rb_setselect(conn
->plain_fd
, RB_SELECT_READ
, conn_plain_read_shutdown_cb
, conn
);
883 wsock_process(mod_ctl_t
* ctl
, mod_ctl_buf_t
* ctlb
)
888 conn
= make_conn(ctl
, ctlb
->F
[0], ctlb
->F
[1]);
890 id
= buf_to_uint32(&ctlb
->buf
[1]);
891 conn_add_id_hash(conn
, id
);
894 if(rb_get_type(conn
->mod_fd
) & RB_FD_UNKNOWN
)
895 rb_set_type(conn
->mod_fd
, RB_FD_SOCKET
);
897 if(rb_get_type(conn
->plain_fd
) == RB_FD_UNKNOWN
)
898 rb_set_type(conn
->plain_fd
, RB_FD_SOCKET
);
900 conn_mod_read_cb(conn
->mod_fd
, conn
);
901 conn_plain_read_cb(conn
->plain_fd
, conn
);
905 mod_process_cmd_recv(mod_ctl_t
* ctl
)
907 rb_dlink_node
*ptr
, *next
;
908 mod_ctl_buf_t
*ctl_buf
;
910 RB_DLINK_FOREACH_SAFE(ptr
, next
, ctl
->readq
.head
)
914 switch (*ctl_buf
->buf
)
918 if (ctl_buf
->nfds
!= 2 || ctl_buf
->buflen
!= 5)
920 cleanup_bad_message(ctl
, ctl_buf
);
923 wsock_process(ctl
, ctl_buf
);
928 /* Log unknown commands */
930 rb_dlinkDelete(ptr
, &ctl
->readq
);
931 rb_free(ctl_buf
->buf
);
938 mod_read_ctl(rb_fde_t
*F
, void *data
)
940 mod_ctl_buf_t
*ctl_buf
;
941 mod_ctl_t
*ctl
= data
;
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
,
954 rb_free(ctl_buf
->buf
);
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
++)
968 if(retlen
== 0 || (retlen
< 0 && !rb_ignore_errno(errno
)))
971 mod_process_cmd_recv(ctl
);
972 rb_setselect(ctl
->F
, RB_SELECT_READ
, mod_read_ctl
, ctl
);
976 read_pipe_ctl(rb_fde_t
*F
, void *data
)
978 char inbuf
[READBUF_SIZE
];
980 while((retlen
= rb_read(F
, inbuf
, sizeof(inbuf
))) > 0)
982 ;; /* we don't do anything with the pipe really, just care if the other process dies.. */
984 if(retlen
== 0 || (retlen
< 0 && !rb_ignore_errno(errno
)))
986 rb_setselect(F
, RB_SELECT_READ
, read_pipe_ctl
, NULL
);
990 main(int argc
, char **argv
)
992 const char *s_ctlfd
, *s_pipe
, *s_pid
;
993 int ctlfd
, pipefd
, x
, maxfd
;
996 s_ctlfd
= getenv("CTL_FD");
997 s_pipe
= getenv("CTL_PIPE");
998 s_pid
= getenv("CTL_PPID");
1000 if(s_ctlfd
== NULL
|| s_pipe
== NULL
|| s_pid
== NULL
)
1003 "This is the charybdis wsockd for internal ircd use.\n");
1005 "You aren't supposed to run me directly. Exiting.\n");
1009 ctlfd
= atoi(s_ctlfd
);
1010 pipefd
= atoi(s_pipe
);
1014 for(x
= 0; x
< maxfd
; x
++)
1016 if(x
!= ctlfd
&& x
!= pipefd
&& x
> 2)
1019 x
= open("/dev/null", O_RDWR
);
1023 if(ctlfd
!= 0 && pipefd
!= 0)
1025 if(ctlfd
!= 1 && pipefd
!= 1)
1027 if(ctlfd
!= 2 && pipefd
!= 2)
1034 rb_lib_init(NULL
, NULL
, NULL
, 0, maxfd
, 1024, 4096);
1035 rb_linebuf_init(4096);
1036 rb_init_rawbuffers(4096);
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
);