]> jfr.im git - solanum.git/blame - src/listener.c
Trying again - this will need ssld integration from me
[solanum.git] / src / listener.c
CommitLineData
f7e2c5b3
VY
1/*\r
2 * ircd-ratbox: A slightly useful ircd.\r
3 * listener.c: Listens on a port.\r
4 *\r
5 * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center\r
6 * Copyright (C) 1996-2002 Hybrid Development Team\r
7 * Copyright (C) 2002-2005 ircd-ratbox development team\r
8 *\r
9 * This program is free software; you can redistribute it and/or modify\r
10 * it under the terms of the GNU General Public License as published by\r
11 * the Free Software Foundation; either version 2 of the License, or\r
12 * (at your option) any later version.\r
13 *\r
14 * This program is distributed in the hope that it will be useful,\r
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
17 * GNU General Public License for more details.\r
18 *\r
19 * You should have received a copy of the GNU General Public License\r
20 * along with this program; if not, write to the Free Software\r
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301\r
22 * USA\r
23 *\r
24 * $Id: listener.c 25169 2008-03-29 21:41:31Z jilles $\r
25 */\r
26\r
27#include "stdinc.h"\r
28#include "struct.h"\r
29#include "ratbox_lib.h"\r
30#include "listener.h"\r
31#include "client.h"\r
32#include "match.h"\r
33#include "ircd.h"\r
34#include "numeric.h"\r
35#include "s_conf.h"\r
36#include "s_newconf.h"\r
37#include "s_stats.h"\r
38#include "send.h"\r
39#include "s_auth.h"\r
40#include "reject.h"\r
41#include "s_log.h"\r
42#include "sslproc.h"\r
43#include "hash.h"\r
44\r
45static rb_dlink_list listener_list;\r
46static int accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, void *data);\r
47static void accept_callback(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t addrlen, void *data);\r
48\r
49\r
50\r
51static struct Listener *\r
52make_listener(struct rb_sockaddr_storage *addr)\r
53{\r
54 struct Listener *listener = rb_malloc(sizeof(struct Listener));\r
55 s_assert(0 != listener);\r
56 listener->name = ServerInfo.name; /* me.name may not be valid yet -- jilles */\r
57 listener->F = NULL;\r
58 memcpy(&listener->addr, addr, sizeof(struct rb_sockaddr_storage));\r
59 return listener;\r
60}\r
61\r
62void\r
63free_listener(struct Listener *listener)\r
64{\r
65 s_assert(NULL != listener);\r
66 if(listener == NULL)\r
67 return;\r
68 \r
69 rb_dlinkDelete(&listener->node, &listener_list);\r
70 rb_free(listener);\r
71}\r
72\r
73#define PORTNAMELEN 6 /* ":31337" */\r
74\r
75/*\r
76 * get_listener_name - return displayable listener name and port\r
77 * returns "host.foo.org:6667" for a given listener\r
78 */\r
79const char *\r
80get_listener_name(struct Listener *listener)\r
81{\r
82 static char buf[HOSTLEN + HOSTLEN + PORTNAMELEN + 4];\r
83 int port = 0;\r
84\r
85 s_assert(NULL != listener);\r
86 if(listener == NULL)\r
87 return NULL;\r
88\r
89#ifdef IPV6\r
90 if(GET_SS_FAMILY(&listener->addr) == AF_INET6)\r
91 port = ntohs(((const struct sockaddr_in6 *)&listener->addr)->sin6_port);\r
92 else\r
93#endif\r
94 port = ntohs(((const struct sockaddr_in *)&listener->addr)->sin_port); \r
95\r
96 rb_snprintf(buf, sizeof(buf), "%s[%s/%u]", me.name, listener->name, port);\r
97 return buf;\r
98}\r
99\r
100/*\r
101 * show_ports - send port listing to a client\r
102 * inputs - pointer to client to show ports to\r
103 * output - none\r
104 * side effects - show ports\r
105 */\r
106void\r
107show_ports(struct Client *source_p)\r
108{\r
109 struct Listener *listener;\r
110 rb_dlink_node *ptr;\r
111 \r
112 RB_DLINK_FOREACH(ptr, listener_list.head)\r
113 {\r
114 listener = ptr->data;\r
115 sendto_one_numeric(source_p, RPL_STATSPLINE, \r
116 form_str(RPL_STATSPLINE), 'P',\r
117#ifdef IPV6\r
118 ntohs(GET_SS_FAMILY(&listener->addr) == AF_INET ? ((struct sockaddr_in *)&listener->addr)->sin_port :\r
119 ((struct sockaddr_in6 *)&listener->addr)->sin6_port),\r
120#else\r
121 ntohs(((struct sockaddr_in *)&listener->addr)->sin_port),\r
122#endif\r
123 IsOperAdmin(source_p) ? listener->name : me.name,\r
124 listener->ref_count, (listener->active) ? "active" : "disabled",\r
125 listener->ssl ? " ssl" : "");\r
126 }\r
127}\r
128\r
129/*\r
130 * inetport - create a listener socket in the AF_INET or AF_INET6 domain,\r
131 * bind it to the port given in 'port' and listen to it\r
132 * returns true (1) if successful false (0) on error.\r
133 *\r
134 * If the operating system has a define for SOMAXCONN, use it, otherwise\r
135 * use RATBOX_SOMAXCONN\r
136 */\r
137#ifdef SOMAXCONN\r
138#undef RATBOX_SOMAXCONN\r
139#define RATBOX_SOMAXCONN SOMAXCONN\r
140#endif\r
141\r
142static int\r
143inetport(struct Listener *listener)\r
144{\r
145 rb_fde_t *F;\r
146 int ret;\r
147 int opt = 1;\r
148\r
149 /*\r
150 * At first, open a new socket\r
151 */\r
152 \r
153 F = rb_socket(GET_SS_FAMILY(&listener->addr), SOCK_STREAM, 0, "Listener socket");\r
154\r
155#ifdef IPV6\r
156 if(GET_SS_FAMILY(&listener->addr) == AF_INET6)\r
157 {\r
158 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)&listener->addr;\r
159 if(!IN6_ARE_ADDR_EQUAL(&in6->sin6_addr, &in6addr_any))\r
160 {\r
161 rb_inet_ntop(AF_INET6, &in6->sin6_addr, listener->vhost, sizeof(listener->vhost));\r
162 listener->name = listener->vhost;\r
163 }\r
164 } else\r
165#endif\r
166 {\r
167 struct sockaddr_in *in = (struct sockaddr_in *)&listener->addr;\r
168 if(in->sin_addr.s_addr != INADDR_ANY)\r
169 {\r
170 rb_inet_ntop(AF_INET, &in->sin_addr, listener->vhost, sizeof(listener->vhost));\r
171 listener->name = listener->vhost;\r
172 } \r
173 }\r
174\r
175\r
176 if(F == NULL)\r
177 {\r
178 report_error("opening listener socket %s:%s",\r
179 get_listener_name(listener), \r
180 get_listener_name(listener), errno);\r
181 return 0;\r
182 }\r
183 else if((maxconnections - 10) < rb_get_fd(F)) /* XXX this is kinda bogus*/\r
184 {\r
185 report_error("no more connections left for listener %s:%s",\r
186 get_listener_name(listener), \r
187 get_listener_name(listener), errno);\r
188 rb_close(F);\r
189 return 0;\r
190 }\r
191 /*\r
192 * XXX - we don't want to do all this crap for a listener\r
193 * set_sock_opts(listener);\r
194 */\r
195 if(setsockopt(rb_get_fd(F), SOL_SOCKET, SO_REUSEADDR, (char *) &opt, sizeof(opt)))\r
196 {\r
197 report_error("setting SO_REUSEADDR for listener %s:%s",\r
198 get_listener_name(listener), \r
199 get_listener_name(listener), errno);\r
200 rb_close(F);\r
201 return 0;\r
202 }\r
203\r
204 /*\r
205 * Bind a port to listen for new connections if port is non-null,\r
206 * else assume it is already open and try get something from it.\r
207 */\r
208\r
209 if(bind(rb_get_fd(F), (struct sockaddr *) &listener->addr, GET_SS_LEN(&listener->addr)))\r
210 {\r
211 report_error("binding listener socket %s:%s",\r
212 get_listener_name(listener), \r
213 get_listener_name(listener), errno);\r
214 rb_close(F);\r
215 return 0;\r
216 }\r
217\r
218 if((ret = rb_listen(F, RATBOX_SOMAXCONN)))\r
219 {\r
220 report_error("listen failed for %s:%s", \r
221 get_listener_name(listener), \r
222 get_listener_name(listener), errno);\r
223 rb_close(F);\r
224 return 0;\r
225 }\r
226\r
227 listener->F = F;\r
228\r
229 rb_accept_tcp(listener->F, accept_precallback, accept_callback, listener);\r
230 return 1;\r
231}\r
232\r
233static struct Listener *\r
234find_listener(struct rb_sockaddr_storage *addr)\r
235{\r
236 struct Listener *listener = NULL;\r
237 struct Listener *last_closed = NULL;\r
238 rb_dlink_node *ptr;\r
239\r
240 RB_DLINK_FOREACH(ptr, listener_list.head)\r
241 {\r
242 listener = ptr->data;\r
243 if(GET_SS_FAMILY(addr) != GET_SS_FAMILY(&listener->addr))\r
244 continue;\r
245 \r
246 switch(GET_SS_FAMILY(addr))\r
247 {\r
248 case AF_INET:\r
249 {\r
250 struct sockaddr_in *in4 = (struct sockaddr_in *)addr;\r
251 struct sockaddr_in *lin4 = (struct sockaddr_in *)&listener->addr;\r
252 if(in4->sin_addr.s_addr == lin4->sin_addr.s_addr && \r
253 in4->sin_port == lin4->sin_port )\r
254 {\r
255 if(listener->F == NULL)\r
256 last_closed = listener;\r
257 else\r
258 return(listener);\r
259 }\r
260 break;\r
261 }\r
262#ifdef IPV6\r
263 case AF_INET6:\r
264 {\r
265 struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)addr;\r
266 struct sockaddr_in6 *lin6 =(struct sockaddr_in6 *)&listener->addr;\r
267 if(IN6_ARE_ADDR_EQUAL(&in6->sin6_addr, &lin6->sin6_addr) &&\r
268 in6->sin6_port == lin6->sin6_port)\r
269 {\r
270 if(listener->F == NULL)\r
271 last_closed = listener;\r
272 else\r
273 return(listener);\r
274 }\r
275 break;\r
276 \r
277 }\r
278#endif\r
279\r
280 default:\r
281 break;\r
282 }\r
283 }\r
284 return last_closed;\r
285}\r
286\r
287/*\r
288 * add_listener- create a new listener\r
289 * port - the port number to listen on\r
290 * vhost_ip - if non-null must contain a valid IP address string in\r
291 * the format "255.255.255.255"\r
292 */\r
293void\r
294add_listener(int port, const char *vhost_ip, int family, int ssl)\r
295{\r
296 struct Listener *listener;\r
297 struct rb_sockaddr_storage vaddr;\r
298\r
299 /*\r
300 * if no port in conf line, don't bother\r
301 */\r
302 if(port == 0)\r
303 return;\r
304 \r
305 memset(&vaddr, 0, sizeof(vaddr));\r
306 GET_SS_FAMILY(&vaddr) = family;\r
307\r
308 if(vhost_ip != NULL)\r
309 {\r
310 if(rb_inet_pton_sock(vhost_ip, (struct sockaddr *)&vaddr) <= 0)\r
311 return;\r
312 } else\r
313 {\r
314 switch(family)\r
315 {\r
316 case AF_INET:\r
317 ((struct sockaddr_in *)&vaddr)->sin_addr.s_addr = INADDR_ANY;\r
318 break;\r
319#ifdef IPV6\r
320 case AF_INET6:\r
321 memcpy(&((struct sockaddr_in6 *)&vaddr)->sin6_addr, &in6addr_any, sizeof(struct in6_addr));\r
322 break;\r
323#endif\r
324 default:\r
325 return;\r
326 } \r
327 }\r
328 switch(family)\r
329 {\r
330 case AF_INET:\r
331 SET_SS_LEN(&vaddr, sizeof(struct sockaddr_in));\r
332 ((struct sockaddr_in *)&vaddr)->sin_port = htons(port);\r
333 break;\r
334#ifdef IPV6\r
335 case AF_INET6:\r
336 SET_SS_LEN(&vaddr, sizeof(struct sockaddr_in6));\r
337 ((struct sockaddr_in6 *)&vaddr)->sin6_port = htons(port);\r
338 break;\r
339#endif\r
340 default:\r
341 break;\r
342 }\r
343 if((listener = find_listener(&vaddr)))\r
344 {\r
345 if(listener->F != NULL)\r
346 return;\r
347 }\r
348 else\r
349 {\r
350 listener = make_listener(&vaddr);\r
351 rb_dlinkAdd(listener, &listener->node, &listener_list);\r
352 }\r
353\r
354 listener->F = NULL;\r
355 listener->ssl = ssl;\r
356 if(inetport(listener))\r
357 listener->active = 1;\r
358 else\r
359 close_listener(listener);\r
360}\r
361\r
362/*\r
363 * close_listener - close a single listener\r
364 */\r
365void\r
366close_listener(struct Listener *listener)\r
367{\r
368 s_assert(listener != NULL);\r
369 if(listener == NULL)\r
370 return;\r
371 if(listener->F != NULL)\r
372 {\r
373 rb_close(listener->F);\r
374 listener->F = NULL;\r
375 }\r
376\r
377 listener->active = 0;\r
378\r
379 if(listener->ref_count)\r
380 return;\r
381\r
382 free_listener(listener);\r
383}\r
384\r
385/*\r
386 * close_listeners - close and free all listeners that are not being used\r
387 */\r
388void\r
389close_listeners()\r
390{\r
391 struct Listener *listener;\r
392 rb_dlink_node *ptr, *next;\r
393\r
394 RB_DLINK_FOREACH_SAFE(ptr, next, listener_list.head)\r
395 {\r
396 listener = ptr->data;\r
397 close_listener(listener);\r
398 }\r
399}\r
400\r
401/*\r
402 * add_connection - creates a client which has just connected to us on \r
403 * the given fd. The sockhost field is initialized with the ip# of the host.\r
404 * The client is sent to the auth module for verification, and not put in\r
405 * any client list yet.\r
406 */\r
407static void\r
408add_connection(struct Listener *listener, rb_fde_t *F, struct sockaddr *sai, struct sockaddr *lai, void *ssl_ctl)\r
409{\r
410 struct Client *new_client;\r
411 s_assert(NULL != listener);\r
412 /* \r
413 * get the client socket name from the socket\r
414 * the client has already been checked out in accept_connection\r
415 */\r
416 new_client = make_client(NULL);\r
417\r
418 memcpy(&new_client->localClient->ip, sai, sizeof(struct rb_sockaddr_storage));\r
419 new_client->localClient->lip = rb_malloc(sizeof(struct rb_sockaddr_storage));\r
420 memcpy(new_client->localClient->lip, lai, sizeof(struct rb_sockaddr_storage));\r
421\r
422 /* \r
423 * copy address to 'sockhost' as a string, copy it to host too\r
424 * so we have something valid to put into error messages...\r
425 */\r
426 rb_inet_ntop_sock((struct sockaddr *)&new_client->localClient->ip, new_client->sockhost, \r
427 sizeof(new_client->sockhost));\r
428\r
429\r
430 rb_strlcpy(new_client->host, new_client->sockhost, sizeof(new_client->host));\r
431\r
432#ifdef IPV6\r
433 if(GET_SS_FAMILY(&new_client->localClient->ip) == AF_INET6 && ConfigFileEntry.dot_in_ip6_addr == 1)\r
434 {\r
435 rb_strlcat(new_client->host, ".", sizeof(new_client->host));\r
436 }\r
437#endif\r
438\r
439 new_client->localClient->F = F;\r
440 add_to_cli_fd_hash(new_client);\r
441 new_client->localClient->listener = listener;\r
442 new_client->localClient->ssl_ctl = ssl_ctl;\r
443 if(ssl_ctl != NULL || rb_fd_ssl(F))\r
444 SetSSL(new_client);\r
445\r
446 ++listener->ref_count;\r
447\r
448 start_auth(new_client);\r
449}\r
450\r
451static time_t last_oper_notice = 0;\r
452\r
453static const char *toofast = "ERROR :Reconnecting too fast, throttled.\r\n";\r
454\r
455static int\r
456accept_precallback(rb_fde_t *F, struct sockaddr *addr, rb_socklen_t addrlen, void *data)\r
457{\r
458 struct Listener *listener = (struct Listener *)data;\r
459 char buf[BUFSIZE];\r
460 struct ConfItem *aconf;\r
461\r
462 if(listener->ssl && (!ssl_ok || !get_ssld_count()))\r
463 {\r
464 fprintf(stderr, "closed socket\n");\r
465 rb_close(F);\r
466 return 0;\r
467 }\r
468 \r
469 if((maxconnections - 10) < rb_get_fd(F)) /* XXX this is kinda bogus */\r
470 {\r
471 ++ServerStats.is_ref;\r
472 /*\r
473 * slow down the whining to opers bit\r
474 */\r
475 if((last_oper_notice + 20) <= rb_current_time())\r
476 {\r
477 sendto_realops_flags(UMODE_ALL, L_ALL,\r
478 "All connections in use. (%s)",\r
479 get_listener_name(listener));\r
480 last_oper_notice = rb_current_time();\r
481 }\r
482 \r
483 rb_write(F, "ERROR :All connections in use\r\n", 32);\r
484 rb_close(F);\r
485 /* Re-register a new IO request for the next accept .. */\r
486 return 0;\r
487 }\r
488\r
489 aconf = find_dline(addr);\r
490 if(aconf != NULL && (aconf->status & CONF_EXEMPTDLINE))\r
491 return 1;\r
492 \r
493 /* Do an initial check we aren't connecting too fast or with too many\r
494 * from this IP... */\r
495 if(aconf != NULL)\r
496 {\r
497 ServerStats.is_ref++;\r
498 \r
499 if(ConfigFileEntry.dline_with_reason)\r
500 {\r
501 if (rb_snprintf(buf, sizeof(buf), "ERROR :*** Banned: %s\r\n", aconf->passwd) >= (int)(sizeof(buf)-1))\r
502 {\r
503 buf[sizeof(buf) - 3] = '\r';\r
504 buf[sizeof(buf) - 2] = '\n';\r
505 buf[sizeof(buf) - 1] = '\0';\r
506 }\r
507 }\r
508 else\r
509 strcpy(buf, "ERROR :You have been D-lined.\r\n");\r
510 \r
511 rb_write(F, buf, strlen(buf));\r
512 rb_close(F);\r
513 return 0;\r
514 }\r
515\r
516 if(check_reject(F, addr))\r
517 return 0;\r
518 \r
519 if(throttle_add(addr))\r
520 {\r
521 rb_write(F, toofast, strlen(toofast));\r
522 rb_close(F);\r
523 return 0;\r
524 }\r
525\r
526 return 1;\r
527}\r
528\r
529static void\r
530accept_ssld(rb_fde_t *F, struct sockaddr *addr, struct sockaddr *laddr, struct Listener *listener)\r
531{\r
532 ssl_ctl_t *ctl;\r
533 rb_fde_t *xF[2];\r
534 rb_socketpair(AF_UNIX, SOCK_STREAM, 0, &xF[0], &xF[1], "Incoming ssld Connection");\r
535 ctl = start_ssld_accept(F, xF[1], rb_get_fd(xF[0])); /* this will close F for us */\r
536 add_connection(listener, xF[0], addr, laddr, ctl);\r
537}\r
538\r
539static void\r
540accept_callback(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t addrlen, void *data)\r
541{\r
542 struct Listener *listener = data;\r
543 struct rb_sockaddr_storage lip;\r
544 unsigned int locallen = sizeof(struct rb_sockaddr_storage);\r
545 \r
546 ServerStats.is_ac++;\r
547 if(getsockname(rb_get_fd(F), (struct sockaddr *) &lip, &locallen) < 0)\r
548 {\r
549 /* this shouldn't fail so... */\r
550 /* XXX add logging of this */\r
551 rb_close(F);\r
552 }\r
553 if(listener->ssl)\r
554 accept_ssld(F, addr, (struct sockaddr *)&lip, listener);\r
555 else\r
556 add_connection(listener, F, addr, (struct sockaddr *)&lip, NULL);\r
557}\r