ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/branches/newio/src/listener.c
Revision: 2392
Committed: Sat Jul 13 22:13:28 2013 UTC (10 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 11285 byte(s)
Log Message:
- ioengine changes as of 14JUL13

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

Properties

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