]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
Fix bug #113
[user/henk/code/inspircd.git] / src / socket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2010 InspIRCd Development Team
6  * See: http://wiki.inspircd.org/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 "socket.h"
16 #include "socketengine.h"
17 using irc::sockets::sockaddrs;
18
19 /** This will bind a socket to a port. It works for UDP/TCP.
20  * It can only bind to IP addresses, if you wish to bind to hostnames
21  * you should first resolve them using class 'Resolver'.
22  */
23 bool InspIRCd::BindSocket(int sockfd, int port, const char* addr, bool dolisten)
24 {
25         sockaddrs servaddr;
26         int ret;
27
28         if ((*addr == '*' || *addr == '\0') && port == -1)
29         {
30                 /* Port -1: Means UDP IPV4 port binding - Special case
31                  * used by DNS engine.
32                  */
33                 memset(&servaddr, 0, sizeof(servaddr));
34                 servaddr.in4.sin_family = AF_INET;
35         }
36         else if (!irc::sockets::aptosa(addr, port, servaddr))
37                 return false;
38
39         ret = SE->Bind(sockfd, servaddr);
40
41         if (ret < 0)
42         {
43                 return false;
44         }
45         else
46         {
47                 if (dolisten)
48                 {
49                         if (SE->Listen(sockfd, Config->MaxConn) == -1)
50                         {
51                                 this->Logs->Log("SOCKET",DEFAULT,"ERROR in listen(): %s",strerror(errno));
52                                 return false;
53                         }
54                         else
55                         {
56                                 this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d with listen: %s:%d", sockfd, addr, port);
57                                 SE->NonBlocking(sockfd);
58                                 return true;
59                         }
60                 }
61                 else
62                 {
63                         this->Logs->Log("SOCKET",DEBUG,"New socket binding for %d without listen: %s:%d", sockfd, addr, port);
64                         return true;
65                 }
66         }
67 }
68
69 int InspIRCd::BindPorts(FailedPortList &failed_ports)
70 {
71         int bound = 0;
72         std::vector<ListenSocket*> old_ports(ports.begin(), ports.end());
73
74         ConfigTagList tags = ServerInstance->Config->ConfTags("bind");
75         for(ConfigIter i = tags.first; i != tags.second; ++i)
76         {
77                 ConfigTag* tag = i->second;
78                 std::string porttag = tag->getString("port");
79                 std::string Addr = tag->getString("address");
80
81                 if (strncasecmp(Addr.c_str(), "::ffff:", 7) == 0)
82                         this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
83
84                 irc::portparser portrange(porttag, false);
85                 int portno = -1;
86                 while (0 != (portno = portrange.GetToken()))
87                 {
88                         irc::sockets::sockaddrs bindspec;
89                         if (!irc::sockets::aptosa(Addr, portno, bindspec))
90                                 continue;
91                         std::string bind_readable = bindspec.str();
92
93                         bool skip = false;
94                         for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
95                         {
96                                 if ((**n).bind_desc == bind_readable)
97                                 {
98                                         skip = true;
99                                         old_ports.erase(n);
100                                         break;
101                                 }
102                         }
103                         if (!skip)
104                         {
105                                 ListenSocket* ll = new ListenSocket(tag, bindspec);
106
107                                 if (ll->GetFd() > -1)
108                                 {
109                                         bound++;
110                                         ports.push_back(ll);
111                                 }
112                                 else
113                                 {
114                                         failed_ports.push_back(std::make_pair(bind_readable, strerror(errno)));
115                                         delete ll;
116                                 }
117                         }
118                 }
119         }
120
121         std::vector<ListenSocket*>::iterator n = ports.begin();
122         for (std::vector<ListenSocket*>::iterator o = old_ports.begin(); o != old_ports.end(); ++o)
123         {
124                 while (n != ports.end() && *n != *o)
125                         n++;
126                 if (n == ports.end())
127                 {
128                         this->Logs->Log("SOCKET",DEFAULT,"Port bindings slipped out of vector, aborting close!");
129                         break;
130                 }
131
132                 this->Logs->Log("SOCKET",DEFAULT, "Port binding %s was removed from the config file, closing.",
133                         (**n).bind_desc.c_str());
134                 delete *n;
135
136                 // this keeps the iterator valid, pointing to the next element
137                 n = ports.erase(n);
138         }
139
140         return bound;
141 }
142
143 bool irc::sockets::aptosa(const std::string& addr, int port, irc::sockets::sockaddrs& sa)
144 {
145         memset(&sa, 0, sizeof(sa));
146         if (addr.empty() || addr.c_str()[0] == '*')
147         {
148                 if (ServerInstance->Config->WildcardIPv6)
149                 {
150                         sa.in6.sin6_family = AF_INET6;
151                         sa.in6.sin6_port = htons(port);
152                 }
153                 else
154                 {
155                         sa.in4.sin_family = AF_INET;
156                         sa.in4.sin_port = htons(port);
157                 }
158                 return true;
159         }
160         else if (inet_pton(AF_INET, addr.c_str(), &sa.in4.sin_addr) > 0)
161         {
162                 sa.in4.sin_family = AF_INET;
163                 sa.in4.sin_port = htons(port);
164                 return true;
165         }
166         else if (inet_pton(AF_INET6, addr.c_str(), &sa.in6.sin6_addr) > 0)
167         {
168                 sa.in6.sin6_family = AF_INET6;
169                 sa.in6.sin6_port = htons(port);
170                 return true;
171         }
172         return false;
173 }
174
175 int irc::sockets::sockaddrs::port() const
176 {
177         if (sa.sa_family == AF_INET)
178                 return ntohs(in4.sin_port);
179         if (sa.sa_family == AF_INET6)
180                 return ntohs(in6.sin6_port);
181         return -1;
182 }
183
184 std::string irc::sockets::sockaddrs::addr() const
185 {
186         char addrv[INET6_ADDRSTRLEN+1];
187         if (sa.sa_family == AF_INET)
188         {
189                 if (!inet_ntop(AF_INET, &in4.sin_addr, addrv, sizeof(addrv)))
190                         return "";
191                 return addrv;
192         }
193         else if (sa.sa_family == AF_INET6)
194         {
195                 if (!inet_ntop(AF_INET6, &in6.sin6_addr, addrv, sizeof(addrv)))
196                         return "";
197                 return addrv;
198         }
199         return "";
200 }
201
202 bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port)
203 {
204         port = sa.port();
205         addr = sa.addr();
206         return !addr.empty();
207 }
208
209 static const char all_zero[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
210
211 std::string irc::sockets::sockaddrs::str() const
212 {
213         char buffer[MAXBUF];
214         if (sa.sa_family == AF_INET)
215         {
216                 const uint8_t* bits = reinterpret_cast<const uint8_t*>(&in4.sin_addr);
217                 sprintf(buffer, "%d.%d.%d.%d:%u", bits[0], bits[1], bits[2], bits[3], ntohs(in4.sin_port));
218         }
219         else if (sa.sa_family == AF_INET6)
220         {
221                 buffer[0] = '[';
222                 if (!inet_ntop(AF_INET6, &in6.sin6_addr, buffer+1, MAXBUF - 10))
223                         return "<unknown>"; // should never happen, buffer is large enough
224                 int len = strlen(buffer);
225                 // no need for snprintf, buffer has at least 9 chars left, max short len = 5
226                 sprintf(buffer + len, "]:%u", ntohs(in6.sin6_port));
227         }
228         else
229                 return "<unknown>";
230         return std::string(buffer);
231 }
232
233 int irc::sockets::sockaddrs::sa_size() const
234 {
235         if (sa.sa_family == AF_INET)
236                 return sizeof(in4);
237         if (sa.sa_family == AF_INET6)
238                 return sizeof(in6);
239         return 0;
240 }
241
242 bool irc::sockets::sockaddrs::operator==(const irc::sockets::sockaddrs& other) const
243 {
244         if (sa.sa_family != other.sa.sa_family)
245                 return false;
246         if (sa.sa_family == AF_INET)
247                 return (in4.sin_port == other.in4.sin_port) && (in4.sin_addr.s_addr == other.in4.sin_addr.s_addr);
248         if (sa.sa_family == AF_INET6)
249                 return (in6.sin6_port == other.in6.sin6_port) && !memcmp(in6.sin6_addr.s6_addr, other.in6.sin6_addr.s6_addr, 16);
250         return !memcmp(this, &other, sizeof(*this));
251 }
252
253 static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs& sa, int range)
254 {
255         const unsigned char* base;
256         cidr.type = sa.sa.sa_family;
257         if (cidr.type == AF_INET)
258         {
259                 base = (unsigned char*)&sa.in4.sin_addr;
260                 if (range > 32)
261                         range = 32;
262         }
263         else if (cidr.type == AF_INET6)
264         {
265                 base = (unsigned char*)&sa.in6.sin6_addr;
266                 if (range > 128)
267                         range = 128;
268         }
269         else
270         {
271                 base = (unsigned char*)"";
272                 range = 0;
273         }
274         cidr.length = range;
275         unsigned int border = range / 8;
276         unsigned int bitmask = (0xFF00 >> (range & 7)) & 0xFF;
277         for(unsigned int i=0; i < 16; i++)
278         {
279                 if (i < border)
280                         cidr.bits[i] = base[i];
281                 else if (i == border)
282                         cidr.bits[i] = base[i] & bitmask;
283                 else
284                         cidr.bits[i] = 0;
285         }
286 }
287
288 irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, int range)
289 {
290         sa2cidr(*this, sa, range);
291 }
292
293 irc::sockets::cidr_mask::cidr_mask(const std::string& mask)
294 {
295         std::string::size_type bits_chars = mask.rfind('/');
296         irc::sockets::sockaddrs sa;
297
298         if (bits_chars == std::string::npos)
299         {
300                 irc::sockets::aptosa(mask, 0, sa);
301                 sa2cidr(*this, sa, 128);
302         }
303         else
304         {
305                 int range = atoi(mask.substr(bits_chars + 1).c_str());
306                 irc::sockets::aptosa(mask.substr(0, bits_chars), 0, sa);
307                 sa2cidr(*this, sa, range);
308         }
309 }
310
311 std::string irc::sockets::cidr_mask::str() const
312 {
313         irc::sockets::sockaddrs sa;
314         sa.sa.sa_family = type;
315         unsigned char* base;
316         int len;
317         if (type == AF_INET)
318         {
319                 base = (unsigned char*)&sa.in4.sin_addr;
320                 len = 4;
321         }
322         else if (type == AF_INET6)
323         {
324                 base = (unsigned char*)&sa.in6.sin6_addr;
325                 len = 16;
326         }
327         else
328                 return "";
329         memcpy(base, bits, len);
330         return sa.addr() + "/" + ConvToStr((int)length);
331 }
332
333 bool irc::sockets::cidr_mask::operator==(const cidr_mask& other) const
334 {
335         return type == other.type && length == other.length &&
336                 0 == memcmp(bits, other.bits, 16);
337 }
338
339 bool irc::sockets::cidr_mask::operator<(const cidr_mask& other) const
340 {
341         return type < other.type || length < other.length ||
342                 memcmp(bits, other.bits, 16) < 0;
343 }
344
345 bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const
346 {
347         if (addr.sa.sa_family != type)
348                 return false;
349         irc::sockets::cidr_mask tmp(addr, length);
350         return tmp == *this;
351 }
352