]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
Create irc::sockets::cidr_mask
[user/henk/code/inspircd.git] / src / socket.cpp
1 /*       +------------------------------------+
2  *       | Inspire Internet Relay Chat Daemon |
3  *       +------------------------------------+
4  *
5  *  InspIRCd: (C) 2002-2009 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.sa, sa_size(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 // Open a TCP Socket
70 int irc::sockets::OpenTCPSocket(const std::string& addr, int socktype)
71 {
72         int sockfd;
73         int on = 1;
74         struct linger linger = { 0, 0 };
75         if (addr.empty())
76         {
77 #ifdef IPV6
78                 sockfd = socket (PF_INET6, socktype, 0);
79                 if (sockfd < 0)
80 #endif
81                         sockfd = socket (PF_INET, socktype, 0);
82         }
83         else if (addr.find(':') != std::string::npos)
84                 sockfd = socket (PF_INET6, socktype, 0);
85         else
86                 sockfd = socket (PF_INET, socktype, 0);
87
88         if (sockfd < 0)
89         {
90                 return ERROR;
91         }
92         else
93         {
94                 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
95                 /* This is BSD compatible, setting l_onoff to 0 is *NOT* http://web.irc.org/mla/ircd-dev/msg02259.html */
96                 linger.l_onoff = 1;
97                 linger.l_linger = 1;
98                 setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(linger));
99                 return (sockfd);
100         }
101 }
102
103 int InspIRCd::BindPorts(FailedPortList &failed_ports)
104 {
105         int bound = 0;
106         std::vector<ListenSocket*> old_ports(ports.begin(), ports.end());
107
108         ConfigTagList tags = ServerInstance->Config->ConfTags("bind");
109         for(ConfigIter i = tags.first; i != tags.second; ++i)
110         {
111                 ConfigTag* tag = i->second;
112                 std::string porttag = tag->getString("port");
113                 std::string Addr = tag->getString("address");
114
115                 if (strncmp(Addr.c_str(), "::ffff:", 7) == 0)
116                         this->Logs->Log("SOCKET",DEFAULT, "Using 4in6 (::ffff:) isn't recommended. You should bind IPv4 addresses directly instead.");
117
118                 irc::portparser portrange(porttag, false);
119                 int portno = -1;
120                 while (0 != (portno = portrange.GetToken()))
121                 {
122                         irc::sockets::sockaddrs bindspec;
123                         irc::sockets::aptosa(Addr, portno, bindspec);
124                         std::string bind_readable = irc::sockets::satouser(bindspec);
125
126                         bool skip = false;
127                         for (std::vector<ListenSocket*>::iterator n = old_ports.begin(); n != old_ports.end(); ++n)
128                         {
129                                 if ((**n).bind_desc == bind_readable)
130                                 {
131                                         skip = true;
132                                         old_ports.erase(n);
133                                         break;
134                                 }
135                         }
136                         if (!skip)
137                         {
138                                 ListenSocket *ll = new ListenSocket(tag, Addr, portno);
139                                 if (ll->GetFd() > -1)
140                                 {
141                                         bound++;
142                                         ports.push_back(ll);
143                                 }
144                                 else
145                                 {
146                                         failed_ports.push_back(std::make_pair(bind_readable, strerror(errno)));
147                                         delete ll;
148                                 }
149                         }
150                 }
151         }
152
153         std::vector<ListenSocket*>::iterator n = ports.begin();
154         for (std::vector<ListenSocket*>::iterator o = old_ports.begin(); o != old_ports.end(); ++o)
155         {
156                 while (n != ports.end() && *n != *o)
157                         n++;
158                 if (n == ports.end())
159                 {
160                         this->Logs->Log("SOCKET",ERROR,"Port bindings slipped out of vector, aborting close!");
161                         break;
162                 }
163
164                 this->Logs->Log("SOCKET",DEFAULT, "Port binding %s was removed from the config file, closing.",
165                         (**n).bind_desc.c_str());
166                 delete *n;
167
168                 // this keeps the iterator valid, pointing to the next element
169                 n = ports.erase(n);
170         }
171
172         return bound;
173 }
174
175 bool irc::sockets::aptosa(const std::string& addr, int port, irc::sockets::sockaddrs& sa)
176 {
177         memset(&sa, 0, sizeof(sa));
178         if (addr.empty())
179         {
180 #ifdef IPV6
181                 sa.in6.sin6_family = AF_INET6;
182                 sa.in6.sin6_port = htons(port);
183 #else
184                 sa.in4.sin_family = AF_INET;
185                 sa.in4.sin_port = htons(port);
186 #endif
187                 return true;
188         }
189         else if (inet_pton(AF_INET, addr.c_str(), &sa.in4.sin_addr) > 0)
190         {
191                 sa.in4.sin_family = AF_INET;
192                 sa.in4.sin_port = htons(port);
193                 return true;
194         }
195         else if (inet_pton(AF_INET6, addr.c_str(), &sa.in6.sin6_addr) > 0)
196         {
197                 sa.in6.sin6_family = AF_INET6;
198                 sa.in6.sin6_port = htons(port);
199                 return true;
200         }
201         return false;
202 }
203
204 int irc::sockets::sockaddrs::port() const
205 {
206         if (sa.sa_family == AF_INET)
207                 return ntohs(in4.sin_port);
208         if (sa.sa_family == AF_INET6)
209                 return ntohs(in6.sin6_port);
210         return -1;
211 }
212
213 std::string irc::sockets::sockaddrs::addr() const
214 {
215         char addrv[INET6_ADDRSTRLEN+1];
216         if (sa.sa_family == AF_INET)
217         {
218                 if (!inet_ntop(AF_INET, &in4.sin_addr, addrv, sizeof(addrv)))
219                         return "";
220                 return addrv;
221         }
222         else if (sa.sa_family == AF_INET6)
223         {
224                 if (!inet_ntop(AF_INET6, &in6.sin6_addr, addrv, sizeof(addrv)))
225                         return "";
226                 return addrv;
227         }
228         return "";
229 }
230
231 bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port)
232 {
233         port = sa.port();
234         addr = sa.addr();
235         return !addr.empty();
236 }
237
238 static const char all_zero[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
239
240 std::string irc::sockets::sockaddrs::str() const
241 {
242         char buffer[MAXBUF];
243         if (sa.sa_family == AF_INET)
244         {
245                 if (in4.sin_addr.s_addr == 0)
246                 {
247                         sprintf(buffer, "*:%u", ntohs(in4.sin_port));
248                 }
249                 else
250                 {
251                         const uint8_t* bits = reinterpret_cast<const uint8_t*>(&in4.sin_addr);
252                         sprintf(buffer, "%d.%d.%d.%d:%u", bits[0], bits[1], bits[2], bits[3], ntohs(in4.sin_port));
253                 }
254         }
255         else if (sa.sa_family == AF_INET6)
256         {
257                 if (!memcmp(all_zero, &in6.sin6_addr, 16))
258                 {
259                         sprintf(buffer, "*:%u", ntohs(in6.sin6_port));
260                 }
261                 else
262                 {
263                         buffer[0] = '[';
264                         if (!inet_ntop(AF_INET6, &in6.sin6_addr, buffer+1, MAXBUF - 10))
265                                 return "<unknown>"; // should never happen, buffer is large enough
266                         int len = strlen(buffer);
267                         // no need for snprintf, buffer has at least 9 chars left, max short len = 5
268                         sprintf(buffer + len, "]:%u", ntohs(in6.sin6_port));
269                 }
270         }
271         else
272                 return "<unknown>";
273         return std::string(buffer);
274 }
275
276 int irc::sockets::sockaddrs::sa_size() const
277 {
278         if (sa.sa_family == AF_INET)
279                 return sizeof(in4);
280         if (sa.sa_family == AF_INET6)
281                 return sizeof(in6);
282         return 0;
283 }
284
285 static void sa2cidr(const irc::sockets::sockaddrs& sa, irc::sockets::cidr_mask& cidr, int range)
286 {
287         const unsigned char* base;
288         cidr.type = sa.sa.sa_family;
289         if (cidr.type == AF_INET)
290         {
291                 base = (unsigned char*)&sa.in4.sin_addr;
292                 if (range > 32)
293                         range = 32;
294         }
295         else if (cidr.type == AF_INET6)
296         {
297                 base = (unsigned char*)&sa.in6.sin6_addr;
298                 if (range > 128)
299                         range = 128;
300         }
301         else
302         {
303                 base = (unsigned char*)"";
304                 range = 0;
305         }
306         cidr.length = range;
307         unsigned int border = range / 8;
308         unsigned int bitmask = (0xFF00 >> (range & 7)) & 0xFF;
309         for(unsigned int i=0; i < 16; i++)
310         {
311                 if (i < border)
312                         cidr.bits[i] = base[i];
313                 else if (i == border)
314                         cidr.bits[i] = base[i] & bitmask;
315                 else
316                         cidr.bits[i] = 0;
317         }
318 }
319
320 irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, int range)
321 {
322         sa2cidr(sa, *this, range);
323 }
324
325 irc::sockets::cidr_mask::cidr_mask(const std::string& mask)
326 {
327         std::string::size_type bits_chars = mask.rfind('/');
328         irc::sockets::sockaddrs sa;
329
330         if (bits_chars == std::string::npos)
331         {
332                 irc::sockets::aptosa(mask, 0, sa);
333                 sa2cidr(sa, *this, 128);
334         }
335         else
336         {
337                 int range = atoi(mask.substr(bits_chars + 1).c_str());
338                 irc::sockets::aptosa(mask.substr(0, bits_chars), 0, sa);
339                 sa2cidr(sa, *this, range);
340         }
341 }
342
343 std::string irc::sockets::cidr_mask::str() const
344 {
345         irc::sockets::sockaddrs sa;
346         sa.sa.sa_family = type;
347         unsigned char* base;
348         int len;
349         if (type == AF_INET)
350         {
351                 base = (unsigned char*)&sa.in4.sin_addr;
352                 len = 4;
353         }
354         else if (type == AF_INET6)
355         {
356                 base = (unsigned char*)&sa.in6.sin6_addr;
357                 len = 16;
358         }
359         else
360                 return "";
361         memcpy(base, bits, len);
362         return sa.addr() + "/" + ConvToStr(length);
363 }
364
365 bool irc::sockets::cidr_mask::operator==(const cidr_mask& other) const
366 {
367         return type == other.type && length == other.length &&
368                 0 == memcmp(bits, other.bits, 16);
369 }
370
371 bool irc::sockets::cidr_mask::operator<(const cidr_mask& other) const
372 {
373         return type < other.type || length < other.length ||
374                 memcmp(bits, other.bits, 16) < 0;
375 }
376
377 bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const
378 {
379         if (addr.sa.sa_family != type)
380                 return false;
381         irc::sockets::cidr_mask tmp(addr, length);
382         return tmp == *this;
383 }
384