Make IP_RANGE::resolve_ip() thread safe
[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>
7050bac5 34#include <string.h>
e93545dd
GE
35
36#include <ipfunc.hxx>
37
38using namespace std;
c24a8a9c 39using namespace ip_type;
e93545dd 40
bb20d988
GE
41IP_RANGE& IP_RANGE::operator=(const IP_RANGE& other)
42{
43 IP_RANGE temp(other);
44 temp.swap(*this);
45 return *this;
46}
47
48void 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
c24a8a9c 56// can decode IP, IP-IP (as range) and IP/MASK (as network)
a0105b80 57void IP_RANGE::load(const std::string& ip)
c24a8a9c 58{
63dc9939 59 string::size_type delim_pos;
ac4ebb48 60
c24a8a9c
GE
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);
ac4ebb48 66
c24a8a9c
GE
67 if (ip.at(delim_pos) == '/')
68 t=NETWORK;
69 else
70 t=RANGE;
ac4ebb48 71
c24a8a9c
GE
72 load(t,ip.substr(0,delim_pos),ip.substr(delim_pos+1));
73 }
74 else
75 {
ac4ebb48 76 load(IP,ip,"");
c24a8a9c
GE
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{
a0105b80 146 this->t=t;
ac4ebb48 147
a0105b80
GE
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;
ac4ebb48 158
a0105b80
GE
159 // make sure ip is really the network address
160 this->ip=(this->ip & this->mask);
ac4ebb48 161
a0105b80
GE
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;
ac4ebb48 169
a0105b80
GE
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
c24a8a9c
GE
179bool 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
198bool 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
9141afbd
GE
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*/
225bool 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
939b5544
TJ
254IP_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))
3a488cb3 269 throw range_error("range end lower than begin");
939b5544
TJ
270
271 return ret;
272}
273
274int IP_RANGE::operator-(const IP_RANGE &other)
275{
276 return turn_ip(ip)-turn_ip(other.ip);
277}
278
d9ed3132
GE
279bool 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
f4e41f93
GE
285unsigned 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
2910669d 293std::string IP_RANGE::to_string(bool always_mask, bool cidr_only) const
c24a8a9c
GE
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 {
2910669d
TJ
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 }
c24a8a9c
GE
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 }
2910669d
TJ
328 else if (t==IP && always_mask) {
329 if (cidr_only)
330 output+="/32";
331 else
332 output+="/255.255.255.255";
333 }
334
322771b2 335 return output;
c24a8a9c
GE
336}
337
a0105b80 338vector<IP_RANGE> IP_RANGE::to_cidr(void) const
180ad61f 339{
a0105b80 340 vector<IP_RANGE> cidrs;
180ad61f 341
a0105b80
GE
342 if (t==IP || t==NETWORK) {
343 cidrs.push_back(*this);
344 } else {
180ad61f
TJ
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) {
a0105b80 350 cidrs.push_back(IP_RANGE(ip_type::NETWORK,0,0));
180ad61f 351 } else {
180ad61f
TJ
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)) {
a0105b80 356 cidrs.push_back(IP_RANGE(ip_type::NETWORK,turn_ip(cidr_addr),calc_netmask_from_cidr(32-bit)));
180ad61f
TJ
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
63f9b745
TJ
371vector<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();
83d700e9 378 for (sub = to_substract.begin(); sub != to_substract_end; ++sub) {
63f9b745
TJ
379 if (!sub->is_within(*this))
380 throw runtime_error("IP_RANGE::substract error: "+ sub->to_string() + " is not within " + to_string());
381
9f9289e1 382 // Get begin of network/range/IP
63f9b745
TJ
383 unsigned int sub_begin = turn_ip(sub->get_base());
384
385 // Got a fragment?
9f9289e1
TJ
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
63f9b745
TJ
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
c24a8a9c 407unsigned int IP_RANGE::turn_ip(unsigned int src)
e93545dd
GE
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
c24a8a9c 421unsigned int IP_RANGE::calc_netmask_bits(unsigned int netmask)
e93545dd 422{
180ad61f
TJ
423 netmask = turn_ip(netmask);
424
c24a8a9c
GE
425 switch (netmask)
426 {
e93545dd
GE
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
180ad61f
TJ
497unsigned 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
c24a8a9c 607std::string IP_RANGE::ip_string(unsigned int ip)
e93545dd 608{
c24a8a9c
GE
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;
e93545dd 618}
f4e41f93
GE
619
620std::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}
e9852ad9 637
7050bac5 638string IP_RANGE::resolve_ip(const string &iporname, const bool enable_ipv6)
e9852ad9
TJ
639{
640 struct in_addr ip_adr;
7050bac5 641
e9852ad9
TJ
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
7050bac5
TJ
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;
e9852ad9 655
7050bac5
TJ
656 if (getaddrinfo(iporname.c_str(), NULL, &hints, &result) != 0)
657 throw dns_exception("no corresponding ip found for: "+iporname);
e9852ad9 658
7050bac5 659 if (result == NULL || result->ai_addrlen == 0 || result->ai_addr == NULL)
7a1513b2 660 throw dns_exception("no corresponding ip found for: "+iporname);
e9852ad9 661
7050bac5
TJ
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);
e9852ad9 670}