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