1 /************************************************************************
2 * IRC - Internet Relay Chat, src/listener.c
3 * Copyright (C) 1999 Thomas Helvey <tomh@inxpress.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 1, or (at your option)
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #include "ircd_alloc.h"
27 #include "ircd_events.h"
28 #include "ircd_features.h"
29 #include "ircd_osdep.h"
30 #include "ircd_reply.h"
31 #include "ircd_snprintf.h"
32 #include "ircd_string.h"
40 #include "sys.h" /* MAXCLIENTS */
49 #include <sys/socket.h>
50 #include <arpa/inet.h>
53 #define INADDR_NONE ((unsigned int) 0xffffffff)
56 struct Listener* ListenerPollList = 0;
58 static void accept_connection(struct Event* ev);
60 static struct Listener* make_listener(int port, const struct irc_in_addr *addr)
62 struct Listener* listener =
63 (struct Listener*) MyMalloc(sizeof(struct Listener));
64 assert(0 != listener);
66 memset(listener, 0, sizeof(struct Listener));
69 listener->addr.port = port;
70 memcpy(&listener->addr.addr, addr, sizeof(listener->addr.addr));
72 #ifdef NULL_POINTER_NOT_ZERO
73 listener->next = NULL;
74 listener->conf = NULL;
79 static void free_listener(struct Listener* listener)
81 assert(0 != listener);
85 #define PORTNAMELEN 10 /* ":31337" */
88 * get_listener_name - return displayable listener name and port
89 * returns "host.foo.org:6667" for a given listener
91 const char* get_listener_name(const struct Listener* listener)
93 static char buf[HOSTLEN + PORTNAMELEN + 4];
94 assert(0 != listener);
95 ircd_snprintf(0, buf, sizeof(buf), "%s:%u", cli_name(&me), listener->addr.port);
100 * count_listener_memory - count memory and listeners
102 void count_listener_memory(int* count_out, size_t* size_out)
106 assert(0 != count_out);
107 assert(0 != size_out);
108 for (l = ListenerPollList; l; l = l->next)
111 *size_out = count * sizeof(struct Listener);
115 * show_ports - send port listing to a client
116 * inputs - pointer to client to show ports to
118 * side effects - show ports
121 void show_ports(struct Client* sptr, const struct StatDesc* sd,
124 struct Listener *listener = 0;
126 int show_hidden = IsOper(sptr);
127 int count = (IsOper(sptr) || MyUser(sptr)) ? 100 : 8;
135 for (listener = ListenerPollList; listener; listener = listener->next) {
136 if (port && port != listener->addr.port)
138 flags[0] = (listener->server) ? 'S' : 'C';
139 if (listener->hidden) {
148 send_reply(sptr, RPL_STATSPLINE, listener->addr.port, listener->ref_count,
149 flags, (listener->active) ? "active" : "disabled");
156 * inetport - create a listener socket in the AF_INET domain,
157 * bind it to the port given in 'port' and listen to it
158 * returns true (1) if successful false (0) on error.
160 * If the operating system has a define for SOMAXCONN, use it, otherwise
161 * use HYBRID_SOMAXCONN -Dianora
162 * NOTE: Do this in os_xxxx.c files
165 #define HYBRID_SOMAXCONN SOMAXCONN
167 #define HYBRID_SOMAXCONN 64
170 static int inetport(struct Listener* listener)
175 * At first, open a new socket
177 fd = os_socket(&listener->addr, SOCK_STREAM, get_listener_name(listener));
181 * Set the buffer sizes for the listener. Accepted connections
182 * inherit the accepting sockets settings for SO_RCVBUF S_SNDBUF
183 * The window size is set during the SYN ACK so setting it anywhere
184 * else has no effect whatsoever on the connection.
185 * NOTE: this must be set before listen is called
187 if (!os_set_sockbufs(fd,
188 (listener->server) ? feature_int(FEAT_SOCKSENDBUF) : CLIENT_TCP_WINDOW,
189 (listener->server) ? feature_int(FEAT_SOCKRECVBUF) : CLIENT_TCP_WINDOW)) {
190 report_error(SETBUFS_ERROR_MSG, get_listener_name(listener), errno);
194 if (!os_set_listen(fd, HYBRID_SOMAXCONN)) {
195 report_error(LISTEN_ERROR_MSG, get_listener_name(listener), errno);
200 * Set the TOS bits - this is nonfatal if it doesn't stick.
202 if (!os_set_tos(fd,feature_int((listener->server)?FEAT_TOS_SERVER : FEAT_TOS_CLIENT))) {
203 report_error(TOS_ERROR_MSG, get_listener_name(listener), errno);
206 if (!socket_add(&listener->socket, accept_connection, (void*) listener,
207 SS_LISTENING, 0, fd)) {
208 /* Error should already have been reported to the logs */
219 * find_listener - find a listener in the list
221 * XXX - this function does N comparisons so if the list is huge
222 * we may want to do something else for this. (rehash and init use this)
224 static struct Listener* find_listener(int port, const struct irc_in_addr *addr)
226 struct Listener* listener;
227 for (listener = ListenerPollList; listener; listener = listener->next) {
228 if (port == listener->addr.port && !memcmp(addr, &listener->addr.addr, sizeof(*addr)))
235 * add_listener- create a new listener
236 * port - the port number to listen on
237 * vhost_ip - if non-null must contain a valid IP address string in
238 * the format "255.255.255.255"
240 void add_listener(int port, const char* vhost_ip, const char* mask,
241 int is_server, int is_hidden)
243 struct Listener* listener;
244 struct irc_in_addr vaddr;
247 * if no port in conf line, don't bother
252 memset(&vaddr, 0, sizeof(vaddr));
254 if (!EmptyString(vhost_ip)
255 && strcmp(vhost_ip, "*")
256 && !ircd_aton(&vaddr, vhost_ip))
259 listener = find_listener(port, &vaddr);
261 listener = make_listener(port, &vaddr);
262 listener->active = 1;
263 listener->hidden = is_hidden;
264 listener->server = is_server;
266 ipmask_parse(mask, &listener->mask, &listener->mask_bits);
268 listener->mask_bits = 0;
270 if (listener->fd >= 0) {
271 /* If the listener is already open, do not try to re-open. */
273 else if (inetport(listener)) {
274 listener->next = ListenerPollList;
275 ListenerPollList = listener;
278 free_listener(listener);
282 * mark_listeners_closing - iterate through listeners and mark them as
285 void mark_listeners_closing(void)
287 struct Listener* listener;
288 for (listener = ListenerPollList; listener; listener = listener->next)
289 listener->active = 0;
293 * close_listener - close a single listener
295 void close_listener(struct Listener* listener)
297 assert(0 != listener);
299 * remove from listener list
301 if (listener == ListenerPollList)
302 ListenerPollList = listener->next;
304 struct Listener* prev = ListenerPollList;
305 for ( ; prev; prev = prev->next) {
306 if (listener == prev->next) {
307 prev->next = listener->next;
312 if (-1 < listener->fd)
314 socket_del(&listener->socket);
318 * close_listeners - close and free all listeners that are not being used
320 void close_listeners()
322 struct Listener* listener;
323 struct Listener* listener_next = 0;
325 * close all 'extra' listening ports we have
327 for (listener = ListenerPollList; listener; listener = listener_next) {
328 listener_next = listener->next;
329 if (0 == listener->active && 0 == listener->ref_count)
330 close_listener(listener);
334 void release_listener(struct Listener* listener)
336 assert(0 != listener);
337 assert(0 < listener->ref_count);
338 if (0 == --listener->ref_count && !listener->active)
339 close_listener(listener);
343 * accept_connection - accept a connection on a listener
345 static void accept_connection(struct Event* ev)
347 struct Listener* listener;
348 struct irc_sockaddr addr;
351 assert(0 != ev_socket(ev));
352 assert(0 != s_data(ev_socket(ev)));
354 listener = (struct Listener*) s_data(ev_socket(ev));
356 if (ev_type(ev) == ET_DESTROY) /* being destroyed */
357 free_listener(listener);
359 assert(ev_type(ev) == ET_ACCEPT || ev_type(ev) == ET_ERROR);
361 listener->last_accept = CurrentTime;
363 * There may be many reasons for error return, but
364 * in otherwise correctly working environment the
365 * probable cause is running out of file descriptors
366 * (EMFILE, ENFILE or others?). The man pages for
367 * accept don't seem to list these as possible,
368 * although it's obvious that it may happen here.
369 * Thus no specific errors are tested at this
370 * point, just assume that connections cannot
371 * be accepted until some old is closed first.
373 * This piece of code implements multi-accept, based
374 * on the idea that poll/select can only be efficient,
375 * if we succeed in handling all available events,
376 * i.e. accept all pending connections.
378 * http://www.hpl.hp.com/techreports/2000/HPL-2000-174.html
382 if ((fd = os_accept(listener->fd, &addr)) == -1)
384 if (errno == EAGAIN ||
386 errno == EWOULDBLOCK)
389 /* Lotsa admins seem to have problems with not giving enough file
390 * descriptors to their server so we'll add a generic warning mechanism
391 * here. If it turns out too many messages are generated for
392 * meaningless reasons we can filter them back.
394 sendto_opmask_butone(0, SNO_TCPCOMMON,
395 "Unable to accept connection: %m");
399 * check for connection limit. If this fd exceeds the limit,
400 * all further accept()ed connections will also exceed it.
401 * Enable the server to clear out other connections before
402 * continuing to accept() new connections.
404 if (fd > MAXCLIENTS - 1)
406 ++ServerStats->is_ref;
407 send(fd, "ERROR :All connections in use\r\n", 32, 0);
412 * check to see if listener is shutting down. Continue
413 * to accept(), because it makes sense to clear our the
414 * socket's queue as fast as possible.
416 if (!listener->active)
418 ++ServerStats->is_ref;
419 send(fd, "ERROR :Use another port\r\n", 25, 0);
424 * check to see if connection is allowed for this address mask
426 if (!ipmask_check(&addr.addr, &listener->mask, listener->mask_bits))
428 ++ServerStats->is_ref;
429 send(fd, "ERROR :Use another port\r\n", 25, 0);
433 ++ServerStats->is_ac;
434 /* nextping = CurrentTime; */
435 add_connection(listener, fd);