blob: a2c4bc17d420071f29643bb83de0de0e0322f46c [file] [log] [blame]
// 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_INET;
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_INET;
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