Line data Source code
1 : // Network Address -- classes functions to ease handling IP addresses
2 : // Copyright (C) 2012-2017 Made to Order Software Corp.
3 : //
4 : // http://snapwebsites.org/project/libaddr
5 : //
6 : // This program is free software; you can redistribute it and/or modify
7 : // it under the terms of the GNU General Public License as published by
8 : // the Free Software Foundation; either version 2 of the License, or
9 : // (at your option) any later version.
10 : //
11 : // This program is distributed in the hope that it will be useful,
12 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : // GNU General Public License for more details.
15 : //
16 : // You should have received a copy of the GNU General Public License
17 : // along with this program; if not, write to the Free Software
18 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 :
20 : /** \file
21 : * \brief The implementation of the addr class.
22 : *
23 : * This file includes the implementation of the addr class. The one that
24 : * deals with low level classes.
25 : */
26 :
27 : // self
28 : //
29 : #include "libaddr/addr.h"
30 : #include "libaddr/addr_exceptions.h"
31 :
32 : // C++ library
33 : //
34 : //#include <algorithm>
35 : #include <sstream>
36 : #include <iostream>
37 :
38 : // C library
39 : //
40 : #include <ifaddrs.h>
41 : #include <netdb.h>
42 :
43 :
44 :
45 : /** \mainpage
46 : * \brief libaddr, a C++ library to handle network IP addresses in IPv4 and IPv6.
47 : *
48 : * ### Introduction
49 : *
50 : * This library is used to parse strings of IP addresses to lists of
51 : * binary IP addresses ready to be used by functions such as bind(),
52 : * send(), recv(), etc.
53 : *
54 : * The library supports multiple addresses separated by commas and/or
55 : * spaces, ports, and CIDR masks. It can check whether an address matches
56 : * another taking the mask in account. It can sort IPs numerically. It
57 : * can determine the type of an IP address (i.e. is it a local address,
58 : * a private address, a public address?)
59 : *
60 : * The library also has a function to read IP addresses from your
61 : * computer interfaces and return that list. Very practical to know
62 : * whether an IP address represents your computer or not.
63 : *
64 : * ### Usage
65 : *
66 : * The library is composed of three main classes:
67 : *
68 : * \li addr
69 : *
70 : * The address class holds one address, a port, a protocol and a few
71 : * other parts. This is what one uses to connect or listen with an
72 : * address.
73 : *
74 : * The address is kept by addr in an IPv6 address structure.
75 : *
76 : * By default the CIDR of the address is all 1s (i.e. no masking, all
77 : * bits considered important.) The mask is always 128 bits. If you are
78 : * dealing with IPv4, make sure that the first 12 bytes are set to 255.
79 : *
80 : * The class also offers a set of functions to transform the binary
81 : * address it is holding to a string.
82 : *
83 : * \li addr_range
84 : *
85 : * It is possible to define a range of addresses and ports. This class
86 : * holds a 'from' address and a 'to' address. By default neither is
87 : * defined. You have to call the set_from() and set_to() functions.
88 : *
89 : * The addr_range::vector_t is what the addr_parser returns after
90 : * parsing a string representing one of more addresses.
91 : *
92 : * \note
93 : * The range is functional, however, the parser does not yet support
94 : * parsing range of addresses and ports.
95 : *
96 : * \li addr_parser
97 : *
98 : * The parser is used to transform a string to an address.
99 : *
100 : * It supports many variations of its input, which are handled by
101 : * the 'allow' flags. The set_allow() and get_allow() functions can
102 : * be used to tweak the parser in supporting such and such feature.
103 : *
104 : * By default, the input is expected to be an address and a port
105 : * separated by a colon (i.e. `"1.2.3.4:1234"` in IPv4 and `"[::1]:1234"`
106 : * in IPv6.)
107 : *
108 : * ### Parser
109 : *
110 : * The parser supports the following syntax (ranges are not yet supported
111 : * and they do not appear in the following list):
112 : *
113 : * \code
114 : * start: address_list
115 : *
116 : * address_list: address_cidr
117 : * | address_list address_list_separators address_cidr
118 : *
119 : * address_list_separators: ' '
120 : * | ','
121 : * | address_list_separators address_list_separators
122 : *
123 : * address_cidr: address_port
124 : * | address_port '/' cidr
125 : *
126 : * address_port: address
127 : * | address ':' port
128 : *
129 : * address: ipv4
130 : * | ipv6
131 : *
132 : * cidr: decimal_number
133 : * | ipv4
134 : * | ipv6
135 : *
136 : * ipv4: decimal_number '.' decimal_number '.' decimal_number '.' decimal_number
137 : *
138 : * ipv6: '[' hexadecimal_number_list ']'
139 : *
140 : * port: decimal_number
141 : *
142 : * hexadecimal_number_list: hexadecimal_number
143 : * | hexadecimal_number_list ':' hexadecimal_number
144 : *
145 : * decimal_number: [0-9]+
146 : *
147 : * hexadecimal_number: [0-9a-fA-F]+
148 : * \endcode
149 : *
150 : * When accepting multiple addresses separated by commas or spaces, the
151 : * number of commas and spaces separating two address is not significant.
152 : * The input string can also start or end with commas and spaces. The
153 : * following variable defines exactly two IP address:
154 : *
155 : * \code
156 : * addresses= ,1.2.3.4, ,5.6.7.8,,
157 : * \endcode
158 : *
159 : * (note that the parser should not be passed the "addresses=" part.)
160 : */
161 :
162 :
163 : /** \brief The libaddr classes are all defined in this namespace.
164 : *
165 : * The addr namespace includes all the addr classes.
166 : */
167 : namespace addr
168 : {
169 :
170 : /*
171 : * Various sytem address structures
172 :
173 : // Any address is 16 bytes or less
174 : struct sockaddr {
175 : unsigned short sa_family; // address family, AF_xxx
176 : char sa_data[14]; // 14 bytes of protocol address
177 : };
178 :
179 : struct sockaddr_storage {
180 : sa_family_t ss_family; // address family
181 :
182 : // all this is padding, implementation specific, ignore it:
183 : char __ss_pad1[_SS_PAD1SIZE];
184 : int64_t __ss_align;
185 : char __ss_pad2[_SS_PAD2SIZE];
186 : };
187 :
188 :
189 : // IPv4
190 : struct sockaddr_in {
191 : short sin_family; // e.g. AF_INET, AF_INET6
192 : unsigned short sin_port; // e.g. htons(3490)
193 : struct in_addr sin_addr; // see struct in_addr, below
194 : char sin_zero[8]; // zero this if you want to
195 : };
196 :
197 : struct in_addr {
198 : __be32 s_addr;
199 : };
200 :
201 :
202 : // IPv6
203 : struct sockaddr_in6 {
204 : u_int16_t sin6_family; // address family, AF_INET6
205 : u_int16_t sin6_port; // port number, Network Byte Order
206 : u_int32_t sin6_flowinfo; // IPv6 flow information
207 : struct in6_addr sin6_addr; // IPv6 address
208 : u_int32_t sin6_scope_id; // Scope ID
209 : };
210 :
211 : struct in6_addr
212 : {
213 : union
214 : {
215 : uint8_t __u6_addr8[16];
216 : #ifdef __USE_MISC
217 : uint16_t __u6_addr16[8];
218 : uint32_t __u6_addr32[4];
219 : #endif
220 : } __in6_u;
221 : #define s6_addr __in6_u.__u6_addr8
222 : #ifdef __USE_MISC
223 : # define s6_addr16 __in6_u.__u6_addr16
224 : # define s6_addr32 __in6_u.__u6_addr32
225 : #endif
226 : };
227 :
228 :
229 : */
230 :
231 :
232 : /** \brief Details used by the addr class implementation.
233 : *
234 : * We have a function to check whether an address is part of
235 : * the interfaces of your computer. This check requires the
236 : * use of a `struct ifaddrs` and as such it requires to
237 : * delete that structure. We define a deleter for that
238 : * strucure here.
239 : */
240 : namespace
241 : {
242 :
243 : /** \brief Delete an ifaddrs structure.
244 : *
245 : * This deleter is used to make sure all the ifaddrs get released when
246 : * an exception occurs or the function using such exists.
247 : *
248 : * \param[in] ia The ifaddrs structure to free.
249 : */
250 14 : void ifaddrs_deleter(struct ifaddrs * ia)
251 : {
252 14 : freeifaddrs(ia);
253 14 : }
254 :
255 :
256 : }
257 : // no name namespace
258 :
259 :
260 : /** \brief Create an addr object that represents an ANY address.
261 : *
262 : * This function initializes the addr object with the ANY address.
263 : * The port is set to 0 and the protocol to TCP.
264 : *
265 : * It is strongly suggested that you change those parameters
266 : * before really using this address since a port of zero and
267 : * the protocol may be wrong.
268 : */
269 264166 : addr::addr()
270 : {
271 : // keep default protocol (TCP)
272 264166 : }
273 :
274 :
275 : /** \brief Create an addr object from a binary IPv4 address.
276 : *
277 : * This function initializes this addr object with the specified IPv4
278 : * address. The is_ipv4() function will return true.
279 : *
280 : * \param[in] in The binary IPv4 address.
281 : */
282 65911 : addr::addr(struct sockaddr_in const & in)
283 : {
284 65886 : set_ipv4(in);
285 : // keep default protocol (TCP)
286 65861 : }
287 :
288 :
289 : /** \brief Create an addr object from a binary IPv6 address.
290 : *
291 : * This function initializes this addr object with the specified IPv6
292 : * address. The is_ipv4() function will return false.
293 : *
294 : * \param[in] in6 The binary IPv6 address.
295 : */
296 65991 : addr::addr(struct sockaddr_in6 const & in6)
297 : {
298 65990 : set_ipv6(in6);
299 : // keep default protocol (TCP)
300 65989 : }
301 :
302 :
303 : /** \brief Save an IPv4 in this addr object.
304 : *
305 : * This function saves the specified IPv4 in this addr object.
306 : *
307 : * Since we save the data in an IPv6 structure, it is kept in
308 : * the addr as an IPv4 mapped in an IPv6 address. It can still
309 : * be retrieved right back as an IPv4 with the get_ipv4() function.
310 : *
311 : * \param[in] in The IPv4 address to save in this addr object.
312 : */
313 66422 : void addr::set_ipv4(struct sockaddr_in const & in)
314 : {
315 66422 : if(in.sin_family != AF_INET)
316 : {
317 : // although we convert the IPv4 to an IPv6 below, we really only
318 : // support AF_INET on entry
319 : //
320 50 : throw addr_invalid_argument_exception("addr::set_ipv4(): the input address does not represent an IPv4 address (family is not AF_INET).");
321 : }
322 :
323 : // reset the address first
324 66372 : memset(&f_address, 0, sizeof(f_address));
325 :
326 : // then transform the IPv4 to an IPv6
327 : //
328 : // Note: this is not an IPv6 per se, it is an IPv4 mapped within an
329 : // IPv6 and your network anwway stack needs to support IPv4
330 : // in order to use that IP...
331 : //
332 66372 : f_address.sin6_family = AF_INET6;
333 66372 : f_address.sin6_port = in.sin_port;
334 66372 : f_address.sin6_addr.s6_addr16[5] = 0xFFFF;
335 66372 : f_address.sin6_addr.s6_addr32[3] = in.sin_addr.s_addr;
336 :
337 66372 : address_changed();
338 66372 : }
339 :
340 :
341 : /** \brief Set the port of this address.
342 : *
343 : * This function changes the port of this address to \p port.
344 : *
345 : * \exception addr_invalid_argument_exception
346 : * This exception is raised whenever the \p port parameter is set to
347 : * an invalid number (negative or larger than 65535.)
348 : *
349 : * \param[in] port The new port to save in this address.
350 : */
351 65741 : void addr::set_port(int port)
352 : {
353 65741 : if(port > 65535
354 65641 : || port < 0)
355 : {
356 200 : throw addr_invalid_argument_exception("port to set_port() cannot be out of the allowed range [0..65535].");
357 : }
358 65541 : f_address.sin6_port = htons(port);
359 65541 : }
360 :
361 :
362 : /** \brief Change the protocol using a string.
363 : *
364 : * This function is used to change the current protocol defined in
365 : * this addr object.
366 : *
367 : * \exception addr_invalid_argument_exception
368 : * We currently support "tcp", "udp", and "ip". Any other protocol
369 : * name generates this exception.
370 : *
371 : * \param[in] protocol The name of the protocol ("tcp", "udp", or "ip")
372 : */
373 7 : void addr::set_protocol(char const * protocol)
374 : {
375 7 : if(protocol == nullptr)
376 : {
377 1 : throw addr_invalid_argument_exception("protocol pointer to set_protocol() cannot be a nullptr.");
378 : }
379 :
380 6 : if(strcmp(protocol, "ip") == 0)
381 : {
382 1 : f_protocol = IPPROTO_IP;
383 : }
384 5 : else if(strcmp(protocol, "tcp") == 0)
385 : {
386 1 : f_protocol = IPPROTO_TCP;
387 : }
388 4 : else if(strcmp(protocol, "udp") == 0)
389 : {
390 1 : f_protocol = IPPROTO_UDP;
391 : }
392 : else
393 : {
394 : throw addr_invalid_argument_exception(
395 : std::string("unknown protocol \"")
396 6 : + protocol
397 9 : + "\", expected \"tcp\" or \"udp\" (string).");
398 : }
399 :
400 3 : address_changed();
401 3 : }
402 :
403 :
404 : /** \brief Set the protocol numerically.
405 : *
406 : * This function sets the protocol from a number instead of a name.
407 : *
408 : * Note that we only support IPPROTO_TCP and IPPROTO_UDP for now.
409 : * Any other protocol will make this function raise an exception.
410 : *
411 : * \todo
412 : * We may want to support any protocol number at this level. If your
413 : * application is limited then it should verify the protocol and
414 : * make sure it supports it before using this address. At the same
415 : * time, the IP protocol is pretty much locked up with just TCP
416 : * and UDP these days (there is the IP protocol, but that's not
417 : * useful at our level.)
418 : *
419 : * \exception addr_invalid_argument_exception
420 : * This exception is raised if the specified protocol is not currently
421 : * supported by the addr implementation.
422 : *
423 : * \param[in] protocol The new numeric protocol.
424 : */
425 131934 : void addr::set_protocol(int protocol)
426 : {
427 131934 : switch(protocol)
428 : {
429 : case IPPROTO_IP:
430 : case IPPROTO_TCP:
431 : case IPPROTO_UDP:
432 131834 : f_protocol = protocol;
433 131834 : break;
434 :
435 : default:
436 : throw addr_invalid_argument_exception(
437 : "unknown protocol number "
438 200 : + std::to_string(protocol)
439 200 : + ", expected \"tcp\" ("
440 400 : + std::to_string(static_cast<int>(IPPROTO_TCP))
441 200 : + ") or \"udp\" ("
442 400 : + std::to_string(static_cast<int>(IPPROTO_UDP))
443 300 : + ") (numeric).");
444 :
445 : }
446 131834 : }
447 :
448 :
449 : /** \brief Set the mask.
450 : *
451 : * The input mask must be exactly 16 bytes. If you are dealing with an
452 : * IPv4, make sure the first 12 bytes are 255.
453 : *
454 : * \param[in] mask The mask to save in this address.
455 : */
456 425 : void addr::set_mask(uint8_t const * mask)
457 : {
458 425 : memcpy(f_mask, mask, sizeof(f_mask));
459 425 : }
460 :
461 :
462 : /** \brief Apply the mask to the IP address.
463 : *
464 : * This function applies the mask to this address IP address. This means
465 : * the bits that are 0 in the mask will also be 0 in the address once
466 : * the function returns.
467 : *
468 : * This should be called if you are trying to canonicalize an IP/mask.
469 : */
470 1 : void addr::apply_mask()
471 : {
472 17 : for(int idx(0); idx < 16; ++idx)
473 : {
474 16 : f_address.sin6_addr.s6_addr[idx] &= f_mask[idx];
475 : }
476 1 : }
477 :
478 :
479 : /** \brief Get the mask.
480 : *
481 : * The output buffer for the mask must be at least 16 bytes. If you are
482 : * dealing with an IPv4, all the bytes are expected to be 255 except
483 : * the bottom 4 bytes (offset 12, 13, 14, 15).
484 : *
485 : * \param[out] mask The buffer where the mask gets copied.
486 : */
487 142 : void addr::get_mask(uint8_t * mask)
488 : {
489 142 : memcpy(mask, f_mask, sizeof(f_mask));
490 142 : }
491 :
492 :
493 : /** \brief Check whether this address represents an IPv4 address.
494 : *
495 : * The IPv6 format supports embedding IPv4 addresses. This function
496 : * returns true if the embedded address is an IPv4. When this function
497 : * returns true, the get_ipv4() can be called. Otherwise, the get_ipv4()
498 : * function throws an error.
499 : *
500 : * \return true if this address represents an IPv4 address.
501 : */
502 527579 : bool addr::is_ipv4() const
503 : {
504 527579 : return f_address.sin6_addr.s6_addr32[0] == 0
505 329858 : && f_address.sin6_addr.s6_addr32[1] == 0
506 329854 : && f_address.sin6_addr.s6_addr16[4] == 0
507 857431 : && f_address.sin6_addr.s6_addr16[5] == 0xFFFF;
508 : }
509 :
510 :
511 : /** \brief Retreive the IPv4 address.
512 : *
513 : * This function can be used to retrieve the IPv4 address of this addr
514 : * object. If the address is not an IPv4, then the function throws.
515 : *
516 : * \exception addr_invalid_structure_exception
517 : * This exception is raised if the address is not an IPv4 address.
518 : *
519 : * \param[out] in The structure where the IPv4 Internet address gets saved.
520 : */
521 23 : void addr::get_ipv4(struct sockaddr_in & in) const
522 : {
523 23 : if(is_ipv4())
524 : {
525 : // this is an IPv4 mapped in an IPv6, "unmap" that IP
526 : //
527 22 : memset(&in, 0, sizeof(in));
528 22 : in.sin_family = AF_INET;
529 22 : in.sin_port = f_address.sin6_port;
530 22 : in.sin_addr.s_addr = f_address.sin6_addr.s6_addr32[3];
531 44 : return;
532 : }
533 :
534 1 : throw addr_invalid_state_exception("Not an IPv4 compatible address.");
535 : }
536 :
537 :
538 : /** \brief Save the specified IPv6 address in this addr object.
539 : *
540 : * This function saves the specified IPv6 address in this addr object.
541 : * The function does not check the validity of the address. It is
542 : * expected to be valid.
543 : *
544 : * The address may be an embedded IPv4 address.
545 : *
546 : * \param[in] in6 The source IPv6 to save in the addr object.
547 : */
548 66117 : void addr::set_ipv6(struct sockaddr_in6 const & in6)
549 : {
550 66117 : if(in6.sin6_family != AF_INET6)
551 : {
552 2 : throw addr_invalid_argument_exception("addr::set_ipv6(): the input address does not represent an IPv6 address (family is not AF_INET6).");
553 : }
554 66115 : memcpy(&f_address, &in6, sizeof(in6));
555 :
556 66115 : address_changed();
557 66115 : }
558 :
559 :
560 : /** \brief Retrieve a copy of this addr IP address.
561 : *
562 : * This function returns the current IP address saved in this
563 : * addr object. The IP may represent an IPv4 address in which
564 : * case the is_ipv4() returns true.
565 : *
566 : * \param[out] in6 The structure where the address gets saved.
567 : */
568 21 : void addr::get_ipv6(struct sockaddr_in6 & in6) const
569 : {
570 21 : memcpy(&in6, &f_address, sizeof(in6));
571 21 : }
572 :
573 :
574 : /** \brief Retrive the IPv4 as a string.
575 : *
576 : * This function returns a string representing the IP address
577 : * defined in this addr object.
578 : *
579 : * The \p mode parameter defines what gets output.
580 : *
581 : * \li ip_string_t::IP_STRING_ONLY -- only the IP address
582 : * \li ip_string_t::IP_STRING_PORT -- the IP and port
583 : * \li ip_string_t::IP_STRING_MASK -- the IP and mask
584 : * \li ip_string_t::IP_STRING_ALL -- the IP, port, and mask
585 : *
586 : * The ip_string_t::IP_STRING_BRACKET is viewed as
587 : * ip_string_t::IP_STRING_ONLY.
588 : *
589 : * The ip_string_t::IP_STRING_BRACKET_MASK is viewed as
590 : * ip_string_t::IP_STRING_MASK.
591 : *
592 : * \exception addr_invalid_state_exception
593 : * If the addr object does not currently represent an IPv4 then
594 : * this exception is raised.
595 : *
596 : * \param[in] mode How the output string is to be built.
597 : */
598 131703 : std::string addr::to_ipv4_string(string_ip_t mode) const
599 : {
600 131703 : if(is_ipv4())
601 : {
602 : // this is an IPv4 mapped in an IPv6, "unmap" that IP
603 : // so the inet_ntop() can correctly generate an output IP
604 : //
605 : struct in_addr in;
606 131697 : memset(&in, 0, sizeof(in));
607 131697 : in.s_addr = f_address.sin6_addr.s6_addr32[3];
608 : char buf[INET_ADDRSTRLEN + 1];
609 131697 : if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
610 : {
611 131697 : if(mode != string_ip_t::STRING_IP_ONLY)
612 : {
613 1152 : std::stringstream result;
614 576 : result << buf;
615 576 : if(mode == string_ip_t::STRING_IP_PORT
616 266 : || mode == string_ip_t::STRING_IP_ALL)
617 : {
618 546 : result << ":";
619 546 : result << ntohs(f_address.sin6_port);
620 : }
621 576 : if(mode == string_ip_t::STRING_IP_MASK
622 566 : || mode == string_ip_t::STRING_IP_BRACKETS_MASK
623 556 : || mode == string_ip_t::STRING_IP_ALL)
624 : {
625 256 : memset(&in, 0, sizeof(in));
626 256 : in.s_addr = htonl((f_mask[12] << 24) | (f_mask[13] << 16) | (f_mask[14] << 8) | f_mask[15]);
627 256 : if(inet_ntop(AF_INET, &in, buf, sizeof(buf)) != nullptr)
628 : {
629 256 : result << "/";
630 256 : result << buf; // TODO: convert to simple number if possible
631 : }
632 : }
633 576 : return result.str();
634 : }
635 131121 : return std::string(buf);
636 : }
637 : // IPv4 should never fail converting the address unless the
638 : // buffer was too small...
639 : }
640 :
641 6 : throw addr_invalid_state_exception("Not an IPv4 compatible address.");
642 : }
643 :
644 :
645 : /** \brief Convert the addr object to a string.
646 : *
647 : * This function converts the addr object to a canonicalized string.
648 : * This can be used to compare two IPv6 together as strings, although
649 : * it is probably better to compare them using the < and == operators.
650 : *
651 : * By default the function returns with the IPv6 address defined
652 : * between square bracket, so the output of this function can be
653 : * used as the input of the set_addr_port() function. You may
654 : * also request the address without the brackets.
655 : *
656 : * \exception addr_invalid_argument_exception
657 : * If the binary IP address cannot be converted to ASCII, this exception
658 : * is raised.
659 : *
660 : * \param[in] mode How the output string is to be built.
661 : *
662 : * \return The addr object converted to an IPv6 address.
663 : */
664 263054 : std::string addr::to_ipv6_string(string_ip_t mode) const
665 : {
666 : char buf[INET6_ADDRSTRLEN + 1];
667 263054 : if(inet_ntop(AF_INET6, &f_address.sin6_addr, buf, sizeof(buf)) != nullptr)
668 : {
669 : bool const include_brackets(mode == string_ip_t::STRING_IP_BRACKETS
670 197502 : || mode == string_ip_t::STRING_IP_BRACKETS_MASK
671 197490 : || mode == string_ip_t::STRING_IP_PORT // port requires us to add brackets
672 329260 : || mode == string_ip_t::STRING_IP_ALL);
673 :
674 526108 : std::stringstream result;
675 :
676 : // always insert the IP, even if ANY or "BROADCAST"
677 : //
678 263054 : if(include_brackets)
679 : {
680 197476 : result << "[";
681 : }
682 263054 : result << buf;
683 263054 : if(include_brackets)
684 : {
685 197476 : result << "]";
686 : }
687 :
688 : // got a port?
689 : //
690 263054 : if(mode == string_ip_t::STRING_IP_PORT
691 131770 : || mode == string_ip_t::STRING_IP_ALL)
692 : {
693 131912 : result << ":";
694 131912 : result << ntohs(f_address.sin6_port);
695 : }
696 :
697 : // got a mask?
698 : //
699 263054 : if(mode == string_ip_t::STRING_IP_MASK
700 263042 : || mode == string_ip_t::STRING_IP_BRACKETS_MASK
701 263030 : || mode == string_ip_t::STRING_IP_ALL)
702 : {
703 652 : if(inet_ntop(AF_INET6, f_mask, buf, sizeof(buf)) != nullptr)
704 : {
705 652 : result << "/";
706 652 : if(include_brackets)
707 : {
708 640 : result << "[";
709 : }
710 652 : result << buf; // TODO: convert to simple number if possible
711 652 : if(include_brackets)
712 : {
713 640 : result << "]";
714 : }
715 : }
716 : }
717 :
718 526108 : return result.str();
719 : }
720 :
721 : throw addr_invalid_argument_exception("The address from this addr could not be converted to a valid canonicalized IPv6 address."); // LCOV_EXCL_LINE
722 : }
723 :
724 :
725 : /** \brief Return the address as IPv4 or IPv6.
726 : *
727 : * Depending on whether the address represents an IPv4 or an IPv6,
728 : * this function returns the corresponding address. Since the format
729 : * of both types of addresses can always be distinguished, it poses
730 : * no concerns.
731 : *
732 : * \exception
733 : * If include_brackets is false and include_port is true, this
734 : * exception is raised because we cannot furfill the request.
735 : *
736 : * \param[in] mode How the output string is to be built.
737 : *
738 : * \return The addr object converted to an IPv4 or an IPv6 address.
739 : */
740 131774 : std::string addr::to_ipv4or6_string(string_ip_t mode) const
741 : {
742 131774 : return is_ipv4() ? to_ipv4_string(mode)
743 131774 : : to_ipv6_string(mode);
744 : }
745 :
746 :
747 : /** \brief Determine the type of network this IP represents.
748 : *
749 : * The IP address may represent various type of networks. This
750 : * function returns that type.
751 : *
752 : * The function checks the address either as IPv4 when is_ipv4()
753 : * returns true, otherwise as IPv6.
754 : *
755 : * See:
756 : *
757 : * \li https://en.wikipedia.org/wiki/Reserved_IP_addresses
758 : * \li https://tools.ietf.org/html/rfc3330
759 : * \li https://tools.ietf.org/html/rfc5735 (IPv4)
760 : * \li https://tools.ietf.org/html/rfc5156 (IPv6)
761 : *
762 : * \return One of the possible network types as defined in the
763 : * network_type_t enumeration.
764 : */
765 131773 : addr::network_type_t addr::get_network_type() const
766 : {
767 131773 : if(f_private_network_defined == network_type_t::NETWORK_TYPE_UNDEFINED)
768 : {
769 131614 : f_private_network_defined = network_type_t::NETWORK_TYPE_UNKNOWN;
770 :
771 131614 : if(is_ipv4())
772 : {
773 : // get the address in host order
774 : //
775 : // we can use a simple mask + compare to know whether it is
776 : // this or that once in host order
777 : //
778 65886 : uint32_t const host_ip(ntohl(f_address.sin6_addr.s6_addr32[3]));
779 :
780 65886 : if((host_ip & 0xFF000000) == 0x0A000000 // 10.0.0.0/8
781 65873 : || (host_ip & 0xFFF00000) == 0xAC100000 // 172.16.0.0/12
782 65753 : || (host_ip & 0xFFFF0000) == 0xC0A80000) // 192.168.0.0/16
783 : {
784 65682 : f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
785 : }
786 204 : else if((host_ip & 0xFFC00000) == 0x64400000) // 100.64.0.0/10
787 : {
788 10 : f_private_network_defined = network_type_t::NETWORK_TYPE_CARRIER;
789 : }
790 194 : else if((host_ip & 0xFFFF0000) == 0xA9FE0000) // 169.254.0.0/16
791 : {
792 10 : f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
793 : }
794 184 : else if((host_ip & 0xF0000000) == 0xE0000000) // 224.0.0.0/4
795 : {
796 : // there are many sub-groups on this one which are probably
797 : // still in use...
798 : //
799 10 : f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
800 : }
801 174 : else if((host_ip & 0xFF000000) == 0x7F000000) // 127.0.0.0/8
802 : {
803 13 : f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK; // i.e. localhost
804 : }
805 161 : else if(host_ip == 0x00000000)
806 : {
807 1 : f_private_network_defined = network_type_t::NETWORK_TYPE_ANY; // i.e. 0.0.0.0
808 : }
809 : }
810 : else //if(is_ipv6()) -- if not IPv4, we have an IPv6
811 : {
812 : // for IPv6 it was simplified by using a prefix for
813 : // all types; really way easier than IPv4
814 : //
815 65728 : if(f_address.sin6_addr.s6_addr32[0] == 0 // ::
816 34 : && f_address.sin6_addr.s6_addr32[1] == 0
817 30 : && f_address.sin6_addr.s6_addr32[2] == 0
818 26 : && f_address.sin6_addr.s6_addr32[3] == 0)
819 : {
820 : // this is the "any" IP address
821 2 : f_private_network_defined = network_type_t::NETWORK_TYPE_ANY;
822 : }
823 : else
824 : {
825 65726 : uint16_t const prefix(ntohs(f_address.sin6_addr.s6_addr16[0]));
826 :
827 65726 : if((prefix & 0xFF00) == 0xFD00) // fd00::/8
828 : {
829 10 : f_private_network_defined = network_type_t::NETWORK_TYPE_PRIVATE;
830 : }
831 65716 : else if((prefix & 0xFFC0) == 0xFE80 // fe80::/10
832 65706 : || (prefix & 0xFF0F) == 0xFF02) // ffx2::/16
833 : {
834 120 : f_private_network_defined = network_type_t::NETWORK_TYPE_LINK_LOCAL; // i.e. DHCP
835 : }
836 65596 : else if((prefix & 0xFF0F) == 0xFF01 // ffx1::/16
837 50 : || (f_address.sin6_addr.s6_addr32[0] == 0 // ::1
838 32 : && f_address.sin6_addr.s6_addr32[1] == 0
839 28 : && f_address.sin6_addr.s6_addr32[2] == 0
840 24 : && f_address.sin6_addr.s6_addr16[6] == 0
841 22 : && f_address.sin6_addr.s6_addr16[7] == htons(1)))
842 : {
843 65566 : f_private_network_defined = network_type_t::NETWORK_TYPE_LOOPBACK;
844 : }
845 30 : else if((prefix & 0xFF00) == 0xFF00) // ff00::/8
846 : {
847 : // this one must be after the link-local and loopback networks
848 10 : f_private_network_defined = network_type_t::NETWORK_TYPE_MULTICAST;
849 : }
850 : }
851 : }
852 : }
853 :
854 131773 : return f_private_network_defined;
855 : }
856 :
857 :
858 : /** \brief Get the network type string
859 : *
860 : * Translate the network type into a string, which can be really useful
861 : * to log that information.
862 : *
863 : * Note that PUBLIC is the same as UNKNOWN, this function returns
864 : * "Unknown" in that case, though.
865 : *
866 : * \return The string representing the type of network.
867 : */
868 159 : std::string addr::get_network_type_string() const
869 : {
870 159 : std::string name;
871 159 : switch( get_network_type() )
872 : {
873 : case addr::network_type_t::NETWORK_TYPE_UNDEFINED : name= "Undefined"; break; // LCOV_EXCL_LINE -- get_network_type() defines it...
874 40 : case addr::network_type_t::NETWORK_TYPE_PRIVATE : name= "Private"; break;
875 10 : case addr::network_type_t::NETWORK_TYPE_CARRIER : name= "Carrier"; break;
876 30 : case addr::network_type_t::NETWORK_TYPE_LINK_LOCAL : name= "Local Link"; break;
877 20 : case addr::network_type_t::NETWORK_TYPE_MULTICAST : name= "Multicast"; break;
878 40 : case addr::network_type_t::NETWORK_TYPE_LOOPBACK : name= "Loopback"; break;
879 3 : case addr::network_type_t::NETWORK_TYPE_ANY : name= "Any"; break;
880 16 : case addr::network_type_t::NETWORK_TYPE_UNKNOWN : name= "Unknown"; break; // == NETWORK_TYPE_PUBLIC
881 : }
882 159 : return name;
883 : }
884 :
885 :
886 : /** \brief Retrieve the interface name
887 : *
888 : * This function retrieves the name of the interface of the address.
889 : * This is set using the get_local_addresses() static method.
890 : */
891 1 : std::string addr::get_iface_name() const
892 : {
893 1 : return f_iface_name;
894 : }
895 :
896 :
897 : /** \brief Create a socket from the IP address held by this addr object.
898 : *
899 : * This function creates a socket that corresponds to the addr object
900 : * definitions, it takes the protocol and family information in account.
901 : *
902 : * The flags can be used to add one or more of the following flags:
903 : *
904 : * \li SOCKET_FLAG_NONBLOCK -- create socket as non-block
905 : * \li SOCKET_FLAG_CLOEXEC -- close socket on an execv()
906 : * \li SOCKET_FLAG_REUSE -- for TCP socket, mark the address as immediately
907 : * reusable, ignored for UDP; only useful for server (bind + listen after
908 : * this call)
909 : *
910 : * \note
911 : * The IP protocol is viewed as TCP in this function.
912 : *
913 : * \warning
914 : * This class does not hold the socket created by this function.
915 : *
916 : * \todo
917 : * Move this to our libsnapnetwork once we create that separate library.
918 : * Probably within a form of low level socket class.
919 : *
920 : * \param[in] flags A set of socket flags to use when creating the socket.
921 : * \param[in] reuse_address Set the reuse address flag.
922 : *
923 : * \return The socket file descriptor.
924 : */
925 6 : int addr::create_socket(socket_flag_t flags) const
926 : {
927 : int const sock_flags(
928 6 : ((flags & SOCKET_FLAG_CLOEXEC) != 0 ? SOCK_CLOEXEC : 0)
929 6 : | ((flags & SOCKET_FLAG_NONBLOCK) != 0 ? SOCK_NONBLOCK : 0));
930 6 : int const family(is_ipv4() ? AF_INET : AF_INET6);
931 :
932 6 : switch(f_protocol)
933 : {
934 : case IPPROTO_IP: // interpret as TCP...
935 : case IPPROTO_TCP:
936 : {
937 4 : int s(socket(family, SOCK_STREAM | sock_flags, IPPROTO_TCP));
938 :
939 4 : if((flags & SOCKET_FLAG_REUSE) != 0)
940 : {
941 : // set the "reuse that address immediately" flag, we totally
942 : // ignore errors on that one
943 : //
944 2 : int optval(1);
945 2 : socklen_t const optlen(sizeof(optval));
946 2 : static_cast<void>(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, optlen));
947 : }
948 4 : return s;
949 : }
950 :
951 : case IPPROTO_UDP:
952 2 : return socket(family, SOCK_DGRAM | sock_flags, IPPROTO_UDP);
953 :
954 : default:
955 : // this should never happen since we control the f_protocol field
956 : //
957 : return -1; // LCOV_EXCL_LINE
958 :
959 : }
960 : }
961 :
962 :
963 : /** \brief Connect the specified socket to this IP address.
964 : *
965 : * When you create a TCP client, you can connect to a server. This
966 : * is done by using the connect() function which makes use of the
967 : * address to connect to the server.
968 : *
969 : * This function makes sure to select the correct connect() function
970 : * depending on whether this IP address is an IPv4 or an IPv6 address
971 : * (although we could always try with the IPv6 structure, it may or
972 : * may not work properly on all systems, so for now we use the
973 : * distinction.)
974 : *
975 : * \todo
976 : * Move this to our libsnapnetwork once we create that separate library.
977 : * Probably within a form of low level socket class.
978 : *
979 : * \param[in] s The socket to connect to the address.
980 : *
981 : * \return 0 if the bind() succeeded, -1 on errors
982 : */
983 4 : int addr::connect(int s) const
984 : {
985 : // only TCP can connect, UDP binds and sends only
986 : //
987 4 : switch(f_protocol)
988 : {
989 : case IPPROTO_IP: // interpret as TCP...
990 : case IPPROTO_TCP:
991 2 : if(is_ipv4())
992 : {
993 : // this would most certainly work using the IPv6 address
994 : // as in the else part, but to be sure, we use the IPv4
995 : // as specified in the address (there could be other reasons
996 : // than just your OS for this to fail if using IPv6.)
997 : //
998 : // IMPORTANT NOTE: also the family is used in the socket()
999 : // call above and must match the address here...
1000 : //
1001 : sockaddr_in ipv4;
1002 1 : get_ipv4(ipv4);
1003 1 : return ::connect(s, reinterpret_cast<sockaddr const *>(&ipv4), sizeof(ipv4));
1004 : }
1005 : else
1006 : {
1007 1 : return ::connect(s, reinterpret_cast<sockaddr const *>(&f_address), sizeof(struct sockaddr_in6));
1008 : }
1009 : break;
1010 :
1011 : }
1012 :
1013 2 : return -1;
1014 : }
1015 :
1016 :
1017 : /** \brief Create a server with this socket listening on this IP address.
1018 : *
1019 : * This function will bind the socket \p s to the address defined in
1020 : * this addr object. This creates a server listening on that IP address.
1021 : *
1022 : * If the IP address is 127.0.0.1, then only local processes can connect
1023 : * to that server. If the IP address is 0.0.0.0, then anyone can connect
1024 : * to the server.
1025 : *
1026 : * This function works for TCP and UDP servers.
1027 : *
1028 : * If the IP address represents an IPv4 addressm then the bind() is done
1029 : * with an IPv4 address and not the IPv6 as it is stored.
1030 : *
1031 : * \todo
1032 : * Move this to our libsnapnetwork once we create that separate library.
1033 : * Probably within a form of low level socket class.
1034 : *
1035 : * \param[in] s The socket to bind to this address.
1036 : *
1037 : * \return 0 if the bind() succeeded, -1 on errors
1038 : */
1039 2 : int addr::bind(int s) const
1040 : {
1041 2 : if(is_ipv4())
1042 : {
1043 : sockaddr_in ipv4;
1044 1 : get_ipv4(ipv4);
1045 1 : return ::bind(s, reinterpret_cast<sockaddr const *>(&ipv4), sizeof(ipv4));
1046 : }
1047 : else
1048 : {
1049 1 : return ::bind(s, reinterpret_cast<sockaddr const *>(&f_address), sizeof(struct sockaddr_in6));
1050 : }
1051 : }
1052 :
1053 :
1054 : /** \brief Initializes this addr object from a socket information.
1055 : *
1056 : * When you connect to a server or a clients connect to your server, the
1057 : * socket defines two IP addresses and ports: one on your side and one on
1058 : * the other side.
1059 : *
1060 : * The other side is called the _peer name_.
1061 : *
1062 : * You side is called the _socket name_ (i.e. the IP address of your computer,
1063 : * representing the interface used to perform that connection.)
1064 : *
1065 : * If you call this function with \p peer set to false then you get the
1066 : * address and port from your side. If you set \p peer to true,
1067 : * you get the other side address and port details.
1068 : *
1069 : * \todo
1070 : * Move this to our libsnapnetwork once we create that separate library.
1071 : * Probably within a form of low level socket class.
1072 : *
1073 : * \param[in] s The socket from which you want to retrieve peer information.
1074 : * \param[in] peer Whether to retrieve the peer or socket name.
1075 : */
1076 14 : void addr::set_from_socket(int s, bool peer)
1077 : {
1078 : // make sure the socket is defined and well
1079 : //
1080 14 : if(s < 0)
1081 : {
1082 2 : throw addr_invalid_argument_exception("addr::set_from_socket(): the socket cannot be a negative number.");
1083 : }
1084 :
1085 12 : struct sockaddr_storage address = sockaddr_storage();
1086 12 : socklen_t length(sizeof(address));
1087 : int r;
1088 12 : if(peer)
1089 : {
1090 : // this retrieves the information from the other side
1091 : //
1092 6 : r = getpeername(s, reinterpret_cast<struct sockaddr *>(&address), &length);
1093 : }
1094 : else
1095 : {
1096 : // retrieve the local socket information
1097 : //
1098 6 : r = getsockname(s, reinterpret_cast<struct sockaddr *>(&address), &length);
1099 : }
1100 12 : if(r != 0)
1101 : {
1102 5 : int const e(errno);
1103 : throw addr_io_exception(
1104 : std::string("addr::set_from_socket(): ")
1105 10 : + (peer ? "getpeername()" : "getsockname()")
1106 10 : + " failed to retrieve IP address details (errno: "
1107 20 : + std::to_string(e)
1108 10 : + ", "
1109 15 : + strerror(e)
1110 15 : + ").");
1111 : }
1112 :
1113 7 : switch(address.ss_family)
1114 : {
1115 : case AF_INET:
1116 3 : set_ipv4(reinterpret_cast<struct sockaddr_in &>(address));
1117 3 : break;
1118 :
1119 : case AF_INET6:
1120 3 : set_ipv6(reinterpret_cast<struct sockaddr_in6 &>(address));
1121 3 : break;
1122 :
1123 : default:
1124 : throw addr_invalid_state_exception(
1125 : std::string("addr::set_from_socket(): ")
1126 2 : + (peer ? "getpeername()" : "getsockname()")
1127 3 : + " returned a type of address, which is not understood, i.e. not AF_INET or AF_INET6.");
1128 :
1129 : }
1130 6 : }
1131 :
1132 :
1133 : /** \brief Transform the IP into a domain name.
1134 : *
1135 : * This function transforms the IP address in this `addr` object in a
1136 : * name such as "snap.website".
1137 : *
1138 : * \note
1139 : * The function does not cache the result because it is rarely used (at least
1140 : * at this time). So you should cache the result and avoid calling this
1141 : * function more than once as the process can be very slow.
1142 : *
1143 : * \todo
1144 : * Speed enhancement can be achieved by using getaddrinfo_a(). That would
1145 : * work with a vector of addr objects.
1146 : *
1147 : * \return The domain name. If not available, an empty string.
1148 : */
1149 7 : std::string addr::get_name() const
1150 : {
1151 : char host[NI_MAXHOST];
1152 :
1153 7 : int flags(NI_NAMEREQD);
1154 7 : if(f_protocol == IPPROTO_UDP)
1155 : {
1156 4 : flags |= NI_DGRAM;
1157 : }
1158 :
1159 : // TODO: test with the NI_IDN* flags and make sure we know what we get
1160 : // (i.e. we want UTF-8 as a result)
1161 : //
1162 7 : int const r(getnameinfo(reinterpret_cast<sockaddr const *>(&f_address), sizeof(f_address), host, sizeof(host), nullptr, 0, flags));
1163 :
1164 : // return value is 0, then it worked
1165 : //
1166 7 : return r == 0 ? host : std::string();
1167 : }
1168 :
1169 :
1170 : /** \brief Transform the port into a service name.
1171 : *
1172 : * This function transforms the port in this `addr` object in a
1173 : * name such as "http".
1174 : *
1175 : * \note
1176 : * The function does not cache the result because it is rarely used (at least
1177 : * at this time). So you should cache the result and avoid calling this
1178 : * function more than once as the process is somewhat slow.
1179 : *
1180 : * \warning
1181 : * The getnameinfo() will return a string with a number if it does not
1182 : * know the server (i.e. this is the equivalent to std::to_string() of
1183 : * the port.) For port 0, the function always returns an empty string.
1184 : *
1185 : * \return The service name. If not available, an empty string.
1186 : */
1187 5 : std::string addr::get_service() const
1188 : {
1189 5 : if(f_address.sin6_port == 0)
1190 : {
1191 1 : return std::string();
1192 : }
1193 :
1194 : char service[NI_MAXSERV];
1195 :
1196 4 : int flags(NI_NAMEREQD);
1197 4 : if(f_protocol == IPPROTO_UDP)
1198 : {
1199 2 : flags |= NI_DGRAM;
1200 : }
1201 4 : int const r(getnameinfo(reinterpret_cast<sockaddr const *>(&f_address), sizeof(f_address), nullptr, 0, service, sizeof(service), flags));
1202 :
1203 : // return value is 0, then it worked
1204 : //
1205 : return r == 0 ? service
1206 4 : : std::string();
1207 : }
1208 :
1209 :
1210 : /** \brief Retrieve the port.
1211 : *
1212 : * This function retrieves the port of the IP address in host order.
1213 : *
1214 : * \return The port defined along this address.
1215 : */
1216 197592 : int addr::get_port() const
1217 : {
1218 197592 : return ntohs(f_address.sin6_port);
1219 : }
1220 :
1221 :
1222 : /** \brief Retrieve the protocol.
1223 : *
1224 : * This function retrieves the protocol as specified on the
1225 : * set_addr_port() function or corresponding constructor.
1226 : *
1227 : * You may change the protocol with the set_protocol() function.
1228 : *
1229 : * \return protocol such as IPPROTO_TCP or IPPROTO_UDP.
1230 : */
1231 132051 : int addr::get_protocol() const
1232 : {
1233 132051 : return f_protocol;
1234 : }
1235 :
1236 :
1237 : /** \brief Check whether an IP matches a CIDR.
1238 : *
1239 : * When an IP address is defined along a mask, it can match a set of
1240 : * other IP addresses. This function can be used to see whether
1241 : * \p ip matches \p this IP address and mask.
1242 : *
1243 : * \warning
1244 : * This function only checks the IP address. It totally ignores the
1245 : * port, family, protocol and other peripheral details.
1246 : *
1247 : * \param[in] ip The address to match against this IP/mask CIDR.
1248 : *
1249 : * \return true if \p ip is a match.
1250 : */
1251 22 : bool addr::match(addr const & ip) const
1252 : {
1253 332 : for(int idx(0); idx < 16; ++idx)
1254 : {
1255 320 : if((f_address.sin6_addr.s6_addr[idx] & f_mask[idx]) != (ip.f_address.sin6_addr.s6_addr[idx] & f_mask[idx]))
1256 : {
1257 10 : return false;
1258 : }
1259 : }
1260 :
1261 12 : return true;
1262 : }
1263 :
1264 :
1265 : /** \brief Check whether two addresses are equal.
1266 : *
1267 : * This function compares the left hand side (this) and the right
1268 : * hand side (rhs) for equality. If both represent the same IP
1269 : * address, then the function returns true.
1270 : *
1271 : * \warning
1272 : * The function only compares the address itself. The family, port,
1273 : * flow info, scope identifier, protocol are all ignored.
1274 : *
1275 : * \return true if \p this is equal to \p rhs.
1276 : */
1277 35 : bool addr::operator == (addr const & rhs) const
1278 : {
1279 35 : return f_address.sin6_addr == rhs.f_address.sin6_addr;
1280 : }
1281 :
1282 :
1283 : /** \brief Check whether two addresses are not equal.
1284 : *
1285 : * This function compares the left hand side (this) and the right
1286 : * hand side (rhs) for inequality. If both represent the same IP
1287 : * address, then the function returns false.
1288 : *
1289 : * \warning
1290 : * The function only compares the address itself. The family, port,
1291 : * flow info, scope identifier, protocol are all ignored.
1292 : *
1293 : * \return true if \p this is not equal to \p rhs.
1294 : */
1295 9 : bool addr::operator != (addr const & rhs) const
1296 : {
1297 9 : return f_address.sin6_addr != rhs.f_address.sin6_addr;
1298 : }
1299 :
1300 :
1301 : /** \brief Compare two addresses to know which one is smaller.
1302 : *
1303 : * This function compares the left hand side (this) and the right
1304 : * hand side (rhs) to know which one is the smallest. If both
1305 : * are equal or the left hand side is larger than the right hand
1306 : * side, then it returns false, otherwise it returns true.
1307 : *
1308 : * \warning
1309 : * The function only compares the address itself. The family, port,
1310 : * flow info, scope identifier, protocol are all ignored.
1311 : *
1312 : * \return true if \p this is smaller than \p rhs.
1313 : */
1314 7 : bool addr::operator < (addr const & rhs) const
1315 : {
1316 7 : return f_address.sin6_addr < rhs.f_address.sin6_addr;
1317 : }
1318 :
1319 :
1320 : /** \brief Compare two addresses to know which one is smaller or equal.
1321 : *
1322 : * This function compares the left hand side (this) and the right
1323 : * hand side (rhs) to know whether the left hand side is smaller or
1324 : * equal to thr right handside.
1325 : *
1326 : * \warning
1327 : * The function only compares the address itself. The family, port,
1328 : * flow info, scope identifier, protocol are all ignored.
1329 : *
1330 : * \return true if \p this is smaller than \p rhs.
1331 : */
1332 685 : bool addr::operator <= (addr const & rhs) const
1333 : {
1334 685 : return f_address.sin6_addr <= rhs.f_address.sin6_addr;
1335 : }
1336 :
1337 :
1338 : /** \brief Compare two addresses to know which one is smaller.
1339 : *
1340 : * This function compares the left hand side (this) and the right
1341 : * hand side (rhs) to know which one is the smallest. If both
1342 : * are equal or the left hand side is larger than the right hand
1343 : * side, then it returns false, otherwise it returns true.
1344 : *
1345 : * \warning
1346 : * The function only compares the address itself. The family, port,
1347 : * flow info, scope identifier, protocol are all ignored.
1348 : *
1349 : * \return true if \p this is smaller than \p rhs.
1350 : */
1351 27 : bool addr::operator > (addr const & rhs) const
1352 : {
1353 27 : return f_address.sin6_addr > rhs.f_address.sin6_addr;
1354 : }
1355 :
1356 :
1357 : /** \brief Compare two addresses to know which one is smaller.
1358 : *
1359 : * This function compares the left hand side (this) and the right
1360 : * hand side (rhs) to know which one is the smallest. If both
1361 : * are equal or the left hand side is larger than the right hand
1362 : * side, then it returns false, otherwise it returns true.
1363 : *
1364 : * \warning
1365 : * The function only compares the address itself. The family, port,
1366 : * flow info, scope identifier, protocol are all ignored.
1367 : *
1368 : * \return true if \p this is smaller than \p rhs.
1369 : */
1370 290 : bool addr::operator >= (addr const & rhs) const
1371 : {
1372 290 : return f_address.sin6_addr >= rhs.f_address.sin6_addr;
1373 : }
1374 :
1375 :
1376 : /** \brief Mark that the address changed.
1377 : *
1378 : * This functions makes sure that some of the parameters being cached
1379 : * get reset in such a way that checking the cache will again return
1380 : * the correct answer.
1381 : *
1382 : * \sa get_network_type()
1383 : */
1384 132490 : void addr::address_changed()
1385 : {
1386 132490 : f_private_network_defined = network_type_t::NETWORK_TYPE_UNDEFINED;
1387 132490 : }
1388 :
1389 :
1390 : /** \brief Return a list of local addresses on this machine.
1391 : *
1392 : * Peruse the list of available interfaces, and return any detected ip addresses
1393 : * in a vector.
1394 : *
1395 : * \return A vector of all the local interface IP addresses.
1396 : */
1397 1 : addr::vector_t addr::get_local_addresses()
1398 : {
1399 : // get the list of interface addresses
1400 : //
1401 1 : struct ifaddrs * ifa_start(nullptr);
1402 1 : if(getifaddrs(&ifa_start) != 0)
1403 : {
1404 : // TODO: Should this throw, or just return an empty list quietly?
1405 : //
1406 : return vector_t(); // LCOV_EXCL_LINE
1407 : }
1408 :
1409 2 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
1410 :
1411 2 : vector_t addr_list;
1412 16 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
1413 : {
1414 21 : if( ifa->ifa_addr == nullptr ) continue;
1415 :
1416 24 : addr the_address;
1417 :
1418 15 : the_address.f_iface_name = ifa->ifa_name;
1419 15 : uint16_t const family( ifa->ifa_addr->sa_family );
1420 15 : if( family == AF_INET )
1421 : {
1422 5 : the_address.set_ipv4( *(reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)) );
1423 : }
1424 10 : else if( family == AF_INET6 )
1425 : {
1426 4 : the_address.set_ipv6( *(reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)) );
1427 : }
1428 : else
1429 : {
1430 : // TODO: can we just ignore invalid addresses?
1431 : //throw addr_invalid_structure_exception( "Unknown address family!" );
1432 6 : continue;
1433 : }
1434 :
1435 9 : addr_list.push_back( the_address );
1436 : }
1437 :
1438 1 : return addr_list;
1439 : }
1440 :
1441 :
1442 : /** \brief Check whether this address represents this computer.
1443 : *
1444 : * This function reads the addresses as given to us by the getifaddrs()
1445 : * function. This is a system function that returns a complete list of
1446 : * all the addresses this computer is managing / represents. In other
1447 : * words, a list of address that other computers can use to connect
1448 : * to this computer (assuming proper firewall, of course.)
1449 : *
1450 : * \warning
1451 : * The list of addresses from getifaddrs() is not being cached. So you
1452 : * probably do not want to call this function in a loop. That being
1453 : * said, I still would imagine that retrieving that list is fast.
1454 : *
1455 : * \return a computer_interface_address_t enumeration: error, true, or
1456 : * false at this time; on error errno should be set to represent
1457 : * what the error was.
1458 : */
1459 13 : addr::computer_interface_address_t addr::is_computer_interface_address() const
1460 : {
1461 : // TBD: maybe we could cache the ifaddrs for a small amount of time
1462 : // (i.e. 1 minute) so additional calls within that time
1463 : // can go even faster?
1464 : //
1465 :
1466 : // get the list of interface addresses
1467 : //
1468 13 : struct ifaddrs * ifa_start(nullptr);
1469 13 : if(getifaddrs(&ifa_start) != 0)
1470 : {
1471 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_ERROR; // LCOV_EXCL_LINE
1472 : }
1473 26 : std::shared_ptr<struct ifaddrs> auto_free(ifa_start, ifaddrs_deleter);
1474 :
1475 13 : bool const ipv4(is_ipv4());
1476 13 : uint16_t const family(ipv4 ? AF_INET : AF_INET6);
1477 195 : for(struct ifaddrs * ifa(ifa_start); ifa != nullptr; ifa = ifa->ifa_next)
1478 : {
1479 184 : if(ifa->ifa_addr != nullptr
1480 184 : && ifa->ifa_addr->sa_family == family)
1481 : {
1482 56 : if(ipv4)
1483 : {
1484 : // the interface address structure is a 'struct sockaddr_in'
1485 : //
1486 102 : if(memcmp(&reinterpret_cast<struct sockaddr_in *>(ifa->ifa_addr)->sin_addr,
1487 51 : f_address.sin6_addr.s6_addr32 + 3, //&reinterpret_cast<struct sockaddr_in const *>(&f_address)->sin_addr,
1488 : sizeof(struct in_addr)) == 0)
1489 : {
1490 1 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
1491 : }
1492 : }
1493 : else
1494 : {
1495 : // the interface address structure is a 'struct sockaddr_in6'
1496 : //
1497 5 : if(memcmp(&reinterpret_cast<struct sockaddr_in6 *>(ifa->ifa_addr)->sin6_addr, &f_address.sin6_addr, sizeof(f_address.sin6_addr)) == 0)
1498 : {
1499 1 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_TRUE;
1500 : }
1501 : }
1502 : }
1503 : }
1504 :
1505 11 : return computer_interface_address_t::COMPUTER_INTERFACE_ADDRESS_FALSE;
1506 : }
1507 :
1508 :
1509 :
1510 :
1511 6 : }
1512 : // addr namespace
1513 : // vim: ts=4 sw=4 et
|