Switch license from Intranator license to GPLv2 + linking exception (ACKed by Steffen)
[libi2ncommon] / src / ipfunc.cpp
CommitLineData
0e23f538
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
e93545dd
GE
20/***************************************************************************
21 ip_functions.cpp - description
22 -------------------
23 begin : Sat Feb 07 2004
24 copyright : (C) 2004 by Intra2net AG
e93545dd
GE
25 ***************************************************************************/
26
27#include <string>
28#include <sstream>
c24a8a9c 29#include <stdexcept>
a74046f1 30#include <stdio.h>
e93545dd
GE
31
32#include <arpa/inet.h>
e9852ad9 33#include <netdb.h>
e93545dd
GE
34
35#include <ipfunc.hxx>
36
37using namespace std;
c24a8a9c 38using namespace ip_type;
e93545dd 39
bb20d988
GE
40IP_RANGE& IP_RANGE::operator=(const IP_RANGE& other)
41{
42 IP_RANGE temp(other);
43 temp.swap(*this);
44 return *this;
45}
46
47void 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
c24a8a9c 55// can decode IP, IP-IP (as range) and IP/MASK (as network)
a0105b80 56void IP_RANGE::load(const std::string& ip)
c24a8a9c 57{
63dc9939 58 string::size_type delim_pos;
c24a8a9c
GE
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())
7a1513b2 65 throw runtime_error("invalid IP given: "+ip);
c24a8a9c
GE
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
80void 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))
7a1513b2 89 throw runtime_error("invalid IP given: "+ip);
c24a8a9c
GE
90
91 this->ip=ia_ip1.s_addr;
92 this->mask=0xFFFFFFFF;
93 this->end=this->ip;
94 }
95 else if (t==NETWORK)
96 {
180ad61f
TJ
97 // TODO: Better IP checks: "16" != "0.0.0.16"
98 if(!inet_aton(ip.c_str(),&ia_ip1))
7a1513b2 99 throw runtime_error("invalid IP given: "+ip);
c24a8a9c 100
180ad61f
TJ
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)
7a1513b2 108 throw runtime_error("invalid cidr mask given: "+mask_or_end);
180ad61f
TJ
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))
7a1513b2 113 throw runtime_error("invalid IP given: "+mask_or_end);
180ad61f 114
c24a8a9c
GE
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 {
1a1dcc97 126 if(!inet_aton(ip.c_str(),&ia_ip1))
7a1513b2 127 throw runtime_error("invalid IP given: "+ip);
1a1dcc97
GE
128
129 if(!inet_aton(mask_or_end.c_str(),&ia_ip2))
130 throw runtime_error("invalid IP given: "+mask_or_end);
131
c24a8a9c
GE
132 this->ip=ia_ip1.s_addr;
133 this->end=ia_ip2.s_addr;
939b5544 134
d16d1d55 135 // Automatically turn IP if IP & end are swapped
939b5544
TJ
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 }
c24a8a9c
GE
141 }
142}
143
a0105b80
GE
144void 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
c24a8a9c
GE
181bool 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
200bool 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
9141afbd
GE
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*/
227bool 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
939b5544
TJ
256IP_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))
3a488cb3 271 throw range_error("range end lower than begin");
939b5544
TJ
272
273 return ret;
274}
275
276int IP_RANGE::operator-(const IP_RANGE &other)
277{
278 return turn_ip(ip)-turn_ip(other.ip);
279}
280
d9ed3132
GE
281bool 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
f4e41f93
GE
287unsigned 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
2910669d 295std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const
c24a8a9c
GE
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 {
2910669d
TJ
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 }
c24a8a9c
GE
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 }
2910669d
TJ
330 else if (t==IP && always_mask) {
331 if (cidr_only)
332 output+="/32";
333 else
334 output+="/255.255.255.255";
335 }
336
322771b2 337 return output;
c24a8a9c
GE
338}
339
a0105b80 340vector<IP_RANGE> IP_RANGE::to_cidr(void) const
180ad61f 341{
a0105b80 342 vector<IP_RANGE> cidrs;
180ad61f 343
a0105b80
GE
344 if (t==IP || t==NETWORK) {
345 cidrs.push_back(*this);
346 } else {
180ad61f
TJ
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) {
a0105b80 352 cidrs.push_back(IP_RANGE(ip_type::NETWORK,0,0));
180ad61f 353 } else {
180ad61f
TJ
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)) {
a0105b80 358 cidrs.push_back(IP_RANGE(ip_type::NETWORK,turn_ip(cidr_addr),calc_netmask_from_cidr(32-bit)));
180ad61f
TJ
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
63f9b745
TJ
373vector<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
9f9289e1 384 // Get begin of network/range/IP
63f9b745
TJ
385 unsigned int sub_begin = turn_ip(sub->get_base());
386
387 // Got a fragment?
9f9289e1
TJ
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
63f9b745
TJ
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
c24a8a9c 409unsigned int IP_RANGE::turn_ip(unsigned int src)
e93545dd
GE
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
c24a8a9c 423unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask)
e93545dd 424{
180ad61f
TJ
425 netmask = turn_ip(netmask);
426
c24a8a9c
GE
427 switch (netmask)
428 {
e93545dd
GE
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
180ad61f
TJ
499unsigned 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
c24a8a9c 609std::string IP_RANGE::ip_string(unsigned int ip)
e93545dd 610{
c24a8a9c
GE
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;
e93545dd 620}
f4e41f93
GE
621
622std::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}
e9852ad9
TJ
639
640string 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)
7a1513b2 658 throw dns_exception("no corresponding ip found for: "+iporname);
e9852ad9
TJ
659
660 return inet_ntoa(*(struct in_addr*)(*dnsdata->h_addr_list));
661}