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