Switch license from Intranator license to GPLv2 + linking exception (ACKed by Steffen)
[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     struct in_addr ia_ip1, ia_ip2;
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     struct in_addr ia_ip1, ia_ip2;
147
148     this->t=t;
149         
150     if (t==IP)
151     {
152         this->ip=ip;
153         this->mask=0xFFFFFFFF;
154         this->end=this->ip;
155     }
156     else if (t==NETWORK)
157     {
158         this->ip=ip;
159         this->mask=mask_or_end;
160         
161         // make sure ip is really the network address
162         this->ip=(this->ip & this->mask);
163         
164         // calculate end of the network (=broadcast addr)
165         this->end=((this->mask ^ 0xFFFFFFFF) | this->ip);
166     }
167     else if (t==RANGE)
168     {
169         this->ip=ip;
170         this->end=mask_or_end;
171         
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;
176             this->end = tmp;
177         }
178     }
179 }
180
181 bool IP_RANGE::is_within(const IP_RANGE& a) const
182 {
183     if (t != RANGE && a.t != RANGE)
184     {
185         // mask checking possible
186         if ((ip & a.mask) == a.ip && mask >= a.mask)
187             return true; 
188     }
189     else
190     {
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))
194             return true;    
195     }
196     
197     return false;
198 }
199
200 bool IP_RANGE::overlapping(const IP_RANGE& a) const
201 {
202     if (t != RANGE && a.t != RANGE)
203     {
204         // mask checking possible
205         if ((ip & a.mask) == a.ip ||
206             (a.ip & mask) == ip)
207             return true; 
208     }
209     else
210     {
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)))
215             return true;    
216     }
217     
218     return false;
219 }
220
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
226 */
227 bool IP_RANGE::netmap(const IP_RANGE& original_net, const IP_RANGE& target_net)
228 {
229     // input validation
230     if (original_net.t != ip_type::NETWORK ||
231         target_net.t != ip_type::NETWORK)
232         return false;
233
234     if (original_net.mask != target_net.mask)
235         return false;
236
237     // only map if we are within the original net
238     if (!is_within(original_net))
239         return false;
240
241     // everything ok, ready to go
242
243     // difference to start of original net
244     int ipno=turn_ip(ip)-turn_ip(original_net.ip);
245
246     // add that difference to the target net
247     ip=turn_ip(turn_ip(target_net.ip)+ipno);
248
249     // turn the end too
250     ipno=turn_ip(end)-turn_ip(original_net.ip);
251     end=turn_ip(turn_ip(target_net.ip)+ipno);
252
253     return true;
254 }
255
256 IP_RANGE operator+(const IP_RANGE &base, const int &ips)
257 {
258     IP_RANGE ret=base;
259
260     ret.ip = ret.turn_ip(ret.turn_ip(ret.ip) + ips);
261     ret.end = ret.turn_ip(ret.turn_ip(ret.end) + ips);
262     
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) {
266         ret.t = RANGE;
267         ret.mask = 0;
268     }
269     
270     if (ret.t == RANGE && ret.turn_ip(ret.end) < ret.turn_ip(ret.ip))
271         throw range_error("range end lower than begin");
272     
273     return ret;
274 }
275
276 int IP_RANGE::operator-(const IP_RANGE &other)
277 {
278     return turn_ip(ip)-turn_ip(other.ip);
279 }
280
281 bool operator<(const IP_RANGE& a, const IP_RANGE& b)
282 {
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)));
285 }
286
287 unsigned int IP_RANGE::get_netmask() const
288 {
289     if (t==ip_type::RANGE)
290         throw out_of_range("no netmask for ip range");
291
292     return mask;
293 }
294
295 std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const
296 {
297     struct in_addr ia_ip;
298     static const int bufsize=16;
299     char buffer[bufsize];
300     string output;
301     
302     ia_ip.s_addr=ip;    
303     if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
304         return "";
305         
306     output=buffer;
307     
308     if (t==NETWORK)
309     {
310         if (cidr_only) {
311             ostringstream out;
312             out << "/" << get_mask_bits();
313             output+=out.str();
314         } else {
315             ia_ip.s_addr=mask;    
316             if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
317                 return "";
318                 
319             output=output+"/"+buffer;
320         }
321     }
322     else if (t==RANGE)
323     {
324         ia_ip.s_addr=end;    
325         if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
326             return "";
327             
328         output=output+"-"+buffer;
329     }
330     else if (t==IP && always_mask) {
331         if (cidr_only)
332             output+="/32";
333         else
334             output+="/255.255.255.255";
335     }
336         
337     return output;    
338 }
339
340 vector<IP_RANGE> IP_RANGE::to_cidr(void) const
341 {
342     vector<IP_RANGE> cidrs;
343     
344     if (t==IP || t==NETWORK) {
345         cidrs.push_back(*this);
346     } else {
347         // do the real work
348         unsigned int cidr_addr = turn_ip(ip), cidr_end = turn_ip(end);
349         
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));
353         } else {
354             unsigned int bit = 0, mask = 0;
355             while (cidr_addr <= cidr_end) {
356                 mask |= (1 << bit);
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)));
359                     
360                     cidr_addr += (1 << bit);
361                     bit = 0;
362                     mask = 0;
363                 } else {
364                     bit++;
365                 }
366             }
367         }
368     }
369         
370     return cidrs;
371 }
372
373 vector<IP_RANGE> IP_RANGE::substract(const std::set<IP_RANGE> &to_substract) const
374 {
375     vector<IP_RANGE> rtn;
376     
377     unsigned int ip_pos = turn_ip(ip), ip_end = turn_ip(end);
378     
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());
383     
384         // Get begin of network/range/IP
385         unsigned int sub_begin = turn_ip(sub->get_base());
386         
387         // Got a fragment?
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)));
391             else
392                 rtn.push_back(IP_RANGE(ip_type::RANGE, turn_ip(ip_pos), turn_ip(sub_begin-1)));
393         }
394             
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;
398     }
399
400     // Last fragment
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)));
405                         
406     return rtn;
407 }
408
409 unsigned int IP_RANGE::turn_ip(unsigned int src)
410 {
411     int dst;
412     char* si=(char*)&src;
413     char* di=(char*)&dst;
414
415     di[0]=si[3];
416     di[1]=si[2];
417     di[2]=si[1];
418     di[3]=si[0];
419
420     return dst;
421 }
422
423 unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask)
424 {
425     netmask = turn_ip(netmask);
426
427     switch (netmask)
428     {
429         case 0x00000000:
430             return 0;
431         case 0x80000000:
432             return 1;
433         case 0xC0000000:
434             return 2;
435         case 0xE0000000:
436             return 3;
437         case 0xF0000000:
438             return 4;
439         case 0xF8000000:
440             return 5;
441         case 0xFC000000:
442             return 6;
443         case 0xFE000000:
444             return 7;
445         case 0xFF000000:
446             return 8;
447         case 0xFF800000:
448             return 9;
449         case 0xFFC00000:
450             return 10;
451         case 0xFFE00000:
452             return 11;
453         case 0xFFF00000:
454             return 12;
455         case 0xFFF80000:
456             return 13;
457         case 0xFFFC0000:
458             return 14;
459         case 0xFFFE0000:
460             return 15;
461         case 0xFFFF0000:
462             return 16;
463         case 0xFFFF8000:
464             return 17;
465         case 0xFFFFC000:
466             return 18;
467         case 0xFFFFE000:
468             return 19;
469         case 0xFFFFF000:
470             return 20;
471         case 0xFFFFF800:
472             return 21;
473         case 0xFFFFFC00:
474             return 22;
475         case 0xFFFFFE00:
476             return 23;
477         case 0xFFFFFF00:
478             return 24;
479         case 0xFFFFFF80:
480             return 25;
481         case 0xFFFFFFC0:
482             return 26;
483         case 0xFFFFFFE0:
484             return 27;
485         case 0xFFFFFFF0:
486             return 28;
487         case 0xFFFFFFF8:
488             return 29;
489         case 0xFFFFFFFC:
490             return 30;
491         case 0xFFFFFFFE:
492             return 31;
493         case 0xFFFFFFFF:
494         default:
495             return 32;
496     }
497 }
498
499 unsigned int IP_RANGE::calc_netmask_from_cidr(unsigned int cidr)
500 {
501     unsigned int rtn=0;    
502
503     switch (cidr)
504     {
505         case 0:
506             rtn=0x00000000;
507             break;
508         case 1:
509             rtn=0x80000000;
510             break;
511         case 2:
512             rtn=0xC0000000;
513             break;
514         case 3:
515             rtn=0xE0000000;
516             break;
517         case 4:
518             rtn=0xE0000000;
519             break;
520         case 5:
521             rtn=0xF8000000;
522             break;
523         case 6:
524             rtn=0xFC000000;
525             break;
526         case 7:
527             rtn=0xFE000000;
528             break;
529         case 8:
530             rtn=0xFF000000;
531             break;
532         case 9:
533             rtn=0xFF800000;
534             break;
535         case 10:
536             rtn=0xFFC00000;
537             break;
538         case 11:
539             rtn=0xFFE00000;
540             break;
541         case 12:
542             rtn=0xFFF00000;
543             break;
544         case 13:
545             rtn=0xFFF80000;
546             break;
547         case 14:
548             rtn=0xFFFC0000;
549             break;
550         case 15:
551             rtn=0xFFFE0000;
552             break;
553         case 16:
554             rtn=0xFFFF0000;
555             break;
556         case 17:
557             rtn=0xFFFF8000;
558             break;
559         case 18:
560             rtn=0xFFFFC000;
561             break;
562         case 19:
563             rtn=0xFFFFE000;
564             break;
565         case 20:
566             rtn=0xFFFFF000;
567             break;
568         case 21:
569             rtn=0xFFFFF800;
570             break;
571         case 22:
572             rtn=0xFFFFFC00;
573             break;
574         case 23:
575             rtn=0xFFFFFE00;
576             break;
577         case 24:
578             rtn=0xFFFFFF00;
579             break;
580         case 25:
581             rtn=0xFFFFFF80;
582             break;
583         case 26:
584             rtn=0xFFFFFFC0;
585             break;
586         case 27:
587             rtn=0xFFFFFFE0;
588             break;
589         case 28:
590             rtn=0xFFFFFFF0;
591             break;
592         case 29:
593             rtn=0xFFFFFFF8;
594             break;
595         case 30:
596             rtn=0xFFFFFFFC;
597             break;
598         case 31:
599             rtn=0xFFFFFFFE;
600             break;
601         case 32:
602         default:
603             rtn=0xFFFFFFFF;
604     }
605     
606     return turn_ip(rtn);
607 }
608
609 std::string IP_RANGE::ip_string(unsigned int ip)
610 {
611     struct in_addr ia_ip;
612     static const int bufsize=16;
613     char buffer[bufsize];
614     
615     ia_ip.s_addr=ip;    
616     if (!inet_ntop(AF_INET,&ia_ip,buffer,bufsize))
617         return "";
618     else        
619         return buffer;
620 }
621
622 std::string IP_RANGE::ip_num_string(unsigned int ip)
623 {
624     string target;
625     static const int bufsize=16;
626     unsigned char a0,a1,a2,a3;
627     char buf[bufsize];
628     
629     a0=((char*)&ip)[0];
630     a1=((char*)&ip)[1];
631     a2=((char*)&ip)[2];
632     a3=((char*)&ip)[3];
633     snprintf(buf,13,"%03u%03u%03u%03u",a0,a1,a2,a3);
634     buf[12]=0;
635     target=buf;
636
637     return target;
638 }
639
640 string IP_RANGE::resolve_ip(const string &iporname)
641 {
642     struct in_addr ip_adr;
643     struct hostent *dnsdata;
644     
645     if (inet_aton(iporname.c_str(),&ip_adr) != 0)
646     {
647         // is already a ip
648         return iporname;
649     }
650
651     // we need dns
652     dnsdata=gethostbyname(iporname.c_str());
653
654     if (dnsdata==NULL)
655         throw dns_exception(hstrerror(h_errno));
656
657     if (dnsdata->h_addr_list == NULL || *(dnsdata->h_addr_list) == NULL)
658         throw dns_exception("no corresponding ip found for: "+iporname);
659
660     return inet_ntoa(*(struct in_addr*)(*dnsdata->h_addr_list));
661 }