ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid-8/src/listener.c
Revision: 900
Committed: Sun Nov 4 13:49:52 2007 UTC (16 years, 4 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

# 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 "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 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 #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 return 0;
179 }
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 return 0;
196 }
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 return 0;
207 }
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 while ((fd = comm_accept(listener, &addr)) != -1)
404 {
405 /*
406 * check for connection limit
407 */
408 if (number_fd > hard_fdlimit - 10)
409 {
410 ++ServerStats.is_ref;
411
412 /*
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 /*
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 {
438 ++ServerStats.is_ref;
439
440 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 ++ServerStats.is_ac;
460 add_connection(listener, &addr, fd);
461 }
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