2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
20 /***************************************************************************
21 ip_functions.cpp - description
23 begin : Sat Feb 07 2004
24 copyright : (C) 2004 by Intra2net AG
25 ***************************************************************************/
32 #include <arpa/inet.h>
38 using namespace ip_type;
40 IP_RANGE& IP_RANGE::operator=(const IP_RANGE& other)
47 void IP_RANGE::swap(IP_RANGE& other)
49 std::swap(ip,other.ip);
50 std::swap(mask,other.mask);
51 std::swap(end,other.end);
55 // can decode IP, IP-IP (as range) and IP/MASK (as network)
56 void IP_RANGE::load(const std::string& ip)
58 string::size_type delim_pos;
60 if ((delim_pos=ip.find('/')) != string::npos ||
61 (delim_pos=ip.find('-')) != string::npos)
63 if (delim_pos+1 >= ip.size())
64 throw runtime_error("invalid IP given: "+ip);
66 if (ip.at(delim_pos) == '/')
71 load(t,ip.substr(0,delim_pos),ip.substr(delim_pos+1));
79 void IP_RANGE::load(type t, const std::string& ip, const std::string& mask_or_end)
81 struct in_addr ia_ip1, ia_ip2;
87 if(!inet_aton(ip.c_str(),&ia_ip1))
88 throw runtime_error("invalid IP given: "+ip);
90 this->ip=ia_ip1.s_addr;
91 this->mask=0xFFFFFFFF;
96 // TODO: Better IP checks: "16" != "0.0.0.16"
97 if(!inet_aton(ip.c_str(),&ia_ip1))
98 throw runtime_error("invalid IP given: "+ip);
100 // Check if mask is in cidr notation
101 if (mask_or_end.find(".") == string::npos) {
102 unsigned int calc_mask = 0;
103 istringstream in(mask_or_end);
107 throw runtime_error("invalid cidr mask given: "+mask_or_end);
109 ia_ip2.s_addr = calc_netmask_from_cidr(calc_mask);
111 if (!inet_aton(mask_or_end.c_str(),&ia_ip2))
112 throw runtime_error("invalid IP given: "+mask_or_end);
114 this->ip=ia_ip1.s_addr;
115 this->mask=ia_ip2.s_addr;
117 // make sure ip is really the network address
118 this->ip=(this->ip & this->mask);
120 // calculate end of the network (=broadcast addr)
121 this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
125 if(!inet_aton(ip.c_str(),&ia_ip1))
126 throw runtime_error("invalid IP given: "+ip);
128 if(!inet_aton(mask_or_end.c_str(),&ia_ip2))
129 throw runtime_error("invalid IP given: "+mask_or_end);
131 this->ip=ia_ip1.s_addr;
132 this->end=ia_ip2.s_addr;
134 // Automatically turn IP if IP & end are swapped
135 if (turn_ip(this->ip) > turn_ip(this->end)) {
136 unsigned int tmp = this->ip;
137 this->ip = this->end;
143 void IP_RANGE::load(type t, unsigned int ip, unsigned int mask_or_end)
150 this->mask=0xFFFFFFFF;
156 this->mask=mask_or_end;
158 // make sure ip is really the network address
159 this->ip=(this->ip & this->mask);
161 // calculate end of the network (=broadcast addr)
162 this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
167 this->end=mask_or_end;
169 // Automatically turn IP if IP & end are swapped
170 if (turn_ip(this->ip) > turn_ip(this->end)) {
171 unsigned int tmp = this->ip;
172 this->ip = this->end;
178 bool IP_RANGE::is_within(const IP_RANGE& a) const
180 if (t != RANGE && a.t != RANGE)
182 // mask checking possible
183 if ((ip & a.mask) == a.ip && mask >= a.mask)
188 // no mask checking since we have a range somewhere
189 // use turn_ip because of network byte order
190 if (turn_ip(ip) >= turn_ip(a.ip) && turn_ip(end) <= turn_ip(a.end))
197 bool IP_RANGE::overlapping(const IP_RANGE& a) const
199 if (t != RANGE && a.t != RANGE)
201 // mask checking possible
202 if ((ip & a.mask) == a.ip ||
208 // no mask checking since we have a range somewhere
209 // use turn_ip because of network byte order
210 if ((turn_ip(ip) >= turn_ip(a.ip) && turn_ip(ip) <= turn_ip(a.end)) ||
211 (turn_ip(a.ip) >= turn_ip(ip) && turn_ip(a.ip) <= turn_ip(end)))
218 /** @brief map this IP_RANGE to another network (resembles iptables NETMAP-target)
219 @param original_net the original net that is netmapped. *this needs to be within it.
220 @param target_net the net that original_net is netmapped to.
221 @retval true if the netmap was successful, false in case of an error
222 @note no support for overlapping nets or ranges
224 bool IP_RANGE::netmap(const IP_RANGE& original_net, const IP_RANGE& target_net)
227 if (original_net.t != ip_type::NETWORK ||
228 target_net.t != ip_type::NETWORK)
231 if (original_net.mask != target_net.mask)
234 // only map if we are within the original net
235 if (!is_within(original_net))
238 // everything ok, ready to go
240 // difference to start of original net
241 int ipno=turn_ip(ip)-turn_ip(original_net.ip);
243 // add that difference to the target net
244 ip=turn_ip(turn_ip(target_net.ip)+ipno);
247 ipno=turn_ip(end)-turn_ip(original_net.ip);
248 end=turn_ip(turn_ip(target_net.ip)+ipno);
253 IP_RANGE operator+(const IP_RANGE &base, const int &ips)
257 ret.ip = ret.turn_ip(ret.turn_ip(ret.ip) + ips);
258 ret.end = ret.turn_ip(ret.turn_ip(ret.end) + ips);
260 // Check if this is still a valid network
261 // If not: Convert network to range
262 if (ret.t == NETWORK && (ret.ip&ret.mask) != ret.ip) {
267 if (ret.t == RANGE && ret.turn_ip(ret.end) < ret.turn_ip(ret.ip))
268 throw range_error("range end lower than begin");
273 int IP_RANGE::operator-(const IP_RANGE &other)
275 return turn_ip(ip)-turn_ip(other.ip);
278 bool operator<(const IP_RANGE& a, const IP_RANGE& b)
280 return ((IP_RANGE::turn_ip(a.ip) < IP_RANGE::turn_ip(b.ip)) ||
281 ((a.ip==b.ip) && IP_RANGE::turn_ip(a.end) < IP_RANGE::turn_ip(b.end)));
284 unsigned int IP_RANGE::get_netmask() const
286 if (t==ip_type::RANGE)
287 throw out_of_range("no netmask for ip range");
292 std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const
294 struct in_addr ia_ip;
295 static const int bufsize=16;
296 char buffer[bufsize];
300 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
309 out << "/" << get_mask_bits();
313 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
316 output=output+"/"+buffer;
322 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
325 output=output+"-"+buffer;
327 else if (t==IP && always_mask) {
331 output+="/255.255.255.255";
337 vector<IP_RANGE> IP_RANGE::to_cidr(void) const
339 vector<IP_RANGE> cidrs;
341 if (t==IP || t==NETWORK) {
342 cidrs.push_back(*this);
345 unsigned int cidr_addr = turn_ip(ip), cidr_end = turn_ip(end);
347 // special case: 0.0.0.0-255.255.255.255
348 if (cidr_addr == 0 && cidr_end == 0xFFFFFFFF) {
349 cidrs.push_back(IP_RANGE(ip_type::NETWORK,0,0));
351 unsigned int bit = 0, mask = 0;
352 while (cidr_addr <= cidr_end) {
354 if ((cidr_addr & mask) || ((cidr_addr | mask) > cidr_end)) {
355 cidrs.push_back(IP_RANGE(ip_type::NETWORK,turn_ip(cidr_addr),calc_netmask_from_cidr(32-bit)));
357 cidr_addr += (1 << bit);
370 vector<IP_RANGE> IP_RANGE::substract(const std::set<IP_RANGE> &to_substract) const
372 vector<IP_RANGE> rtn;
374 unsigned int ip_pos = turn_ip(ip), ip_end = turn_ip(end);
376 set<IP_RANGE>::const_iterator sub = to_substract.begin(), to_substract_end = to_substract.end();
377 for (sub = to_substract.begin(); sub != to_substract_end; ++sub) {
378 if (!sub->is_within(*this))
379 throw runtime_error("IP_RANGE::substract error: "+ sub->to_string() + " is not within " + to_string());
381 // Get begin of network/range/IP
382 unsigned int sub_begin = turn_ip(sub->get_base());
385 if (ip_pos < sub_begin) {
386 if (ip_pos == sub_begin-1)
387 rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos)));
389 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(sub_begin-1)));
392 // Set position to end if end is higher than current position
393 if (ip_pos < turn_ip(sub->get_broadcast())+1)
394 ip_pos = turn_ip(sub->get_broadcast())+1;
398 if (ip_pos == ip_end)
399 rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos)));
400 else if (ip_pos < ip_end)
401 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(ip_end)));
406 unsigned int IP_RANGE::turn_ip(unsigned int src)
409 char* si=(char*)&src;
410 char* di=(char*)&dst;
420 unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask)
422 netmask = turn_ip(netmask);
496 unsigned int IP_RANGE::calc_netmask_from_cidr(unsigned int cidr)
606 std::string IP_RANGE::ip_string(unsigned int ip)
608 struct in_addr ia_ip;
609 static const int bufsize=16;
610 char buffer[bufsize];
613 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
619 std::string IP_RANGE::ip_num_string(unsigned int ip)
622 static const int bufsize=16;
623 unsigned char a0,a1,a2,a3;
630 snprintf(buf,13,"%03u%03u%03u%03u",a0,a1,a2,a3);
637 string IP_RANGE::resolve_ip(const string &iporname)
639 struct in_addr ip_adr;
640 struct hostent *dnsdata;
642 if (inet_aton(iporname.c_str(),&ip_adr) != 0)
649 dnsdata=gethostbyname(iporname.c_str());
652 throw dns_exception(hstrerror(h_errno));
654 if (dnsdata->h_addr_list == NULL || *(dnsdata->h_addr_list) == NULL)
655 throw dns_exception("no corresponding ip found for: "+iporname);
657 return inet_ntoa(*(struct in_addr*)(*dnsdata->h_addr_list));