ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/listener.c
Revision: 2691
Committed: Tue Dec 17 18:55:59 2013 UTC (10 years, 4 months ago) by michael
Content type: text/x-csrc
File size: 11365 byte(s)
Log Message:
- Avoid magically sized temporary buffers

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

Properties

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