/** * Check whether hosts are "equivalent", modulo conversion to IPv4 for IPv6. * * @attention * This routine CANNOT be used directly or indirectly as a comparison function * for hash tables because it's not possible to have two items being "equal" * that do not hash to the same value! For hash tables, use host_addr_equal() * which tests true equality. */ bool host_addr_equiv(const host_addr_t a, const host_addr_t b) { if (a.net == b.net) { switch (a.net) { case NET_TYPE_IPV4: return host_addr_ipv4(a) == host_addr_ipv4(b); case NET_TYPE_IPV6: if (0 != memcmp(a.addr.ipv6, b.addr.ipv6, sizeof a.addr.ipv6)) { host_addr_t a_ipv4, b_ipv4; return host_addr_convert(a, &a_ipv4, NET_TYPE_IPV4) && host_addr_convert(b, &b_ipv4, NET_TYPE_IPV4) && host_addr_ipv4(a_ipv4) == host_addr_ipv4(b_ipv4); } return TRUE; case NET_TYPE_LOCAL: case NET_TYPE_NONE: return TRUE; } g_assert_not_reached(); } else { host_addr_t to; return host_addr_convert(a, &to, b.net) && host_addr_equiv(to, b); } return FALSE; }
/** * Check whether host can be reached from the Internet. * We rule out IPs of private networks, plus some other invalid combinations. */ bool host_addr_is_routable(const host_addr_t addr) { host_addr_t ha; if (!is_host_addr(addr) || is_private_addr(addr)) return FALSE; if (!host_addr_convert(addr, &ha, NET_TYPE_IPV4)) ha = addr; switch (host_addr_net(ha)) { case NET_TYPE_IPV4: return ipv4_addr_is_routable(host_addr_ipv4(ha)); case NET_TYPE_IPV6: return !host_addr_matches(ha, ipv6_unspecified, 8) && !host_addr_matches(ha, ipv6_multicast, 8) && !host_addr_matches(ha, ipv6_site_local, 10) && !host_addr_matches(ha, ipv6_link_local, 10) && !( host_addr_is_tunneled(ha) && !ipv4_addr_is_routable(host_addr_tunnel_client_ipv4(ha)) ); case NET_TYPE_LOCAL: case NET_TYPE_NONE: return FALSE; } g_assert_not_reached(); return FALSE; }
/** * Retrieve value associated with an IP address, i.e. that of the range * containing it. * * @param db the IP range database * @param ha the IP address to lookup * * @return The data associated with the IP address or 0 if not found. */ uint16 iprange_get_addr(const struct iprange_db *idb, const host_addr_t ha) { host_addr_t to; if ( host_addr_convert(ha, &to, NET_TYPE_IPV4) || host_addr_tunnel_client(ha, &to) ) { return iprange_get(idb, host_addr_ipv4(to)); } else if (host_addr_is_ipv6(ha)) { return iprange_get6(idb, host_addr_ipv6(&ha)); } return 0; }
/** * Checks for RFC1918 private addresses but also IPv6 link-local and site-local * addresses. * * @return TRUE if is a private address. */ bool is_private_addr(const host_addr_t addr) { host_addr_t addr_ipv4; if (host_addr_convert(addr, &addr_ipv4, NET_TYPE_IPV4)) { uint32 ip = host_addr_ipv4(addr_ipv4); /* 10.0.0.0 -- (10/8 prefix) */ if ((ip & 0xff000000) == 0xa000000) return TRUE; /* 172.16.0.0 -- (172.16/12 prefix) */ if ((ip & 0xfff00000) == 0xac100000) return TRUE; /* 169.254.0.0 -- (169.254/16 prefix) -- since Jan 2001 */ if ((ip & 0xffff0000) == 0xa9fe0000) return TRUE; /* 192.168.0.0 -- (192.168/16 prefix) */ if ((ip & 0xffff0000) == 0xc0a80000) return TRUE; } else { switch (host_addr_net(addr)) { case NET_TYPE_IPV4: g_assert_not_reached(); case NET_TYPE_IPV6: return host_addr_equal(addr, ipv6_loopback) || host_addr_matches(addr, ipv6_link_local, 10) || host_addr_matches(addr, ipv6_site_local, 10); case NET_TYPE_LOCAL: return TRUE; case NET_TYPE_NONE: break; } } return FALSE; }
/** * Alternate hashing of host_addr_t. */ unsigned host_addr_hash2(host_addr_t ha) { switch (ha.net) { case NET_TYPE_IPV6: { host_addr_t ha_ipv4; if (!host_addr_convert(ha, &ha_ipv4, NET_TYPE_IPV4)) return binary_hash2(&ha.addr.ipv6[0], sizeof ha.addr.ipv6); ha = ha_ipv4; } /* FALL THROUGH */ case NET_TYPE_IPV4: return ha.net ^ integer_hash2(host_addr_ipv4(ha)); case NET_TYPE_LOCAL: case NET_TYPE_NONE: return ha.net; } g_assert_not_reached(); return (unsigned) -1; }
/** * Checks whether the given address is 127.0.0.1 or ::1. */ bool host_addr_is_loopback(const host_addr_t addr) { host_addr_t ha; if (!host_addr_convert(addr, &ha, NET_TYPE_IPV4)) ha = addr; switch (host_addr_net(ha)) { case NET_TYPE_IPV4: return host_addr_ipv4(ha) == 0x7f000001; /* 127.0.0.1 in host endian */ case NET_TYPE_IPV6: return host_addr_equal(ha, ipv6_loopback); case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } g_assert_not_reached(); return FALSE; }
/** * Hashing of host_addr_t + port. */ unsigned host_addr_port_hash(host_addr_t ha, uint16 port) { switch (ha.net) { case NET_TYPE_IPV6: { host_addr_t ha_ipv4; if (!host_addr_convert(ha, &ha_ipv4, NET_TYPE_IPV4)) return binary_hash(&ha.addr.ipv6[0], sizeof ha.addr.ipv6); ha = ha_ipv4; } /* FALL THROUGH */ case NET_TYPE_IPV4: return hashing_mix32( ha.net + integer_hash_fast(host_addr_ipv4(ha)) + u16_hash(port)); case NET_TYPE_LOCAL: case NET_TYPE_NONE: return hashing_mix32(ha.net + u16_hash(port)); } g_assert_not_reached(); return (unsigned) -1; }
/** * Mask out given address, clearing the trailing v4 or v6 bits, depending * on the address type. * * @param addr the address to mask out * @param v4 amount of trailing bits to mask out for IPv4 address * @param v6 amount of trailing bits to mask out for IPv6 address * * @return masked out address, possibly converted from IPv6 to IPv4. */ host_addr_t host_addr_mask_net(host_addr_t addr, int v4, int v6) { host_addr_t masked = addr; g_assert(v4 >= 0 && v4 <= 32); g_assert(v6 >= 0 && v6 <= 128); if (host_addr_can_convert(addr, NET_TYPE_IPV4)) (void) host_addr_convert(addr, &masked, NET_TYPE_IPV4); switch (host_addr_net(addr)) { case NET_TYPE_IPV4: if (32 == v4) masked.addr.ipv4 = 0; else masked.addr.ipv4 &= ~((1U << v4) - 1); break; case NET_TYPE_IPV6: { int bytes = v6 / 8; /* Amount of trailing bytes to clear */ int bits = v6 % 8; /* Trailing bits in upper byte */ int i; for (i = 0; i < bytes; i++) { masked.addr.ipv6[15 - i] = 0; } masked.addr.ipv6[15 - bytes] &= ~((1U << bits) - 1); } break; case NET_TYPE_LOCAL: case NET_TYPE_NONE: break; } return masked; }
/** * Resolves an IP address to a hostname per DNS. * * @param ha The host address to resolve. * @return On success, the hostname is returned. Otherwise, NULL is * returned. The resulting string points to a static buffer. */ const char * host_addr_to_name(host_addr_t addr) { socket_addr_t sa; if (host_addr_can_convert(addr, NET_TYPE_IPV4)) { (void) host_addr_convert(addr, &addr, NET_TYPE_IPV4); } if (0 == socket_addr_set(&sa, addr, 0)) { return NULL; } #ifdef HAS_GETNAMEINFO { static char host[1025]; int error; error = getnameinfo(socket_addr_get_const_sockaddr(&sa), socket_addr_get_len(&sa), host, sizeof host, NULL, 0, 0); if (error) { char buf[HOST_ADDR_BUFLEN]; host_addr_to_string_buf(addr, buf, sizeof buf); g_message("getnameinfo() failed for \"%s\": %s", buf, gai_strerror(error)); return NULL; } return host; } #else /* !HAS_GETNAMEINFO */ { const struct hostent *he; socklen_t len = 0; const char *ptr = NULL; switch (host_addr_net(addr)) { case NET_TYPE_IPV4: ptr = cast_to_gchar_ptr(&sa.inet4.sin_addr); len = sizeof sa.inet4.sin_addr; break; case NET_TYPE_IPV6: #ifdef HAS_IPV6 ptr = cast_to_gchar_ptr(&sa.inet6.sin6_addr); len = sizeof sa.inet6.sin6_addr; break; #endif /* HAS_IPV6 */ case NET_TYPE_LOCAL: case NET_TYPE_NONE: return NULL; } g_return_val_if_fail(ptr, NULL); g_return_val_if_fail(0 != len, NULL); he = gethostbyaddr(ptr, len, socket_addr_get_family(&sa)); if (!he) { char buf[HOST_ADDR_BUFLEN]; host_addr_to_string_buf(addr, buf, sizeof buf); gethostbyname_error(buf); return NULL; } return he->h_name; } #endif /* HAS_GETNAMEINFO */ }