Make IP_RANGE::resolve_ip() thread safe
[libi2ncommon] / src / ipfunc.cpp
1 /*
2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
4
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
7
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.
13
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.
16
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.
19 */
20 /***************************************************************************
21                        ip_functions.cpp  -  description
22                              -------------------
23     begin                : Sat Feb 07 2004
24     copyright            : (C) 2004 by Intra2net AG
25  ***************************************************************************/
26
27 #include <string>
28 #include <sstream>
29 #include <stdexcept>
30 #include <stdio.h>
31
32 #include <arpa/inet.h>
33 #include <netdb.h>
34 #include <string.h>
35
36 #include <ipfunc.hxx>
37
38 using namespace std;
39 using namespace ip_type;
40
41 IP_RANGE& IP_RANGE::operator=(const IP_RANGE& other)
42 {
43     IP_RANGE temp(other);
44     temp.swap(*this);
45     return *this;
46 }
47
48 void IP_RANGE::swap(IP_RANGE& other)
49 {
50     std::swap(ip,other.ip);
51     std::swap(mask,other.mask);
52     std::swap(end,other.end);
53     std::swap(t,other.t);
54 }
55
56 // can decode IP, IP-IP (as range) and IP/MASK (as network)
57 void IP_RANGE::load(const std::string& ip)
58 {
59     string::size_type delim_pos;
60
61     if ((delim_pos=ip.find('/')) != string::npos ||
62         (delim_pos=ip.find('-')) != string::npos)
63     {
64         if (delim_pos+1 >= ip.size())
65             throw runtime_error("invalid IP given: "+ip);
66
67         if (ip.at(delim_pos) == '/')
68             t=NETWORK;
69         else
70             t=RANGE;
71
72         load(t,ip.substr(0,delim_pos),ip.substr(delim_pos+1));
73     }
74     else
75     {
76         load(IP,ip,"");
77     }
78 }
79
80 void IP_RANGE::load(type t, const std::string& ip, const std::string& mask_or_end)
81 {
82     struct in_addr ia_ip1, ia_ip2;
83
84     this->t=t;
85         
86     if (t==IP)
87     {
88         if(!inet_aton(ip.c_str(),&ia_ip1))
89             throw runtime_error("invalid IP given: "+ip);
90     
91         this->ip=ia_ip1.s_addr;
92         this->mask=0xFFFFFFFF;
93         this->end=this->ip;
94     }
95     else if (t==NETWORK)
96     {
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);
100         
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);
105             in >> calc_mask;
106             
107             if(calc_mask > 32)
108                 throw runtime_error("invalid cidr mask given: "+mask_or_end);
109             
110             ia_ip2.s_addr = calc_netmask_from_cidr(calc_mask);
111         } else
112             if (!inet_aton(mask_or_end.c_str(),&ia_ip2))
113                 throw runtime_error("invalid IP given: "+mask_or_end);
114                 
115         this->ip=ia_ip1.s_addr;
116         this->mask=ia_ip2.s_addr;
117         
118         // make sure ip is really the network address
119         this->ip=(this->ip & this->mask);
120         
121         // calculate end of the network (=broadcast addr)
122         this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
123     }
124     else if (t==RANGE)
125     {
126         if(!inet_aton(ip.c_str(),&ia_ip1))
127             throw runtime_error("invalid IP given: "+ip);
128
129         if(!inet_aton(mask_or_end.c_str(),&ia_ip2))
130             throw runtime_error("invalid IP given: "+mask_or_end);
131         
132         this->ip=ia_ip1.s_addr;
133         this->end=ia_ip2.s_addr;
134         
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;
139             this->end = tmp;
140         }
141     }
142 }
143
144 void IP_RANGE::load(type t, unsigned int ip, unsigned int mask_or_end)
145 {
146     this->t=t;
147
148     if (t==IP)
149     {
150         this->ip=ip;
151         this->mask=0xFFFFFFFF;
152         this->end=this->ip;
153     }
154     else if (t==NETWORK)
155     {
156         this->ip=ip;
157         this->mask=mask_or_end;
158
159         // make sure ip is really the network address
160         this->ip=(this->ip & this->mask);
161
162         // calculate end of the network (=broadcast addr)
163         this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
164     }
165     else if (t==RANGE)
166     {
167         this->ip=ip;
168         this->end=mask_or_end;
169
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;
174             this->end = tmp;
175         }
176     }
177 }
178
179 bool IP_RANGE::is_within(const IP_RANGE& a) const
180 {
181     if (t != RANGE && a.t != RANGE)
182     {
183         // mask checking possible
184         if ((ip & a.mask) == a.ip && mask >= a.mask)
185             return true; 
186     }
187     else
188     {
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))
192             return true;    
193     }
194     
195     return false;
196 }
197
198 bool IP_RANGE::overlapping(const IP_RANGE& a) const
199 {
200     if (t != RANGE && a.t != RANGE)
201     {
202         // mask checking possible
203         if ((ip & a.mask) == a.ip ||
204             (a.ip & mask) == ip)
205             return true; 
206     }
207     else
208     {
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)))
213             return true;    
214     }
215     
216     return false;
217 }
218
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
224 */
225 bool IP_RANGE::netmap(const IP_RANGE& original_net, const IP_RANGE& target_net)
226 {
227     // input validation
228     if (original_net.t != ip_type::NETWORK ||
229         target_net.t != ip_type::NETWORK)
230         return false;
231
232     if (original_net.mask != target_net.mask)
233         return false;
234
235     // only map if we are within the original net
236     if (!is_within(original_net))
237         return false;
238
239     // everything ok, ready to go
240
241     // difference to start of original net
242     int ipno=turn_ip(ip)-turn_ip(original_net.ip);
243
244     // add that difference to the target net
245     ip=turn_ip(turn_ip(target_net.ip)+ipno);
246
247     // turn the end too
248     ipno=turn_ip(end)-turn_ip(original_net.ip);
249     end=turn_ip(turn_ip(target_net.ip)+ipno);
250
251     return true;
252 }
253
254 IP_RANGE operator+(const IP_RANGE &base, const int &ips)
255 {
256     IP_RANGE ret=base;
257
258     ret.ip = ret.turn_ip(ret.turn_ip(ret.ip) + ips);
259     ret.end = ret.turn_ip(ret.turn_ip(ret.end) + ips);
260     
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) {
264         ret.t = RANGE;
265         ret.mask = 0;
266     }
267     
268     if (ret.t == RANGE && ret.turn_ip(ret.end) < ret.turn_ip(ret.ip))
269         throw range_error("range end lower than begin");
270     
271     return ret;
272 }
273
274 int IP_RANGE::operator-(const IP_RANGE &other)
275 {
276     return turn_ip(ip)-turn_ip(other.ip);
277 }
278
279 bool operator<(const IP_RANGE& a, const IP_RANGE& b)
280 {
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)));
283 }
284
285 unsigned int IP_RANGE::get_netmask() const
286 {
287     if (t==ip_type::RANGE)
288         throw out_of_range("no netmask for ip range");
289
290     return mask;
291 }
292
293 std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const
294 {
295     struct in_addr ia_ip;
296     static const int bufsize=16;
297     char buffer[bufsize];
298     string output;
299     
300     ia_ip.s_addr=ip;    
301     if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
302         return "";
303         
304     output=buffer;
305     
306     if (t==NETWORK)
307     {
308         if (cidr_only) {
309             ostringstream out;
310             out << "/" << get_mask_bits();
311             output+=out.str();
312         } else {
313             ia_ip.s_addr=mask;    
314             if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
315                 return "";
316                 
317             output=output+"/"+buffer;
318         }
319     }
320     else if (t==RANGE)
321     {
322         ia_ip.s_addr=end;    
323         if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
324             return "";
325             
326         output=output+"-"+buffer;
327     }
328     else if (t==IP && always_mask) {
329         if (cidr_only)
330             output+="/32";
331         else
332             output+="/255.255.255.255";
333     }
334         
335     return output;    
336 }
337
338 vector<IP_RANGE> IP_RANGE::to_cidr(void) const
339 {
340     vector<IP_RANGE> cidrs;
341     
342     if (t==IP || t==NETWORK) {
343         cidrs.push_back(*this);
344     } else {
345         // do the real work
346         unsigned int cidr_addr = turn_ip(ip), cidr_end = turn_ip(end);
347         
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));
351         } else {
352             unsigned int bit = 0, mask = 0;
353             while (cidr_addr <= cidr_end) {
354                 mask |= (1 << bit);
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)));
357                     
358                     cidr_addr += (1 << bit);
359                     bit = 0;
360                     mask = 0;
361                 } else {
362                     bit++;
363                 }
364             }
365         }
366     }
367         
368     return cidrs;
369 }
370
371 vector<IP_RANGE> IP_RANGE::substract(const std::set<IP_RANGE> &to_substract) const
372 {
373     vector<IP_RANGE> rtn;
374     
375     unsigned int ip_pos = turn_ip(ip), ip_end = turn_ip(end);
376     
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());
381     
382         // Get begin of network/range/IP
383         unsigned int sub_begin = turn_ip(sub->get_base());
384         
385         // Got a fragment?
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)));
389             else
390                 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(sub_begin-1)));
391         }
392             
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;
396     }
397
398     // Last fragment
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)));
403                         
404     return rtn;
405 }
406
407 unsigned int IP_RANGE::turn_ip(unsigned int src)
408 {
409     int dst;
410     char* si=(char*)&src;
411     char* di=(char*)&dst;
412
413     di[0]=si[3];
414     di[1]=si[2];
415     di[2]=si[1];
416     di[3]=si[0];
417
418     return dst;
419 }
420
421 unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask)
422 {
423     netmask = turn_ip(netmask);
424
425     switch (netmask)
426     {
427         case 0x00000000:
428             return 0;
429         case 0x80000000:
430             return 1;
431         case 0xC0000000:
432             return 2;
433         case 0xE0000000:
434             return 3;
435         case 0xF0000000:
436             return 4;
437         case 0xF8000000:
438             return 5;
439         case 0xFC000000:
440             return 6;
441         case 0xFE000000:
442             return 7;
443         case 0xFF000000:
444             return 8;
445         case 0xFF800000:
446             return 9;
447         case 0xFFC00000:
448             return 10;
449         case 0xFFE00000:
450             return 11;
451         case 0xFFF00000:
452             return 12;
453         case 0xFFF80000:
454             return 13;
455         case 0xFFFC0000:
456             return 14;
457         case 0xFFFE0000:
458             return 15;
459         case 0xFFFF0000:
460             return 16;
461         case 0xFFFF8000:
462             return 17;
463         case 0xFFFFC000:
464             return 18;
465         case 0xFFFFE000:
466             return 19;
467         case 0xFFFFF000:
468             return 20;
469         case 0xFFFFF800:
470             return 21;
471         case 0xFFFFFC00:
472             return 22;
473         case 0xFFFFFE00:
474             return 23;
475         case 0xFFFFFF00:
476             return 24;
477         case 0xFFFFFF80:
478             return 25;
479         case 0xFFFFFFC0:
480             return 26;
481         case 0xFFFFFFE0:
482             return 27;
483         case 0xFFFFFFF0:
484             return 28;
485         case 0xFFFFFFF8:
486             return 29;
487         case 0xFFFFFFFC:
488             return 30;
489         case 0xFFFFFFFE:
490             return 31;
491         case 0xFFFFFFFF:
492         default:
493             return 32;
494     }
495 }
496
497 unsigned int IP_RANGE::calc_netmask_from_cidr(unsigned int cidr)
498 {
499     unsigned int rtn=0;    
500
501     switch (cidr)
502     {
503         case 0:
504             rtn=0x00000000;
505             break;
506         case 1:
507             rtn=0x80000000;
508             break;
509         case 2:
510             rtn=0xC0000000;
511             break;
512         case 3:
513             rtn=0xE0000000;
514             break;
515         case 4:
516             rtn=0xE0000000;
517             break;
518         case 5:
519             rtn=0xF8000000;
520             break;
521         case 6:
522             rtn=0xFC000000;
523             break;
524         case 7:
525             rtn=0xFE000000;
526             break;
527         case 8:
528             rtn=0xFF000000;
529             break;
530         case 9:
531             rtn=0xFF800000;
532             break;
533         case 10:
534             rtn=0xFFC00000;
535             break;
536         case 11:
537             rtn=0xFFE00000;
538             break;
539         case 12:
540             rtn=0xFFF00000;
541             break;
542         case 13:
543             rtn=0xFFF80000;
544             break;
545         case 14:
546             rtn=0xFFFC0000;
547             break;
548         case 15:
549             rtn=0xFFFE0000;
550             break;
551         case 16:
552             rtn=0xFFFF0000;
553             break;
554         case 17:
555             rtn=0xFFFF8000;
556             break;
557         case 18:
558             rtn=0xFFFFC000;
559             break;
560         case 19:
561             rtn=0xFFFFE000;
562             break;
563         case 20:
564             rtn=0xFFFFF000;
565             break;
566         case 21:
567             rtn=0xFFFFF800;
568             break;
569         case 22:
570             rtn=0xFFFFFC00;
571             break;
572         case 23:
573             rtn=0xFFFFFE00;
574             break;
575         case 24:
576             rtn=0xFFFFFF00;
577             break;
578         case 25:
579             rtn=0xFFFFFF80;
580             break;
581         case 26:
582             rtn=0xFFFFFFC0;
583             break;
584         case 27:
585             rtn=0xFFFFFFE0;
586             break;
587         case 28:
588             rtn=0xFFFFFFF0;
589             break;
590         case 29:
591             rtn=0xFFFFFFF8;
592             break;
593         case 30:
594             rtn=0xFFFFFFFC;
595             break;
596         case 31:
597             rtn=0xFFFFFFFE;
598             break;
599         case 32:
600         default:
601             rtn=0xFFFFFFFF;
602     }
603     
604     return turn_ip(rtn);
605 }
606
607 std::string IP_RANGE::ip_string(unsigned int ip)
608 {
609     struct in_addr ia_ip;
610     static const int bufsize=16;
611     char buffer[bufsize];
612     
613     ia_ip.s_addr=ip;    
614     if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
615         return "";
616     else        
617         return buffer;
618 }
619
620 std::string IP_RANGE::ip_num_string(unsigned int ip)
621 {
622     string target;
623     static const int bufsize=16;
624     unsigned char a0,a1,a2,a3;
625     char buf[bufsize];
626     
627     a0=((char*)&ip)[0];
628     a1=((char*)&ip)[1];
629     a2=((char*)&ip)[2];
630     a3=((char*)&ip)[3];
631     snprintf(buf,13,"%03u%03u%03u%03u",a0,a1,a2,a3);
632     buf[12]=0;
633     target=buf;
634
635     return target;
636 }
637
638 string IP_RANGE::resolve_ip(const string &iporname, const bool enable_ipv6)
639 {
640     struct in_addr ip_adr;
641
642     if (inet_aton(iporname.c_str(),&ip_adr) != 0)
643     {
644         // is already a ip
645         return iporname;
646     }
647
648     // we need dns
649     struct addrinfo hints, *result = NULL;
650     memset(&hints, 0, sizeof(struct addrinfo));
651     if (enable_ipv6)
652         hints.ai_family = AF_UNSPEC;
653     else
654         hints.ai_family = AF_INET;
655
656     if (getaddrinfo(iporname.c_str(), NULL, &hints, &result) != 0)
657         throw dns_exception("no corresponding ip found for: "+iporname);
658
659     if (result == NULL || result->ai_addrlen == 0 || result->ai_addr == NULL)
660         throw dns_exception("no corresponding ip found for: "+iporname);
661
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);
666
667     freeaddrinfo(result);
668
669     return string(str_ip);
670 }