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