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>
39 using namespace ip_type;
41 IP_RANGE& IP_RANGE::operator=(const IP_RANGE& other)
48 void IP_RANGE::swap(IP_RANGE& other)
50 std::swap(ip,other.ip);
51 std::swap(mask,other.mask);
52 std::swap(end,other.end);
56 // can decode IP, IP-IP (as range) and IP/MASK (as network)
57 void IP_RANGE::load(const std::string& ip)
59 string::size_type delim_pos;
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)
151 this->mask=0xFFFFFFFF;
157 this->mask=mask_or_end;
159 // make sure ip is really the network address
160 this->ip=(this->ip & this->mask);
162 // calculate end of the network (=broadcast addr)
163 this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
168 this->end=mask_or_end;
170 // Automatically turn IP if IP & end are swapped
171 if (turn_ip(this->ip) > turn_ip(this->end)) {
172 unsigned int tmp = this->ip;
173 this->ip = this->end;
179 bool IP_RANGE::is_within(const IP_RANGE& a) const
181 if (t != RANGE && a.t != RANGE)
183 // mask checking possible
184 if ((ip & a.mask) == a.ip && mask >= a.mask)
189 // no mask checking since we have a range somewhere
190 // use turn_ip because of network byte order
191 if (turn_ip(ip) >= turn_ip(a.ip) && turn_ip(end) <= turn_ip(a.end))
198 bool IP_RANGE::overlapping(const IP_RANGE& a) const
200 if (t != RANGE && a.t != RANGE)
202 // mask checking possible
203 if ((ip & a.mask) == a.ip ||
209 // no mask checking since we have a range somewhere
210 // use turn_ip because of network byte order
211 if ((turn_ip(ip) >= turn_ip(a.ip) && turn_ip(ip) <= turn_ip(a.end)) ||
212 (turn_ip(a.ip) >= turn_ip(ip) && turn_ip(a.ip) <= turn_ip(end)))
219 /** @brief map this IP_RANGE to another network (resembles iptables NETMAP-target)
220 @param original_net the original net that is netmapped. *this needs to be within it.
221 @param target_net the net that original_net is netmapped to.
222 @retval true if the netmap was successful, false in case of an error
223 @note no support for overlapping nets or ranges
225 bool IP_RANGE::netmap(const IP_RANGE& original_net, const IP_RANGE& target_net)
228 if (original_net.t != ip_type::NETWORK ||
229 target_net.t != ip_type::NETWORK)
232 if (original_net.mask != target_net.mask)
235 // only map if we are within the original net
236 if (!is_within(original_net))
239 // everything ok, ready to go
241 // difference to start of original net
242 int ipno=turn_ip(ip)-turn_ip(original_net.ip);
244 // add that difference to the target net
245 ip=turn_ip(turn_ip(target_net.ip)+ipno);
248 ipno=turn_ip(end)-turn_ip(original_net.ip);
249 end=turn_ip(turn_ip(target_net.ip)+ipno);
254 IP_RANGE operator+(const IP_RANGE &base, const int &ips)
258 ret.ip = ret.turn_ip(ret.turn_ip(ret.ip) + ips);
259 ret.end = ret.turn_ip(ret.turn_ip(ret.end) + ips);
261 // Check if this is still a valid network
262 // If not: Convert network to range
263 if (ret.t == NETWORK && (ret.ip&ret.mask) != ret.ip) {
268 if (ret.t == RANGE && ret.turn_ip(ret.end) < ret.turn_ip(ret.ip))
269 throw range_error("range end lower than begin");
274 int IP_RANGE::operator-(const IP_RANGE &other)
276 return turn_ip(ip)-turn_ip(other.ip);
279 bool operator<(const IP_RANGE& a, const IP_RANGE& b)
281 return ((IP_RANGE::turn_ip(a.ip) < IP_RANGE::turn_ip(b.ip)) ||
282 ((a.ip==b.ip) && IP_RANGE::turn_ip(a.end) < IP_RANGE::turn_ip(b.end)));
285 unsigned int IP_RANGE::get_netmask() const
287 if (t==ip_type::RANGE)
288 throw out_of_range("no netmask for ip range");
293 std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const
295 struct in_addr ia_ip;
296 static const int bufsize=16;
297 char buffer[bufsize];
301 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
310 out << "/" << get_mask_bits();
314 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
317 output=output+"/"+buffer;
323 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
326 output=output+"-"+buffer;
328 else if (t==IP && always_mask) {
332 output+="/255.255.255.255";
338 vector<IP_RANGE> IP_RANGE::to_cidr(void) const
340 vector<IP_RANGE> cidrs;
342 if (t==IP || t==NETWORK) {
343 cidrs.push_back(*this);
346 unsigned int cidr_addr = turn_ip(ip), cidr_end = turn_ip(end);
348 // special case: 0.0.0.0-255.255.255.255
349 if (cidr_addr == 0 && cidr_end == 0xFFFFFFFF) {
350 cidrs.push_back(IP_RANGE(ip_type::NETWORK,0,0));
352 unsigned int bit = 0, mask = 0;
353 while (cidr_addr <= cidr_end) {
355 if ((cidr_addr & mask) || ((cidr_addr | mask) > cidr_end)) {
356 cidrs.push_back(IP_RANGE(ip_type::NETWORK,turn_ip(cidr_addr),calc_netmask_from_cidr(32-bit)));
358 cidr_addr += (1 << bit);
371 vector<IP_RANGE> IP_RANGE::substract(const std::set<IP_RANGE> &to_substract) const
373 vector<IP_RANGE> rtn;
375 unsigned int ip_pos = turn_ip(ip), ip_end = turn_ip(end);
377 set<IP_RANGE>::const_iterator sub = to_substract.begin(), to_substract_end = to_substract.end();
378 for (sub = to_substract.begin(); sub != to_substract_end; ++sub) {
379 if (!sub->is_within(*this))
380 throw runtime_error("IP_RANGE::substract error: "+ sub->to_string() + " is not within " + to_string());
382 // Get begin of network/range/IP
383 unsigned int sub_begin = turn_ip(sub->get_base());
386 if (ip_pos < sub_begin) {
387 if (ip_pos == sub_begin-1)
388 rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos)));
390 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(sub_begin-1)));
393 // Set position to end if end is higher than current position
394 if (ip_pos < turn_ip(sub->get_broadcast())+1)
395 ip_pos = turn_ip(sub->get_broadcast())+1;
399 if (ip_pos == ip_end)
400 rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos)));
401 else if (ip_pos < ip_end)
402 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(ip_end)));
407 unsigned int IP_RANGE::turn_ip(unsigned int src)
410 char* si=(char*)&src;
411 char* di=(char*)&dst;
421 unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask)
423 netmask = turn_ip(netmask);
497 unsigned int IP_RANGE::calc_netmask_from_cidr(unsigned int cidr)
607 std::string IP_RANGE::ip_string(unsigned int ip)
609 struct in_addr ia_ip;
610 static const int bufsize=16;
611 char buffer[bufsize];
614 if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
620 std::string IP_RANGE::ip_num_string(unsigned int ip)
623 static const int bufsize=16;
624 unsigned char a0,a1,a2,a3;
631 snprintf(buf,13,"%03u%03u%03u%03u",a0,a1,a2,a3);
638 string IP_RANGE::resolve_ip(const string &iporname, const bool enable_ipv6)
640 struct in_addr ip_adr;
642 if (inet_aton(iporname.c_str(),&ip_adr) != 0)
649 struct addrinfo hints, *result = NULL;
650 memset(&hints, 0, sizeof(struct addrinfo));
652 hints.ai_family = AF_UNSPEC;
654 hints.ai_family = AF_INET;
656 if (getaddrinfo(iporname.c_str(), NULL, &hints, &result) != 0)
657 throw dns_exception("no corresponding ip found for: "+iporname);
659 if (result == NULL || result->ai_addrlen == 0 || result->ai_addr == NULL)
660 throw dns_exception("no corresponding ip found for: "+iporname);
662 // Convert addr to ip string
663 char str_ip[NI_MAXHOST], dummy_serv[NI_MAXSERV];
664 if (getnameinfo(result->ai_addr, result->ai_addrlen, str_ip, NI_MAXHOST, dummy_serv, NI_MAXSERV, NI_NUMERICHOST) != 0)
665 throw dns_exception("can't convert ip for: "+iporname);
667 freeaddrinfo(result);
669 return string(str_ip);