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;
59 struct in_addr ia_ip1, ia_ip2;
61 if ((delim_pos=ip.find('/')) != string::npos ||
62 (delim_pos=ip.find('-')) != string::npos)
64 if (delim_pos+1 >= ip.size())
65 throw runtime_error("invalid IP given: "+ip);
67 if (ip.at(delim_pos) == '/')
72 load(t,ip.substr(0,delim_pos),ip.substr(delim_pos+1));
80 void IP_RANGE::load(type t, const std::string& ip, const std::string& mask_or_end)
82 struct in_addr ia_ip1, ia_ip2;
88 if(!inet_aton(ip.c_str(),&ia_ip1))
89 throw runtime_error("invalid IP given: "+ip);
91 this->ip=ia_ip1.s_addr;
92 this->mask=0xFFFFFFFF;
97 // TODO: Better IP checks: "16" != "0.0.0.16"
98 if(!inet_aton(ip.c_str(),&ia_ip1))
99 throw runtime_error("invalid IP given: "+ip);
101 // Check if mask is in cidr notation
102 if (mask_or_end.find(".") == string::npos) {
103 unsigned int calc_mask = 0;
104 istringstream in(mask_or_end);
108 throw runtime_error("invalid cidr mask given: "+mask_or_end);
110 ia_ip2.s_addr = calc_netmask_from_cidr(calc_mask);
112 if (!inet_aton(mask_or_end.c_str(),&ia_ip2))
113 throw runtime_error("invalid IP given: "+mask_or_end);
115 this->ip=ia_ip1.s_addr;
116 this->mask=ia_ip2.s_addr;
118 // make sure ip is really the network address
119 this->ip=(this->ip & this->mask);
121 // calculate end of the network (=broadcast addr)
122 this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
126 if(!inet_aton(ip.c_str(),&ia_ip1))
127 throw runtime_error("invalid IP given: "+ip);
129 if(!inet_aton(mask_or_end.c_str(),&ia_ip2))
130 throw runtime_error("invalid IP given: "+mask_or_end);
132 this->ip=ia_ip1.s_addr;
133 this->end=ia_ip2.s_addr;
135 // Automatically turn IP if IP & end are swapped
136 if (turn_ip(this->ip) > turn_ip(this->end)) {
137 unsigned int tmp = this->ip;
138 this->ip = this->end;
144 void IP_RANGE::load(type t, unsigned int ip, unsigned int mask_or_end)
146 struct in_addr ia_ip1, ia_ip2;
153 this->mask=0xFFFFFFFF;
159 this->mask=mask_or_end;
161 // make sure ip is really the network address
162 this->ip=(this->ip & this->mask);
164 // calculate end of the network (=broadcast addr)
165 this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
170 this->end=mask_or_end;
172 // Automatically turn IP if IP & end are swapped
173 if (turn_ip(this->ip) > turn_ip(this->end)) {
174 unsigned int tmp = this->ip;
175 this->ip = this->end;
181 bool IP_RANGE::is_within(const IP_RANGE& a) const
183 if (t != RANGE && a.t != RANGE)
185 // mask checking possible
186 if ((ip & a.mask) == a.ip && mask >= a.mask)
191 // no mask checking since we have a range somewhere
192 // use turn_ip because of network byte order
193 if (turn_ip(ip) >= turn_ip(a.ip) && turn_ip(end) <= turn_ip(a.end))
200 bool IP_RANGE::overlapping(const IP_RANGE& a) const
202 if (t != RANGE && a.t != RANGE)
204 // mask checking possible
205 if ((ip & a.mask) == a.ip ||
211 // no mask checking since we have a range somewhere
212 // use turn_ip because of network byte order
213 if ((turn_ip(ip) >= turn_ip(a.ip) && turn_ip(ip) <= turn_ip(a.end)) ||
214 (turn_ip(a.ip) >= turn_ip(ip) && turn_ip(a.ip) <= turn_ip(end)))
221 /** @brief map this IP_RANGE to another network (resembles iptables NETMAP-target)
222 @param original_net the original net that is netmapped. *this needs to be within it.
223 @param target_net the net that original_net is netmapped to.
224 @retval true if the netmap was successful, false in case of an error
225 @note no support for overlapping nets or ranges
227 bool IP_RANGE::netmap(const IP_RANGE& original_net, const IP_RANGE& target_net)
230 if (original_net.t != ip_type::NETWORK ||
231 target_net.t != ip_type::NETWORK)
234 if (original_net.mask != target_net.mask)
237 // only map if we are within the original net
238 if (!is_within(original_net))
241 // everything ok, ready to go
243 // difference to start of original net
244 int ipno=turn_ip(ip)-turn_ip(original_net.ip);
246 // add that difference to the target net
247 ip=turn_ip(turn_ip(target_net.ip)+ipno);
250 ipno=turn_ip(end)-turn_ip(original_net.ip);
251 end=turn_ip(turn_ip(target_net.ip)+ipno);
256 IP_RANGE operator+(const IP_RANGE &base, const int &ips)
260 ret.ip = ret.turn_ip(ret.turn_ip(ret.ip) + ips);
261 ret.end = ret.turn_ip(ret.turn_ip(ret.end) + ips);
263 // Check if this is still a valid network
264 // If not: Convert network to range
265 if (ret.t == NETWORK && (ret.ip&ret.mask) != ret.ip) {
270 if (ret.t == RANGE && ret.turn_ip(ret.end) < ret.turn_ip(ret.ip))
271 throw range_error("range end lower than begin");
276 int IP_RANGE::operator-(const IP_RANGE &other)
278 return turn_ip(ip)-turn_ip(other.ip);
281 bool operator<(const IP_RANGE& a, const IP_RANGE& b)
283 return ((IP_RANGE::turn_ip(a.ip) < IP_RANGE::turn_ip(b.ip)) ||
284 ((a.ip==b.ip) && IP_RANGE::turn_ip(a.end) < IP_RANGE::turn_ip(b.end)));
287 unsigned int IP_RANGE::get_netmask() const
289 if (t==ip_type::RANGE)
290 throw out_of_range("no netmask for ip range");
295 std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const
297 struct in_addr ia_ip;
298 static const int bufsize=16;
299 char buffer[bufsize];
303 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
312 out << "/" << get_mask_bits();
316 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
319 output=output+"/"+buffer;
325 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
328 output=output+"-"+buffer;
330 else if (t==IP && always_mask) {
334 output+="/255.255.255.255";
340 vector<IP_RANGE> IP_RANGE::to_cidr(void) const
342 vector<IP_RANGE> cidrs;
344 if (t==IP || t==NETWORK) {
345 cidrs.push_back(*this);
348 unsigned int cidr_addr = turn_ip(ip), cidr_end = turn_ip(end);
350 // special case: 0.0.0.0-255.255.255.255
351 if (cidr_addr == 0 && cidr_end == 0xFFFFFFFF) {
352 cidrs.push_back(IP_RANGE(ip_type::NETWORK,0,0));
354 unsigned int bit = 0, mask = 0;
355 while (cidr_addr <= cidr_end) {
357 if ((cidr_addr & mask) || ((cidr_addr | mask) > cidr_end)) {
358 cidrs.push_back(IP_RANGE(ip_type::NETWORK,turn_ip(cidr_addr),calc_netmask_from_cidr(32-bit)));
360 cidr_addr += (1 << bit);
373 vector<IP_RANGE> IP_RANGE::substract(const std::set<IP_RANGE> &to_substract) const
375 vector<IP_RANGE> rtn;
377 unsigned int ip_pos = turn_ip(ip), ip_end = turn_ip(end);
379 set<IP_RANGE>::const_iterator sub = to_substract.begin(), to_substract_end = to_substract.end();
380 for (sub = to_substract.begin(); sub != to_substract_end; sub++) {
381 if (!sub->is_within(*this))
382 throw runtime_error("IP_RANGE::substract error: "+ sub->to_string() + " is not within " + to_string());
384 // Get begin of network/range/IP
385 unsigned int sub_begin = turn_ip(sub->get_base());
388 if (ip_pos < sub_begin) {
389 if (ip_pos == sub_begin-1)
390 rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos)));
392 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(sub_begin-1)));
395 // Set position to end if end is higher than current position
396 if (ip_pos < turn_ip(sub->get_broadcast())+1)
397 ip_pos = turn_ip(sub->get_broadcast())+1;
401 if (ip_pos == ip_end)
402 rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos)));
403 else if (ip_pos < ip_end)
404 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(ip_end)));
409 unsigned int IP_RANGE::turn_ip(unsigned int src)
412 char* si=(char*)&src;
413 char* di=(char*)&dst;
423 unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask)
425 netmask = turn_ip(netmask);
499 unsigned int IP_RANGE::calc_netmask_from_cidr(unsigned int cidr)
609 std::string IP_RANGE::ip_string(unsigned int ip)
611 struct in_addr ia_ip;
612 static const int bufsize=16;
613 char buffer[bufsize];
616 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
622 std::string IP_RANGE::ip_num_string(unsigned int ip)
625 static const int bufsize=16;
626 unsigned char a0,a1,a2,a3;
633 snprintf(buf,13,"%03u%03u%03u%03u",a0,a1,a2,a3);
640 string IP_RANGE::resolve_ip(const string &iporname)
642 struct in_addr ip_adr;
643 struct hostent *dnsdata;
645 if (inet_aton(iporname.c_str(),&ip_adr) != 0)
652 dnsdata=gethostbyname(iporname.c_str());
655 throw dns_exception(hstrerror(h_errno));
657 if (dnsdata->h_addr_list == NULL || *(dnsdata->h_addr_list) == NULL)
658 throw dns_exception("no corresponding ip found for: "+iporname);
660 return inet_ntoa(*(struct in_addr*)(*dnsdata->h_addr_list));