/[svn]/ircd-hybrid/branches/8.2.x/src/listener.c
ViewVC logotype

Contents of /ircd-hybrid/branches/8.2.x/src/listener.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 10003 - (show annotations)
Sat Jun 26 15:15:13 2021 UTC (17 months ago) by michael
File MIME type: text/x-chdr
File size: 11108 byte(s)
- listener.c:listener_finalize(): fix `STATS P` displaying issue with compressed ipv6 loopback/inaddr_any

1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1999-2021 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file listener.c
23 * \brief Implementation for handling listening sockets.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include <netinet/in_systm.h>
29 #include <netinet/ip.h>
30 #include <netinet/tcp.h>
31 #include "list.h"
32 #include "listener.h"
33 #include "client.h"
34 #include "fdlist.h"
35 #include "irc_string.h"
36 #include "ircd.h"
37 #include "ircd_defs.h"
38 #include "s_bsd.h"
39 #include "conf.h"
40 #include "send.h"
41 #include "memory.h"
42
43 #define TOOFAST_WARNING "ERROR :Your host is trying to (re)connect too fast -- throttled.\r\n"
44 #define DLINE_WARNING "ERROR :You have been D-lined.\r\n"
45 #define ALLINUSE_WARNING "ERROR :All connections in use\r\n"
46
47 static dlink_list listener_list;
48
49 const dlink_list *
50 listener_get_list(void)
51 {
52 return &listener_list;
53 }
54
55 static struct Listener *
56 listener_make(const int port, const struct irc_ssaddr *addr)
57 {
58 struct Listener *listener = xcalloc(sizeof(*listener));
59
60 listener->port = port;
61 listener->addr = *addr;
62 dlinkAdd(listener, &listener->node, &listener_list);
63
64 return listener;
65 }
66
67 static void
68 listener_free(struct Listener *listener)
69 {
70 dlinkDelete(&listener->node, &listener_list);
71 xfree(listener->name);
72 xfree(listener);
73 }
74
75 /*
76 * listener_get_name - return displayable listener name and port
77 * returns "host.foo.org/6667" for a given listener
78 */
79 const char *
80 listener_get_name(const struct Listener *listener)
81 {
82 static char buf[HOSTLEN + HOSTIPLEN + PORTNAMELEN + 4]; /* +4 for [,/,],\0 */
83
84 snprintf(buf, sizeof(buf), "%s[%s/%u]", me.name,
85 listener->name, listener->port);
86 return buf;
87 }
88
89 bool
90 listener_has_flag(const struct Listener *listener, unsigned int flags)
91 {
92 if (listener && (listener->flags & flags))
93 return true;
94 return false;
95 }
96
97 void
98 listener_count_memory(unsigned int *count, size_t *bytes)
99 {
100 (*count) = dlink_list_length(&listener_list);
101 (*bytes) = *count * sizeof(struct Listener);
102 }
103
104 static void
105 listener_accept_connection(fde_t *F, void *data)
106 {
107 static uintmax_t rate = 0;
108 struct irc_ssaddr addr;
109 int fd;
110 int pe;
111 struct Listener *const listener = data;
112
113 assert(listener);
114 assert(listener->fd == F);
115 assert(listener->fd);
116 assert(listener->fd->flags.open);
117
118 /* There may be many reasons for error return, but
119 * in otherwise correctly working environment the
120 * probable cause is running out of file descriptors
121 * (EMFILE, ENFILE or others?). The man pages for
122 * accept don't seem to list these as possible,
123 * although it's obvious that it may happen here.
124 * Thus no specific errors are tested at this
125 * point, just assume that connections cannot
126 * be accepted until some old is closed first.
127 */
128 while ((fd = comm_accept(listener->fd, &addr)) != -1)
129 {
130 /*
131 * check for connection limit
132 */
133 if (number_fd > hard_fdlimit - 10)
134 {
135 ++ServerStats.is_ref;
136 sendto_realops_flags_ratelimited(&rate, "All connections in use. (%s)",
137 listener_get_name(listener));
138
139 if (!(listener->flags & LISTENER_TLS))
140 send(fd, ALLINUSE_WARNING, sizeof(ALLINUSE_WARNING) - 1, 0);
141
142 close(fd);
143 break; /* jump out and re-register a new io request */
144 }
145
146 /*
147 * Do an initial check we aren't connecting too fast or with too many
148 * from this IP...
149 */
150 if ((pe = conf_connect_allowed(&addr)))
151 {
152 ++ServerStats.is_ref;
153
154 if (!(listener->flags & LISTENER_TLS))
155 {
156 switch (pe)
157 {
158 case BANNED_CLIENT:
159 send(fd, DLINE_WARNING, sizeof(DLINE_WARNING)-1, 0);
160 break;
161 case TOO_FAST:
162 send(fd, TOOFAST_WARNING, sizeof(TOOFAST_WARNING)-1, 0);
163 break;
164 }
165 }
166
167 close(fd);
168 continue; /* drop the one and keep on clearing the queue */
169 }
170
171 ++ServerStats.is_ac;
172 add_connection(listener, &addr, fd);
173 }
174
175 /* Re-register a new IO request for the next accept .. */
176 comm_setselect(listener->fd, COMM_SELECT_READ, listener_accept_connection,
177 listener, 0);
178 }
179
180
181 /*
182 * inetport - create a listener socket in the AF_INET or AF_INET6 domain,
183 * bind it to the port given in 'port' and listen to it
184 * returns true (1) if successful false (0) on error.
185 *
186 * If the operating system has a define for SOMAXCONN, use it, otherwise
187 * use HYBRID_SOMAXCONN
188 */
189 #ifdef SOMAXCONN
190 #undef HYBRID_SOMAXCONN
191 #define HYBRID_SOMAXCONN SOMAXCONN
192 #endif
193
194 static bool
195 listener_finalize(struct Listener *listener)
196 {
197 char buf[HOSTIPLEN + 1];
198 const socklen_t opt = 1;
199
200 getnameinfo((const struct sockaddr *)&listener->addr, listener->addr.ss_len,
201 buf, sizeof(buf), NULL, 0, NI_NUMERICHOST);
202
203 if (buf[0] == ':' && buf[1] == ':')
204 {
205 memmove(buf + 1, buf, sizeof(buf) - 1);
206 buf[0] = '0';
207 }
208
209 listener->name = xstrdup(buf);
210
211 /*
212 * At first, open a new socket
213 */
214 int fd = comm_socket(listener->addr.ss.ss_family, SOCK_STREAM, 0);
215 if (fd == -1)
216 {
217 report_error(L_ALL, "opening listener socket %s:%s",
218 listener_get_name(listener), errno);
219 return false;
220 }
221
222 #ifdef IPV6_V6ONLY
223 if (listener->addr.ss.ss_family == AF_INET6 &&
224 IN6_IS_ADDR_UNSPECIFIED(&((struct sockaddr_in6 *)&listener->addr)->sin6_addr))
225 {
226 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)))
227 {
228 report_error(L_ALL, "setting IPV6_V6ONLY for listener %s:%s",
229 listener_get_name(listener), errno);
230 close(fd);
231 return false;
232 }
233 }
234 #endif
235
236 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
237 {
238 report_error(L_ALL, "setting SO_REUSEADDR for listener %s:%s",
239 listener_get_name(listener), errno);
240 close(fd);
241 return false;
242 }
243
244 /*
245 * Bind a port to listen for new connections if port is non-null,
246 * else assume it is already open and try get something from it.
247 */
248 if (bind(fd, (const struct sockaddr *)&listener->addr, listener->addr.ss_len))
249 {
250 report_error(L_ALL, "binding listener socket %s:%s",
251 listener_get_name(listener), errno);
252 close(fd);
253 return false;
254 }
255
256 if (listen(fd, HYBRID_SOMAXCONN))
257 {
258 report_error(L_ALL, "listen failed for %s:%s",
259 listener_get_name(listener), errno);
260 close(fd);
261 return false;
262 }
263
264 #ifdef TCP_DEFER_ACCEPT
265 if (listener_has_flag(listener, LISTENER_DEFER))
266 {
267 int timeout = 1;
268
269 setsockopt(fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, sizeof(timeout));
270 }
271 #endif
272 #ifdef SO_ACCEPTFILTER
273 if (listener_has_flag(listener, LISTENER_DEFER))
274 {
275 struct accept_filter_arg afa;
276
277 memset(&afa, 0, sizeof(afa));
278 strlcpy(afa.af_name, "dataready", sizeof(afa.af_name));
279 setsockopt(fd, SOL_SOCKET, SO_ACCEPTFILTER, &afa, sizeof(afa));
280 }
281 #endif
282
283 listener->fd = fd_open(fd, true, "Listener socket");
284
285 /* Listen completion events are READ events .. */
286
287 listener_accept_connection(listener->fd, listener);
288 return true;
289 }
290
291 static struct Listener *
292 listener_find(int port, struct irc_ssaddr *addr)
293 {
294 dlink_node *node;
295 struct Listener *listener = NULL;
296 struct Listener *last_closed = NULL;
297
298 DLINK_FOREACH(node, listener_list.head)
299 {
300 listener = node->data;
301
302 if ((port == listener->port) &&
303 (!memcmp(addr, &listener->addr, sizeof(*addr))))
304 {
305 /* Try to return an open listener, otherwise reuse a closed one */
306 if (listener->fd)
307 {
308 assert(listener->fd->flags.open);
309 last_closed = listener;
310 }
311 else
312 return (listener);
313 }
314 }
315
316 return (last_closed);
317 }
318
319 /*
320 * close_listener - close a single listener
321 */
322 static void
323 listener_close(struct Listener *listener)
324 {
325 if (listener->fd)
326 {
327 assert(listener->fd->flags.open);
328
329 fd_close(listener->fd);
330 listener->fd = NULL;
331 }
332
333 listener->active = false;
334
335 if (listener->ref_count)
336 return;
337
338 listener_free(listener);
339 }
340
341 /*
342 * listener_close_marked - close and free all listeners that are not being used
343 */
344 void
345 listener_close_marked(void)
346 {
347 dlink_node *node, *node_next;
348
349 /* close all 'extra' listening ports we have */
350 DLINK_FOREACH_SAFE(node, node_next, listener_list.head)
351 listener_close(node->data);
352 }
353
354 void
355 listener_release(struct Listener *listener)
356 {
357 assert(listener->ref_count > 0);
358
359 if (--listener->ref_count == 0 && listener->active == false)
360 listener_close(listener);
361 }
362
363 /*
364 * listener_add- create a new listener
365 * port - the port number to listen on
366 * vhost_ip - if non-null must contain a valid IP address string in
367 * the format "255.255.255.255"
368 */
369 void
370 listener_add(int port, const char *vhost_ip, unsigned int flags)
371 {
372 struct irc_ssaddr vaddr;
373 struct addrinfo hints, *res;
374 char portname[PORTNAMELEN + 1];
375 static short int pass = 0; /* if ipv6 and no address specified we need to
376 have two listeners; one for each protocol. */
377
378 /*
379 * if no or invalid port in conf line, don't bother
380 */
381 if (!(port > 0 && port <= 0xFFFF))
382 return;
383
384 /* Set up the hints structure */
385 memset(&hints, 0, sizeof(hints));
386 hints.ai_family = AF_UNSPEC;
387 hints.ai_socktype = SOCK_STREAM;
388 /* Get us ready for a bind() and don't bother doing dns lookup */
389 hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
390
391 snprintf(portname, sizeof(portname), "%d", port);
392
393 getaddrinfo("::", portname, &hints, &res);
394 assert(res);
395
396 memcpy((struct sockaddr *)&vaddr, res->ai_addr, res->ai_addrlen);
397 vaddr.ss_len = res->ai_addrlen;
398 freeaddrinfo(res);
399
400 if (!EmptyString(vhost_ip))
401 {
402 if (getaddrinfo(vhost_ip, portname, &hints, &res))
403 return;
404
405 assert(res);
406
407 memcpy((struct sockaddr *)&vaddr, res->ai_addr, res->ai_addrlen);
408 vaddr.ss_len = res->ai_addrlen;
409 freeaddrinfo(res);
410 }
411 else if (pass == 0)
412 {
413 /* add the ipv4 listener if we havent already */
414 pass = 1;
415 listener_add(port, "0.0.0.0", flags);
416 }
417
418 pass = 0;
419
420 struct Listener *listener = listener_find(port, &vaddr);
421 if (listener)
422 {
423 listener->flags = flags;
424
425 if (listener->fd)
426 {
427 assert(listener->fd->flags.open);
428 return;
429 }
430 }
431 else
432 {
433 listener = listener_make(port, &vaddr);
434 listener->flags = flags;
435 }
436
437 if (listener_finalize(listener) == true)
438 listener->active = true;
439 else
440 listener_close(listener);
441 }

Properties

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

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