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