ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/src/listener.c
Revision: 1155
Committed: Tue Aug 9 20:27:45 2011 UTC (12 years, 8 months ago) by michael
Content type: text/x-csrc
Original Path: ircd-hybrid/src/listener.c
File size: 11152 byte(s)
Log Message:
- recreate "trunk"

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

Properties

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