]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
11a617e418591b6dbea39a42fa59e9a004ad6529
[user/henk/code/inspircd.git] / src / socket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2007 InspIRCd Development Team
6  * See: http://www.inspircd.org/wiki/index.php/Credits
7  *
8  * This program is free but copyrighted software; see
9  *            the file COPYING for details.
10  *
11  * ---------------------------------------------------
12  */
13
14 #include "inspircd.h"
15 #include <string>
16 #include "configreader.h"
17 #include "socket.h"
18 #include "socketengine.h"
19 #include "wildcard.h"
20
21 using namespace irc::sockets;
22
23 /* Used when comparing CIDR masks for the modulus bits left over.
24  * A lot of ircd's seem to do this:
25  * ((-1) << (8 - (mask % 8)))
26  * But imho, it sucks in comparison to a nice neat lookup table.
27  */
28 const unsigned char inverted_bits[8] = {        0x00, /* 00000000 - 0 bits - never actually used */
29                                 0x80, /* 10000000 - 1 bits */
30                                 0xC0, /* 11000000 - 2 bits */
31                                 0xE0, /* 11100000 - 3 bits */
32                                 0xF0, /* 11110000 - 4 bits */
33                                 0xF8, /* 11111000 - 5 bits */
34                                 0xFC, /* 11111100 - 6 bits */
35                                 0xFE  /* 11111110 - 7 bits */
36 };
37
38
39 ListenSocket::ListenSocket(InspIRCd* Instance, int port, char* addr) : ServerInstance(Instance), desc("plaintext"), bind_addr(addr), bind_port(port)
40 {
41         this->SetFd(OpenTCPSocket(addr));
42         if (this->GetFd() > -1)
43         {
44                 if (!Instance->BindSocket(this->fd,port,addr))
45                         this->fd = -1;
46 #ifdef IPV6
47                 if ((!*addr) || (strchr(addr,':')))
48                         this->family = AF_INET6;
49                 else
50                         this->family = AF_INET;
51 #else
52                 this->family = AF_INET;
53 #endif
54                 Instance->SE->AddFd(this);
55         }
56 }
57
58 ListenSocket::~ListenSocket()
59 {
60         if (this->GetFd() > -1)
61         {
62                 ServerInstance->SE->DelFd(this);
63                 ServerInstance->Log(DEBUG,"Shut down listener on fd %d", this->fd);
64                 if (shutdown(this->fd, 2) || close(this->fd))
65                         ServerInstance->Log(DEBUG,"Failed to cancel listener: %s", strerror(errno));
66                 this->fd = -1;
67         }
68 }
69
70 void ListenSocket::HandleEvent(EventType et, int errornum)
71 {
72         socklen_t uslen, length;                // length of our port number
73         int incomingSockfd, in_port;
74         int clients;
75
76 #ifdef IPV6
77         if (this->family == AF_INET6)
78         {
79                 uslen = sizeof(sockaddr_in6);
80                 length = sizeof(sockaddr_in6);
81         }
82         else
83         {
84                 uslen = sizeof(sockaddr_in);
85                 length = sizeof(sockaddr_in);
86         }
87 #else
88         uslen = sizeof(sockaddr_in);
89         length = sizeof(sockaddr_in);
90 #endif
91
92         /*
93          * This loop may make you wonder 'why' - simple reason. If we just sit here accept()ing until the
94          * metaphorical cows come home, then we could very well end up with unresponsiveness in a ddos style
95          * situation, which is not desirable (to put it mildly!). This will mean we'll stop accepting and get
96          * on with our lives after accepting enough clients to sink a metaphorical battleship. :) -- w00t
97          */
98         for (clients = 0; clients < ServerInstance->Config->MaxConn; clients++)
99         {
100                 sockaddr* sock_us = new sockaddr[2];    // our port number
101                 sockaddr* client = new sockaddr[2];
102         
103                 incomingSockfd = _accept (this->GetFd(), (sockaddr*)client, &length);
104
105                 if ((incomingSockfd > -1) && (!_getsockname(incomingSockfd, sock_us, &uslen)))
106                 {
107                         char buf[MAXBUF];
108 #ifdef IPV6
109                         if (this->family == AF_INET6)
110                         {
111                                 inet_ntop(AF_INET6, &((const sockaddr_in6*)client)->sin6_addr, buf, sizeof(buf));
112                                 in_port = ntohs(((sockaddr_in6*)sock_us)->sin6_port);
113                         }
114                         else
115                         {
116                                 inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf));
117                                 in_port = ntohs(((sockaddr_in*)sock_us)->sin_port);
118                         }
119 #else
120                         inet_ntop(AF_INET, &((const sockaddr_in*)client)->sin_addr, buf, sizeof(buf));
121                         in_port = ntohs(((sockaddr_in*)sock_us)->sin_port);
122 #endif
123                         NonBlocking(incomingSockfd);
124                         if (ServerInstance->Config->GetIOHook(in_port))
125                         {
126                                 try
127                                 {
128                                         ServerInstance->Config->GetIOHook(in_port)->OnRawSocketAccept(incomingSockfd, buf, in_port);
129                                 }
130                                 catch (CoreException& modexcept)
131                                 {
132                                         ServerInstance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
133                                 }
134                         }
135                         ServerInstance->stats->statsAccept++;
136                         userrec::AddClient(ServerInstance, incomingSockfd, in_port, false, this->family, client);
137                 }
138                 else
139                 {
140                         /*
141                          * bail, bail, bail! if we get here, accept failed, meaning something is hardcore wrong.
142                          * cut our losses and don't try soak up any more clients during this loop iteration. -- w00t
143                          */
144                         shutdown(incomingSockfd,2);
145                         close(incomingSockfd);
146                         ServerInstance->stats->statsRefused++;
147                         return;
148                 }
149
150                 delete[] client;
151                 delete[] sock_us;
152         }
153 }
154
155 /* Match raw bytes using CIDR bit matching, used by higher level MatchCIDR() */
156 bool irc::sockets::MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits)
157 {
158         unsigned int modulus = mask_bits % 8; /* Number of whole bytes in the mask */
159         unsigned int divisor = mask_bits / 8; /* Remaining bits in the mask after whole bytes are dealt with */
160
161         /* First compare the whole bytes, if they dont match, return false */
162         if (memcmp(address, mask, divisor))
163                 return false;
164
165         /* Now if there are any remainder bits, we compare them with logic AND */
166         if (modulus)
167                 if ((address[divisor] & inverted_bits[modulus]) != (mask[divisor] & inverted_bits[modulus]))
168                         /* If they dont match, return false */
169                         return false;
170
171         /* The address matches the mask, to mask_bits bits of mask */
172         return true;
173 }
174
175 /* Match CIDR, but dont attempt to match() against leading *!*@ sections */
176 bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask)
177 {
178         return MatchCIDR(address, cidr_mask, false);
179 }
180
181 /* Match CIDR strings, e.g. 127.0.0.1 to 127.0.0.0/8 or 3ffe:1:5:6::8 to 3ffe:1::0/32
182  * If you have a lot of hosts to match, youre probably better off building your mask once
183  * and then using the lower level MatchCIDRBits directly.
184  *
185  * This will also attempt to match any leading usernames or nicknames on the mask, using
186  * match(), when match_with_username is true.
187  */
188 bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username)
189 {
190         unsigned char addr_raw[16];
191         unsigned char mask_raw[16];
192         unsigned int bits = 0;
193         char* mask = NULL;
194
195         /* The caller is trying to match ident@<mask>/bits.
196          * Chop off the ident@ portion, use match() on it
197          * seperately.
198          */
199         if (match_with_username)
200         {
201                 /* Duplicate the strings, and try to find the position
202                  * of the @ symbol in each */
203                 char* address_dupe = strdup(address);
204                 char* cidr_dupe = strdup(cidr_mask);
205         
206                 /* Use strchr not strrchr, because its going to be nearer to the left */
207                 char* username_mask_pos = strrchr(cidr_dupe, '@');
208                 char* username_addr_pos = strrchr(address_dupe, '@');
209
210                 /* Both strings have an @ symbol in them */
211                 if (username_mask_pos && username_addr_pos)
212                 {
213                         /* Zero out the location of the @ symbol */
214                         *username_mask_pos = *username_addr_pos = 0;
215
216                         /* Try and match() the strings before the @
217                          * symbols, and recursively call MatchCIDR without
218                          * username matching enabled to match the host part.
219                          */
220                         bool result = (match(address_dupe, cidr_dupe) && MatchCIDR(username_addr_pos + 1, username_mask_pos + 1, false));
221
222                         /* Free the stuff we created */
223                         free(address_dupe);
224                         free(cidr_dupe);
225
226                         /* Return a result */
227                         return result;
228                 }
229                 else
230                 {
231                         /* One or both didnt have an @ in,
232                          * just match as CIDR
233                          */
234                         free(address_dupe);
235                         free(cidr_dupe);
236                         mask = strdup(cidr_mask);
237                 }
238         }
239         else
240         {
241                 /* Make a copy of the cidr mask string,
242                  * we're going to change it
243                  */
244                 mask = strdup(cidr_mask);
245         }
246
247         in_addr  address_in4;
248         in_addr  mask_in4;
249
250
251         /* Use strrchr for this, its nearer to the right */
252         char* bits_chars = strrchr(mask,'/');
253
254         if (bits_chars)
255         {
256                 bits = atoi(bits_chars + 1);
257                 *bits_chars = 0;
258         }
259         else
260         {
261                 /* No 'number of bits' field! */
262                 free(mask);
263                 return false;
264         }
265
266 #ifdef SUPPORT_IP6LINKS
267         in6_addr address_in6;
268         in6_addr mask_in6;
269
270         if (inet_pton(AF_INET6, address, &address_in6) > 0)
271         {
272                 if (inet_pton(AF_INET6, mask, &mask_in6) > 0)
273                 {
274                         memcpy(&addr_raw, &address_in6.s6_addr, 16);
275                         memcpy(&mask_raw, &mask_in6.s6_addr, 16);
276
277                         if (bits > 128)
278                                 bits = 128;
279                 }
280                 else
281                 {
282                         /* The address was valid ipv6, but the mask
283                          * that goes with it wasnt.
284                          */
285                         free(mask);
286                         return false;
287                 }
288         }
289         else
290 #endif
291         if (inet_pton(AF_INET, address, &address_in4) > 0)
292         {
293                 if (inet_pton(AF_INET, mask, &mask_in4) > 0)
294                 {
295                         memcpy(&addr_raw, &address_in4.s_addr, 4);
296                         memcpy(&mask_raw, &mask_in4.s_addr, 4);
297
298                         if (bits > 32)
299                                 bits = 32;
300                 }
301                 else
302                 {
303                         /* The address was valid ipv4,
304                          * but the mask that went with it wasnt.
305                          */
306                         free(mask);
307                         return false;
308                 }
309         }
310         else
311         {
312                 /* The address was neither ipv4 or ipv6 */
313                 free(mask);
314                 return false;
315         }
316
317         /* Low-level-match the bits in the raw data */
318         free(mask);
319         return MatchCIDRBits(addr_raw, mask_raw, bits);
320 }
321
322 void irc::sockets::Blocking(int s)
323 {
324 #ifndef WIN32
325         int flags = fcntl(s, F_GETFL, 0);
326         fcntl(s, F_SETFL, flags ^ O_NONBLOCK);
327 #else
328         unsigned long opt = 0;
329         ioctlsocket(s, FIONBIO, &opt);
330 #endif
331 }
332
333 void irc::sockets::NonBlocking(int s)
334 {
335 #ifndef WIN32
336         int flags = fcntl(s, F_GETFL, 0);
337         fcntl(s, F_SETFL, flags | O_NONBLOCK);
338 #else
339         unsigned long opt = 1;
340         ioctlsocket(s, FIONBIO, &opt);
341 #endif
342 }
343
344 /** This will bind a socket to a port. It works for UDP/TCP.
345  * It can only bind to IP addresses, if you wish to bind to hostnames
346  * you should first resolve them using class 'Resolver'.
347  */ 
348 bool InspIRCd::BindSocket(int sockfd, int port, char* addr, bool dolisten)
349 {
350         /* We allocate 2 of these, because sockaddr_in6 is larger than sockaddr (ugh, hax) */
351         sockaddr* server = new sockaddr[2];
352         memset(server,0,sizeof(sockaddr)*2);
353
354         int ret, size;
355
356         if (*addr == '*')
357                 *addr = 0;
358
359 #ifdef IPV6
360         if (*addr)
361         {
362                 /* There is an address here. Is it ipv6? */
363                 if (strchr(addr,':'))
364                 {
365                         /* Yes it is */
366                         in6_addr addy;
367                         if (inet_pton(AF_INET6, addr, &addy) < 1)
368                         {
369                                 delete[] server;
370                                 return false;
371                         }
372
373                         ((sockaddr_in6*)server)->sin6_family = AF_INET6;
374                         memcpy(&(((sockaddr_in6*)server)->sin6_addr), &addy, sizeof(in6_addr));
375                         ((sockaddr_in6*)server)->sin6_port = htons(port);
376                         size = sizeof(sockaddr_in6);
377                 }
378                 else
379                 {
380                         /* No, its not */
381                         in_addr addy;
382                         if (inet_pton(AF_INET, addr, &addy) < 1)
383                         {
384                                 delete[] server;
385                                 return false;
386                         }
387
388                         ((sockaddr_in*)server)->sin_family = AF_INET;
389                         ((sockaddr_in*)server)->sin_addr = addy;
390                         ((sockaddr_in*)server)->sin_port = htons(port);
391                         size = sizeof(sockaddr_in);
392                 }
393         }
394         else
395         {
396                 if (port == -1)
397                 {
398                         /* Port -1: Means UDP IPV4 port binding - Special case
399                          * used by DNS engine.
400                          */
401                         ((sockaddr_in*)server)->sin_family = AF_INET;
402                         ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY);
403                         ((sockaddr_in*)server)->sin_port = 0;
404                         size = sizeof(sockaddr_in);
405                 }
406                 else
407                 {
408                         /* Theres no address here, default to ipv6 bind to all */
409                         ((sockaddr_in6*)server)->sin6_family = AF_INET6;
410                         memset(&(((sockaddr_in6*)server)->sin6_addr), 0, sizeof(in6_addr));
411                         ((sockaddr_in6*)server)->sin6_port = htons(port);
412                         size = sizeof(sockaddr_in6);
413                 }
414         }
415 #else
416         /* If we aren't built with ipv6, the choice becomes simple */
417         ((sockaddr_in*)server)->sin_family = AF_INET;
418         if (*addr)
419         {
420                 /* There is an address here. */
421                 in_addr addy;
422                 if (inet_pton(AF_INET, addr, &addy) < 1)
423                 {
424                         delete[] server;
425                         return false;
426                 }
427                 ((sockaddr_in*)server)->sin_addr = addy;
428         }
429         else
430         {
431                 /* Bind ipv4 to all */
432                 ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY);
433         }
434         /* Bind ipv4 port number */
435         ((sockaddr_in*)server)->sin_port = htons(port);
436         size = sizeof(sockaddr_in);
437 #endif
438         ret = bind(sockfd, server, size);
439         delete[] server;
440
441         if (ret < 0)
442         {
443                 return false;
444         }
445         else
446         {
447                 if (dolisten)
448                 {
449                         if (listen(sockfd, Config->MaxConn) == -1)
450                         {
451                                 this->Log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
452                                 return false;
453                         }
454                         else
455                         {
456                                 this->Log(DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
457                                 NonBlocking(sockfd);
458                                 return true;
459                         }
460                 }
461                 else
462                 {
463                         this->Log(DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
464                         return true;
465                 }
466         }
467 }
468
469 // Open a TCP Socket
470 int irc::sockets::OpenTCPSocket(char* addr, int socktype)
471 {
472         int sockfd;
473         int on = 1;
474         struct linger linger = { 0 };
475 #ifdef IPV6
476         if (strchr(addr,':') || (!*addr))
477                 sockfd = socket (PF_INET6, socktype, 0);
478         else
479                 sockfd = socket (PF_INET, socktype, 0);
480         if (sockfd < 0)
481 #else
482         if ((sockfd = socket (PF_INET, socktype, 0)) < 0)
483 #endif
484         {
485                 return ERROR;
486         }
487         else
488         {
489                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
490                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
491                 linger.l_onoff = 1;
492                 linger.l_linger = 1;
493                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger,sizeof(linger));
494                 return (sockfd);
495         }
496 }
497
498 /* XXX: Probably belongs in class InspIRCd */
499 int InspIRCd::BindPorts(bool bail, int &ports_found, FailedPortList &failed_ports)
500 {
501         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
502         int bound = 0;
503         bool started_with_nothing = (Config->ports.size() == 0);
504         std::vector<std::pair<std::string, int> > old_ports;
505
506         /* XXX: Make a copy of the old ip/port pairs here */
507         for (std::vector<ListenSocket*>::iterator o = Config->ports.begin(); o != Config->ports.end(); ++o)
508                 old_ports.push_back(make_pair((*o)->GetIP(), (*o)->GetPort()));
509
510         for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
511         {
512                 Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
513                 Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
514                 Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
515
516                 if ((!*Type) || (!strcmp(Type,"clients")))
517                 {
518                         irc::portparser portrange(configToken, false);
519                         int portno = -1;
520                         while ((portno = portrange.GetToken()))
521                         {
522                                 if (*Addr == '*')
523                                         *Addr = 0;
524
525                                 bool skip = false;
526                                 for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
527                                 {
528                                         if (((*n)->GetIP() == Addr) && ((*n)->GetPort() == portno))
529                                         {
530                                                 skip = true;
531                                                 /* XXX: Here, erase from our copy of the list */
532                                                 for (std::vector<std::pair<std::string, int> >::iterator k = old_ports.begin(); k != old_ports.end(); ++k)
533                                                 {
534                                                         if ((k->first == Addr) && (k->second == portno))
535                                                         {
536                                                                 old_ports.erase(k);
537                                                                 break;
538                                                         }
539                                                 }
540                                         }
541                                 }
542                                 if (!skip)
543                                 {
544                                         ListenSocket* ll = new ListenSocket(this, portno, Addr);
545                                         if (ll->GetFd() > -1)
546                                         {
547                                                 bound++;
548                                                 Config->ports.push_back(ll);
549                                         }
550                                         else
551                                         {
552                                                 failed_ports.push_back(std::make_pair(Addr, portno));
553                                         }
554                                         ports_found++;
555                                 }
556                         }
557                 }
558         }
559
560         /* XXX: Here, anything left in our copy list, close as removed */
561         if (!started_with_nothing)
562         {
563                 for (size_t k = 0; k < old_ports.size(); ++k)
564                 {
565                         for (std::vector<ListenSocket*>::iterator n = Config->ports.begin(); n != Config->ports.end(); ++n)
566                         {
567                                 if (((*n)->GetIP() == old_ports[k].first) && ((*n)->GetPort() == old_ports[k].second))
568                                 {
569                                         this->Log(DEFAULT,"Port binding %s:%d was removed from the config file, closing.", old_ports[k].first.c_str(), old_ports[k].second);
570                                         delete *n;
571                                         Config->ports.erase(n);
572                                         break;
573                                 }
574                         }
575                 }
576         }
577
578         return bound;
579 }
580
581 const char* irc::sockets::insp_ntoa(insp_inaddr n)
582 {
583         static char buf[1024];
584         inet_ntop(AF_FAMILY, &n, buf, sizeof(buf));
585         return buf;
586 }
587
588 int irc::sockets::insp_aton(const char* a, insp_inaddr* n)
589 {
590         return inet_pton(AF_FAMILY, a, n);
591 }
592