| // Copyright 2017 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "garnet/lib/inet/ip_address.h" |
| |
| #include <sstream> |
| |
| #include <arpa/inet.h> |
| #include <endian.h> |
| #include <netdb.h> |
| #include <sys/socket.h> |
| |
| namespace inet { |
| |
| // static |
| const IpAddress IpAddress::kInvalid; |
| // static |
| const IpAddress IpAddress::kV4Loopback(127, 0, 0, 1); |
| // static |
| const IpAddress IpAddress::kV6Loopback(0, 0, 0, 0, 0, 0, 0, 1); |
| |
| // static |
| IpAddress IpAddress::FromString(const std::string address_string, |
| sa_family_t family) { |
| FXL_DCHECK(family == AF_UNSPEC || family == AF_INET || family == AF_INET6); |
| struct addrinfo hints; |
| memset(&hints, 0, sizeof(struct addrinfo)); |
| hints.ai_family = family; |
| hints.ai_socktype = 0; |
| hints.ai_flags = AI_NUMERICHOST; |
| hints.ai_protocol = 0; |
| |
| struct addrinfo* addrinfos; |
| int result = getaddrinfo(address_string.c_str(), nullptr, &hints, &addrinfos); |
| if (result != 0) { |
| FXL_DLOG(ERROR) << "Failed to getaddrinfo for address " << address_string |
| << ", errno" << errno; |
| return kInvalid; |
| } |
| |
| if (addrinfos == nullptr) { |
| return kInvalid; |
| } |
| |
| FXL_DCHECK(addrinfos->ai_family == family || |
| (family == AF_UNSPEC && (addrinfos->ai_family == AF_INET || |
| addrinfos->ai_family == AF_INET6))); |
| |
| IpAddress ip_address = IpAddress(addrinfos->ai_addr); |
| |
| freeaddrinfo(addrinfos); |
| |
| return ip_address; |
| } |
| |
| IpAddress::IpAddress() { |
| family_ = AF_UNSPEC; |
| std::memset(&v6_, 0, sizeof(v6_)); |
| } |
| |
| IpAddress::IpAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3) { |
| family_ = AF_INET; |
| uint8_t* bytes = reinterpret_cast<uint8_t*>(&v4_.s_addr); |
| bytes[0] = b0; |
| bytes[1] = b1; |
| bytes[2] = b2; |
| bytes[3] = b3; |
| } |
| |
| IpAddress::IpAddress(in_addr_t addr) { |
| family_ = AF_INET; |
| v4_.s_addr = addr; |
| } |
| |
| IpAddress::IpAddress(const in_addr& addr) { |
| family_ = AF_INET; |
| v4_ = addr; |
| } |
| |
| IpAddress::IpAddress(uint16_t w0, uint16_t w1, uint16_t w2, uint16_t w3, |
| uint16_t w4, uint16_t w5, uint16_t w6, uint16_t w7) { |
| family_ = AF_INET6; |
| uint16_t* words = v6_.s6_addr16; |
| words[0] = htobe16(w0); |
| words[1] = htobe16(w1); |
| words[2] = htobe16(w2); |
| words[3] = htobe16(w3); |
| words[4] = htobe16(w4); |
| words[5] = htobe16(w5); |
| words[6] = htobe16(w6); |
| words[7] = htobe16(w7); |
| } |
| |
| IpAddress::IpAddress(uint16_t w0, uint16_t w7) { |
| family_ = AF_INET6; |
| std::memset(&v6_, 0, sizeof(v6_)); |
| uint16_t* words = v6_.s6_addr16; |
| words[0] = htobe16(w0); |
| words[7] = htobe16(w7); |
| } |
| |
| IpAddress::IpAddress(const in6_addr& addr) { |
| family_ = AF_INET6; |
| v6_ = addr; |
| } |
| |
| IpAddress::IpAddress(const sockaddr* addr) { |
| FXL_DCHECK(addr != nullptr); |
| switch (addr->sa_family) { |
| case AF_INET: |
| family_ = AF_INET; |
| v4_ = reinterpret_cast<const sockaddr_in*>(addr)->sin_addr; |
| break; |
| case AF_INET6: |
| family_ = AF_INET6; |
| v6_ = reinterpret_cast<const sockaddr_in6*>(addr)->sin6_addr; |
| break; |
| default: |
| family_ = AF_UNSPEC; |
| std::memset(&v6_, 0, sizeof(v6_)); |
| break; |
| } |
| } |
| |
| IpAddress::IpAddress(const sockaddr_storage& addr) { |
| switch (addr.ss_family) { |
| case AF_INET: |
| family_ = AF_INET; |
| v4_ = reinterpret_cast<const sockaddr_in*>(&addr)->sin_addr; |
| break; |
| case AF_INET6: |
| family_ = AF_INET6; |
| v6_ = reinterpret_cast<const sockaddr_in6*>(&addr)->sin6_addr; |
| break; |
| default: |
| family_ = AF_UNSPEC; |
| std::memset(&v6_, 0, sizeof(v6_)); |
| break; |
| } |
| } |
| |
| IpAddress::IpAddress(const fuchsia::net::IpAddress* addr) { |
| FXL_DCHECK(addr != nullptr); |
| switch (addr->Which()) { |
| case fuchsia::net::IpAddress::Tag::kIpv4: |
| family_ = AF_INET; |
| memcpy(&v4_, addr->ipv4().addr.data(), 4); |
| break; |
| case fuchsia::net::IpAddress::Tag::kIpv6: |
| family_ = AF_INET6; |
| memcpy(&v6_, addr->ipv6().addr.data(), 16); |
| break; |
| default: |
| FXL_DCHECK(false); |
| break; |
| } |
| } |
| |
| bool IpAddress::is_loopback() const { |
| switch (family_) { |
| case AF_INET: |
| return *this == kV4Loopback; |
| case AF_INET6: |
| return *this == kV6Loopback; |
| default: |
| return false; |
| } |
| } |
| |
| std::string IpAddress::ToString() const { |
| std::ostringstream os; |
| os << *this; |
| return os.str(); |
| } |
| |
| std::ostream& operator<<(std::ostream& os, const IpAddress& value) { |
| if (!value.is_valid()) { |
| return os << "<invalid>"; |
| } |
| |
| if (value.is_v4()) { |
| const uint8_t* bytes = value.as_bytes(); |
| return os << static_cast<int>(bytes[0]) << '.' << static_cast<int>(bytes[1]) |
| << '.' << static_cast<int>(bytes[2]) << '.' |
| << static_cast<int>(bytes[3]); |
| } else { |
| // IPV6 text representation per RFC 5952: |
| // 1) Suppress leading zeros in hex representation of words. |
| // 2) Don't use '::' to shorten a just single zero word. |
| // 3) Shorten the longest sequence of zero words preferring the leftmost |
| // sequence if there's a tie. |
| // 4) Use lower-case hexadecimal. |
| |
| const uint16_t* words = value.as_words(); |
| |
| // Figure out where the longest span of zeros is. |
| uint8_t start_of_zeros; |
| uint8_t zeros_seen = 0; |
| uint8_t start_of_best_zeros = 255; |
| // Don't bother if the longest sequence is length 1. |
| uint8_t best_zeros_seen = 1; |
| |
| for (uint8_t i = 0; i < 8; ++i) { |
| if (words[i] == 0) { |
| if (zeros_seen == 0) { |
| start_of_zeros = i; |
| } |
| ++zeros_seen; |
| } else if (zeros_seen != 0) { |
| if (zeros_seen > best_zeros_seen) { |
| start_of_best_zeros = start_of_zeros; |
| best_zeros_seen = zeros_seen; |
| } |
| zeros_seen = 0; |
| } |
| } |
| |
| if (zeros_seen > best_zeros_seen) { |
| start_of_best_zeros = start_of_zeros; |
| best_zeros_seen = zeros_seen; |
| } |
| |
| os << "[" << std::hex; |
| for (uint8_t i = 0; i < 8; ++i) { |
| if (i < start_of_best_zeros || |
| i >= start_of_best_zeros + best_zeros_seen) { |
| os << words[i]; |
| if (i != 7) { |
| os << ":"; |
| } |
| } else if (i == start_of_best_zeros) { |
| if (i == 0) { |
| os << "::"; |
| } else { |
| os << ":"; // We just wrote a ':', so we only need one more. |
| } |
| } |
| } |
| return os << std::dec << "]"; |
| } |
| } |
| |
| } // namespace inet |