/[svn]/ircd-hybrid-7.2/src/listener.c
ViewVC logotype

Contents of /ircd-hybrid-7.2/src/listener.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 549 - (show annotations)
Thu Apr 20 12:46:18 2006 UTC (14 years, 5 months ago) by michael
File MIME type: text/x-chdr
File size: 11757 byte(s)
- Backported add_connection() speedups

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 "listener.h"
27 #include "client.h"
28 #include "fdlist.h"
29 #include "irc_string.h"
30 #include "sprintf_irc.h"
31 #include "ircd.h"
32 #include "ircd_defs.h"
33 #include "s_bsd.h"
34 #include "irc_getnameinfo.h"
35 #include "irc_getaddrinfo.h"
36 #include "numeric.h"
37 #include "s_conf.h"
38 #include "s_stats.h"
39 #include "send.h"
40 #include "memory.h"
41 #include "tools.h"
42 #ifdef HAVE_LIBCRYPTO
43 #include <openssl/bio.h>
44 #endif
45
46
47 static PF accept_connection;
48
49 static dlink_list ListenerPollList = { NULL, NULL, 0 };
50 static void close_listener(struct Listener *listener);
51
52 static struct Listener *
53 make_listener(int port, struct irc_ssaddr *addr)
54 {
55 struct Listener *listener = MyMalloc(sizeof(struct Listener));
56 assert(listener != 0);
57
58 listener->name = me.name;
59 listener->port = port;
60 memcpy(&listener->addr, addr, sizeof(struct irc_ssaddr));
61
62 return listener;
63 }
64
65 void
66 free_listener(struct Listener *listener)
67 {
68 assert(listener != NULL);
69
70 if (listener == NULL)
71 return;
72
73 dlinkDelete(&listener->listener_node, &ListenerPollList);
74 MyFree(listener);
75 }
76
77 /*
78 * get_listener_name - return displayable listener name and port
79 * returns "host.foo.org:6667" for a given listener
80 */
81 const char *
82 get_listener_name(const struct Listener *listener)
83 {
84 static char buf[HOSTLEN + HOSTLEN + PORTNAMELEN + 4];
85
86 assert(listener != NULL);
87
88 if (listener == NULL)
89 return(NULL);
90
91 ircsprintf(buf, "%s[%s/%u]",
92 me.name, listener->name, listener->port);
93 return(buf);
94 }
95
96 /* show_ports()
97 *
98 * inputs - pointer to client to show ports to
99 * output - none
100 * side effects - send port listing to a client
101 */
102 void
103 show_ports(struct Client *source_p)
104 {
105 char buf[4];
106 char *p = NULL;
107 dlink_node *ptr;
108
109 DLINK_FOREACH(ptr, ListenerPollList.head)
110 {
111 const struct Listener *listener = ptr->data;
112 p = buf;
113
114 if (listener->flags & LISTENER_HIDDEN) {
115 if (!IsAdmin(source_p))
116 continue;
117 *p++ = 'H';
118 }
119
120 if (listener->flags & LISTENER_SSL)
121 *p++ = 's';
122 *p = '\0';
123 sendto_one(source_p, form_str(RPL_STATSPLINE),
124 me.name, source_p->name, 'P', listener->port,
125 IsAdmin(source_p) ? listener->name : me.name,
126 listener->ref_count, buf,
127 listener->active ? "active" : "disabled");
128 }
129 }
130
131 /*
132 * inetport - create a listener socket in the AF_INET or AF_INET6 domain,
133 * bind it to the port given in 'port' and listen to it
134 * returns true (1) if successful false (0) on error.
135 *
136 * If the operating system has a define for SOMAXCONN, use it, otherwise
137 * use HYBRID_SOMAXCONN
138 */
139 #ifdef SOMAXCONN
140 #undef HYBRID_SOMAXCONN
141 #define HYBRID_SOMAXCONN SOMAXCONN
142 #endif
143
144 static int
145 inetport(struct Listener *listener)
146 {
147 struct irc_ssaddr lsin;
148 socklen_t opt = 1;
149
150 /*
151 * At first, open a new socket
152 */
153 if (comm_open(&listener->fd, listener->addr.ss.ss_family, SOCK_STREAM, 0,
154 "Listener socket") == -1)
155 {
156 report_error(L_ALL, "opening listener socket %s:%s",
157 get_listener_name(listener), errno);
158 return 0;
159 }
160
161 memset(&lsin, 0, sizeof(lsin));
162 memcpy(&lsin, &listener->addr, sizeof(struct irc_ssaddr));
163
164 irc_getnameinfo((struct sockaddr*)&lsin, lsin.ss_len, listener->vhost,
165 HOSTLEN, NULL, 0, NI_NUMERICHOST);
166 listener->name = listener->vhost;
167
168 /*
169 * XXX - we don't want to do all this crap for a listener
170 * set_sock_opts(listener);
171 */
172 if (setsockopt(listener->fd.fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
173 {
174 #ifdef _WIN32
175 errno = WSAGetLastError();
176 #endif
177 report_error(L_ALL, "setting SO_REUSEADDR for listener %s:%s",
178 get_listener_name(listener), errno);
179 fd_close(&listener->fd);
180 return(0);
181 }
182
183 /*
184 * Bind a port to listen for new connections if port is non-null,
185 * else assume it is already open and try get something from it.
186 */
187 lsin.ss_port = htons(listener->port);
188
189 if (bind(listener->fd.fd, (struct sockaddr *)&lsin, lsin.ss_len))
190 {
191 #ifdef _WIN32
192 errno = WSAGetLastError();
193 #endif
194 report_error(L_ALL, "binding listener socket %s:%s",
195 get_listener_name(listener), errno);
196 fd_close(&listener->fd);
197 return(0);
198 }
199
200 if (listen(listener->fd.fd, HYBRID_SOMAXCONN))
201 {
202 #ifdef _WIN32
203 errno = WSAGetLastError();
204 #endif
205 report_error(L_ALL, "listen failed for %s:%s",
206 get_listener_name(listener), errno);
207 fd_close(&listener->fd);
208 return(0);
209 }
210
211 /* Listen completion events are READ events .. */
212
213 accept_connection(&listener->fd, listener);
214 return 1;
215 }
216
217 static struct Listener *
218 find_listener(int port, struct irc_ssaddr *addr)
219 {
220 dlink_node *ptr;
221 struct Listener *listener = NULL;
222 struct Listener *last_closed = NULL;
223
224 DLINK_FOREACH(ptr, ListenerPollList.head)
225 {
226 listener = ptr->data;
227
228 if ((port == listener->port) &&
229 (!memcmp(addr, &listener->addr, sizeof(struct irc_ssaddr))))
230 {
231 /* Try to return an open listener, otherwise reuse a closed one */
232 if (!listener->fd.flags.open)
233 last_closed = listener;
234 else
235 return (listener);
236 }
237 }
238
239 return (last_closed);
240 }
241
242 /*
243 * add_listener- create a new listener
244 * port - the port number to listen on
245 * vhost_ip - if non-null must contain a valid IP address string in
246 * the format "255.255.255.255"
247 */
248 void
249 add_listener(int port, const char *vhost_ip, unsigned int flags)
250 {
251 struct Listener *listener;
252 struct irc_ssaddr vaddr;
253 struct addrinfo hints, *res;
254 char portname[PORTNAMELEN + 1];
255 #ifdef IPV6
256 static short int pass = 0; /* if ipv6 and no address specified we need to
257 have two listeners; one for each protocol. */
258 #endif
259
260 /*
261 * if no or invalid port in conf line, don't bother
262 */
263 if (!(port > 0 && port <= 0xFFFF))
264 return;
265
266 memset(&vaddr, 0, sizeof(vaddr));
267
268 /* Set up the hints structure */
269 memset(&hints, 0, sizeof(hints));
270 hints.ai_family = AF_UNSPEC;
271 hints.ai_socktype = SOCK_STREAM;
272 /* Get us ready for a bind() and don't bother doing dns lookup */
273 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
274
275 #ifdef IPV6
276 if (ServerInfo.can_use_v6)
277 {
278 snprintf(portname, PORTNAMELEN, "%d", port);
279 irc_getaddrinfo("::", portname, &hints, &res);
280 vaddr.ss.ss_family = AF_INET6;
281 assert(res != NULL);
282
283 memcpy((struct sockaddr*)&vaddr, res->ai_addr, res->ai_addrlen);
284 vaddr.ss_port = port;
285 vaddr.ss_len = res->ai_addrlen;
286 irc_freeaddrinfo(res);
287 }
288 else
289 #endif
290 {
291 struct sockaddr_in *v4 = (struct sockaddr_in*) &vaddr;
292 v4->sin_addr.s_addr = INADDR_ANY;
293 vaddr.ss.ss_family = AF_INET;
294 vaddr.ss_len = sizeof(struct sockaddr_in);
295 v4->sin_port = htons(port);
296 }
297
298 snprintf(portname, PORTNAMELEN, "%d", port);
299
300 if (vhost_ip)
301 {
302 if (irc_getaddrinfo(vhost_ip, portname, &hints, &res))
303 return;
304
305 assert(res != NULL);
306
307 memcpy((struct sockaddr*)&vaddr, res->ai_addr, res->ai_addrlen);
308 vaddr.ss_port = port;
309 vaddr.ss_len = res->ai_addrlen;
310 irc_freeaddrinfo(res);
311 }
312 #ifdef IPV6
313 else if (pass == 0 && ServerInfo.can_use_v6)
314 {
315 /* add the ipv4 listener if we havent already */
316 pass = 1;
317 add_listener(port, "0.0.0.0", flags);
318 }
319 pass = 0;
320 #endif
321
322 if ((listener = find_listener(port, &vaddr)))
323 {
324 listener->flags = flags;
325 if (listener->fd.flags.open)
326 return;
327 }
328 else
329 {
330 listener = make_listener(port, &vaddr);
331 dlinkAdd(listener, &listener->listener_node, &ListenerPollList);
332 listener->flags = flags;
333 }
334
335 if (inetport(listener))
336 listener->active = 1;
337 else
338 close_listener(listener);
339 }
340
341 /*
342 * close_listener - close a single listener
343 */
344 static void
345 close_listener(struct Listener *listener)
346 {
347 assert(listener != NULL);
348
349 if (listener == NULL)
350 return;
351
352 if (listener->fd.flags.open)
353 fd_close(&listener->fd);
354
355 listener->active = 0;
356
357 if (listener->ref_count)
358 return;
359
360 free_listener(listener);
361 }
362
363 /*
364 * close_listeners - close and free all listeners that are not being used
365 */
366 void
367 close_listeners(void)
368 {
369 dlink_node *ptr;
370 dlink_node *next_ptr;
371
372 /* close all 'extra' listening ports we have */
373 DLINK_FOREACH_SAFE(ptr, next_ptr, ListenerPollList.head)
374 close_listener(ptr->data);
375 }
376
377 #define TOOFAST_WARNING "ERROR :Trying to reconnect too fast.\r\n"
378 #define DLINE_WARNING "ERROR :You have been D-lined.\r\n"
379
380 static void
381 accept_connection(fde_t *pfd, void *data)
382 {
383 static time_t last_oper_notice = 0;
384 struct irc_ssaddr sai;
385 struct irc_ssaddr addr;
386 int fd;
387 int pe;
388 struct Listener *listener = data;
389
390 memset(&sai, 0, sizeof(sai));
391 memset(&addr, 0, sizeof(addr));
392
393 assert(listener != NULL);
394 if (listener == NULL)
395 return;
396
397 /* There may be many reasons for error return, but
398 * in otherwise correctly working environment the
399 * probable cause is running out of file descriptors
400 * (EMFILE, ENFILE or others?). The man pages for
401 * accept don't seem to list these as possible,
402 * although it's obvious that it may happen here.
403 * Thus no specific errors are tested at this
404 * point, just assume that connections cannot
405 * be accepted until some old is closed first.
406 */
407 while ((fd = comm_accept(listener, &sai)) != -1)
408 {
409 memcpy(&addr, &sai, sizeof(struct irc_ssaddr));
410
411 /*
412 * check for connection limit
413 */
414 if (number_fd > hard_fdlimit - 10)
415 {
416 ++ServerStats->is_ref;
417 /*
418 * slow down the whining to opers bit
419 */
420 if ((last_oper_notice + 20) <= CurrentTime)
421 {
422 sendto_realops_flags(UMODE_ALL, L_ALL, "All connections in use. (%s)",
423 get_listener_name(listener));
424 last_oper_notice = CurrentTime;
425 }
426
427 if (!(listener->flags & LISTENER_SSL))
428 send(fd, "ERROR :All connections in use\r\n", 32, 0);
429 #ifdef _WIN32
430 closesocket(fd);
431 #else
432 close(fd);
433 #endif
434 break; /* jump out and re-register a new io request */
435 }
436
437 /* Do an initial check we aren't connecting too fast or with too many
438 * from this IP... */
439 if ((pe = conf_connect_allowed(&addr, sai.ss.ss_family)) != 0)
440 {
441 ServerStats->is_ref++;
442 if (!(listener->flags & LISTENER_SSL))
443 switch (pe)
444 {
445 case BANNED_CLIENT:
446 send(fd, DLINE_WARNING, sizeof(DLINE_WARNING)-1, 0);
447 break;
448 case TOO_FAST:
449 send(fd, TOOFAST_WARNING, sizeof(TOOFAST_WARNING)-1, 0);
450 break;
451 }
452
453 #ifdef _WIN32
454 closesocket(fd);
455 #else
456 close(fd);
457 #endif
458 continue; /* drop the one and keep on clearing the queue */
459 }
460
461 ServerStats->is_ac++;
462 add_connection(listener, &addr, fd);
463 }
464
465 /* Re-register a new IO request for the next accept .. */
466 comm_setselect(&listener->fd, COMM_SELECT_READ, accept_connection,
467 listener, 0);
468 }

Properties

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

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