/* The software in this package is distributed under the GNU General Public License version 2 (with a special exception described below). A copy of GNU General Public License (GPL) is included in this distribution, in the file COPYING.GPL. As a special exception, if other files instantiate templates or use macros or inline functions from this file, or you compile this file and link it with other works to produce a work based on this file, this file does not by itself cause the resulting work to be covered by the GNU General Public License. However the source code for this file must still be made available in accordance with section (3) of the GNU General Public License. This exception does not invalidate any other reasons why a work based on this file might be covered by the GNU General Public License. */ /*************************************************************************** ip_functions.cpp - description ------------------- begin : Sat Feb 07 2004 copyright : (C) 2004 by Intra2net AG ***************************************************************************/ #include #include #include #include #include #include #include #include using namespace std; using namespace ip_type; IP_RANGE& IP_RANGE::operator=(const IP_RANGE& other) { IP_RANGE temp(other); temp.swap(*this); return *this; } void IP_RANGE::swap(IP_RANGE& other) { std::swap(ip,other.ip); std::swap(mask,other.mask); std::swap(end,other.end); std::swap(t,other.t); } // can decode IP, IP-IP (as range) and IP/MASK (as network) void IP_RANGE::load(const std::string& ip) { string::size_type delim_pos; if ((delim_pos=ip.find('/')) != string::npos || (delim_pos=ip.find('-')) != string::npos) { if (delim_pos+1 >= ip.size()) throw runtime_error("invalid IP given: "+ip); if (ip.at(delim_pos) == '/') t=NETWORK; else t=RANGE; load(t,ip.substr(0,delim_pos),ip.substr(delim_pos+1)); } else { load(IP,ip,""); } } void IP_RANGE::load(type t, const std::string& ip, const std::string& mask_or_end) { struct in_addr ia_ip1, ia_ip2; this->t=t; if (t==IP) { if(!inet_aton(ip.c_str(),&ia_ip1)) throw runtime_error("invalid IP given: "+ip); this->ip=ia_ip1.s_addr; this->mask=0xFFFFFFFF; this->end=this->ip; } else if (t==NETWORK) { // TODO: Better IP checks: "16" != "0.0.0.16" if(!inet_aton(ip.c_str(),&ia_ip1)) throw runtime_error("invalid IP given: "+ip); // Check if mask is in cidr notation if (mask_or_end.find(".") == string::npos) { unsigned int calc_mask = 0; istringstream in(mask_or_end); in >> calc_mask; if(calc_mask > 32) throw runtime_error("invalid cidr mask given: "+mask_or_end); ia_ip2.s_addr = calc_netmask_from_cidr(calc_mask); } else if (!inet_aton(mask_or_end.c_str(),&ia_ip2)) throw runtime_error("invalid IP given: "+mask_or_end); this->ip=ia_ip1.s_addr; this->mask=ia_ip2.s_addr; // make sure ip is really the network address this->ip=(this->ip & this->mask); // calculate end of the network (=broadcast addr) this->end=((this->mask ^ 0xFFFFFFFF) | this->ip); } else if (t==RANGE) { if(!inet_aton(ip.c_str(),&ia_ip1)) throw runtime_error("invalid IP given: "+ip); if(!inet_aton(mask_or_end.c_str(),&ia_ip2)) throw runtime_error("invalid IP given: "+mask_or_end); this->ip=ia_ip1.s_addr; this->end=ia_ip2.s_addr; // Automatically turn IP if IP & end are swapped if (turn_ip(this->ip) > turn_ip(this->end)) { unsigned int tmp = this->ip; this->ip = this->end; this->end = tmp; } } } void IP_RANGE::load(type t, unsigned int ip, unsigned int mask_or_end) { this->t=t; if (t==IP) { this->ip=ip; this->mask=0xFFFFFFFF; this->end=this->ip; } else if (t==NETWORK) { this->ip=ip; this->mask=mask_or_end; // make sure ip is really the network address this->ip=(this->ip & this->mask); // calculate end of the network (=broadcast addr) this->end=((this->mask ^ 0xFFFFFFFF) | this->ip); } else if (t==RANGE) { this->ip=ip; this->end=mask_or_end; // Automatically turn IP if IP & end are swapped if (turn_ip(this->ip) > turn_ip(this->end)) { unsigned int tmp = this->ip; this->ip = this->end; this->end = tmp; } } } bool IP_RANGE::is_within(const IP_RANGE& a) const { if (t != RANGE && a.t != RANGE) { // mask checking possible if ((ip & a.mask) == a.ip && mask >= a.mask) return true; } else { // no mask checking since we have a range somewhere // use turn_ip because of network byte order if (turn_ip(ip) >= turn_ip(a.ip) && turn_ip(end) <= turn_ip(a.end)) return true; } return false; } bool IP_RANGE::overlapping(const IP_RANGE& a) const { if (t != RANGE && a.t != RANGE) { // mask checking possible if ((ip & a.mask) == a.ip || (a.ip & mask) == ip) return true; } else { // no mask checking since we have a range somewhere // use turn_ip because of network byte order if ((turn_ip(ip) >= turn_ip(a.ip) && turn_ip(ip) <= turn_ip(a.end)) || (turn_ip(a.ip) >= turn_ip(ip) && turn_ip(a.ip) <= turn_ip(end))) return true; } return false; } /** @brief map this IP_RANGE to another network (resembles iptables NETMAP-target) @param original_net the original net that is netmapped. *this needs to be within it. @param target_net the net that original_net is netmapped to. @retval true if the netmap was successful, false in case of an error @note no support for overlapping nets or ranges */ bool IP_RANGE::netmap(const IP_RANGE& original_net, const IP_RANGE& target_net) { // input validation if (original_net.t != ip_type::NETWORK || target_net.t != ip_type::NETWORK) return false; if (original_net.mask != target_net.mask) return false; // only map if we are within the original net if (!is_within(original_net)) return false; // everything ok, ready to go // difference to start of original net int ipno=turn_ip(ip)-turn_ip(original_net.ip); // add that difference to the target net ip=turn_ip(turn_ip(target_net.ip)+ipno); // turn the end too ipno=turn_ip(end)-turn_ip(original_net.ip); end=turn_ip(turn_ip(target_net.ip)+ipno); return true; } IP_RANGE operator+(const IP_RANGE &base, const int &ips) { IP_RANGE ret=base; ret.ip = ret.turn_ip(ret.turn_ip(ret.ip) + ips); ret.end = ret.turn_ip(ret.turn_ip(ret.end) + ips); // Check if this is still a valid network // If not: Convert network to range if (ret.t == NETWORK && (ret.ip&ret.mask) != ret.ip) { ret.t = RANGE; ret.mask = 0; } if (ret.t == RANGE && ret.turn_ip(ret.end) < ret.turn_ip(ret.ip)) throw range_error("range end lower than begin"); return ret; } int IP_RANGE::operator-(const IP_RANGE &other) { return turn_ip(ip)-turn_ip(other.ip); } bool operator<(const IP_RANGE& a, const IP_RANGE& b) { return ((IP_RANGE::turn_ip(a.ip) < IP_RANGE::turn_ip(b.ip)) || ((a.ip==b.ip) && IP_RANGE::turn_ip(a.end) < IP_RANGE::turn_ip(b.end))); } unsigned int IP_RANGE::get_netmask() const { if (t==ip_type::RANGE) throw out_of_range("no netmask for ip range"); return mask; } std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const { struct in_addr ia_ip; static const int bufsize=16; char buffer[bufsize]; string output; ia_ip.s_addr=ip; if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize)) return ""; output=buffer; if (t==NETWORK) { if (cidr_only) { ostringstream out; out << "/" << get_mask_bits(); output+=out.str(); } else { ia_ip.s_addr=mask; if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize)) return ""; output=output+"/"+buffer; } } else if (t==RANGE) { ia_ip.s_addr=end; if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize)) return ""; output=output+"-"+buffer; } else if (t==IP && always_mask) { if (cidr_only) output+="/32"; else output+="/255.255.255.255"; } return output; } vector IP_RANGE::to_cidr(void) const { vector cidrs; if (t==IP || t==NETWORK) { cidrs.push_back(*this); } else { // do the real work unsigned int cidr_addr = turn_ip(ip), cidr_end = turn_ip(end); // special case: 0.0.0.0-255.255.255.255 if (cidr_addr == 0 && cidr_end == 0xFFFFFFFF) { cidrs.push_back(IP_RANGE(ip_type::NETWORK,0,0)); } else { unsigned int bit = 0, mask = 0; while (cidr_addr <= cidr_end) { mask |= (1 << bit); if ((cidr_addr & mask) || ((cidr_addr | mask) > cidr_end)) { cidrs.push_back(IP_RANGE(ip_type::NETWORK,turn_ip(cidr_addr),calc_netmask_from_cidr(32-bit))); cidr_addr += (1 << bit); bit = 0; mask = 0; } else { bit++; } } } } return cidrs; } vector IP_RANGE::substract(const std::set &to_substract) const { vector rtn; unsigned int ip_pos = turn_ip(ip), ip_end = turn_ip(end); set::const_iterator sub = to_substract.begin(), to_substract_end = to_substract.end(); for (sub = to_substract.begin(); sub != to_substract_end; ++sub) { if (!sub->is_within(*this)) throw runtime_error("IP_RANGE::substract error: "+ sub->to_string() + " is not within " + to_string()); // Get begin of network/range/IP unsigned int sub_begin = turn_ip(sub->get_base()); // Got a fragment? if (ip_pos < sub_begin) { if (ip_pos == sub_begin-1) rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos))); else rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(sub_begin-1))); } // Set position to end if end is higher than current position if (ip_pos < turn_ip(sub->get_broadcast())+1) ip_pos = turn_ip(sub->get_broadcast())+1; } // Last fragment if (ip_pos == ip_end) rtn.push_back(IP_RANGE(ip_type::IP, turn_ip(ip_pos))); else if (ip_pos < ip_end) rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(ip_end))); return rtn; } unsigned int IP_RANGE::turn_ip(unsigned int src) { int dst; char* si=(char*)&src; char* di=(char*)&dst; di[0]=si[3]; di[1]=si[2]; di[2]=si[1]; di[3]=si[0]; return dst; } unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask) { netmask = turn_ip(netmask); switch (netmask) { case 0x00000000: return 0; case 0x80000000: return 1; case 0xC0000000: return 2; case 0xE0000000: return 3; case 0xF0000000: return 4; case 0xF8000000: return 5; case 0xFC000000: return 6; case 0xFE000000: return 7; case 0xFF000000: return 8; case 0xFF800000: return 9; case 0xFFC00000: return 10; case 0xFFE00000: return 11; case 0xFFF00000: return 12; case 0xFFF80000: return 13; case 0xFFFC0000: return 14; case 0xFFFE0000: return 15; case 0xFFFF0000: return 16; case 0xFFFF8000: return 17; case 0xFFFFC000: return 18; case 0xFFFFE000: return 19; case 0xFFFFF000: return 20; case 0xFFFFF800: return 21; case 0xFFFFFC00: return 22; case 0xFFFFFE00: return 23; case 0xFFFFFF00: return 24; case 0xFFFFFF80: return 25; case 0xFFFFFFC0: return 26; case 0xFFFFFFE0: return 27; case 0xFFFFFFF0: return 28; case 0xFFFFFFF8: return 29; case 0xFFFFFFFC: return 30; case 0xFFFFFFFE: return 31; case 0xFFFFFFFF: default: return 32; } } unsigned int IP_RANGE::calc_netmask_from_cidr(unsigned int cidr) { unsigned int rtn=0; switch (cidr) { case 0: rtn=0x00000000; break; case 1: rtn=0x80000000; break; case 2: rtn=0xC0000000; break; case 3: rtn=0xE0000000; break; case 4: rtn=0xE0000000; break; case 5: rtn=0xF8000000; break; case 6: rtn=0xFC000000; break; case 7: rtn=0xFE000000; break; case 8: rtn=0xFF000000; break; case 9: rtn=0xFF800000; break; case 10: rtn=0xFFC00000; break; case 11: rtn=0xFFE00000; break; case 12: rtn=0xFFF00000; break; case 13: rtn=0xFFF80000; break; case 14: rtn=0xFFFC0000; break; case 15: rtn=0xFFFE0000; break; case 16: rtn=0xFFFF0000; break; case 17: rtn=0xFFFF8000; break; case 18: rtn=0xFFFFC000; break; case 19: rtn=0xFFFFE000; break; case 20: rtn=0xFFFFF000; break; case 21: rtn=0xFFFFF800; break; case 22: rtn=0xFFFFFC00; break; case 23: rtn=0xFFFFFE00; break; case 24: rtn=0xFFFFFF00; break; case 25: rtn=0xFFFFFF80; break; case 26: rtn=0xFFFFFFC0; break; case 27: rtn=0xFFFFFFE0; break; case 28: rtn=0xFFFFFFF0; break; case 29: rtn=0xFFFFFFF8; break; case 30: rtn=0xFFFFFFFC; break; case 31: rtn=0xFFFFFFFE; break; case 32: default: rtn=0xFFFFFFFF; } return turn_ip(rtn); } std::string IP_RANGE::ip_string(unsigned int ip) { struct in_addr ia_ip; static const int bufsize=16; char buffer[bufsize]; ia_ip.s_addr=ip; if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize)) return ""; else return buffer; } std::string IP_RANGE::ip_num_string(unsigned int ip) { string target; static const int bufsize=16; unsigned char a0,a1,a2,a3; char buf[bufsize]; a0=((char*)&ip)[0]; a1=((char*)&ip)[1]; a2=((char*)&ip)[2]; a3=((char*)&ip)[3]; snprintf(buf,13,"%03u%03u%03u%03u",a0,a1,a2,a3); buf[12]=0; target=buf; return target; } string IP_RANGE::resolve_ip(const string &iporname, const bool enable_ipv6) { struct in_addr ip_adr; if (inet_aton(iporname.c_str(),&ip_adr) != 0) { // is already a ip return iporname; } // we need dns struct addrinfo hints, *result = NULL; memset(&hints, 0, sizeof(struct addrinfo)); if (enable_ipv6) hints.ai_family = AF_UNSPEC; else hints.ai_family = AF_INET; if (getaddrinfo(iporname.c_str(), NULL, &hints, &result) != 0) throw dns_exception("no corresponding ip found for: "+iporname); if (result == NULL || result->ai_addrlen == 0 || result->ai_addr == NULL) throw dns_exception("no corresponding ip found for: "+iporname); // Convert addr to ip string char str_ip[NI_MAXHOST], dummy_serv[NI_MAXSERV]; if (getnameinfo(result->ai_addr, result->ai_addrlen, str_ip, NI_MAXHOST, dummy_serv, NI_MAXSERV, NI_NUMERICHOST) != 0) throw dns_exception("can't convert ip for: "+iporname); freeaddrinfo(result); return string(str_ip); }