]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
dd52bb33ac84351e7d10e5044eb542c5b164375e
[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 <string>
15 #include "configreader.h"
16 #include "socket.h"
17 #include "inspircd.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 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 sockfd, insp_sockaddr client, insp_sockaddr server, int port, char* addr) : ServerInstance(Instance), desc("plaintext")
40 {
41         this->SetFd(sockfd);
42         Instance->Log(DEBUG,"CRAP");
43         if (!Instance->BindSocket(this->fd,client,server,port,addr))
44                 this->fd = -1;
45 }
46
47 ListenSocket::~ListenSocket()
48 {
49         if (this->GetFd() > -1)
50         {
51                 shutdown(this->fd, 2);
52                 close(this->fd);
53                 this->fd = -1;
54         }
55 }
56
57 void ListenSocket::HandleEvent(EventType et, int errornum)
58 {
59         insp_sockaddr sock_us;  // our port number
60         socklen_t uslen;        // length of our port number
61         insp_sockaddr client;
62         socklen_t length;
63         int incomingSockfd, in_port;
64         uslen = sizeof(sock_us);
65         length = sizeof(client);
66         incomingSockfd = accept (this->GetFd(),(struct sockaddr*)&client, &length);
67         
68         if ((incomingSockfd > -1) && (!getsockname(incomingSockfd, (sockaddr*)&sock_us, &uslen)))
69         {
70 #ifdef IPV6
71                 in_port = ntohs(sock_us.sin6_port);
72 #else
73                 in_port = ntohs(sock_us.sin_port);
74 #endif
75                 NonBlocking(incomingSockfd);
76                 if (ServerInstance->Config->GetIOHook(in_port))
77                 {
78                         try
79                         {
80 #ifdef IPV6
81                                 ServerInstance->Config->GetIOHook(in_port)->OnRawSocketAccept(incomingSockfd, insp_ntoa(client.sin6_addr), in_port);
82 #else
83                                 ServerInstance->Config->GetIOHook(in_port)->OnRawSocketAccept(incomingSockfd, insp_ntoa(client.sin_addr), in_port);
84 #endif
85                         }
86                         catch (CoreException& modexcept)
87                         {
88                                 ServerInstance->Log(DEBUG,"%s threw an exception: %s", modexcept.GetSource(), modexcept.GetReason());
89                         }
90                 }
91                 ServerInstance->stats->statsAccept++;
92 #ifdef IPV6
93                 userrec::AddClient(ServerInstance, incomingSockfd, in_port, false, client.sin6_addr);
94 #else
95                 userrec::AddClient(ServerInstance, incomingSockfd, in_port, false, client.sin_addr);
96 #endif
97         }
98         else
99         {
100                 shutdown(incomingSockfd,2);
101                 close(incomingSockfd);
102                 ServerInstance->stats->statsRefused++;
103         }
104 }
105
106 /* Match raw bytes using CIDR bit matching, used by higher level MatchCIDR() */
107 bool irc::sockets::MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits)
108 {
109         unsigned int modulus = mask_bits % 8; /* Number of whole bytes in the mask */
110         unsigned int divisor = mask_bits / 8; /* Remaining bits in the mask after whole bytes are dealt with */
111
112         /* First compare the whole bytes, if they dont match, return false */
113         if (memcmp(address, mask, divisor))
114                 return false;
115
116         /* Now if there are any remainder bits, we compare them with logic AND */
117         if (modulus)
118                 if ((address[divisor] & inverted_bits[modulus]) != (mask[divisor] & inverted_bits[modulus]))
119                         /* If they dont match, return false */
120                         return false;
121
122         /* The address matches the mask, to mask_bits bits of mask */
123         return true;
124 }
125
126 /* Match CIDR, but dont attempt to match() against leading *!*@ sections */
127 bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask)
128 {
129         return MatchCIDR(address, cidr_mask, false);
130 }
131
132 /* 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
133  * If you have a lot of hosts to match, youre probably better off building your mask once
134  * and then using the lower level MatchCIDRBits directly.
135  *
136  * This will also attempt to match any leading usernames or nicknames on the mask, using
137  * match(), when match_with_username is true.
138  */
139 bool irc::sockets::MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username)
140 {
141         unsigned char addr_raw[16];
142         unsigned char mask_raw[16];
143         unsigned int bits = 0;
144         char* mask = NULL;
145
146         /* The caller is trying to match ident@<mask>/bits.
147          * Chop off the ident@ portion, use match() on it
148          * seperately.
149          */
150         if (match_with_username)
151         {
152                 /* Duplicate the strings, and try to find the position
153                  * of the @ symbol in each */
154                 char* address_dupe = strdup(address);
155                 char* cidr_dupe = strdup(cidr_mask);
156         
157                 /* Use strchr not strrchr, because its going to be nearer to the left */
158                 char* username_mask_pos = strrchr(cidr_dupe, '@');
159                 char* username_addr_pos = strrchr(address_dupe, '@');
160
161                 /* Both strings have an @ symbol in them */
162                 if (username_mask_pos && username_addr_pos)
163                 {
164                         /* Zero out the location of the @ symbol */
165                         *username_mask_pos = *username_addr_pos = 0;
166
167                         /* Try and match() the strings before the @
168                          * symbols, and recursively call MatchCIDR without
169                          * username matching enabled to match the host part.
170                          */
171                         bool result = (match(address_dupe, cidr_dupe) && MatchCIDR(username_addr_pos + 1, username_mask_pos + 1, false));
172
173                         /* Free the stuff we created */
174                         free(address_dupe);
175                         free(cidr_dupe);
176
177                         /* Return a result */
178                         return result;
179                 }
180                 else
181                 {
182                         /* One or both didnt have an @ in,
183                          * just match as CIDR
184                          */
185                         free(address_dupe);
186                         free(cidr_dupe);
187                         mask = strdup(cidr_mask);
188                 }
189         }
190         else
191         {
192                 /* Make a copy of the cidr mask string,
193                  * we're going to change it
194                  */
195                 mask = strdup(cidr_mask);
196         }
197
198         in_addr  address_in4;
199         in_addr  mask_in4;
200
201
202         /* Use strrchr for this, its nearer to the right */
203         char* bits_chars = strrchr(mask,'/');
204
205         if (bits_chars)
206         {
207                 bits = atoi(bits_chars + 1);
208                 *bits_chars = 0;
209         }
210         else
211         {
212                 /* No 'number of bits' field! */
213                 free(mask);
214                 return false;
215         }
216
217 #ifdef SUPPORT_IP6LINKS
218         in6_addr address_in6;
219         in6_addr mask_in6;
220
221         if (inet_pton(AF_INET6, address, &address_in6) > 0)
222         {
223                 if (inet_pton(AF_INET6, mask, &mask_in6) > 0)
224                 {
225                         memcpy(&addr_raw, &address_in6.s6_addr, 16);
226                         memcpy(&mask_raw, &mask_in6.s6_addr, 16);
227
228                         if (bits > 128)
229                                 bits = 128;
230                 }
231                 else
232                 {
233                         /* The address was valid ipv6, but the mask
234                          * that goes with it wasnt.
235                          */
236                         free(mask);
237                         return false;
238                 }
239         }
240         else
241 #endif
242         if (inet_pton(AF_INET, address, &address_in4) > 0)
243         {
244                 if (inet_pton(AF_INET, mask, &mask_in4) > 0)
245                 {
246                         memcpy(&addr_raw, &address_in4.s_addr, 4);
247                         memcpy(&mask_raw, &mask_in4.s_addr, 4);
248
249                         if (bits > 32)
250                                 bits = 32;
251                 }
252                 else
253                 {
254                         /* The address was valid ipv4,
255                          * but the mask that went with it wasnt.
256                          */
257                         free(mask);
258                         return false;
259                 }
260         }
261         else
262         {
263                 /* The address was neither ipv4 or ipv6 */
264                 free(mask);
265                 return false;
266         }
267
268         /* Low-level-match the bits in the raw data */
269         free(mask);
270         return MatchCIDRBits(addr_raw, mask_raw, bits);
271 }
272
273 void irc::sockets::Blocking(int s)
274 {
275         int flags = fcntl(s, F_GETFL, 0);
276         fcntl(s, F_SETFL, flags ^ O_NONBLOCK);
277 }
278
279 void irc::sockets::NonBlocking(int s)
280 {
281         int flags = fcntl(s, F_GETFL, 0);
282         fcntl(s, F_SETFL, flags | O_NONBLOCK);
283 }
284
285 /** This will bind a socket to a port. It works for UDP/TCP.
286  * It can only bind to IP addresses, if you wish to bind to hostnames
287  * you should first resolve them using class 'Resolver'.
288  */ 
289 bool InspIRCd::BindSocket(int sockfd, insp_sockaddr clientn, insp_sockaddr servern, int port, char* addr)
290 {
291         /* We allocate 2 of these, because sockaddr_in6 is larger than sockaddr (ugh, hax) */
292         sockaddr* server = new sockaddr[2];
293         memset(server,0,sizeof(sockaddr)*2);
294
295         int ret, size;
296
297         if (*addr == '*')
298                 *addr = 0;
299
300 #ifdef IPV6
301         if (*addr)
302         {
303                 printf("Address %s not empty\n", addr);
304                 /* There is an address here. Is it ipv6? */
305                 if (strchr(addr,':'))
306                 {
307                         printf("Address %s is ipv6\n", addr);
308                         /* Yes it is */
309                         in6_addr addy;
310                         if (inet_pton(AF_INET6, addr, &addy) < 1)
311                         {
312                                 printf("Failed to interpret address %s", addr);
313                                 delete[] server;
314                                 return false;
315                         }
316
317                         ((sockaddr_in6*)server)->sin6_family = AF_INET6;
318                         memcpy(&(((sockaddr_in6*)server)->sin6_addr), &addy, sizeof(in6_addr));
319                         ((sockaddr_in6*)server)->sin6_port = htons(port);
320                         size = sizeof(sockaddr_in6);
321                 }
322                 else
323                 {
324                         /* No, its not */
325                         printf("Address %s is ipv4\n", addr);
326                         in_addr addy;
327                         if (inet_pton(AF_INET, addr, &addy) < 1)
328                         {
329                                 delete[] server;
330                                 return false;
331                         }
332
333                         ((sockaddr_in*)server)->sin_family = AF_INET;
334                         ((sockaddr_in*)server)->sin_addr = addy;
335                         ((sockaddr_in*)server)->sin_port = htons(port);
336                         size = sizeof(sockaddr_in);
337                 }
338         }
339         else
340         {
341                 printf("Address empty\n");
342                 /* Theres no address here, default to ipv6 bind to all */
343                 ((sockaddr_in6*)server)->sin6_family = AF_INET6;
344                 memset(&(((sockaddr_in6*)server)->sin6_addr), 0, sizeof(in6_addr));
345                 ((sockaddr_in6*)server)->sin6_port = htons(port);
346                 size = sizeof(sockaddr_in6);
347         }
348 #else
349         /* If we aren't built with ipv6, the choice becomes simple */
350         ((sockaddr_in*)server)->sin_family = AF_INET;
351         if (*addr)
352         {
353                 /* There is an address here. */
354                 in_addr addy;
355                 if (inet_pton(AF_INET, addr, &addy) < 1)
356                 {
357                         delete[] server;
358                         return false;
359                 }
360                 ((sockaddr_in*)server)->sin_addr = addy;
361         }
362         else
363         {
364                 /* Bind ipv4 to all */
365                 ((sockaddr_in*)server)->sin_addr.s_addr = htonl(INADDR_ANY);
366         }
367         /* Bind ipv4 port number */
368         ((sockaddr_in*)server)->sin_port = htons(port);
369         size = sizeof(sockaddr_in);
370 #endif
371         ret = bind(sockfd, server, size);
372         delete[] server;
373
374         if (ret < 0)
375         {
376                 Log(DEBUG,"Bind fails it! %s", strerror(errno));
377                 return false;
378         }
379         else
380         {
381                 if (listen(sockfd, Config->MaxConn) == -1)
382                 {
383                         this->Log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
384                         return false;
385                 }
386                 else
387                 {
388                         NonBlocking(sockfd);
389                         return true;
390                 }
391         }
392 }
393
394
395 // Open a TCP Socket
396 int irc::sockets::OpenTCPSocket(char* addr)
397 {
398         int sockfd;
399         int on = 1;
400         struct linger linger = { 0 };
401 #ifdef IPV6
402         printf("IPV6 ENABLED OPENTCPSOCKET\n");
403         if (strchr(addr,':') || (!*addr))
404         {
405                 printf("IPV6 OPENTCPSOCKET DO\n");
406                 sockfd = socket (PF_INET6, SOCK_STREAM, 0);
407         }
408         else
409         {
410                 printf("IPV6->IPV4 OPENTCPSOCKET DO\n");
411                 sockfd = socket (PF_INET, SOCK_STREAM, 0);
412         }
413         if (sockfd < 0)
414 #else
415         if ((sockfd = socket (PF_INET, SOCK_STREAM, 0)) < 0)
416 #endif
417         {
418                 printf("SOCKET FAIL: %s\n", strerror(errno));
419                 return ERROR;
420         }
421         else
422         {
423                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
424                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
425                 linger.l_onoff = 1;
426                 linger.l_linger = 1;
427                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &linger,sizeof(linger));
428                 return (sockfd);
429         }
430 }
431
432 /* XXX: Probably belongs in class InspIRCd */
433 bool InspIRCd::HasPort(int port, char* addr)
434 {
435         for (unsigned long count = 0; count < stats->BoundPortCount; count++)
436         {
437                 if ((port == Config->ports[count]) && (!strcasecmp(Config->addrs[count],addr)))
438                 {
439                         return true;
440                 }
441         }
442         return false;
443 }
444
445 /* XXX: Probably belongs in class InspIRCd */
446 int InspIRCd::BindPorts(bool bail, int &ports_found, FailedPortList &failed_ports)
447 {
448         Log(DEBUG,"BINDING PORTS");
449         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
450         insp_sockaddr client, server;
451         int clientportcount = 0;
452         int BoundPortCount = 0;
453
454         ports_found = 0;
455
456         int InitialPortCount = stats->BoundPortCount;
457
458         for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
459         {
460                 Log(DEBUG,"FOUND PORT");
461                 Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
462                 Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
463                 Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
464
465                 if ((!*Type) || (!strcmp(Type,"clients")))
466                 {
467                         irc::portparser portrange(configToken, false);
468                         long portno = -1;
469                         while ((portno = portrange.GetToken()))
470                         {
471                                 if (!HasPort(portno, Addr))
472                                 {
473                                         ports_found++;
474                                         Config->ports[clientportcount+InitialPortCount] = portno;
475                                         if (*Addr == '*')
476                                                 *Addr = 0;
477
478                                         strlcpy(Config->addrs[clientportcount+InitialPortCount],Addr,256);
479                                         clientportcount++;
480                                 }
481                         }
482                 }
483
484                 if (!bail)
485                 {
486                         int PortCount = clientportcount;
487                         if (PortCount)
488                         {
489                                 BoundPortCount = stats->BoundPortCount;
490                                 for (int count = InitialPortCount; count < InitialPortCount + PortCount; count++)
491                                 {
492                                         int fd = OpenTCPSocket(Config->addrs[count]);
493                                         if (fd == ERROR)
494                                         {
495                                                 failed_ports.push_back(std::make_pair(Config->addrs[count],Config->ports[count]));
496                                                 Log(DEBUG,"SOCKET FAIL");
497                                         }
498                                         else
499                                         {
500                                                 Log(DEBUG,"BIND");
501                                                 Config->openSockfd[BoundPortCount] = new ListenSocket(this,fd,client,server,Config->ports[count],Config->addrs[count]);
502                                                 if (Config->openSockfd[BoundPortCount]->GetFd() > -1)
503                                                 {
504                                                         if (!SE->AddFd(Config->openSockfd[BoundPortCount]))
505                                                         {
506                                                                 this->Log(DEFAULT,"ERK! Failed to add listening port to socket engine!");
507                                                                 shutdown(Config->openSockfd[BoundPortCount]->GetFd(),2);
508                                                                 close(Config->openSockfd[BoundPortCount]->GetFd());
509                                                                 delete Config->openSockfd[BoundPortCount];
510                                                                 failed_ports.push_back(std::make_pair(Config->addrs[count],Config->ports[count]));
511                                                         }
512                                                         else
513                                                                 BoundPortCount++;
514                                                 }
515                                         }
516                                 }
517                                 return InitialPortCount + BoundPortCount;
518                         }
519                         return InitialPortCount;
520                 }
521         }
522
523         int PortCount = clientportcount;
524
525         for (int count = 0; count < PortCount; count++)
526         {
527                 int fd = OpenTCPSocket(Config->addrs[count]);
528                 if (fd == ERROR)
529                 {
530                         Log(DEBUG,"SOCKET FAIL");
531                         failed_ports.push_back(std::make_pair(Config->addrs[count],Config->ports[count]));
532                 }
533                 else
534                 {
535                         Log(DEBUG,"BIND");
536                         Config->openSockfd[BoundPortCount] = new ListenSocket(this,fd,client,server,Config->ports[count],Config->addrs[count]);
537                         if (Config->openSockfd[BoundPortCount]->GetFd() > -1)
538                         {
539                                 BoundPortCount++;
540                         }
541                         else
542                                 failed_ports.push_back(std::make_pair(Config->addrs[count],Config->ports[count]));
543                 }
544         }
545         return BoundPortCount;
546 }
547
548 const char* irc::sockets::insp_ntoa(insp_inaddr n)
549 {
550         static char buf[1024];
551         inet_ntop(AF_FAMILY, &n, buf, sizeof(buf));
552         return buf;
553 }
554
555 int irc::sockets::insp_aton(const char* a, insp_inaddr* n)
556 {
557         return inet_pton(AF_FAMILY, a, n);
558 }
559