]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
Sync helpop chmodes s and p with docs
[user/henk/code/inspircd.git] / src / socket.cpp
1 /*
2  * InspIRCd -- Internet Relay Chat Daemon
3  *
4  *   Copyright (C) 2019 linuxdaemon <linuxdaemon.irc@gmail.com>
5  *   Copyright (C) 2013-2014 Attila Molnar <attilamolnar@hush.com>
6  *   Copyright (C) 2013, 2017-2021 Sadie Powell <sadie@witchery.services>
7  *   Copyright (C) 2013 Daniel Vassdal <shutter@canternet.org>
8  *   Copyright (C) 2012 Robby <robby@chatbelgie.be>
9  *   Copyright (C) 2009-2011 Daniel De Graaf <danieldg@inspircd.org>
10  *   Copyright (C) 2009 Uli Schlachter <psychon@inspircd.org>
11  *   Copyright (C) 2008 Thomas Stagner <aquanight@inspircd.org>
12  *   Copyright (C) 2007 John Brooks <special@inspircd.org>
13  *   Copyright (C) 2007 Dennis Friis <peavey@inspircd.org>
14  *   Copyright (C) 2006 Craig Edwards <brain@inspircd.org>
15  *
16  * This file is part of InspIRCd.  InspIRCd is free software: you can
17  * redistribute it and/or modify it under the terms of the GNU General Public
18  * License as published by the Free Software Foundation, version 2.
19  *
20  * This program is distributed in the hope that it will be useful, but WITHOUT
21  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
22  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
23  * details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
27  */
28
29
30 #include "inspircd.h"
31
32 bool InspIRCd::BindPort(ConfigTag* tag, const irc::sockets::sockaddrs& sa, std::vector<ListenSocket*>& old_ports)
33 {
34         for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
35         {
36                 if ((**n).bind_sa == sa)
37                 {
38                         // Replace tag, we know addr and port match, but other info (type, ssl) may not.
39                         ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Replacing listener on %s from old tag at %s with new tag from %s",
40                                 sa.str().c_str(), (*n)->bind_tag->getTagLocation().c_str(), tag->getTagLocation().c_str());
41                         (*n)->bind_tag = tag;
42                         (*n)->ResetIOHookProvider();
43
44                         old_ports.erase(n);
45                         return true;
46                 }
47         }
48
49         ListenSocket* ll = new ListenSocket(tag, sa);
50         if (!ll->HasFd())
51         {
52                 ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Failed to listen on %s from tag at %s: %s",
53                         sa.str().c_str(), tag->getTagLocation().c_str(), strerror(errno));
54                 delete ll;
55                 return false;
56         }
57
58         ServerInstance->Logs->Log("SOCKET", LOG_DEFAULT, "Added a listener on %s from tag at %s", sa.str().c_str(), tag->getTagLocation().c_str());
59         ports.push_back(ll);
60         return true;
61 }
62
63 size_t InspIRCd::BindPorts(FailedPortList& failed_ports)
64 {
65         size_t bound = 0;
66         std::vector<ListenSocket*> old_ports(ports.begin(), ports.end());
67
68         ConfigTagList tags = ServerInstance->Config->ConfTags("bind");
69         for (ConfigIter i = tags.first; i != tags.second; ++i)
70         {
71                 ConfigTag* tag = i->second;
72
73                 // Are we creating a TCP/IP listener?
74                 const std::string address = tag->getString("address");
75                 const std::string portlist = tag->getString("port");
76                 if (!address.empty() || !portlist.empty())
77                 {
78                         // InspIRCd supports IPv4 and IPv6 natively; no 4in6 required.
79                         if (strncasecmp(address.c_str(), "::ffff:", 7) == 0)
80                                 this->Logs->Log("SOCKET", LOG_DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
81
82                         // A TCP listener with no ports is not very useful.
83                         if (portlist.empty())
84                                 this->Logs->Log("SOCKET", LOG_DEFAULT, "TCP listener on %s at %s has no ports specified!",
85                                         address.empty() ? "*" : address.c_str(), tag->getTagLocation().c_str());
86
87                         irc::portparser portrange(portlist, false);
88                         for (int port; (port = portrange.GetToken()); )
89                         {
90                                 irc::sockets::sockaddrs bindspec;
91                                 if (!irc::sockets::aptosa(address, port, bindspec))
92                                         continue;
93
94                                 if (!BindPort(tag, bindspec, old_ports))
95                                         failed_ports.push_back(FailedPort(errno, bindspec, tag));
96                                 else
97                                         bound++;
98                         }
99                         continue;
100                 }
101
102 #ifndef _WIN32
103                 // Are we creating a UNIX listener?
104                 const std::string path = tag->getString("path");
105                 if (!path.empty())
106                 {
107                         // Expand the path relative to the config directory.
108                         const std::string fullpath = ServerInstance->Config->Paths.PrependRuntime(path);
109
110                         // UNIX socket paths are length limited to less than PATH_MAX.
111                         irc::sockets::sockaddrs bindspec;
112                         if (fullpath.length() > std::min(ServerInstance->Config->Limits.MaxHost, sizeof(bindspec.un.sun_path) - 1))
113                         {
114                                 this->Logs->Log("SOCKET", LOG_DEFAULT, "UNIX listener on %s at %s specified a path that is too long!",
115                                         fullpath.c_str(), tag->getTagLocation().c_str());
116                                 continue;
117                         }
118
119                         // Check for characters which are problematic in the IRC message format.
120                         if (fullpath.find_first_of("\n\r\t!@: ") != std::string::npos)
121                         {
122                                 this->Logs->Log("SOCKET", LOG_DEFAULT, "UNIX listener on %s at %s specified a path containing invalid characters!",
123                                         fullpath.c_str(), tag->getTagLocation().c_str());
124                                 continue;
125                         }
126
127                         irc::sockets::untosa(fullpath, bindspec);
128                         if (!BindPort(tag, bindspec, old_ports))
129                                 failed_ports.push_back(FailedPort(errno, bindspec, tag));
130                         else
131                                 bound++;
132                 }
133 #endif
134         }
135
136         std::vector<ListenSocket*>::iterator n = ports.begin();
137         for (std::vector<ListenSocket*>::iterator o = old_ports.begin(); o != old_ports.end(); ++o)
138         {
139                 while (n != ports.end() && *n != *o)
140                         n++;
141                 if (n == ports.end())
142                 {
143                         this->Logs->Log("SOCKET", LOG_DEFAULT, "Port bindings slipped out of vector, aborting close!");
144                         break;
145                 }
146
147                 this->Logs->Log("SOCKET", LOG_DEFAULT, "Port binding %s was removed from the config file, closing.",
148                         (**n).bind_sa.str().c_str());
149                 delete *n;
150
151                 // this keeps the iterator valid, pointing to the next element
152                 n = ports.erase(n);
153         }
154
155         return bound;
156 }
157
158 bool irc::sockets::aptosa(const std::string& addr, int port, irc::sockets::sockaddrs& sa)
159 {
160         memset(&sa, 0, sizeof(sa));
161         if (addr.empty() || addr.c_str()[0] == '*')
162         {
163                 if (ServerInstance->Config->WildcardIPv6)
164                 {
165                         sa.in6.sin6_family = AF_INET6;
166                         sa.in6.sin6_port = htons(port);
167                 }
168                 else
169                 {
170                         sa.in4.sin_family = AF_INET;
171                         sa.in4.sin_port = htons(port);
172                 }
173                 return true;
174         }
175         else if (inet_pton(AF_INET, addr.c_str(), &sa.in4.sin_addr) > 0)
176         {
177                 sa.in4.sin_family = AF_INET;
178                 sa.in4.sin_port = htons(port);
179                 return true;
180         }
181         else if (inet_pton(AF_INET6, addr.c_str(), &sa.in6.sin6_addr) > 0)
182         {
183                 sa.in6.sin6_family = AF_INET6;
184                 sa.in6.sin6_port = htons(port);
185                 return true;
186         }
187         return false;
188 }
189
190 bool irc::sockets::untosa(const std::string& path, irc::sockets::sockaddrs& sa)
191 {
192         memset(&sa, 0, sizeof(sa));
193         if (path.length() >= sizeof(sa.un.sun_path))
194                 return false;
195
196         sa.un.sun_family = AF_UNIX;
197         memcpy(&sa.un.sun_path, path.c_str(), path.length() + 1);
198         return true;
199 }
200
201 bool irc::sockets::isunix(const std::string& file)
202 {
203 #ifndef _WIN32
204         struct stat sb;
205         if (stat(file.c_str(), &sb) == 0 && S_ISSOCK(sb.st_mode))
206                 return true;
207 #endif
208         return false;
209 }
210
211
212 int irc::sockets::sockaddrs::family() const
213 {
214         return sa.sa_family;
215 }
216
217 int irc::sockets::sockaddrs::port() const
218 {
219         switch (family())
220         {
221                 case AF_INET:
222                         return ntohs(in4.sin_port);
223
224                 case AF_INET6:
225                         return ntohs(in6.sin6_port);
226
227                 case AF_UNIX:
228                         return 0;
229         }
230
231         // If we have reached this point then we have encountered a bug.
232         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::port(): socket type %d is unknown!", family());
233         return 0;
234 }
235
236 std::string irc::sockets::sockaddrs::addr() const
237 {
238         switch (family())
239         {
240                 case AF_INET:
241                         char ip4addr[INET_ADDRSTRLEN];
242                         if (!inet_ntop(AF_INET, (void*)&in4.sin_addr, ip4addr, sizeof(ip4addr)))
243                                 return "0.0.0.0";
244                         return ip4addr;
245
246                 case AF_INET6:
247                         char ip6addr[INET6_ADDRSTRLEN];
248                         if (!inet_ntop(AF_INET6, (void*)&in6.sin6_addr, ip6addr, sizeof(ip6addr)))
249                                 return "0:0:0:0:0:0:0:0";
250                         return ip6addr;
251
252                 case AF_UNIX:
253                         return un.sun_path;
254         }
255
256         // If we have reached this point then we have encountered a bug.
257         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::addr(): socket type %d is unknown!", family());
258         return "<unknown>";
259 }
260
261 std::string irc::sockets::sockaddrs::str() const
262 {
263         switch (family())
264         {
265                 case AF_INET:
266                         char ip4addr[INET_ADDRSTRLEN];
267                         if (!inet_ntop(AF_INET, (void*)&in4.sin_addr, ip4addr, sizeof(ip4addr)))
268                                 strcpy(ip4addr, "0.0.0.0");
269                         return InspIRCd::Format("%s:%u", ip4addr, ntohs(in4.sin_port));
270
271                 case AF_INET6:
272                         char ip6addr[INET6_ADDRSTRLEN];
273                         if (!inet_ntop(AF_INET6, (void*)&in6.sin6_addr, ip6addr, sizeof(ip6addr)))
274                                 strcpy(ip6addr, "0:0:0:0:0:0:0:0");
275                         return InspIRCd::Format("[%s]:%u", ip6addr, ntohs(in6.sin6_port));
276
277                 case AF_UNIX:
278                         return un.sun_path;
279         }
280
281         // If we have reached this point then we have encountered a bug.
282         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::str(): socket type %d is unknown!", family());
283         return "<unknown>";
284 }
285
286 socklen_t irc::sockets::sockaddrs::sa_size() const
287 {
288         switch (family())
289         {
290                 case AF_INET:
291                         return sizeof(in4);
292
293                 case AF_INET6:
294                         return sizeof(in6);
295
296                 case AF_UNIX:
297                         return sizeof(un);
298         }
299
300         // If we have reached this point then we have encountered a bug.
301         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::sa_size(): socket type %d is unknown!", family());
302         return 0;
303 }
304
305 bool irc::sockets::sockaddrs::operator==(const irc::sockets::sockaddrs& other) const
306 {
307         if (family() != other.family())
308                 return false;
309
310         switch (family())
311         {
312                 case AF_INET:
313                         return (in4.sin_port == other.in4.sin_port) && (in4.sin_addr.s_addr == other.in4.sin_addr.s_addr);
314
315                 case AF_INET6:
316                         return (in6.sin6_port == other.in6.sin6_port) && !memcmp(in6.sin6_addr.s6_addr, other.in6.sin6_addr.s6_addr, 16);
317
318                 case AF_UNIX:
319                         return !strcmp(un.sun_path, other.un.sun_path);
320         }
321
322         // If we have reached this point then we have encountered a bug.
323         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::sockaddrs::operator==(): socket type %d is unknown!", family());
324         return !memcmp(this, &other, sizeof(*this));
325 }
326
327 static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs& sa, unsigned char range)
328 {
329         const unsigned char* base;
330         unsigned char target_byte;
331
332         memset(cidr.bits, 0, sizeof(cidr.bits));
333
334         cidr.type = sa.family();
335         switch (cidr.type)
336         {
337                 case AF_UNIX:
338                         // XXX: UNIX sockets don't support CIDR. This fix is non-ideal but I can't
339                         // really think of another way to handle it.
340                         cidr.length = 0;
341                         return;
342
343                 case AF_INET:
344                         cidr.length = range > 32 ? 32 : range;
345                         target_byte = sizeof(sa.in4.sin_addr);
346                         base = (unsigned char*)&sa.in4.sin_addr;
347                         break;
348
349                 case AF_INET6:
350                         cidr.length = range > 128 ? 128 : range;
351                         target_byte = sizeof(sa.in6.sin6_addr);
352                         base = (unsigned char*)&sa.in6.sin6_addr;
353                         break;
354
355                 default:
356                         // If we have reached this point then we have encountered a bug.
357                         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: sa2cidr(): socket type %d is unknown!", cidr.type);
358                         cidr.length = 0;
359                         return;
360         }
361
362         unsigned int border = cidr.length / 8;
363         unsigned int bitmask = (0xFF00 >> (range & 7)) & 0xFF;
364         for(unsigned int i=0; i < target_byte; i++)
365         {
366                 if (i < border)
367                         cidr.bits[i] = base[i];
368                 else if (i == border)
369                         cidr.bits[i] = base[i] & bitmask;
370                 else
371                         return;
372         }
373 }
374
375 irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, unsigned char range)
376 {
377         sa2cidr(*this, sa, range);
378 }
379
380 irc::sockets::cidr_mask::cidr_mask(const std::string& mask)
381 {
382         std::string::size_type bits_chars = mask.rfind('/');
383         irc::sockets::sockaddrs sa;
384
385         if (bits_chars == std::string::npos)
386         {
387                 irc::sockets::aptosa(mask, 0, sa);
388                 sa2cidr(*this, sa, 128);
389         }
390         else
391         {
392                 unsigned char range = ConvToNum<unsigned char>(mask.substr(bits_chars + 1));
393                 irc::sockets::aptosa(mask.substr(0, bits_chars), 0, sa);
394                 sa2cidr(*this, sa, range);
395         }
396 }
397
398 std::string irc::sockets::cidr_mask::str() const
399 {
400         irc::sockets::sockaddrs sa;
401         sa.sa.sa_family = type;
402
403         unsigned char* base;
404         size_t len;
405         switch (type)
406         {
407                 case AF_INET:
408                         base = (unsigned char*)&sa.in4.sin_addr;
409                         len = 4;
410                         break;
411
412                 case AF_INET6:
413                         base = (unsigned char*)&sa.in6.sin6_addr;
414                         len = 16;
415                         break;
416
417                 case AF_UNIX:
418                         return sa.un.sun_path;
419
420                 default:
421                         // If we have reached this point then we have encountered a bug.
422                         ServerInstance->Logs->Log("SOCKET", LOG_DEBUG, "BUG: irc::sockets::cidr_mask::str(): socket type %d is unknown!", type);
423                         return "<unknown>";
424         }
425
426         memcpy(base, bits, len);
427         return sa.addr() + "/" + ConvToStr((int)length);
428 }
429
430 bool irc::sockets::cidr_mask::operator==(const cidr_mask& other) const
431 {
432         return type == other.type && length == other.length &&
433                 0 == memcmp(bits, other.bits, 16);
434 }
435
436 bool irc::sockets::cidr_mask::operator<(const cidr_mask& other) const
437 {
438         if (type != other.type)
439                 return type < other.type;
440         if (length != other.length)
441                 return length < other.length;
442         return memcmp(bits, other.bits, 16) < 0;
443 }
444
445 bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const
446 {
447         if (addr.family() != type)
448                 return false;
449         irc::sockets::cidr_mask tmp(addr, length);
450         return tmp == *this;
451 }