]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
0ad39701fa119c5bebbb75019d56267bdd20d4d1
[user/henk/code/inspircd.git] / src / socket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd is copyright (C) 2002-2006 ChatSpike-Dev.
6  *                       E-mail:
7  *                <brain@chatspike.net>
8  *                <Craig@chatspike.net>
9  *     
10  * Written by Craig Edwards, Craig McLure, and others.
11  * This program is free but copyrighted software; see
12  *            the file COPYING for details.
13  *
14  * ---------------------------------------------------
15  */
16
17 #include <string>
18 #include "configreader.h"
19 #include "socket.h"
20 #include "inspircd.h"
21 #include "inspstring.h"
22 #include "helperfuncs.h"
23 #include "socketengine.h"
24 #include "wildcard.h"
25 #include "message.h"
26
27 extern InspIRCd* ServerInstance;
28 extern ServerConfig* Config;
29 extern time_t TIME;
30
31 /* Used when comparing CIDR masks for the modulus bits left over.
32  * A lot of ircd's seem to do this:
33  * ((-1) << (8 - (mask % 8)))
34  * But imho, it sucks in comparison to a nice neat lookup table.
35  */
36 const char inverted_bits[8] = { 0x00, /* 00000000 - 0 bits - never actually used */
37                                 0x80, /* 10000000 - 1 bits */
38                                 0xC0, /* 11000000 - 2 bits */
39                                 0xE0, /* 11100000 - 3 bits */
40                                 0xF0, /* 11110000 - 4 bits */
41                                 0xF8, /* 11111000 - 5 bits */
42                                 0xFC, /* 11111100 - 6 bits */
43                                 0xFE  /* 11111110 - 7 bits */
44 };
45
46 /* Match raw bytes using CIDR bit matching, used by higher level MatchCIDR() */
47 bool MatchCIDRBits(unsigned char* address, unsigned char* mask, unsigned int mask_bits)
48 {
49         unsigned int modulus = mask_bits % 8; /* Number of whole bytes in the mask */
50         unsigned int divisor = mask_bits / 8; /* Remaining bits in the mask after whole bytes are dealt with */
51
52         /* We shouldnt match anything, /0 is always valid */
53         if (!mask_bits)
54                 return true;
55
56         /* First compare the whole bytes, if they dont match, return false */
57         if (memcmp(address, mask, divisor))
58                 return false;
59
60         /* Now if there are any remainder bits, we compare them with logic AND */
61         if (modulus)
62                 if ((address[divisor] & inverted_bits[modulus]) != (mask[divisor] & inverted_bits[modulus]))
63                         /* If they dont match, return false */
64                         return false;
65
66         /* The address matches the mask, to mask_bits bits of mask */
67         return true;
68 }
69
70 /* Match CIDR, but dont attempt to match() against leading *!*@ sections */
71 bool MatchCIDR(const char* address, const char* cidr_mask)
72 {
73         return MatchCIDR(address, cidr_mask, false);
74 }
75
76 /* 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
77  * If you have a lot of hosts to match, youre probably better off building your mask once
78  * and then using the lower level MatchCIDRBits directly.
79  *
80  * This will also attempt to match any leading usernames or nicknames on the mask, using
81  * match(), when match_with_username is true.
82  */
83 bool MatchCIDR(const char* address, const char* cidr_mask, bool match_with_username)
84 {
85         unsigned char addr_raw[16];
86         unsigned char mask_raw[16];
87         unsigned int bits = 0;
88         char* mask = NULL;
89
90         /* The caller is trying to match ident@<mask>/bits.
91          * Chop off the ident@ portion, use match() on it
92          * seperately.
93          */
94         if (match_with_username)
95         {
96                 /* Duplicate the strings, and try to find the position
97                  * of the @ symbol in each */
98                 char* address_dupe = strdup(address);
99                 char* cidr_dupe = strdup(cidr_mask);
100                 
101                 char* username_mask_pos = strchr(cidr_dupe, '@');
102                 char* username_addr_pos = strchr(address_dupe, '@');
103
104                 /* Both strings have an @ symbol in them */
105                 if (username_mask_pos && username_addr_pos)
106                 {
107                         /* Zero out the location of the @ symbol */
108                         *username_mask_pos = *username_addr_pos = 0;
109
110                         /* Try and match() the strings before the @
111                          * symbols, and recursively call MatchCIDR without
112                          * username matching enabled to match the host part.
113                          */
114                         bool result = (match(address_dupe, cidr_dupe) && MatchCIDR(username_addr_pos + 1, username_mask_pos + 1, false));
115
116                         /* Free the stuff we created */
117                         free(address_dupe);
118                         free(cidr_dupe);
119
120                         /* Return a result */
121                         return result;
122                 }
123                 else
124                 {
125                         /* One or both didnt have an @ in,
126                          * just match as CIDR
127                          */
128                         free(address_dupe);
129                         free(cidr_dupe);
130                         mask = strdup(cidr_mask);
131                 }
132         }
133         else
134         {
135                 /* Make a copy of the cidr mask string,
136                  * we're going to change it
137                  */
138                 mask = strdup(cidr_mask);
139         }
140
141         in_addr  address_in4;
142         in_addr  mask_in4;
143
144
145         char* bits_chars = strchr(mask,'/');
146
147         if (bits_chars)
148         {
149                 bits = atoi(bits_chars + 1);
150                 *bits_chars = 0;
151         }
152         else
153         {
154                 /* No 'number of bits' field! */
155                 return false;
156         }
157
158 #ifdef SUPPORT_IP6LINKS
159         in6_addr address_in6;
160         in6_addr mask_in6;
161
162         if (inet_pton(AF_INET6, address, &address_in6) > 0)
163         {
164                 if (inet_pton(AF_INET6, mask, &mask_in6) > 0)
165                 {
166                         memcpy(&addr_raw, &address_in6.s6_addr, 16);
167                         memcpy(&mask_raw, &mask_in6.s6_addr, 16);
168
169                         if (bits > 128)
170                                 bits = 128;
171                 }
172                 else
173                 {
174                         /* The address was valid ipv6, but the mask
175                          * that goes with it wasnt.
176                          */
177                         free(mask);
178                         return false;
179                 }
180         }
181         else
182 #endif
183         if (inet_pton(AF_INET, address, &address_in4) > 0)
184         {
185                 if (inet_pton(AF_INET, mask, &mask_in4) > 0)
186                 {
187                         memcpy(&addr_raw, &address_in4.s_addr, 4);
188                         memcpy(&mask_raw, &mask_in4.s_addr, 4);
189
190                         if (bits > 32)
191                                 bits = 32;
192                 }
193                 else
194                 {
195                         /* The address was valid ipv4,
196                          * but the mask that went with it wasnt.
197                          */
198                         free(mask);
199                         return false;
200                 }
201         }
202         else
203         {
204                 /* The address was neither ipv4 or ipv6 */
205                 free(mask);
206                 return false;
207         }
208
209         /* Low-level-match the bits in the raw data */
210         free(mask);
211         return MatchCIDRBits(addr_raw, mask_raw, bits);
212 }
213
214 /** This will bind a socket to a port. It works for UDP/TCP.
215  * It can only bind to IP addresses, if you wish to bind to hostnames
216  * you should first resolve them using class 'Resolver'.
217  */ 
218 bool BindSocket(int sockfd, insp_sockaddr client, insp_sockaddr server, int port, char* addr)
219 {
220         memset(&server,0,sizeof(server));
221         insp_inaddr addy;
222
223         if (*addr == '*')
224                 *addr = 0;
225
226         if ((*addr) && (insp_aton(addr,&addy) < 1))
227         {
228                 log(DEBUG,"Invalid IP '%s' given to BindSocket()", addr);
229                 return false;;
230         }
231
232 #ifdef IPV6
233         server.sin6_family = AF_FAMILY;
234 #else
235         server.sin_family = AF_FAMILY;
236 #endif
237         if (!*addr)
238         {
239 #ifdef IPV6
240                 memcpy(&addy, &server.sin6_addr, sizeof(in6_addr));
241 #else
242                 server.sin_addr.s_addr = htonl(INADDR_ANY);
243 #endif
244         }
245         else
246         {
247 #ifdef IPV6
248                 memcpy(&addy, &server.sin6_addr, sizeof(in6_addr));
249 #else
250                 server.sin_addr = addy;
251 #endif
252         }
253 #ifdef IPV6
254         server.sin6_port = htons(port);
255 #else
256         server.sin_port = htons(port);
257 #endif
258         if (bind(sockfd,(struct sockaddr*)&server,sizeof(server)) < 0)
259         {
260                 return false;
261         }
262         else
263         {
264                 log(DEBUG,"Bound port %s:%d",*addr ? addr : "*",port);
265                 if (listen(sockfd, Config->MaxConn) == -1)
266                 {
267                         log(DEFAULT,"ERROR in listen(): %s",strerror(errno));
268                         return false;
269                 }
270                 else
271                 {
272                         NonBlocking(sockfd);
273                         return true;
274                 }
275         }
276 }
277
278
279 // Open a TCP Socket
280 int OpenTCPSocket()
281 {
282         int sockfd;
283         int on = 1;
284         struct linger linger = { 0 };
285   
286         if ((sockfd = socket (AF_FAMILY, SOCK_STREAM, 0)) < 0)
287         {
288                 log(DEFAULT,"Error creating TCP socket: %s",strerror(errno));
289                 return (ERROR);
290         }
291         else
292         {
293                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
294                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
295                 linger.l_onoff = 1;
296                 linger.l_linger = 1;
297                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &linger,sizeof(linger));
298                 return (sockfd);
299         }
300 }
301
302 bool HasPort(int port, char* addr)
303 {
304         for (unsigned long count = 0; count < ServerInstance->stats->BoundPortCount; count++)
305         {
306                 if ((port == Config->ports[count]) && (!strcasecmp(Config->addrs[count],addr)))
307                 {
308                         return true;
309                 }
310         }
311         return false;
312 }
313
314 int BindPorts(bool bail)
315 {
316         char configToken[MAXBUF], Addr[MAXBUF], Type[MAXBUF];
317         insp_sockaddr client, server;
318         int clientportcount = 0;
319         int BoundPortCount = 0;
320
321         if (!bail)
322         {
323                 int InitialPortCount = ServerInstance->stats->BoundPortCount;
324                 log(DEBUG,"Initial port count: %d",InitialPortCount);
325
326                 for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
327                 {
328                         Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
329                         Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
330                         Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
331
332                         if (((!*Type) || (!strcmp(Type,"clients"))) && (!HasPort(atoi(configToken),Addr)))
333                         {
334                                 // modules handle server bind types now
335                                 Config->ports[clientportcount+InitialPortCount] = atoi(configToken);
336                                 if (*Addr == '*')
337                                         *Addr = 0;
338
339                                 strlcpy(Config->addrs[clientportcount+InitialPortCount],Addr,256);
340                                 clientportcount++;
341                                 log(DEBUG,"NEW binding %s:%s [%s] from config",Addr,configToken, Type);
342                         }
343                 }
344                 int PortCount = clientportcount;
345                 if (PortCount)
346                 {
347                         for (int count = InitialPortCount; count < InitialPortCount + PortCount; count++)
348                         {
349                                 if ((Config->openSockfd[count] = OpenTCPSocket()) == ERROR)
350                                 {
351                                         log(DEBUG,"Bad fd %d binding port [%s:%d]",Config->openSockfd[count],Config->addrs[count],Config->ports[count]);
352                                         return ERROR;
353                                 }
354                                 if (!BindSocket(Config->openSockfd[count],client,server,Config->ports[count],Config->addrs[count]))
355                                 {
356                                         log(DEFAULT,"Failed to bind port [%s:%d]: %s",Config->addrs[count],Config->ports[count],strerror(errno));
357                                 }
358                                 else
359                                 {
360                                         /* Associate the new open port with a slot in the socket engine */
361                                         if (Config->openSockfd[count] > -1)
362                                         {
363                                                 ServerInstance->SE->AddFd(Config->openSockfd[count],true,X_LISTEN);
364                                                 BoundPortCount++;
365                                         }
366                                 }
367                         }
368                         return InitialPortCount + BoundPortCount;
369                 }
370                 else
371                 {
372                         log(DEBUG,"There is nothing new to bind!");
373                 }
374                 return InitialPortCount;
375         }
376
377         for (int count = 0; count < Config->ConfValueEnum(Config->config_data, "bind"); count++)
378         {
379                 Config->ConfValue(Config->config_data, "bind", "port", count, configToken, MAXBUF);
380                 Config->ConfValue(Config->config_data, "bind", "address", count, Addr, MAXBUF);
381                 Config->ConfValue(Config->config_data, "bind", "type", count, Type, MAXBUF);
382
383                 if ((!*Type) || (!strcmp(Type,"clients")))
384                 {
385                         // modules handle server bind types now
386                         Config->ports[clientportcount] = atoi(configToken);
387
388                         // If the client put bind "*", this is an unrealism.
389                         // We don't actually support this as documented, but
390                         // i got fed up of people trying it, so now it converts
391                         // it to an empty string meaning the same 'bind to all'.
392                         if (*Addr == '*')
393                                 *Addr = 0;
394
395                         strlcpy(Config->addrs[clientportcount],Addr,256);
396                         clientportcount++;
397                         log(DEBUG,"Binding %s:%s [%s] from config",Addr,configToken, Type);
398                 }
399         }
400
401         int PortCount = clientportcount;
402
403         for (int count = 0; count < PortCount; count++)
404         {
405                 if ((Config->openSockfd[BoundPortCount] = OpenTCPSocket()) == ERROR)
406                 {
407                         log(DEBUG,"Bad fd %d binding port [%s:%d]",Config->openSockfd[BoundPortCount],Config->addrs[count],Config->ports[count]);
408                         return ERROR;
409                 }
410
411                 if (!BindSocket(Config->openSockfd[BoundPortCount],client,server,Config->ports[count],Config->addrs[count]))
412                 {
413                         log(DEFAULT,"Failed to bind port [%s:%d]: %s",Config->addrs[count],Config->ports[count],strerror(errno));
414                 }
415                 else
416                 {
417                         /* well we at least bound to one socket so we'll continue */
418                         BoundPortCount++;
419                 }
420         }
421
422         /* if we didn't bind to anything then abort */
423         if (!BoundPortCount)
424         {
425                 log(DEFAULT,"No ports bound, bailing!");
426                 printf("\nERROR: Could not bind any of %d ports! Please check your configuration.\n\n", PortCount);
427                 return ERROR;
428         }
429
430         return BoundPortCount;
431 }
432
433 const char* insp_ntoa(insp_inaddr n)
434 {
435         static char buf[1024];
436         inet_ntop(AF_FAMILY, &n, buf, sizeof(buf));
437         return buf;
438 }
439
440 int insp_aton(const char* a, insp_inaddr* n)
441 {
442         return inet_pton(AF_FAMILY, a, n);
443 }
444