/[svn]/branches/newio/src/listener.c
ViewVC logotype

Contents of /branches/newio/src/listener.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2692 - (show annotations)
Tue Dec 17 18:56:15 2013 UTC (8 years, 11 months ago) by michael
File MIME type: text/x-chdr
File size: 14650 byte(s)
- Avoid magically sized temporary buffers

1 /*
2 * ircd-hybrid: an advanced Internet Relay Chat Daemon(ircd).
3 * listener.c: Listens on a port.
4 *
5 * Copyright (C) 2002 by the past and present ircd coders, and others.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 * USA
21 *
22 * $Id$
23 */
24
25 #include "stdinc.h"
26 #include "list.h"
27 #include "ioengine.h"
28 #include "listener.h"
29 #include "client.h"
30 #include "irc_string.h"
31 #include "ircd.h"
32 #include "ircd_defs.h"
33 #include "s_bsd.h"
34 #include "numeric.h"
35 #include "conf.h"
36 #include "send.h"
37 #include "memory.h"
38 #include "ssl.h"
39
40
41 static dlink_list listener_list;
42
43 static void accept_connection(struct Event *);
44 static void listener_close(struct Listener *);
45
46 static struct Listener *
47 make_listener(const int port, const struct irc_ssaddr *addr)
48 {
49 struct Listener *listener = MyMalloc(sizeof(struct Listener));
50
51 listener->fd_v4 = -1;
52 listener->fd_v6 = -1;
53 listener->port = port;
54 memcpy(&listener->addr, addr, sizeof(struct irc_ssaddr));
55
56 return listener;
57 }
58
59 /** Deallocate a Listener structure.
60 * @param[in] listener Listener to be freed.
61 */
62 static void
63 listener_free(struct Listener *listener)
64 {
65 assert(listener);
66 assert(listener->active == 0);
67 assert(listener->ref_count == 0);
68
69 MyFree(listener);
70 }
71
72 /*
73 * get_listener_name - return displayable listener name and port
74 * returns "host.foo.org/6667" for a given listener
75 */
76 const char *
77 get_listener_name(const struct Listener *const listener)
78 {
79 static char buf[HOSTLEN + HOSTLEN + PORTNAMELEN + 4];
80
81 snprintf(buf, sizeof(buf), "%s[%s/%u]", me.name,
82 listener->name, listener->port);
83 return buf;
84 }
85
86 /* show_ports()
87 *
88 * inputs - pointer to client to show ports to
89 * output - none
90 * side effects - send port listing to a client
91 */
92 void
93 show_ports(struct Client *source_p)
94 {
95 char buf[IRCD_BUFSIZE];
96 char *p = NULL;
97 const dlink_node *ptr = NULL;
98
99 DLINK_FOREACH(ptr, listener_list.head)
100 {
101 const struct Listener *listener = ptr->data;
102 p = buf;
103
104 if (listener->flags & LISTENER_HIDDEN)
105 {
106 if (!HasUMode(source_p, UMODE_ADMIN))
107 continue;
108 *p++ = 'H';
109 }
110
111 if (listener->flags & LISTENER_SERVER)
112 *p++ = 'S';
113 if (listener->flags & LISTENER_SSL)
114 *p++ = 's';
115
116 if (listener->flags & LISTENER_IPV4)
117 {
118 *p++ = '4';
119 if (listener->fd_v4 < 0)
120 *p++ = '-';
121 }
122
123 if (listener->flags & LISTENER_IPV6)
124 {
125 *p++ = '6';
126 if (listener->fd_v6 < 0)
127 *p++ = '-';
128 }
129
130 *p = '\0';
131
132 if (HasUMode(source_p, UMODE_ADMIN) &&
133 (MyClient(source_p) || !ConfigServerHide.hide_server_ips))
134 sendto_one(source_p, form_str(RPL_STATSPLINE),
135 me.name, source_p->name, 'P', listener->port,
136 listener->name,
137 listener->ref_count, buf,
138 listener->active ? "active" : "disabled");
139 else
140 sendto_one(source_p, form_str(RPL_STATSPLINE),
141 me.name, source_p->name, 'P', listener->port,
142 me.name, listener->ref_count, buf,
143 listener->active ? "active" : "disabled");
144 }
145 }
146
147 /*
148 * inetport - create a listener socket in the AF_INET or AF_INET6 domain,
149 * bind it to the port given in 'port' and listen to it
150 * returns true (1) if successful false (0) on error.
151 *
152 * If the operating system has a define for SOMAXCONN, use it, otherwise
153 * use HYBRID_SOMAXCONN
154 */
155 #ifdef SOMAXCONN
156 #define HYBRID_SOMAXCONN SOMAXCONN
157 #else
158 /** Maximum length of socket connection backlog. */
159 #define HYBRID_SOMAXCONN 64
160 #endif
161
162 /** Set or update socket options for \a listener.
163 * @param[in] listener Listener to determine socket option values.
164 * @param[in] fd File descriptor being updated.
165 * @return Non-zero on success, zero on failure.
166 */
167 static int
168 set_listener_options(struct Listener *listener, int fd)
169 {
170 /*
171 * Set the buffer sizes for the listener. Accepted connections
172 * inherit the accepting sockets settings for SO_RCVBUF S_SNDBUF
173 * The window size is set during the SYN ACK so setting it anywhere
174 * else has no effect whatsoever on the connection.
175 * NOTE: this must be set before listen is called
176 */
177 if (!os_set_sockbufs(fd, CLIENT_TCP_WINDOW, /* XXX */
178 CLIENT_TCP_WINDOW))
179 {
180 report_error(L_ALL, SETBUFS_ERROR_MSG, get_listener_name(listener), errno);
181 close(fd);
182 return 0;
183 }
184
185 #ifdef NOTYET
186 /*
187 * Set the TOS bits - this is nonfatal if it doesn't stick.
188 */
189 if (!os_set_tos(fd,feature_int(is_server ? FEAT_TOS_SERVER : FEAT_TOS_CLIENT)))
190 report_error(L_ALL, TOS_ERROR_MSG, get_listener_name(listener), errno);
191 #endif
192 return 1;
193 }
194
195 /** Open listening socket for \a listener.
196 * @param[in,out] listener Listener to make a socket for.
197 * @return Non-zero on success, zero on failure.
198 */
199 static int
200 inetport(struct Listener *listener, int family)
201 {
202 struct Socket *sock = NULL;
203 struct irc_ssaddr lsin;
204 int fd = 0;
205
206 memset(&lsin, 0, sizeof(lsin));
207 memcpy(&lsin, &listener->addr, sizeof(lsin));
208
209 getnameinfo((struct sockaddr *)&lsin, lsin.ss_len, listener->name,
210 sizeof(listener->name), NULL, 0, NI_NUMERICHOST);
211
212 /*
213 * At first, open a new socket
214 */
215 if ((fd = os_socket(family, SOCK_STREAM, get_listener_name(listener))) < 0)
216 return -1;
217
218 /*
219 * Bind a port to listen for new connections if port is non-null,
220 * else assume it is already open and try get something from it.
221 */
222 lsin.ss_port = htons(listener->port);
223
224 if (bind(fd, (struct sockaddr *)&lsin, lsin.ss_len))
225 {
226 report_error(L_ALL, BIND_ERROR_MSG, get_listener_name(listener), errno);
227 close(fd);
228 return -1;
229 }
230
231 if (!os_set_listen(fd, HYBRID_SOMAXCONN))
232 {
233 report_error(L_ALL, LISTEN_ERROR_MSG, get_listener_name(listener), errno);
234 close(fd);
235 return -1;
236 }
237
238 if (!set_listener_options(listener, fd))
239 return -1;
240
241 sock = (family == AF_INET) ? &listener->socket_v4 : &listener->socket_v6;
242
243 if (!socket_add(sock, accept_connection, listener, SS_LISTENING, 0, fd))
244 {
245 /* Error should already have been reported to the logs */
246 close(fd);
247 return -1;
248 }
249
250 return fd;
251 }
252
253 static struct Listener *
254 listener_find(const int port, const struct irc_ssaddr *addr)
255 {
256 dlink_node *ptr = NULL;
257
258 DLINK_FOREACH(ptr, listener_list.head)
259 {
260 struct Listener *listener = ptr->data;
261
262 if ((port == listener->port) &&
263 (!memcmp(addr, &listener->addr, sizeof(struct irc_ssaddr))))
264 return listener;
265 }
266
267 return NULL;
268 }
269
270 /*
271 * add_listener- create a new listener
272 * port - the port number to listen on
273 * vhost_ip - if non-null must contain a valid IP address string in
274 * the format "255.255.255.255"
275 */
276 void
277 add_listener(int port, const char *vhost_ip, unsigned int flags)
278 {
279 int okay = 0;
280 int new_listener = 0;
281 int fd;
282 struct Listener *listener;
283 struct irc_ssaddr vaddr;
284 struct addrinfo hints, *res;
285 char portname[PORTNAMELEN + 1];
286 const struct sockaddr_in *v4;
287 const struct sockaddr_in6 *v6;
288
289 /*
290 * If no or invalid port in conf line, don't bother
291 */
292 if (!(port > 0 && port <= 0xFFFF))
293 return;
294
295 memset(&vaddr, 0, sizeof(vaddr));
296 memset(&hints, 0, sizeof(hints));
297
298 hints.ai_family = AF_UNSPEC;
299 hints.ai_socktype = SOCK_STREAM;
300 /* Get us ready for a bind() and don't bother doing dns lookup */
301 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
302
303 snprintf(portname, sizeof(portname), "%d", port);
304
305 if (!EmptyString(vhost_ip))
306 {
307 if (getaddrinfo(vhost_ip, portname, &hints, &res))
308 return;
309
310 assert(res != NULL);
311
312 memcpy((struct sockaddr *)&vaddr, res->ai_addr, res->ai_addrlen);
313 vaddr.ss_port = port;
314 vaddr.ss_len = res->ai_addrlen;
315 vaddr.ss.ss_family = res->ai_family;
316 freeaddrinfo(res);
317 }
318
319 if (!(listener = listener_find(port, &vaddr)))
320 {
321 new_listener = 1;
322 listener = make_listener(port, &vaddr);
323 }
324
325 listener->active = 1;
326 listener->flags = flags;
327
328 v4 = (const struct sockaddr_in *)&vaddr;
329 v6 = (const struct sockaddr_in6 *)&vaddr;
330
331 if ((listener->flags & LISTENER_IPV6) &&
332 (IN6_IS_ADDR_UNSPECIFIED(&v6->sin6_addr) || vaddr.ss.ss_family == AF_INET6))
333 {
334 if (listener->fd_v6 >= 0)
335 {
336 set_listener_options(listener, listener->fd_v6);
337 okay = 1;
338 }
339 else if ((fd = inetport(listener, AF_INET6)) >= 0)
340 {
341 listener->fd_v6 = fd;
342 okay = 1;
343 }
344 }
345 else if (listener->fd_v6 > -1)
346 {
347 close(listener->fd_v6);
348 socket_del(&listener->socket_v6);
349 listener->fd_v6 = -1;
350 }
351
352 if ((listener->flags & LISTENER_IPV4) &&
353 (v4->sin_addr.s_addr == INADDR_NONE || vaddr.ss.ss_family == AF_INET))
354 {
355 if (listener->fd_v4 >= 0)
356 {
357 set_listener_options(listener, listener->fd_v4);
358 okay = 1;
359 }
360 else if ((fd = inetport(listener, AF_INET)) >= 0)
361 {
362 listener->fd_v4 = fd;
363 okay = 1;
364 }
365 }
366 else if (listener->fd_v4 > -1)
367 {
368 close(listener->fd_v4);
369 socket_del(&listener->socket_v4);
370 listener->fd_v4 = -1;
371 }
372
373 if (!okay)
374 listener_free(listener);
375 else if (new_listener)
376 dlinkAdd(listener, &listener->node, &listener_list);
377 }
378
379 /** Mark all listeners as closing (inactive).
380 * This is done so unused listeners are closed after a rehash.
381 */
382 void
383 listener_mark_closing(void)
384 {
385 dlink_node *ptr = NULL;
386
387 DLINK_FOREACH(ptr, listener_list.head)
388 ((struct Listener *)ptr->data)->active = 0;
389 }
390
391
392 /** Close a single listener.
393 * @param[in] listener Listener to close.
394 */
395 static void
396 listener_close(struct Listener *listener)
397 {
398 assert(listener);
399 assert(listener->active == 0);
400 assert(listener->ref_count == 0);
401
402 /*
403 * Remove from listener list
404 */
405 dlinkDelete(&listener->node, &listener_list);
406
407 if (listener->fd_v4 > -1)
408 {
409 close(listener->fd_v4);
410 socket_del(&listener->socket_v4);
411 listener->fd_v4 = -1;
412 }
413
414 if (listener->fd_v6 > -1)
415 {
416 close(listener->fd_v6);
417 socket_del(&listener->socket_v6);
418 listener->fd_v6 = -1;
419 }
420
421 listener_free(listener);
422 }
423
424 /** Close all inactive listeners. */
425 void
426 listener_close_marked(void)
427 {
428 dlink_node *ptr = NULL, *ptr_next = NULL;
429
430 /*
431 * Close all 'extra' listening ports we have
432 */
433 DLINK_FOREACH_SAFE(ptr, ptr_next, listener_list.head)
434 {
435 struct Listener *listener = ptr->data;
436
437 if (!listener->active && !listener->ref_count)
438 listener_close(listener);
439 }
440 }
441
442 /** Dereference the listener previously associated with a client.
443 * @param[in] listener Listener to dereference.
444 */
445 void
446 listener_release(struct Listener *listener)
447 {
448 assert(listener);
449 assert(listener->ref_count > 0);
450
451 if (--listener->ref_count == 0 && !listener->active)
452 listener_close(listener);
453 }
454
455
456 #define TOOFAST_WARNING "ERROR :Trying to reconnect too fast.\r\n"
457 #define DLINE_WARNING "ERROR :You have been D-lined.\r\n"
458
459 static void
460 accept_connection(struct Event *ev)
461 {
462 static time_t last_oper_notice = 0;
463 struct irc_ssaddr addr;
464 int fd;
465 int pe;
466 struct Listener *listener = s_data(ev_socket(ev));
467
468 assert(ev_socket(ev));
469 assert(s_data(ev_socket(ev)));
470
471 memset(&addr, 0, sizeof(addr));
472
473 if (ev_type(ev) == ET_DESTROY) /* Being destroyed */
474 return;
475
476 assert(ev_type(ev) == ET_ACCEPT || ev_type(ev) == ET_ERROR);
477
478 /*
479 * There may be many reasons for error return, but
480 * in otherwise correctly working environment the
481 * probable cause is running out of file descriptors
482 * (EMFILE, ENFILE or others?). The man pages for
483 * accept don't seem to list these as possible,
484 * although it's obvious that it may happen here.
485 * Thus no specific errors are tested at this
486 * point, just assume that connections cannot
487 * be accepted until some old is closed first.
488 *
489 * This piece of code implements multi-accept, based
490 * on the idea that poll/select can only be efficient,
491 * if we succeed in handling all available events,
492 * i.e. accept all pending connections.
493 *
494 * http://www.hpl.hp.com/techreports/2000/HPL-2000-174.html
495 */
496 while (1)
497 {
498 if ((fd = os_accept(s_fd(ev_socket(ev)), &addr)) == -1)
499 {
500 if (errno == EAGAIN ||
501 #ifdef EWOULDBLOCK
502 errno == EWOULDBLOCK)
503 #endif
504 return;
505 return;
506 }
507
508 /*
509 * Check for connection limit. If this fd exceeds the limit,
510 * all further accept()ed connections will also exceed it.
511 * Enable the server to clear out other connections before
512 * continuing to accept() new connections.
513 */
514 if (fd > MAXCLIENTS - 1)
515 {
516 ++ServerStats.is_ref;
517
518 /*
519 * slow down the whining to opers bit
520 */
521 if ((last_oper_notice + 20) <= CurrentTime)
522 {
523 sendto_realops_flags(UMODE_ALL, L_ALL, SEND_NOTICE,
524 "All connections in use. (%s)",
525 get_listener_name(listener));
526 last_oper_notice = CurrentTime;
527 }
528
529 if (!(listener->flags & LISTENER_SSL))
530 send(fd, "ERROR :All connections in use\r\n", 32, 0);
531
532 close(fd);
533 return;
534 }
535
536 /*
537 * check to see if listener is shutting down. Continue
538 * to accept(), because it makes sense to clear our the
539 * socket's queue as fast as possible.
540 */
541 if (!listener->active)
542 {
543 ++ServerStats.is_ref;
544 send(fd, "ERROR :Use another port\r\n", 25, 0);
545 close(fd);
546 continue;
547 }
548
549 /*
550 * Do an initial check we aren't connecting too fast or with too many
551 * from this IP...
552 */
553 if ((pe = conf_connect_allowed(&addr, addr.ss.ss_family)) != 0)
554 {
555 ++ServerStats.is_ref;
556
557 if (!(listener->flags & LISTENER_SSL))
558 switch (pe)
559 {
560 case BANNED_CLIENT:
561 send(fd, DLINE_WARNING, sizeof(DLINE_WARNING)-1, 0);
562 break;
563 case TOO_FAST:
564 send(fd, TOOFAST_WARNING, sizeof(TOOFAST_WARNING)-1, 0);
565 break;
566 }
567
568 close(fd);
569 continue; /* drop the one and keep on clearing the queue */
570 }
571
572 ++ServerStats.is_ac;
573 add_connection(listener, fd, &addr);
574 }
575 }

Properties

Name Value
svn:eol-style native
svn:keywords Id Revision

svnadmin@ircd-hybrid.org
ViewVC Help
Powered by ViewVC 1.1.28