Author: Isomer <isomer@coders.net>
[ircu2.10.12-pk.git] / ircd / listener.c
1 /************************************************************************
2  *   IRC - Internet Relay Chat, src/listener.c
3  *   Copyright (C) 1999 Thomas Helvey <tomh@inxpress.net>
4  *
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)
8  *   any later version.
9  *
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.
14  *
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.
18  *
19  *  $Id$
20  */
21 #include "listener.h"
22 #include "client.h"
23 #include "ircd.h"
24 #include "ircd_alloc.h"
25 #include "ircd_features.h"
26 #include "ircd_osdep.h"
27 #include "ircd_reply.h"
28 #include "ircd_string.h"
29 #include "numeric.h"
30 #include "s_bsd.h"
31 #include "s_conf.h"
32 #include "s_misc.h"
33 #include "send.h"
34 #include "sprintf_irc.h"
35 #include "sys.h"         /* MAXCLIENTS */
36
37 #include <assert.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <errno.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <netdb.h>
44 #include <sys/socket.h>
45 #include <arpa/inet.h>
46
47 #ifndef INADDR_NONE
48 #define INADDR_NONE ((unsigned int) 0xffffffff)
49 #endif
50
51 struct Listener* ListenerPollList = 0;
52
53 static struct Listener* make_listener(int port, struct in_addr addr)
54 {
55   struct Listener* listener = 
56     (struct Listener*) MyMalloc(sizeof(struct Listener));
57   assert(0 != listener);
58
59   memset(listener, 0, sizeof(struct Listener));
60
61   listener->fd          = -1;
62   listener->port        = port;
63   listener->addr.s_addr = addr.s_addr;
64
65 #ifdef NULL_POINTER_NOT_ZERO
66   listener->next = NULL;
67   listener->conf = NULL;
68 #endif
69   return listener;
70 }
71
72 static void free_listener(struct Listener* listener)
73 {
74   assert(0 != listener);
75   MyFree(listener);
76 }
77
78 #define PORTNAMELEN 10  /* ":31337" */
79
80 /*
81  * get_listener_name - return displayable listener name and port
82  * returns "host.foo.org:6667" for a given listener
83  */
84 const char* get_listener_name(const struct Listener* listener)
85 {
86   static char buf[HOSTLEN + PORTNAMELEN + 4];
87   assert(0 != listener);
88   sprintf_irc(buf, "%s:%u", cli_name(&me), listener->port);
89   return buf;
90 }
91
92 /*
93  * count_listener_memory - count memory and listeners
94  */
95 void count_listener_memory(int* count_out, size_t* size_out)
96 {
97   struct Listener* l;
98   int              count = 0;
99   assert(0 != count_out);
100   assert(0 != size_out);
101   for (l = ListenerPollList; l; l = l->next)
102     ++count;
103   *count_out = count;
104   *size_out  = count * sizeof(struct Listener);
105 }
106   
107 /*
108  * show_ports - send port listing to a client
109  * inputs       - pointer to client to show ports to
110  * output       - none
111  * side effects - show ports
112  * author       - Dianora
113  */
114 void show_ports(struct Client* sptr, int show_hidden, int port, int count)
115 {
116   struct Listener* listener = 0;
117   char             flags[8];
118   assert(0 != sptr);
119
120   for (listener = ListenerPollList; listener; listener = listener->next) {
121     if (port && port != listener->port)
122       continue;
123     flags[0] = (listener->server) ? 'S' : 'C';
124     if (listener->hidden) {
125       if (!show_hidden)
126         continue;
127       flags[1] = 'H';
128       flags[2] = '\0';
129     }
130     else
131       flags[1] = '\0';
132
133     send_reply(sptr, RPL_STATSPLINE, listener->port, listener->ref_count,
134                flags, (listener->active) ? "active" : "disabled");
135     if (--count == 0)
136       break;
137   }
138 }
139
140 /*
141  * inetport - create a listener socket in the AF_INET domain, 
142  * bind it to the port given in 'port' and listen to it  
143  * returns true (1) if successful false (0) on error.
144  *
145  * If the operating system has a define for SOMAXCONN, use it, otherwise
146  * use HYBRID_SOMAXCONN -Dianora
147  * NOTE: Do this in os_xxxx.c files
148  */
149 #ifdef SOMAXCONN
150 #define HYBRID_SOMAXCONN SOMAXCONN
151 #else
152 #define HYBRID_SOMAXCONN 64
153 #endif
154
155 static int inetport(struct Listener* listener)
156 {
157   struct sockaddr_in sin;
158   int                fd;
159
160   /*
161    * At first, open a new socket
162    */
163   if (-1 == (fd = socket(AF_INET, SOCK_STREAM, 0))) {
164     report_error(SOCKET_ERROR_MSG, get_listener_name(listener), errno);
165     return 0;
166   }
167   else if (fd > MAXCLIENTS - 1) {
168     report_error(CONNLIMIT_ERROR_MSG, get_listener_name(listener), 0);
169     close(fd);
170     return 0;
171   }
172
173   if (!os_set_reuseaddr(fd)) {
174     report_error(REUSEADDR_ERROR_MSG, get_listener_name(listener), errno);
175     close(fd);
176     return 0;
177   }
178   /*
179    * Bind a port to listen for new connections if port is non-null,
180    * else assume it is already open and try get something from it.
181    */
182   memset(&sin, 0, sizeof(sin));
183   sin.sin_family = AF_INET;
184   sin.sin_addr   = listener->addr;
185   sin.sin_port   = htons(listener->port);
186
187   if (bind(fd, (struct sockaddr*) &sin, sizeof(sin))) {
188     report_error(BIND_ERROR_MSG, get_listener_name(listener), errno);
189     close(fd);
190     return 0;
191   }
192   /*
193    * Set the buffer sizes for the listener. Accepted connections
194    * inherit the accepting sockets settings for SO_RCVBUF S_SNDBUF
195    * The window size is set during the SYN ACK so setting it anywhere
196    * else has no effect whatsoever on the connection.
197    * NOTE: this must be set before listen is called
198    */
199   if (!os_set_sockbufs(fd, (listener->server) ? SERVER_TCP_WINDOW : CLIENT_TCP_WINDOW)) {
200     report_error(SETBUFS_ERROR_MSG, get_listener_name(listener), errno);
201     close(fd);
202     return 0;
203   }
204   if (!os_set_listen(fd, HYBRID_SOMAXCONN)) {
205     report_error(LISTEN_ERROR_MSG, get_listener_name(listener), errno);
206     close(fd);
207     return 0;
208   }
209   /*
210    * XXX - this should always work, performance will suck if it doesn't
211    */
212   if (!os_set_nonblocking(fd)) {
213     report_error(NONB_ERROR_MSG, get_listener_name(listener), errno);
214     close(fd);
215     return 0;
216   }
217   /*
218    * Set the TOS bits - this is nonfatal if it doesn't stick.
219    */
220   if (!os_set_tos(fd,feature_int((listener->server)?FEAT_TOS_SERVER : FEAT_TOS_CLIENT))) {
221     report_error(TOS_ERROR_MSG, get_listener_name(listener), errno);
222   }
223   listener->fd = fd;
224
225   return 1;
226 }
227
228 /*
229  * find_listener - find a listener in the list
230  *
231  * XXX - this function does N comparisons so if the list is huge
232  * we may want to do something else for this. (rehash and init use this)
233  */
234 static struct Listener* find_listener(int port, struct in_addr addr)
235 {
236   struct Listener* listener;
237   for (listener = ListenerPollList; listener; listener = listener->next) {
238     if (port == listener->port && addr.s_addr == listener->addr.s_addr)
239       return listener;
240   }
241   return 0;
242 }
243
244 /*
245  * set_listener_mask - set the connection mask for this listener
246  */
247 static void set_listener_mask(struct Listener* listener, const char* mask)
248 {
249   int  ad[4];
250   char ipname[20];
251
252   assert(0 != listener);
253
254   if (EmptyString(mask) || 0 == strcmp(mask, "*")) {
255     listener->mask.s_addr = 0;
256     return;
257   }
258   ad[0] = ad[1] = ad[2] = ad[3] = 0;
259   /*
260    * do it this way because building ip# from separate values for each
261    * byte requires endian knowledge or some nasty messing. Also means
262    * easy conversion of "*" 0.0.0.0 or 134.* to 134.0.0.0 :-)
263    */
264   sscanf(mask, "%d.%d.%d.%d", &ad[0], &ad[1], &ad[2], &ad[3]);
265   sprintf_irc(ipname, "%d.%d.%d.%d", ad[0], ad[1], ad[2], ad[3]);
266   listener->mask.s_addr = inet_addr(ipname);
267 }
268
269 /*
270  * connection_allowed - spin through mask and addr passed to see if connect 
271  * allowed on a listener, uses mask generated by set_listener_mask
272  */
273 static int connection_allowed(const char* addr, const char* mask)
274 {
275   int i = 4;
276   for ( ; i > 0; --i) {
277     if (*mask && *addr != *mask)
278       break;
279     ++addr;
280     ++mask;
281   }
282   return (0 == i);
283 }
284
285
286 /*
287  * add_listener- create a new listener 
288  * port - the port number to listen on
289  * vhost_ip - if non-null must contain a valid IP address string in
290  * the format "255.255.255.255"
291  */
292 void add_listener(int port, const char* vhost_ip, const char* mask,
293                   int is_server, int is_hidden) 
294 {
295   struct Listener* listener;
296   struct in_addr   vaddr;
297
298   /*
299    * if no port in conf line, don't bother
300    */
301   if (0 == port)
302     return;
303
304   vaddr.s_addr = INADDR_ANY;
305
306   if (!EmptyString(vhost_ip) && strcmp(vhost_ip,"*") != 0) {
307     vaddr.s_addr = inet_addr(vhost_ip);
308     if (INADDR_NONE == vaddr.s_addr)
309       return;
310   }
311
312   if ((listener = find_listener(port, vaddr))) {
313     /*
314      * set active flag and change connect mask here, it's the only thing 
315      * that can change on a rehash
316      */
317     listener->active = 1;
318     set_listener_mask(listener, mask);
319     listener->hidden = is_hidden;
320     listener->server = is_server;
321     return;
322   }
323
324   listener = make_listener(port, vaddr);
325
326   if (inetport(listener)) {
327     listener->active = 1;
328     set_listener_mask(listener, mask);
329     listener->hidden = is_hidden;
330     listener->server = is_server;
331     listener->next   = ListenerPollList;
332     ListenerPollList = listener; 
333   }
334   else
335     free_listener(listener);
336 }
337
338 /*
339  * mark_listeners_closing - iterate through listeners and mark them as
340  * inactive
341  */
342 void mark_listeners_closing(void)
343 {
344   struct Listener* listener;
345   for (listener = ListenerPollList; listener; listener = listener->next)
346     listener->active = 0;
347 }
348
349 /*
350  * close_listener - close a single listener
351  */
352 void close_listener(struct Listener* listener)
353 {
354   assert(0 != listener);
355   /*
356    * remove from listener list
357    */
358   if (listener == ListenerPollList)
359     ListenerPollList = listener->next;
360   else {
361     struct Listener* prev = ListenerPollList;
362     for ( ; prev; prev = prev->next) {
363       if (listener == prev->next) {
364         prev->next = listener->next;
365         break; 
366       }
367     }
368   }
369   if (-1 < listener->fd)
370     close(listener->fd);
371   free_listener(listener);
372 }
373  
374 /*
375  * close_listeners - close and free all listeners that are not being used
376  */
377 void close_listeners()
378 {
379   struct Listener* listener;
380   struct Listener* listener_next = 0;
381   /*
382    * close all 'extra' listening ports we have
383    */
384   for (listener = ListenerPollList; listener; listener = listener_next) {
385     listener_next = listener->next;
386     if (0 == listener->active && 0 == listener->ref_count)
387       close_listener(listener);
388   }
389 }
390
391 void release_listener(struct Listener* listener)
392 {
393   assert(0 != listener);
394   assert(0 < listener->ref_count);
395   if (0 == --listener->ref_count && !listener->active)
396     close_listener(listener);
397 }
398
399 /*
400  * accept_connection - accept a connection on a listener
401  */
402 void accept_connection(struct Listener* listener)
403 {
404   struct sockaddr_in addr = { 0 };
405   unsigned int       addrlen = sizeof(struct sockaddr_in);
406   int                fd;
407
408   assert(0 != listener);
409
410   listener->last_accept = CurrentTime;
411   /*
412    * There may be many reasons for error return, but
413    * in otherwise correctly working environment the
414    * probable cause is running out of file descriptors
415    * (EMFILE, ENFILE or others?). The man pages for
416    * accept don't seem to list these as possible,
417    * although it's obvious that it may happen here.
418    * Thus no specific errors are tested at this
419    * point, just assume that connections cannot
420    * be accepted until some old is closed first.
421    */
422   if (-1 == (fd = accept(listener->fd, (struct sockaddr*) &addr, &addrlen))) {
423     /* Lotsa admins seem to have problems with not giving enough file descriptors
424      * to their server so we'll add a generic warning mechanism here.  If it
425      * turns out too many messages are generated for meaningless reasons we
426      * can filter them back.
427      */
428     sendto_opmask_butone(0, SNO_TCPCOMMON, "Unable to accept connection: %m");
429     return;
430   }
431   /*
432    * check for connection limit
433    */
434   if (fd > MAXCLIENTS - 1) {
435     ++ServerStats->is_ref;
436     send(fd, "ERROR :All connections in use\r\n", 32, 0);
437     close(fd);
438     return;
439   }
440   /*
441    * check to see if listener is shutting down
442    */
443   if (!listener->active) {
444     ++ServerStats->is_ref;
445     send(fd, "ERROR :Use another port\r\n", 25, 0);
446     close(fd);
447     return;
448   }
449   /*
450    * check to see if connection is allowed for this address mask
451    */
452   if (!connection_allowed((const char*) &addr, (const char*) &listener->mask)) {
453     ++ServerStats->is_ref;
454     send(fd, "ERROR :Use another port\r\n", 25, 0);
455     close(fd);
456     return;
457   }
458 #if 0
459   /*
460    * check conf for ip address access
461    */
462   if (!conf_connect_allowed(addr.sin_addr)) {
463     ++ServerStats->is_ref;
464     send(fd, "ERROR :Not authorized\r\n", 23, 0);
465     close(fd);
466     return;
467   }
468 #endif
469   ++ServerStats->is_ac;
470   nextping = CurrentTime;
471
472   add_connection(listener, fd);
473 }
474
475