Line data Source code
1 : // Copyright (c) 2012-2021 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/libaddr
4 : //
5 : // Permission is hereby granted, free of charge, to any person obtaining a
6 : // copy of this software and associated documentation files (the
7 : // "Software"), to deal in the Software without restriction, including
8 : // without limitation the rights to use, copy, modify, merge, publish,
9 : // distribute, sublicense, and/or sell copies of the Software, and to
10 : // permit persons to whom the Software is furnished to do so, subject to
11 : // the following conditions:
12 : //
13 : // The above copyright notice and this permission notice shall be included
14 : // in all copies or substantial portions of the Software.
15 : //
16 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 :
24 :
25 : /** \file
26 : * \brief The implementation of the addr_range class.
27 : *
28 : * This file includes the implementation of the addr_range class
29 : * and the address_match_ranges() global function.
30 : */
31 :
32 : // self
33 : //
34 : #include "libaddr/addr_range.h"
35 : #include "libaddr/addr_exception.h"
36 :
37 :
38 : // C++ library
39 : //
40 : #include <algorithm>
41 :
42 :
43 : // last include
44 : //
45 : #include <snapdev/poison.h>
46 :
47 :
48 :
49 : namespace addr
50 : {
51 :
52 :
53 : /** \brief Return true if the range has a 'from' address defined.
54 : *
55 : * By default the 'from' and 'to' addresses of an addr_range are legal
56 : * but considered undefined. After you called the set_from() function
57 : * once, this function will always return true.
58 : *
59 : * \return false until 'set_from()' is called at least once.
60 : */
61 325 : bool addr_range::has_from() const
62 : {
63 325 : return f_has_from;
64 : }
65 :
66 :
67 : /** \brief Return true if the range has a 'to' address defined.
68 : *
69 : * By default the 'from' and 'to' addresses of an addr_range are legal
70 : * but considered undefined. After you called the set_to() function
71 : * once, this function will always return true.
72 : *
73 : * \return false until 'set_to()' is called at least once.
74 : */
75 292 : bool addr_range::has_to() const
76 : {
77 292 : return f_has_to;
78 : }
79 :
80 :
81 : /** \brief Determine whether an addr_range object is considered a range.
82 : *
83 : * This function returns false until both, set_from() and set_to(),
84 : * were called.
85 : *
86 : * Note that the order in which the two functions get called is not
87 : * important, although we generally expect set_from() to be called
88 : * first, it does not matter.
89 : *
90 : * \return true if both, 'from' and 'to', were set.
91 : */
92 1045 : bool addr_range::is_range() const
93 : {
94 1045 : return f_has_from && f_has_to;
95 : }
96 :
97 :
98 : /** \brief Check whether this range is empty.
99 : *
100 : * If you defined the 'from' and 'to' addresses of the range, then you
101 : * can check whether the range is empty or not.
102 : *
103 : * A range is considered empty if 'from' is larger than 'to' because
104 : * in that case nothing can appear in between (no IP can at the same
105 : * time be larger than 'from' and smaller than 'to' if 'from > to'
106 : * is true.)
107 : *
108 : * \return true if 'from > to' and is_range() returns true.
109 : *
110 : * \sa is_range()
111 : * \sa has_from()
112 : * \sa has_to()
113 : */
114 309 : bool addr_range::is_empty() const
115 : {
116 309 : if(!is_range())
117 : {
118 289 : return false;
119 : }
120 20 : return f_from > f_to;
121 : }
122 :
123 :
124 : /** \brief Check whether \p rhs is part of this range.
125 : *
126 : * If the address specified in rhs is part of this range, then the function
127 : * returns true. The 'from' and 'to' addresses are considered inclusive,
128 : * so if rhs is equal to 'from' or 'to', then the function returns true.
129 : *
130 : * If 'from' is larger than 'to' then the function already returns false
131 : * since the range represents an empty range.
132 : *
133 : * \exception addr_invalid_state_exception
134 : * The addr_range object must be a range or this function throws this
135 : * exception. To test whether you can call this function, first call
136 : * the is_range() function. If it returns true, then is_in() is available.
137 : *
138 : * \param[in] rhs The address to check for inclusion.
139 : *
140 : * \return true if rhs is considered part of this range.
141 : */
142 417 : bool addr_range::is_in(addr const & rhs) const
143 : {
144 417 : if(!is_range())
145 : {
146 10 : throw addr_invalid_state("addr_range::is_in(): range is not complete (from or to missing.)");
147 : }
148 :
149 407 : if(f_from <= f_to)
150 : {
151 : //
152 285 : return rhs >= f_from && rhs <= f_to;
153 : }
154 : //else -- from/to are swapped... this represents an empty range
155 :
156 122 : return false;
157 : }
158 :
159 :
160 : /** \brief Check whether this range is an IPv4 range.
161 : *
162 : * This function verifies whether this range represents an IPv6 range or
163 : * an IPv4 range.
164 : *
165 : * If the range is not defined (no from and no to) then the function
166 : * always returns false.
167 : *
168 : * \return true if the range represents an IPv4 address.
169 : */
170 0 : bool addr_range::is_ipv4() const
171 : {
172 0 : if(f_has_from && f_has_to)
173 : {
174 0 : return f_from.is_ipv4() && f_to.is_ipv4();
175 : }
176 :
177 0 : if(f_has_from)
178 : {
179 0 : return f_from.is_ipv4();
180 : }
181 :
182 0 : if(f_has_to)
183 : {
184 0 : return f_to.is_ipv4();
185 : }
186 :
187 0 : return false;
188 : }
189 :
190 :
191 : /** \brief Set 'from' address.
192 : *
193 : * This function saves the 'from' address in this range object.
194 : *
195 : * Once this function was called at least once, the has_from() returns true.
196 : *
197 : * \param[in] from The address to save as the 'from' address.
198 : */
199 131869 : void addr_range::set_from(addr const & from)
200 : {
201 131869 : f_has_from = true;
202 131869 : f_from = from;
203 131869 : }
204 :
205 :
206 : /** \brief Get 'from' address.
207 : *
208 : * This function return the 'from' address as set by the set_from()
209 : * functions.
210 : *
211 : * The get_from() function can be called even if the has_from()
212 : * function returns false. It will return a default address
213 : * (a new 'addr' object.)
214 : *
215 : * \return The address saved as the 'from' address.
216 : */
217 568 : addr & addr_range::get_from()
218 : {
219 568 : return f_from;
220 : }
221 :
222 :
223 : /** \brief Get the 'from' address when addr_range is constant.
224 : *
225 : * This function return the 'from' address as set by the set_from()
226 : * functions.
227 : *
228 : * The get_from() function can be called even if the has_from()
229 : * function returns false. It will return a default address
230 : * (a new 'addr' object.)
231 : *
232 : * \return The address saved as the 'from' address.
233 : */
234 131793 : addr const & addr_range::get_from() const
235 : {
236 131793 : return f_from;
237 : }
238 :
239 :
240 : /** \brief Set 'to' address.
241 : *
242 : * This function saves the 'to' address in this range object.
243 : *
244 : * Once this function was called at least once, the has_to() returns true.
245 : *
246 : * \param[in] to The address to save as the 'to' address.
247 : */
248 8 : void addr_range::set_to(addr const & to)
249 : {
250 8 : f_has_to = true;
251 8 : f_to = to;
252 8 : }
253 :
254 :
255 : /** \brief Get the 'to' address.
256 : *
257 : * This function return the 'to' address as set by the set_to()
258 : * function.
259 : *
260 : * The get_from() function can be called even if the has_from()
261 : * function returns false. It will return a default address
262 : * (a new 'addr' object.)
263 : *
264 : * \return The address saved as the 'to' address.
265 : */
266 9 : addr & addr_range::get_to()
267 : {
268 9 : return f_to;
269 : }
270 :
271 :
272 : /** \brief Get the 'to' address when addr_range is constant.
273 : *
274 : * This function return the 'to' address as set by the set_to()
275 : * function.
276 : *
277 : * The get_to() function can be called even if the has_to()
278 : * function returns false. It will return a default address
279 : * (a new 'addr' object.)
280 : *
281 : * \return The address saved as the 'to' address.
282 : */
283 7 : addr const & addr_range::get_to() const
284 : {
285 7 : return f_to;
286 : }
287 :
288 :
289 : /** \brief Transform an address to a range.
290 : *
291 : * This function transforms an address (\p a) in a range.
292 : *
293 : * This is useful if you have a CIDR type of address (an address with a
294 : * mask length defined along with it) and want to generate a range with
295 : * it.
296 : *
297 : * The range is defined as (a & mask) for the "from" address and
298 : * (a | ~mask) for the "to" address. If the mask is all 1s, then the
299 : * resulting range is just (a).
300 : *
301 : * \exception addr_unsupported_as_range
302 : * If the address cannot be transform into a range, this exception is raised.
303 : * This happens if the mask is not just ending with 0s but 0s are found
304 : * earlier.
305 : *
306 : * \param[in] a The address to convert to a range.
307 : */
308 0 : void addr_range::from_cidr(addr const & a)
309 : {
310 0 : std::uint8_t mask[16];
311 0 : a.get_mask(mask);
312 0 : bool found(false);
313 0 : sockaddr_in6 from = {};
314 0 : a.get_ipv6(from);
315 0 : sockaddr_in6 to(from);
316 0 : for(std::size_t i(0); i < sizeof(mask); ++i)
317 : {
318 0 : std::uint8_t inverse = ~mask[i];
319 :
320 0 : from.sin6_addr.s6_addr[i] &= mask[i];
321 0 : to.sin6_addr.s6_addr[i] |= inverse;
322 :
323 0 : if(found)
324 : {
325 0 : if(inverse != 0xFF)
326 : {
327 0 : throw addr_unsupported_as_range("unsupported mask for a range");
328 : }
329 : }
330 : else
331 : {
332 0 : if(inverse != 0)
333 : {
334 0 : found = true;
335 0 : switch(inverse)
336 : {
337 0 : case 0x01:
338 : case 0x03:
339 : case 0x07:
340 : case 0x0F:
341 : case 0x1F:
342 : case 0x3F:
343 : case 0x7F:
344 : case 0xFF:
345 0 : break;
346 :
347 0 : default:
348 0 : throw addr_unsupported_as_range("unsupported mask for a range");
349 :
350 : }
351 : }
352 : }
353 : }
354 :
355 0 : set_from(from);
356 0 : set_to(to);
357 0 : }
358 :
359 :
360 : /** \brief Compute a new range with the part that is shared between both inputs.
361 : *
362 : * This function computers a range which encompasses all the addresses found
363 : * in \p this range and \p rhs range.
364 : *
365 : * If the two range do not intersect, then the resulting range will be an
366 : * empty range (see is_empty() for details).
367 : *
368 : * The new range receives the largest 'from' address from both inputs and
369 : * the smallest 'to' address from both inputs.
370 : *
371 : * \param[in] rhs The other range to compute the intersection with.
372 : *
373 : * \return The resulting intersection range.
374 : *
375 : * \sa is_empty()
376 : */
377 2 : addr_range addr_range::intersection(addr_range const & rhs) const
378 : {
379 2 : addr_range result;
380 :
381 2 : result.set_from(f_from > rhs.f_from ? f_from : rhs.f_from);
382 2 : result.set_to (f_to < rhs.f_to ? f_to : rhs.f_to);
383 :
384 2 : return result;
385 : }
386 :
387 :
388 : /** \brief Compute a new range with the union of two address ranges.
389 : *
390 : * This function checks whether the two ranges have any addresses in
391 : * common or if they are just one after the other. If so, then it
392 : * computes the union of both ranges. Otherwise it returns an empty
393 : * range.
394 : *
395 : * \note
396 : * If the `from` and `to` addresses of the range have a mask, it is
397 : * ignored.
398 : *
399 : * \param[in] rhs The other range to compute the intersection with.
400 : *
401 : * \return The resulting intersection range.
402 : *
403 : * \sa is_empty()
404 : */
405 0 : addr_range addr_range::union_if_possible(addr_range const & rhs) const
406 : {
407 0 : addr_range result;
408 :
409 0 : if((f_from <= rhs.f_to || f_from.is_previous(rhs.f_to)
410 0 : && (f_to >= rhs.f_from || f_to.is_next(rhs.f_from))))
411 : {
412 0 : result.set_from(f_from < rhs.f_from ? f_from : rhs.f_from);
413 0 : result.set_from(f_to > rhs.f_to ? f_to : rhs.f_to);
414 : }
415 :
416 0 : return result;
417 : }
418 :
419 :
420 : /** \brief Check whether an address matches a range.
421 : *
422 : * This function checks whether an address matches a range of addresses.
423 : *
424 : * The range may be empty, in which case the result is always false.
425 : *
426 : * If the range is a range (i.e. 'from' and 'to' are both defined,)
427 : * then the is_in() function is used to determine whether the address
428 : * is a match.
429 : *
430 : * If only one of the 'from' or 'to' addresses is defined, then that
431 : * one address addr::match() function is used to determine whether the
432 : * input \p address is a match.
433 : *
434 : * \param[in] address The address to match against a range of addresses.
435 : *
436 : * \return true if address matches this range.
437 : */
438 24 : bool addr_range::match(addr const & address) const
439 : {
440 : // if neith 'from' nor 'to' were defined, return
441 : //
442 24 : if(is_empty())
443 : {
444 3 : return false;
445 : }
446 :
447 21 : if(is_range())
448 : {
449 9 : return is_in(address);
450 : }
451 :
452 12 : if(has_from())
453 : {
454 6 : return f_from.match(address);
455 : }
456 : else
457 : {
458 : // if not empty and it does not have 'from', it has to be 'to'
459 : //
460 6 : return f_to.match(address);
461 : }
462 : }
463 :
464 :
465 : /** \brief Compare an address range against another.
466 : *
467 : * Comparing two address ranges against each other can return one of many
468 : * possible results (See the compare_t enumeration).
469 : *
470 : * \param[in] rhs The right hand side to compare against this range.
471 : *
472 : * \return One of the compare_t values.
473 : */
474 0 : compare_t addr_range::compare(addr_range const & rhs) const
475 : {
476 : // check for the empty case first
477 : //
478 0 : if(is_empty())
479 : {
480 0 : if(rhs.is_empty())
481 : {
482 0 : return compare_t::COMPARE_UNORDERED;
483 : }
484 :
485 0 : return compare_t::COMPARE_LAST;
486 : }
487 0 : else if(rhs.is_empty())
488 : {
489 0 : return compare_t::COMPARE_FIRST;
490 : }
491 :
492 : // IPv4 versus IPv6
493 : //
494 0 : if(is_ipv4())
495 : {
496 0 : if(!rhs.is_ipv4())
497 : {
498 0 : return compare_t::COMPARE_IPV4_VS_IPV6;
499 : }
500 : }
501 0 : else if(rhs.is_ipv4())
502 : {
503 0 : return compare_t::COMPARE_IPV6_VS_IPV4;
504 : }
505 :
506 : // no overlap (lhs < rhs)
507 : //
508 0 : if(f_to < rhs.f_from)
509 : {
510 0 : if(f_to.is_next(rhs.f_from))
511 : {
512 0 : return compare_t::COMPARE_FOLLOWS;
513 : }
514 0 : return compare_t::COMPARE_SMALL_VS_LARGE;
515 : }
516 :
517 : // no overlap (lhs > rhs)
518 : //
519 0 : if(f_from > rhs.f_to)
520 : {
521 0 : if(f_to.is_previous(rhs.f_from))
522 : {
523 0 : return compare_t::COMPARE_FOLLOWING;
524 : }
525 0 : return compare_t::COMPARE_LARGE_VS_SMALL;
526 : }
527 :
528 : // overlap (lhs <= rhs)
529 : //
530 0 : if(f_from <= rhs.f_from
531 0 : && f_to >= rhs.f_from)
532 : {
533 0 : if(f_to >= rhs.f_to)
534 : {
535 0 : if(f_from == rhs.f_from
536 0 : && f_to == rhs.f_to)
537 : {
538 0 : return compare_t::COMPARE_EQUAL;
539 : }
540 0 : return compare_t::COMPARE_INCLUDED;
541 : }
542 0 : if(f_from == rhs.f_from)
543 : {
544 0 : return compare_t::COMPARE_INCLUDES;
545 : }
546 0 : return compare_t::COMPARE_OVERLAP_SMALL_VS_LARGE;
547 : }
548 :
549 : // overlap (lhs >= rhs)
550 : //
551 0 : if(f_to >= rhs.f_from
552 0 : && f_to <= rhs.f_to)
553 : {
554 0 : if(f_from >= rhs.f_from)
555 : {
556 : // the previous block already captured this case
557 : //
558 : //if(f_from == rhs.f_from
559 : //&& f_to == rhs.f_to)
560 : //{
561 : // return compare_t::COMPARE_EQUAL;
562 : //}
563 :
564 0 : return compare_t::COMPARE_INCLUDES;
565 : }
566 0 : if(f_to == rhs.f_to)
567 : {
568 0 : return compare_t::COMPARE_INCLUDED;
569 : }
570 0 : return compare_t::COMPARE_OVERLAP_LARGE_VS_SMALL;
571 : }
572 :
573 : // no overlap
574 : //
575 0 : return f_to < rhs.f_from
576 0 : ? compare_t::COMPARE_SMALLER
577 0 : : compare_t::COMPARE_LARGER;
578 : }
579 :
580 :
581 : /** \brief Check whether an address matches a range.
582 : *
583 : * When you call the addr_parser::parse() function, you get a vector of
584 : * ranges as a result. This function allows you to check whether an
585 : * address matches any one of those ranges.
586 : *
587 : * \param[in] ranges The vector of ranges to search for \p address.
588 : * \param[in] address The address to search in \p ranges.
589 : *
590 : * \return true if \p address matches any one of the \p ranges.
591 : */
592 4 : bool address_match_ranges(addr_range::vector_t ranges, addr const & address)
593 : {
594 4 : auto const it(std::find_if
595 : ( ranges.begin()
596 : , ranges.end()
597 6 : , [&address](auto const & range)
598 6 : {
599 : return range.match(address);
600 6 : }
601 4 : ));
602 :
603 4 : return it != ranges.end();
604 : }
605 :
606 :
607 :
608 :
609 : }
610 : // namespace addr
611 : // vim: ts=4 sw=4 et
|