root/trunk/pdns/pdns/iputils.hh

Revision 2629, 9.4 KB (checked in by peter, 22 months ago)

be stricter about parsing netmask prefix lengths, fixes #331

  • Property svn:eol-style set to native
  • Property svn:keywords set to author date id revision
Line 
1/*
2    PowerDNS Versatile Database Driven Nameserver
3    Copyright (C) 2002 - 2011  PowerDNS.COM BV
4
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License version 2
7    as published by the Free Software Foundation
8   
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18*/
19#ifndef PDNS_IPUTILSHH
20#define PDNS_IPUTILSHH
21
22#include <string>
23
24#ifndef WIN32
25#include <sys/socket.h>
26#include <netinet/in.h>
27#include <arpa/inet.h>
28#endif // WIN32
29
30#include <iostream>
31#include <stdio.h>
32#include <functional>
33#include "ahuexception.hh"
34#include "misc.hh"
35#include <sys/socket.h>
36#include <netdb.h>
37
38#include <boost/tuple/tuple.hpp>
39#include <boost/tuple/tuple_comparison.hpp>
40#include <boost/lexical_cast.hpp>
41
42#include "namespaces.hh"
43
44union ComboAddress {
45  struct sockaddr_in sin4;
46  struct sockaddr_in6 sin6;
47
48  bool operator==(const ComboAddress& rhs) const
49  {
50    if(boost::tie(sin4.sin_family, sin4.sin_port) != boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
51      return false;
52    if(sin4.sin_family == AF_INET)
53      return sin4.sin_addr.s_addr == rhs.sin4.sin_addr.s_addr;
54    else
55      return memcmp(&sin6.sin6_addr.s6_addr, &rhs.sin6.sin6_addr.s6_addr, 16)==0;
56  }
57
58  bool operator<(const ComboAddress& rhs) const
59  {
60    if(boost::tie(sin4.sin_family, sin4.sin_port) < boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
61      return true;
62    if(boost::tie(sin4.sin_family, sin4.sin_port) > boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
63      return false;
64   
65    if(sin4.sin_family == AF_INET)
66      return sin4.sin_addr.s_addr < rhs.sin4.sin_addr.s_addr;
67    else
68      return memcmp(&sin6.sin6_addr.s6_addr, &rhs.sin6.sin6_addr.s6_addr, 16) < 0;
69  }
70
71  bool operator>(const ComboAddress& rhs) const
72  {
73    if(boost::tie(sin4.sin_family, sin4.sin_port) > boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
74      return true;
75    if(boost::tie(sin4.sin_family, sin4.sin_port) < boost::tie(rhs.sin4.sin_family, rhs.sin4.sin_port))
76      return false;
77   
78    if(sin4.sin_family == AF_INET)
79      return sin4.sin_addr.s_addr > rhs.sin4.sin_addr.s_addr;
80    else
81      return memcmp(&sin6.sin6_addr.s6_addr, &rhs.sin6.sin6_addr.s6_addr, 16) > 0;
82  }
83
84  struct addressOnlyLessThan: public std::binary_function<string, string, bool>
85  {
86    bool operator()(const ComboAddress& a, const ComboAddress& b) const
87    {
88      if(a.sin4.sin_family < b.sin4.sin_family)
89        return true;
90      if(a.sin4.sin_family > b.sin4.sin_family)
91        return false;
92      if(a.sin4.sin_family == AF_INET)
93        return a.sin4.sin_addr.s_addr < b.sin4.sin_addr.s_addr;
94      else
95        return memcmp(&a.sin6.sin6_addr.s6_addr, &b.sin6.sin6_addr.s6_addr, 16) < 0;
96    }
97  };
98
99  socklen_t getSocklen() const
100  {
101    if(sin4.sin_family == AF_INET)
102      return sizeof(sin4);
103    else
104      return sizeof(sin6);
105  }
106 
107  ComboAddress() 
108  {
109    sin4.sin_family=AF_INET;
110    sin4.sin_addr.s_addr=0;
111    sin4.sin_port=0;
112  }
113
114  // 'port' sets a default value in case 'str' does not set a port
115  explicit ComboAddress(const string& str, uint16_t port=0)
116  {
117    memset(&sin6, 0, sizeof(sin6));
118    sin4.sin_family = AF_INET;
119    sin4.sin_port = 0;
120    if(makeIPv4sockaddr(str, &sin4)) {
121      sin6.sin6_family = AF_INET6;
122      if(makeIPv6sockaddr(str, &sin6) < 0)
123        throw AhuException("Unable to convert presentation address '"+ str +"'"); 
124     
125    }
126    if(!sin4.sin_port) // 'str' overrides port!
127      sin4.sin_port=htons(port);
128  }
129
130  bool isMappedIPv4()  const
131  {
132    if(sin4.sin_family!=AF_INET6)
133      return false;
134   
135    int n=0;
136    const unsigned char*ptr = (unsigned char*) &sin6.sin6_addr.s6_addr;
137    for(n=0; n < 10; ++n)
138      if(ptr[n])
139        return false;
140   
141    for(; n < 12; ++n)
142      if(ptr[n]!=0xff)
143        return false;
144   
145    return true;
146  }
147 
148  ComboAddress mapToIPv4() const
149  {
150    if(!isMappedIPv4())
151      throw AhuException("ComboAddress can't map non-mapped IPv6 address back to IPv4");
152    ComboAddress ret;
153    ret.sin4.sin_family=AF_INET;
154    ret.sin4.sin_port=sin4.sin_port;
155   
156    const unsigned char*ptr = (unsigned char*) &sin6.sin6_addr.s6_addr;
157    ptr+=12;
158    memcpy(&ret.sin4.sin_addr.s_addr, ptr, 4);
159    return ret;
160  }
161
162  string toString() const
163  {
164    char host[1024];
165    getnameinfo((struct sockaddr*) this, getSocklen(), host, sizeof(host),0, 0, NI_NUMERICHOST);
166     
167    return host;
168  }
169
170  string toStringWithPort() const
171  {
172    if(sin4.sin_family==AF_INET)
173      return toString() + ":" + boost::lexical_cast<string>(ntohs(sin4.sin_port));
174    else
175      return "["+toString() + "]:" + boost::lexical_cast<string>(ntohs(sin4.sin_port));
176  }
177};
178
179/** This exception is thrown by the Netmask class and by extension by the NetmaskGroup class */
180class NetmaskException: public AhuException
181{
182public:
183  NetmaskException(const string &a) : AhuException(a) {}
184};
185
186inline ComboAddress makeComboAddress(const string& str)
187{
188  ComboAddress address;
189  address.sin4.sin_family=AF_INET;
190  if(Utility::inet_pton(AF_INET, str.c_str(), &address.sin4.sin_addr) <= 0) {
191    address.sin4.sin_family=AF_INET6;
192    if(makeIPv6sockaddr(str, &address.sin6) < 0)
193      throw NetmaskException("Unable to convert '"+str+"' to a netmask");       
194  }
195  return address;
196}
197
198/** This class represents a netmask and can be queried to see if a certain
199    IP address is matched by this mask */
200class Netmask
201{
202public:
203  Netmask()
204  {
205        d_network.sin4.sin_family=0; // disable this doing anything useful
206  }
207 
208  Netmask(const ComboAddress& network, uint8_t bits=0xff)
209  {
210    d_network = network;
211   
212    if(bits == 0xff)
213      bits = (network.sin4.sin_family == AF_INET) ? 32 : 128;
214   
215    d_bits = bits;
216    if(d_bits<32)
217      d_mask=~(0xFFFFFFFF>>d_bits);
218    else
219      d_mask=0xFFFFFFFF; // not actually used for IPv6
220  }
221 
222  //! Constructor supplies the mask, which cannot be changed
223  Netmask(const string &mask) 
224  {
225    pair<string,string> split=splitField(mask,'/');
226    d_network=makeComboAddress(split.first);
227   
228    if(!split.second.empty()) {
229      d_bits = lexical_cast<unsigned int>(split.second);
230      if(d_bits<32)
231        d_mask=~(0xFFFFFFFF>>d_bits);
232      else
233        d_mask=0xFFFFFFFF;
234    }
235    else if(d_network.sin4.sin_family==AF_INET) {
236      d_bits = 32;
237      d_mask = 0xFFFFFFFF;
238    }
239    else {
240      d_bits=128;
241      d_mask=0;  // silence silly warning - d_mask is unused for IPv6
242    }
243  }
244
245  bool match(const ComboAddress& ip) const
246  {
247    return match(&ip);
248  }
249
250  //! If this IP address in socket address matches
251  bool match(const ComboAddress *ip) const
252  {
253    if(d_network.sin4.sin_family != ip->sin4.sin_family) {
254      return false;
255    }
256    if(d_network.sin4.sin_family == AF_INET) {
257      return match4(htonl((unsigned int)ip->sin4.sin_addr.s_addr));
258    }
259    if(d_network.sin6.sin6_family == AF_INET6) {
260      uint8_t bytes=d_bits/8, n;
261      const uint8_t *us=(const uint8_t*) &d_network.sin6.sin6_addr.s6_addr;
262      const uint8_t *them=(const uint8_t*) &ip->sin6.sin6_addr.s6_addr;
263     
264      for(n=0; n < bytes; ++n) {
265        if(us[n]!=them[n]) {
266          return false;
267        }
268      }
269      // still here, now match remaining bits
270      uint8_t bits= d_bits % 8;
271      uint8_t mask= ~(0xFF>>bits);
272
273      return((us[n] & mask) == (them[n] & mask));
274    }
275    return false;
276  }
277
278  //! If this ASCII IP address matches
279  bool match(const string &ip) const
280  {
281    ComboAddress address=makeComboAddress(ip);
282    return match(&address);
283  }
284
285  //! If this IP address in native format matches
286  bool match4(uint32_t ip) const
287  {
288    return (ip & d_mask) == (ntohl(d_network.sin4.sin_addr.s_addr) & d_mask);
289  }
290
291  string toString() const
292  {
293    return d_network.toString()+"/"+boost::lexical_cast<string>((unsigned int)d_bits);
294  }
295
296  string toStringNoMask() const
297  {
298    return d_network.toString();
299  }
300  const ComboAddress& getNetwork() const
301  {
302    return d_network;
303  }
304  int getBits() const
305  {
306    return d_bits;
307  }
308private:
309  ComboAddress d_network;
310  uint32_t d_mask;
311  uint8_t d_bits;
312};
313
314/** This class represents a group of supplemental Netmask classes. An IP address matchs
315    if it is matched by zero or more of the Netmask classes within.
316*/
317class NetmaskGroup
318{
319public:
320  //! If this IP address is matched by any of the classes within
321  bool match(const ComboAddress *ip)
322  {
323    for(container_t::const_iterator i=d_masks.begin();i!=d_masks.end();++i)
324      if(i->match(ip) || (ip->isMappedIPv4() && i->match(ip->mapToIPv4()) ))
325        return true;
326
327    return false;
328  }
329  //! Add this Netmask to the list of possible matches
330  void addMask(const string &ip)
331  {
332    d_masks.push_back(Netmask(ip));
333  }
334 
335  bool empty()
336  {
337    return d_masks.empty();
338  }
339
340  unsigned int size()
341  {
342    return (unsigned int)d_masks.size();
343  }
344
345  string toString() const
346  {
347    ostringstream str;
348    for(container_t::const_iterator iter = d_masks.begin(); iter != d_masks.end(); ++iter) {
349      if(iter != d_masks.begin())
350        str <<", ";
351      str<<iter->toString();
352    }
353    return str.str();
354  }
355
356
357private:
358  typedef vector<Netmask> container_t;
359  container_t d_masks;
360 
361};
362
363#endif
Note: See TracBrowser for help on using the browser.