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