ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/src/listener.c
Revision: 7667
Committed: Wed Jul 20 17:09:36 2016 UTC (7 years, 8 months ago) by michael
Content type: text/x-csrc
File size: 10237 byte(s)
Log Message:
- Fixed svn properties

File Contents

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

Properties

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