ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/src/listener.c
Revision: 900
Committed: Sun Nov 4 13:49:52 2007 UTC (16 years, 5 months ago) by michael
Content type: text/x-csrc
Original Path: ircd-hybrid-7.2/src/listener.c
File size: 11653 byte(s)
Log Message:
- Added new 'server' option to listener::flags

File Contents

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

Properties

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