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