ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-7.2/src/listener.c
Revision: 1011
Committed: Fri Sep 18 10:14:09 2009 UTC (14 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 11397 byte(s)
Log Message:
- move list manipulation routines from tools.c to list.c
- mem_frob() goes to memory.c
- sort out redundant/unneeded header includes

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 "listener.h"
28 #include "client.h"
29 #include "fdlist.h"
30 #include "irc_string.h"
31 #include "sprintf_irc.h"
32 #include "ircd.h"
33 #include "ircd_defs.h"
34 #include "s_bsd.h"
35 #include "irc_getnameinfo.h"
36 #include "irc_getaddrinfo.h"
37 #include "numeric.h"
38 #include "s_conf.h"
39 #include "send.h"
40 #include "memory.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 return buf;
90 }
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 char buf[6];
102 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 if (listener->flags & LISTENER_SERVER)
117 *p++ = 'S';
118 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 IsAdmin(source_p) ? listener->name : me.name,
124 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 report_error(L_ALL, "setting SO_REUSEADDR for listener %s:%s",
173 get_listener_name(listener), errno);
174 fd_close(&listener->fd);
175 return 0;
176 }
177
178 /*
179 * Bind a port to listen for new connections if port is non-null,
180 * else assume it is already open and try get something from it.
181 */
182 lsin.ss_port = htons(listener->port);
183
184 if (bind(listener->fd.fd, (struct sockaddr *)&lsin, lsin.ss_len))
185 {
186 report_error(L_ALL, "binding listener socket %s:%s",
187 get_listener_name(listener), errno);
188 fd_close(&listener->fd);
189 return 0;
190 }
191
192 if (listen(listener->fd.fd, HYBRID_SOMAXCONN))
193 {
194 report_error(L_ALL, "listen failed for %s:%s",
195 get_listener_name(listener), errno);
196 fd_close(&listener->fd);
197 return 0;
198 }
199
200 /* Listen completion events are READ events .. */
201
202 accept_connection(&listener->fd, listener);
203 return 1;
204 }
205
206 static struct Listener *
207 find_listener(int port, struct irc_ssaddr *addr)
208 {
209 dlink_node *ptr;
210 struct Listener *listener = NULL;
211 struct Listener *last_closed = NULL;
212
213 DLINK_FOREACH(ptr, ListenerPollList.head)
214 {
215 listener = ptr->data;
216
217 if ((port == listener->port) &&
218 (!memcmp(addr, &listener->addr, sizeof(struct irc_ssaddr))))
219 {
220 /* Try to return an open listener, otherwise reuse a closed one */
221 if (!listener->fd.flags.open)
222 last_closed = listener;
223 else
224 return (listener);
225 }
226 }
227
228 return (last_closed);
229 }
230
231 /*
232 * add_listener- create a new listener
233 * port - the port number to listen on
234 * vhost_ip - if non-null must contain a valid IP address string in
235 * the format "255.255.255.255"
236 */
237 void
238 add_listener(int port, const char *vhost_ip, unsigned int flags)
239 {
240 struct Listener *listener;
241 struct irc_ssaddr vaddr;
242 struct addrinfo hints, *res;
243 char portname[PORTNAMELEN + 1];
244 #ifdef IPV6
245 static short int pass = 0; /* if ipv6 and no address specified we need to
246 have two listeners; one for each protocol. */
247 #endif
248
249 /*
250 * if no or invalid port in conf line, don't bother
251 */
252 if (!(port > 0 && port <= 0xFFFF))
253 return;
254
255 memset(&vaddr, 0, sizeof(vaddr));
256
257 /* Set up the hints structure */
258 memset(&hints, 0, sizeof(hints));
259 hints.ai_family = AF_UNSPEC;
260 hints.ai_socktype = SOCK_STREAM;
261 /* Get us ready for a bind() and don't bother doing dns lookup */
262 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
263
264 #ifdef IPV6
265 if (ServerInfo.can_use_v6)
266 {
267 snprintf(portname, PORTNAMELEN, "%d", port);
268 irc_getaddrinfo("::", portname, &hints, &res);
269 vaddr.ss.ss_family = AF_INET6;
270 assert(res != NULL);
271
272 memcpy((struct sockaddr*)&vaddr, res->ai_addr, res->ai_addrlen);
273 vaddr.ss_port = port;
274 vaddr.ss_len = res->ai_addrlen;
275 irc_freeaddrinfo(res);
276 }
277 else
278 #endif
279 {
280 struct sockaddr_in *v4 = (struct sockaddr_in*) &vaddr;
281 v4->sin_addr.s_addr = INADDR_ANY;
282 vaddr.ss.ss_family = AF_INET;
283 vaddr.ss_len = sizeof(struct sockaddr_in);
284 v4->sin_port = htons(port);
285 }
286
287 snprintf(portname, PORTNAMELEN, "%d", port);
288
289 if (vhost_ip)
290 {
291 if (irc_getaddrinfo(vhost_ip, portname, &hints, &res))
292 return;
293
294 assert(res != NULL);
295
296 memcpy((struct sockaddr*)&vaddr, res->ai_addr, res->ai_addrlen);
297 vaddr.ss_port = port;
298 vaddr.ss_len = res->ai_addrlen;
299 irc_freeaddrinfo(res);
300 }
301 #ifdef IPV6
302 else if (pass == 0 && ServerInfo.can_use_v6)
303 {
304 /* add the ipv4 listener if we havent already */
305 pass = 1;
306 add_listener(port, "0.0.0.0", flags);
307 }
308 pass = 0;
309 #endif
310
311 if ((listener = find_listener(port, &vaddr)))
312 {
313 listener->flags = flags;
314 if (listener->fd.flags.open)
315 return;
316 }
317 else
318 {
319 listener = make_listener(port, &vaddr);
320 dlinkAdd(listener, &listener->listener_node, &ListenerPollList);
321 listener->flags = flags;
322 }
323
324 if (inetport(listener))
325 listener->active = 1;
326 else
327 close_listener(listener);
328 }
329
330 /*
331 * close_listener - close a single listener
332 */
333 static void
334 close_listener(struct Listener *listener)
335 {
336 assert(listener != NULL);
337
338 if (listener == NULL)
339 return;
340
341 if (listener->fd.flags.open)
342 fd_close(&listener->fd);
343
344 listener->active = 0;
345
346 if (listener->ref_count)
347 return;
348
349 free_listener(listener);
350 }
351
352 /*
353 * close_listeners - close and free all listeners that are not being used
354 */
355 void
356 close_listeners(void)
357 {
358 dlink_node *ptr;
359 dlink_node *next_ptr;
360
361 /* close all 'extra' listening ports we have */
362 DLINK_FOREACH_SAFE(ptr, next_ptr, ListenerPollList.head)
363 close_listener(ptr->data);
364 }
365
366 #define TOOFAST_WARNING "ERROR :Trying to reconnect too fast.\r\n"
367 #define DLINE_WARNING "ERROR :You have been D-lined.\r\n"
368
369 static void
370 accept_connection(fde_t *pfd, void *data)
371 {
372 static time_t last_oper_notice = 0;
373 struct irc_ssaddr addr;
374 int fd;
375 int pe;
376 struct Listener *listener = data;
377
378 memset(&addr, 0, sizeof(addr));
379
380 assert(listener != NULL);
381 if (listener == NULL)
382 return;
383
384 /* There may be many reasons for error return, but
385 * in otherwise correctly working environment the
386 * probable cause is running out of file descriptors
387 * (EMFILE, ENFILE or others?). The man pages for
388 * accept don't seem to list these as possible,
389 * although it's obvious that it may happen here.
390 * Thus no specific errors are tested at this
391 * point, just assume that connections cannot
392 * be accepted until some old is closed first.
393 */
394 while ((fd = comm_accept(listener, &addr)) != -1)
395 {
396 /*
397 * check for connection limit
398 */
399 if (number_fd > hard_fdlimit - 10)
400 {
401 ++ServerStats.is_ref;
402
403 /*
404 * slow down the whining to opers bit
405 */
406 if ((last_oper_notice + 20) <= CurrentTime)
407 {
408 sendto_realops_flags(UMODE_ALL, L_ALL, "All connections in use. (%s)",
409 get_listener_name(listener));
410 last_oper_notice = CurrentTime;
411 }
412
413 if (!(listener->flags & LISTENER_SSL))
414 send(fd, "ERROR :All connections in use\r\n", 32, 0);
415
416 close(fd);
417 break; /* jump out and re-register a new io request */
418 }
419
420 /*
421 * Do an initial check we aren't connecting too fast or with too many
422 * from this IP...
423 */
424 if ((pe = conf_connect_allowed(&addr, addr.ss.ss_family)) != 0)
425 {
426 ++ServerStats.is_ref;
427
428 if (!(listener->flags & LISTENER_SSL))
429 switch (pe)
430 {
431 case BANNED_CLIENT:
432 send(fd, DLINE_WARNING, sizeof(DLINE_WARNING)-1, 0);
433 break;
434 case TOO_FAST:
435 send(fd, TOOFAST_WARNING, sizeof(TOOFAST_WARNING)-1, 0);
436 break;
437 }
438
439 close(fd);
440 continue; /* drop the one and keep on clearing the queue */
441 }
442
443 ++ServerStats.is_ac;
444 add_connection(listener, &addr, fd);
445 }
446
447 /* Re-register a new IO request for the next accept .. */
448 comm_setselect(&listener->fd, COMM_SELECT_READ, accept_connection,
449 listener, 0);
450 }

Properties

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