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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1242 - (show annotations)
Fri Sep 30 08:17:50 2011 UTC (8 years, 10 months ago) by michael
File MIME type: text/x-chdr
File size: 11024 byte(s)
- listener.c: remove outdated comment. remove useless header include

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

Properties

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

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