ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/branches/newio/src/listener.c
Revision: 2430
Committed: Thu Aug 1 19:01:06 2013 UTC (10 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 14309 byte(s)
Log Message:
- ioengine changes as of 01AUG13

File Contents

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

Properties

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