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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 549 - (hide 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 adx 30 /*
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 knight 31 * $Id$
23 adx 30 */
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 michael 482 IsAdmin(source_p) ? listener->name : me.name,
126 adx 30 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 michael 549 add_connection(listener, &addr, fd);
463 adx 30 }
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