blob: ee6c983a39b4bccdf75fb7ecca0abe439d6c88e8 [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.
#ifndef SRC_LIB_INET_IP_ADDRESS_H_
#define SRC_LIB_INET_IP_ADDRESS_H_
#include <arpa/inet.h>
#include <fuchsia/net/cpp/fidl.h>
#include <lib/syslog/cpp/macros.h>
namespace inet {
// Represents a V4 or V6 IP address.
class IpAddress {
public:
static const IpAddress kInvalid;
static const IpAddress kV4Loopback;
static const IpAddress kV6Loopback;
// Creates an IpAddress from a string containing a numeric IP address. Returns |kInvalid| if
// |address_string| cannot be converted into a valid IP address.
//
// If |family| is |AF_UNSPEC|, this function accepts both IPv4 and IPv6 address strings. If
// |family| is |AF_INET| only IPv4 strings are accepted, and if |family| is |AF_INET6|, only
// IPv6 strings are accepted.
//
// IPv6 address strings may be non-canonical in the following respects:
// 1) A pair of colons may be substituted for one or more zeros (rather than two or more).
// 2) The run of zeros replaced by a pair of colons need not be the longest run of zeros, and,
// in the case of a tie, need not be the left-most run of zeros.
// 3) Zeros may occur adjacent to a pair of colons.
// 4) Upper-case hexadecimal digits are allowed.
// 5) Hexadecimal words may have leading zeros.
//
// The following constraints apply to IPv6 address strings:
// 1) At most one pair of colons may appear in the string.
// 2) A string that has no colon pairs must contain exactly eight words.
// 3) Hexadecimal words may have at most 4 digits.
// 4) Mixed (IPv6 and IPv4) address strings are not supported.
static IpAddress FromString(const std::string& address_string, sa_family_t family = AF_UNSPEC);
// Parses an IpAddress prefixing a string view. If the parse succeeds, this method returns the
// valid IpAddress and the number of characters that were parsed to produce it. If the parse
// fails, this method returns an invalid IpAddress and zero. See |FromString| for details about
// how the string is parsed.
//
// Note that this method looks for a valid address string in the initial position in the string
// view. This means that inputs such as "::anything" will produce a successful result: in this
// case, the IPv6 unspecified address "::" and 2 characters parsed. If the caller wants to
// know whether the entire string view constitutes a valid address, it will be necessary to
// compare the returned number of parsed characters to the length of the string view, e.g.
//
// auto result = IpAddress::FromStringView(string_view);
// if (result.first.is_valid() && result.second == string_view.size()) {
// // Do something with result.first.
// }
//
static std::pair<IpAddress, size_t> FromStringView(std::string_view string_view,
sa_family_t family = AF_UNSPEC);
// Creates an invalid IP address.
IpAddress();
// Creates an IPV4 address from four address bytes.
IpAddress(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3);
// Creates an IPV4 address from an in_addr_t.
explicit IpAddress(in_addr_t addr);
// Creates an IPV4 address from an in_addr struct.
explicit IpAddress(const in_addr& addr);
// Creates an IPV6 address from eight address words.
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);
// Creates an IPV6 address from two address words (first and last).
IpAddress(uint16_t w0, uint16_t w7);
// Creates an IPV6 address from a vector of words. The address starts with |start| words of value
// zero followed by the values from |source|, followed by zeros to fill the remainder.
explicit IpAddress(const std::vector<uint16_t>& source, size_t start = 0);
// Creates an IPV6 address from an in6_addr struct.
explicit IpAddress(const in6_addr& addr);
// Creates an address from a sockaddr struct.
explicit IpAddress(const sockaddr& addr);
// Creates an address from a sockaddr_storage struct.
explicit IpAddress(const sockaddr_storage& addr);
// Creates an address from a fuchsia.net Ipv4Address struct.
explicit IpAddress(const fuchsia::net::Ipv4Address& addr);
// Creates an address from a fuchsia.net Ipv6Address struct.
explicit IpAddress(const fuchsia::net::Ipv6Address& addr);
// Creates an address from a fuchsia.net IpAddress struct.
explicit IpAddress(const fuchsia::net::IpAddress& addr);
// Indicates whether this address is valid.
bool is_valid() const { return family_ != AF_UNSPEC; }
// Returns the family of this address: |AF_INET| for V4, |AF_INET6| for V6 and
// |AF_UNSPEC| for an invalid address.
sa_family_t family() const { return family_; }
// Indicates whether this address is a V4 address.
bool is_v4() const { return family() == AF_INET; }
// Indicates whether this address is a V6 address.
bool is_v6() const { return family() == AF_INET6; }
// Indicates whether this address is a V6 address that is mapped from a V4 address.
bool is_mapped_from_v4() const;
// Returns the V4 address from a V6 address that is mapped from a V4 address. Calling this method
// is only permitted if this address returns true from |is_mapped_from_v4|.
IpAddress mapped_v4_address() const;
// Returns the V6 address that is the mapping of this address, which must be a V4 address.
IpAddress mapped_as_v6() const;
// Indicates whether this address is a loopback address.
bool is_loopback() const;
// Returns this address as an |in_addr|. Only defined for V4 addresses.
const in_addr& as_in_addr() const {
FX_DCHECK(is_v4());
return v4_;
}
// Returns this address as an |in_addr_t|. Only defined for V4 addresses.
in_addr_t as_in_addr_t() const {
FX_DCHECK(is_v4());
return v4_.s_addr;
}
// Returns this address as an |in6_addr|. Only defined for V6 addresses.
const in6_addr& as_in6_addr() const {
FX_DCHECK(is_v6());
return v6_;
}
// Returns a pointer to the bytes that make up this address. |byte_count|
// indicates the byte count. Not defined for invalid addresses.
const uint8_t* as_bytes() const {
FX_DCHECK(is_valid());
return is_v4() ? reinterpret_cast<const uint8_t*>(&v4_.s_addr) : v6_.s6_addr;
}
// Returns a pointer to the network-order words (big-endian) that make up the
// address. |word_count| indicates the byte count. This is only defined for
// V6 addresses.
const uint16_t* as_v6_words() const {
FX_DCHECK(is_v6());
return v6_.s6_addr16;
}
// Returns the number of bytes that make up this address. A V4 address is
// 4 bytes, and a V6 address is 16 bytes. Not defined for invalid addresses.
size_t byte_count() const {
FX_DCHECK(is_valid());
return is_v4() ? sizeof(in_addr) : sizeof(in6_addr);
}
// Returns the number of words that make up this address. A V4 address is
// 2 words, and a V6 address is 8 words. Not defined for invalid addresses.
size_t word_count() const {
FX_DCHECK(is_valid());
return byte_count() / sizeof(uint16_t);
}
// Returns a string representation of this address. V6 addresses are
// represented as specified in RFC 5952. For invalid addresses, this method
// returns "<invalid>".
std::string ToString() const;
explicit operator bool() const { return is_valid(); }
bool operator==(const IpAddress& other) const {
return family() == other.family() &&
(!is_valid() || (std::memcmp(as_bytes(), other.as_bytes(), byte_count()) == 0));
}
bool operator!=(const IpAddress& other) const { return !(*this == other); }
explicit operator fuchsia::net::Ipv4Address() const;
explicit operator fuchsia::net::Ipv6Address() const;
explicit operator fuchsia::net::IpAddress() const;
private:
sa_family_t family_;
union {
in_addr v4_;
in6_addr v6_;
};
};
// Inserts a string representation of |value|. V6 addresses are represented as
// specified in RFC 5952. For invalid addresses, this method inserts
// "<invalid>".
std::ostream& operator<<(std::ostream& os, const IpAddress& value);
} // namespace inet
template <>
struct std::hash<inet::IpAddress> {
std::size_t operator()(const inet::IpAddress& address) const noexcept {
if (!address.is_valid())
return 0;
size_t hash = 0;
if (address.is_v4()) {
auto byte_ptr = address.as_bytes();
for (size_t i = 0; i < address.byte_count(); ++i) {
hash = (hash << 1) ^ *byte_ptr;
++byte_ptr;
}
} else {
auto word_ptr = address.as_v6_words();
for (size_t i = 0; i < address.word_count(); ++i) {
hash = (hash << 1) ^ *word_ptr;
++word_ptr;
}
}
return hash;
}
};
#endif // SRC_LIB_INET_IP_ADDRESS_H_