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