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