ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/listener.c
Revision: 6367
Committed: Wed Aug 19 10:25:00 2015 UTC (8 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 10079 byte(s)
Log Message:
- Rename several functions in listener.c to comply with naming convention

File Contents

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

Properties

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