]> jfr.im git - solanum.git/blob - wsockd/wsockd.c
authd/provider: add options handlers for providers
[solanum.git] / wsockd / wsockd.c
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"
24
25 #define MAXPASSFD 4
26 #ifndef READBUF_SIZE
27 #define READBUF_SIZE 16384
28 #endif
29
30 static void setup_signals(void);
31 static pid_t ppid;
32
33 static inline uint32_t
34 buf_to_uint32(uint8_t *buf)
35 {
36 uint32_t x;
37 memcpy(&x, buf, sizeof(x));
38 return x;
39 }
40
41 static inline void
42 uint32_to_buf(uint8_t *buf, uint32_t x)
43 {
44 memcpy(buf, &x, sizeof(x));
45 return;
46 }
47
48 typedef struct _mod_ctl_buf
49 {
50 rb_dlink_node node;
51 uint8_t *buf;
52 size_t buflen;
53 rb_fde_t *F[MAXPASSFD];
54 int nfds;
55 } mod_ctl_buf_t;
56
57 typedef struct _mod_ctl
58 {
59 rb_dlink_node node;
60 int cli_count;
61 rb_fde_t *F;
62 rb_fde_t *F_pipe;
63 rb_dlink_list readq;
64 rb_dlink_list writeq;
65 } mod_ctl_t;
66
67 static mod_ctl_t *mod_ctl;
68
69 typedef struct _conn
70 {
71 rb_dlink_node node;
72 mod_ctl_t *ctl;
73 rawbuf_head_t *modbuf_out;
74 rawbuf_head_t *plainbuf_out;
75
76 uint32_t id;
77
78 rb_fde_t *mod_fd;
79 rb_fde_t *plain_fd;
80 uint64_t mod_out;
81 uint64_t mod_in;
82 uint64_t plain_in;
83 uint64_t plain_out;
84 uint8_t flags;
85 void *stream;
86 } conn_t;
87
88 #define FLAG_CORK 0x01
89 #define FLAG_DEAD 0x02
90 #define FLAG_WSOCK 0x04
91
92 #define IsCork(x) ((x)->flags & FLAG_CORK)
93 #define IsDead(x) ((x)->flags & FLAG_DEAD)
94 #define IsWS(x) ((x)->flags & FLAG_WSOCK)
95
96 #define SetCork(x) ((x)->flags |= FLAG_CORK)
97 #define SetDead(x) ((x)->flags |= FLAG_DEAD)
98 #define SetWS(x) ((x)->flags |= FLAG_WSOCK)
99
100 #define ClearCork(x) ((x)->flags &= ~FLAG_CORK)
101 #define ClearDead(x) ((x)->flags &= ~FLAG_DEAD)
102 #define ClearWS(x) ((x)->flags &= ~FLAG_WSOCK)
103
104 #define NO_WAIT 0x0
105 #define WAIT_PLAIN 0x1
106
107 #define HASH_WALK_SAFE(i, max, ptr, next, table) for(i = 0; i < max; i++) { RB_DLINK_FOREACH_SAFE(ptr, next, table[i].head)
108 #define HASH_WALK_END }
109 #define CONN_HASH_SIZE 2000
110 #define connid_hash(x) (&connid_hash_table[(x % CONN_HASH_SIZE)])
111
112 static rb_dlink_list connid_hash_table[CONN_HASH_SIZE];
113 static rb_dlink_list dead_list;
114
115 #ifndef _WIN32
116 static void
117 dummy_handler(int sig)
118 {
119 return;
120 }
121 #endif
122
123 static void
124 setup_signals()
125 {
126 #ifndef _WIN32
127 struct sigaction act;
128
129 act.sa_flags = 0;
130 act.sa_handler = SIG_IGN;
131 sigemptyset(&act.sa_mask);
132 sigaddset(&act.sa_mask, SIGPIPE);
133 sigaddset(&act.sa_mask, SIGALRM);
134 #ifdef SIGTRAP
135 sigaddset(&act.sa_mask, SIGTRAP);
136 #endif
137
138 #ifdef SIGWINCH
139 sigaddset(&act.sa_mask, SIGWINCH);
140 sigaction(SIGWINCH, &act, 0);
141 #endif
142 sigaction(SIGPIPE, &act, 0);
143 #ifdef SIGTRAP
144 sigaction(SIGTRAP, &act, 0);
145 #endif
146
147 act.sa_handler = dummy_handler;
148 sigaction(SIGALRM, &act, 0);
149 #endif
150 }
151
152 static int
153 maxconn(void)
154 {
155 #if defined(RLIMIT_NOFILE) && defined(HAVE_SYS_RESOURCE_H)
156 struct rlimit limit;
157
158 if(!getrlimit(RLIMIT_NOFILE, &limit))
159 {
160 return limit.rlim_cur;
161 }
162 #endif /* RLIMIT_FD_MAX */
163 return MAXCONNECTIONS;
164 }
165
166 static conn_t *
167 conn_find_by_id(uint32_t id)
168 {
169 rb_dlink_node *ptr;
170 conn_t *conn;
171
172 RB_DLINK_FOREACH(ptr, (connid_hash(id))->head)
173 {
174 conn = ptr->data;
175 if(conn->id == id && !IsDead(conn))
176 return conn;
177 }
178 return NULL;
179 }
180
181 static void
182 conn_add_id_hash(conn_t * conn, uint32_t id)
183 {
184 conn->id = id;
185 rb_dlinkAdd(conn, &conn->node, connid_hash(id));
186 }
187
188 static void
189 free_conn(conn_t * conn)
190 {
191 rb_free_rawbuffer(conn->modbuf_out);
192 rb_free_rawbuffer(conn->plainbuf_out);
193 rb_free(conn);
194 }
195
196 static void
197 clean_dead_conns(void *unused)
198 {
199 conn_t *conn;
200 rb_dlink_node *ptr, *next;
201
202 RB_DLINK_FOREACH_SAFE(ptr, next, dead_list.head)
203 {
204 conn = ptr->data;
205 free_conn(conn);
206 }
207
208 dead_list.tail = dead_list.head = NULL;
209 }
210
211 static conn_t *
212 make_conn(mod_ctl_t * ctl, rb_fde_t *mod_fd, rb_fde_t *plain_fd)
213 {
214 conn_t *conn = rb_malloc(sizeof(conn_t));
215 conn->ctl = ctl;
216 conn->modbuf_out = rb_new_rawbuffer();
217 conn->plainbuf_out = rb_new_rawbuffer();
218 conn->mod_fd = mod_fd;
219 conn->plain_fd = plain_fd;
220 conn->id = -1;
221 conn->stream = NULL;
222 rb_set_nb(mod_fd);
223 rb_set_nb(plain_fd);
224 return conn;
225 }
226
227 static void
228 cleanup_bad_message(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
229 {
230 int i;
231
232 /* XXX should log this somehow */
233 for (i = 0; i < ctlb->nfds; i++)
234 rb_close(ctlb->F[i]);
235 }
236
237 static void
238 wsock_process_accept(mod_ctl_t * ctl, mod_ctl_buf_t * ctlb)
239 {
240 conn_t *conn;
241 uint32_t id;
242
243 conn = make_conn(ctl, ctlb->F[0], ctlb->F[1]);
244
245 id = buf_to_uint32(&ctlb->buf[1]);
246 conn_add_id_hash(conn, id);
247 SetWS(conn);
248
249 if(rb_get_type(conn->mod_fd) & RB_FD_UNKNOWN)
250 rb_set_type(conn->mod_fd, RB_FD_SOCKET);
251
252 if(rb_get_type(conn->plain_fd) == RB_FD_UNKNOWN)
253 rb_set_type(conn->plain_fd, RB_FD_SOCKET);
254
255 // XXX todo
256 }
257
258 static void
259 mod_process_cmd_recv(mod_ctl_t * ctl)
260 {
261 rb_dlink_node *ptr, *next;
262 mod_ctl_buf_t *ctl_buf;
263
264 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->readq.head)
265 {
266 ctl_buf = ptr->data;
267
268 switch (*ctl_buf->buf)
269 {
270 case 'A':
271 {
272 if (ctl_buf->nfds != 2 || ctl_buf->buflen != 5)
273 {
274 cleanup_bad_message(ctl, ctl_buf);
275 break;
276 }
277 wsock_process_accept(ctl, ctl_buf);
278 break;
279 }
280 default:
281 break;
282 /* Log unknown commands */
283 }
284 rb_dlinkDelete(ptr, &ctl->readq);
285 rb_free(ctl_buf->buf);
286 rb_free(ctl_buf);
287 }
288
289 }
290
291 static void
292 mod_read_ctl(rb_fde_t *F, void *data)
293 {
294 mod_ctl_buf_t *ctl_buf;
295 mod_ctl_t *ctl = data;
296 int retlen;
297 int i;
298
299 do
300 {
301 ctl_buf = rb_malloc(sizeof(mod_ctl_buf_t));
302 ctl_buf->buf = rb_malloc(READBUF_SIZE);
303 ctl_buf->buflen = READBUF_SIZE;
304 retlen = rb_recv_fd_buf(ctl->F, ctl_buf->buf, ctl_buf->buflen, ctl_buf->F,
305 MAXPASSFD);
306 if(retlen <= 0)
307 {
308 rb_free(ctl_buf->buf);
309 rb_free(ctl_buf);
310 }
311 else
312 {
313 ctl_buf->buflen = retlen;
314 rb_dlinkAddTail(ctl_buf, &ctl_buf->node, &ctl->readq);
315 for (i = 0; i < MAXPASSFD && ctl_buf->F[i] != NULL; i++)
316 ;
317 ctl_buf->nfds = i;
318 }
319 }
320 while(retlen > 0);
321
322 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
323 exit(0);
324
325 mod_process_cmd_recv(ctl);
326 rb_setselect(ctl->F, RB_SELECT_READ, mod_read_ctl, ctl);
327 }
328
329 static void
330 mod_write_ctl(rb_fde_t *F, void *data)
331 {
332 mod_ctl_t *ctl = data;
333 mod_ctl_buf_t *ctl_buf;
334 rb_dlink_node *ptr, *next;
335 int retlen, x;
336
337 RB_DLINK_FOREACH_SAFE(ptr, next, ctl->writeq.head)
338 {
339 ctl_buf = ptr->data;
340 retlen = rb_send_fd_buf(ctl->F, ctl_buf->F, ctl_buf->nfds, ctl_buf->buf,
341 ctl_buf->buflen, ppid);
342 if(retlen > 0)
343 {
344 rb_dlinkDelete(ptr, &ctl->writeq);
345 for(x = 0; x < ctl_buf->nfds; x++)
346 rb_close(ctl_buf->F[x]);
347 rb_free(ctl_buf->buf);
348 rb_free(ctl_buf);
349
350 }
351 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
352 exit(0);
353
354 }
355 if(rb_dlink_list_length(&ctl->writeq) > 0)
356 rb_setselect(ctl->F, RB_SELECT_WRITE, mod_write_ctl, ctl);
357 }
358
359 static void
360 read_pipe_ctl(rb_fde_t *F, void *data)
361 {
362 char inbuf[READBUF_SIZE];
363 int retlen;
364 while((retlen = rb_read(F, inbuf, sizeof(inbuf))) > 0)
365 {
366 ;; /* we don't do anything with the pipe really, just care if the other process dies.. */
367 }
368 if(retlen == 0 || (retlen < 0 && !rb_ignore_errno(errno)))
369 exit(0);
370 rb_setselect(F, RB_SELECT_READ, read_pipe_ctl, NULL);
371 }
372
373 int
374 main(int argc, char **argv)
375 {
376 const char *s_ctlfd, *s_pipe, *s_pid;
377 int ctlfd, pipefd, x, maxfd;
378 maxfd = maxconn();
379
380 s_ctlfd = getenv("CTL_FD");
381 s_pipe = getenv("CTL_PIPE");
382 s_pid = getenv("CTL_PPID");
383
384 if(s_ctlfd == NULL || s_pipe == NULL || s_pid == NULL)
385 {
386 fprintf(stderr,
387 "This is the charybdis wsockd for internal ircd use.\n");
388 fprintf(stderr,
389 "You aren't supposed to run me directly. Exiting.\n");
390 exit(1);
391 }
392
393 ctlfd = atoi(s_ctlfd);
394 pipefd = atoi(s_pipe);
395 ppid = atoi(s_pid);
396 x = 0;
397 #ifndef _WIN32
398 for(x = 0; x < maxfd; x++)
399 {
400 if(x != ctlfd && x != pipefd && x > 2)
401 close(x);
402 }
403 x = open("/dev/null", O_RDWR);
404
405 if(x >= 0)
406 {
407 if(ctlfd != 0 && pipefd != 0)
408 dup2(x, 0);
409 if(ctlfd != 1 && pipefd != 1)
410 dup2(x, 1);
411 if(ctlfd != 2 && pipefd != 2)
412 dup2(x, 2);
413 if(x > 2)
414 close(x);
415 }
416 #endif
417 setup_signals();
418 rb_lib_init(NULL, NULL, NULL, 0, maxfd, 1024, 4096);
419 rb_init_rawbuffers(1024);
420
421 mod_ctl = rb_malloc(sizeof(mod_ctl_t));
422 mod_ctl->F = rb_open(ctlfd, RB_FD_SOCKET, "ircd control socket");
423 mod_ctl->F_pipe = rb_open(pipefd, RB_FD_PIPE, "ircd pipe");
424 rb_set_nb(mod_ctl->F);
425 rb_set_nb(mod_ctl->F_pipe);
426 rb_event_addish("clean_dead_conns", clean_dead_conns, NULL, 10);
427 read_pipe_ctl(mod_ctl->F_pipe, NULL);
428 mod_read_ctl(mod_ctl->F, mod_ctl);
429
430 rb_lib_loop(0);
431 return 0;
432 }