]> git.netwichtig.de Git - user/henk/code/inspircd.git/blob - src/socket.cpp
Allow opermotd to specify its file in <files> without also requiring an <opermotd...
[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 #ifdef IPV6
149                 sa.in6.sin6_family = AF_INET6;
150                 sa.in6.sin6_port = htons(port);
151 #else
152                 sa.in4.sin_family = AF_INET;
153                 sa.in4.sin_port = htons(port);
154 #endif
155                 return true;
156         }
157         else if (inet_pton(AF_INET, addr.c_str(), &sa.in4.sin_addr) > 0)
158         {
159                 sa.in4.sin_family = AF_INET;
160                 sa.in4.sin_port = htons(port);
161                 return true;
162         }
163         else if (inet_pton(AF_INET6, addr.c_str(), &sa.in6.sin6_addr) > 0)
164         {
165                 sa.in6.sin6_family = AF_INET6;
166                 sa.in6.sin6_port = htons(port);
167                 return true;
168         }
169         return false;
170 }
171
172 int irc::sockets::sockaddrs::port() const
173 {
174         if (sa.sa_family == AF_INET)
175                 return ntohs(in4.sin_port);
176         if (sa.sa_family == AF_INET6)
177                 return ntohs(in6.sin6_port);
178         return -1;
179 }
180
181 std::string irc::sockets::sockaddrs::addr() const
182 {
183         char addrv[INET6_ADDRSTRLEN+1];
184         if (sa.sa_family == AF_INET)
185         {
186                 if (!inet_ntop(AF_INET, &in4.sin_addr, addrv, sizeof(addrv)))
187                         return "";
188                 return addrv;
189         }
190         else if (sa.sa_family == AF_INET6)
191         {
192                 if (!inet_ntop(AF_INET6, &in6.sin6_addr, addrv, sizeof(addrv)))
193                         return "";
194                 return addrv;
195         }
196         return "";
197 }
198
199 bool irc::sockets::satoap(const irc::sockets::sockaddrs& sa, std::string& addr, int &port)
200 {
201         port = sa.port();
202         addr = sa.addr();
203         return !addr.empty();
204 }
205
206 static const char all_zero[16] = {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 };
207
208 std::string irc::sockets::sockaddrs::str() const
209 {
210         char buffer[MAXBUF];
211         if (sa.sa_family == AF_INET)
212         {
213 #ifndef IPV6
214                 if (in4.sin_addr.s_addr == 0)
215                 {
216                         sprintf(buffer, "*:%u", ntohs(in4.sin_port));
217                 }
218                 else
219 #endif
220                 {
221                         const uint8_t* bits = reinterpret_cast<const uint8_t*>(&in4.sin_addr);
222                         sprintf(buffer, "%d.%d.%d.%d:%u", bits[0], bits[1], bits[2], bits[3], ntohs(in4.sin_port));
223                 }
224         }
225         else if (sa.sa_family == AF_INET6)
226         {
227 #ifdef IPV6
228                 if (!memcmp(all_zero, &in6.sin6_addr, 16))
229                 {
230                         sprintf(buffer, "*:%u", ntohs(in6.sin6_port));
231                 }
232                 else
233 #endif
234                 {
235                         buffer[0] = '[';
236                         if (!inet_ntop(AF_INET6, &in6.sin6_addr, buffer+1, MAXBUF - 10))
237                                 return "<unknown>"; // should never happen, buffer is large enough
238                         int len = strlen(buffer);
239                         // no need for snprintf, buffer has at least 9 chars left, max short len = 5
240                         sprintf(buffer + len, "]:%u", ntohs(in6.sin6_port));
241                 }
242         }
243         else
244                 return "<unknown>";
245         return std::string(buffer);
246 }
247
248 int irc::sockets::sockaddrs::sa_size() const
249 {
250         if (sa.sa_family == AF_INET)
251                 return sizeof(in4);
252         if (sa.sa_family == AF_INET6)
253                 return sizeof(in6);
254         return 0;
255 }
256
257 static void sa2cidr(irc::sockets::cidr_mask& cidr, const irc::sockets::sockaddrs& sa, int range)
258 {
259         const unsigned char* base;
260         cidr.type = sa.sa.sa_family;
261         if (cidr.type == AF_INET)
262         {
263                 base = (unsigned char*)&sa.in4.sin_addr;
264                 if (range > 32)
265                         range = 32;
266         }
267         else if (cidr.type == AF_INET6)
268         {
269                 base = (unsigned char*)&sa.in6.sin6_addr;
270                 if (range > 128)
271                         range = 128;
272         }
273         else
274         {
275                 base = (unsigned char*)"";
276                 range = 0;
277         }
278         cidr.length = range;
279         unsigned int border = range / 8;
280         unsigned int bitmask = (0xFF00 >> (range & 7)) & 0xFF;
281         for(unsigned int i=0; i < 16; i++)
282         {
283                 if (i < border)
284                         cidr.bits[i] = base[i];
285                 else if (i == border)
286                         cidr.bits[i] = base[i] & bitmask;
287                 else
288                         cidr.bits[i] = 0;
289         }
290 }
291
292 irc::sockets::cidr_mask::cidr_mask(const irc::sockets::sockaddrs& sa, int range)
293 {
294         sa2cidr(*this, sa, range);
295 }
296
297 irc::sockets::cidr_mask::cidr_mask(const std::string& mask)
298 {
299         std::string::size_type bits_chars = mask.rfind('/');
300         irc::sockets::sockaddrs sa;
301
302         if (bits_chars == std::string::npos)
303         {
304                 irc::sockets::aptosa(mask, 0, sa);
305                 sa2cidr(*this, sa, 128);
306         }
307         else
308         {
309                 int range = atoi(mask.substr(bits_chars + 1).c_str());
310                 irc::sockets::aptosa(mask.substr(0, bits_chars), 0, sa);
311                 sa2cidr(*this, sa, range);
312         }
313 }
314
315 std::string irc::sockets::cidr_mask::str() const
316 {
317         irc::sockets::sockaddrs sa;
318         sa.sa.sa_family = type;
319         unsigned char* base;
320         int len;
321         if (type == AF_INET)
322         {
323                 base = (unsigned char*)&sa.in4.sin_addr;
324                 len = 4;
325         }
326         else if (type == AF_INET6)
327         {
328                 base = (unsigned char*)&sa.in6.sin6_addr;
329                 len = 16;
330         }
331         else
332                 return "";
333         memcpy(base, bits, len);
334         return sa.addr() + "/" + ConvToStr(length);
335 }
336
337 bool irc::sockets::cidr_mask::operator==(const cidr_mask& other) const
338 {
339         return type == other.type && length == other.length &&
340                 0 == memcmp(bits, other.bits, 16);
341 }
342
343 bool irc::sockets::cidr_mask::operator<(const cidr_mask& other) const
344 {
345         return type < other.type || length < other.length ||
346                 memcmp(bits, other.bits, 16) < 0;
347 }
348
349 bool irc::sockets::cidr_mask::match(const irc::sockets::sockaddrs& addr) const
350 {
351         if (addr.sa.sa_family != type)
352                 return false;
353         irc::sockets::cidr_mask tmp(addr, length);
354         return tmp == *this;
355 }
356