Commit 115830b1 authored by Dominik Charousset's avatar Dominik Charousset

Add IPv6 address abstraction

parent dcb6f5ca
......@@ -67,6 +67,7 @@ set(LIBCAF_CORE_SRCS
src/invoke_result_visitor.cpp
src/ipv4_address.cpp
src/ipv4_subnet.cpp
src/ipv6_address.cpp
src/local_actor.cpp
src/logger.cpp
src/mailbox_element.cpp
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <cstdint>
#include "caf/config.hpp"
#include "caf/detail/parser/add_ascii.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/is_char.hpp"
#include "caf/detail/parser/is_digit.hpp"
#include "caf/detail/parser/state.hpp"
#include "caf/detail/parser/sub_ascii.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv6_address.hpp"
#include "caf/pec.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace caf {
namespace detail {
namespace parser {
// IPv6address = 6( h16 ":" ) ls32
// / "::" 5( h16 ":" ) ls32
// / [ h16 ] "::" 4( h16 ":" ) ls32
// / [ *1( h16 ":" ) h16 ] "::" 3( h16 ":" ) ls32
// / [ *2( h16 ":" ) h16 ] "::" 2( h16 ":" ) ls32
// / [ *3( h16 ":" ) h16 ] "::" h16 ":" ls32
// / [ *4( h16 ":" ) h16 ] "::" ls32
// / [ *5( h16 ":" ) h16 ] "::" h16
// / [ *6( h16 ":" ) h16 ] "::"
//
// ls32 = ( h16 ":" h16 ) / IPv4address
//
// h16 = 1*4HEXDIG
/// Reads 16 (hex) bits of an IPv6 address.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv6_h16(state<Iterator, Sentinel>& ps, Consumer& consumer) {
uint16_t res = 0;
size_t digits = 0;
// Reads the a hexadecimal place.
auto rd_hex = [&](char c) {
++digits;
return add_ascii<16>(res, c);
};
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character)
consumer.value(res);
});
start();
state(init) {
transition(read, hexadecimal_chars, rd_hex(ch), pec::integer_overflow)
}
term_state(read) {
transition_if(digits < 4,
read, hexadecimal_chars, rd_hex(ch), pec::integer_overflow)
}
fin();
}
/// Reads 16 (hex) or 32 (IPv4 notation) bits of an IPv6 address.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv6_h16_or_l32(state<Iterator, Sentinel>& ps, Consumer& consumer) {
enum mode_t { indeterminate, v6_bits, v4_octets };
mode_t mode = indeterminate;
uint16_t hex_res = 0;
uint8_t dec_res = 0;
int digits = 0;
int octet = 0;
// Reads a single character (dec and/or hex).
auto rd_hex = [&](char c) {
++digits;
return add_ascii<16>(hex_res, c);
};
auto rd_dec = [&](char c) {
++digits;
return add_ascii<10>(dec_res, c);
};
auto rd_both = [&](char c) {
CAF_ASSERT(mode == indeterminate);
++digits;
// IPv4 octets cannot have more than 3 digits.
if (!in_whitelist(decimal_chars, c) || !add_ascii<10>(dec_res, c))
mode = v6_bits;
return add_ascii<16>(hex_res, c);
};
auto fin_octet = [&]() {
++octet;
mode = v4_octets;
consumer.value(dec_res);
dec_res = 0;
digits = 0;
};
// Computes the result on success. Note, when reading octets, this will give
// the consumer the final byte. Previous bytes were signaled during parsing.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
if (mode != v4_octets)
consumer.value(hex_res);
else
fin_octet();
}
});
start();
state(init) {
transition(read, hexadecimal_chars, rd_both(ch), pec::integer_overflow)
}
term_state(read) {
transition_if(mode == indeterminate,
read, hexadecimal_chars, rd_both(ch), pec::integer_overflow)
transition_if(mode == v6_bits,
read, hexadecimal_chars, rd_hex(ch), pec::integer_overflow)
transition_if(mode != v6_bits && digits > 0, read_octet, '.', fin_octet())
}
state(read_octet) {
transition(read_octet, decimal_chars, rd_dec(ch), pec::integer_overflow)
transition_if(octet < 2 && digits > 0, read_octet, '.', fin_octet())
transition_if(octet == 2 && digits > 0, read_last_octet, '.', fin_octet())
}
term_state(read_last_octet) {
transition(read_last_octet, decimal_chars,
rd_dec(ch), pec::integer_overflow)
}
fin();
}
template <class F>
struct read_ipv6_address_piece_consumer {
F callback;
inline void value(uint16_t x) {
union {
uint16_t bits;
std::array<uint8_t, 2> bytes;
} val;
val.bits = to_network_order(x);
callback(val.bytes.data(), val.bytes.size());
}
inline void value(uint8_t x) {
callback(&x, 1);
}
};
template <class F>
read_ipv6_address_piece_consumer<F> make_read_ipv6_address_piece_consumer(F f) {
return {f};
}
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv6_address(state<Iterator, Sentinel>& ps, Consumer& consumer) {
// IPv6 allows omitting blocks of zeros, splitting the string into a part
// before the zeros (prefix) and a part after the zeros (suffix). For example,
// ff::1 is 00FF0000000000000000000000000001
ipv6_address::array_type prefix;
ipv6_address::array_type suffix;
prefix.fill(0);
suffix.fill(0);
// Keeps track of all bytes consumed so far, suffix and prefix combined.
size_t filled_bytes = 0;
auto remaining_bytes = [&] {
return ipv6_address::num_bytes - filled_bytes;
};
// Computes the result on success.
auto g = caf::detail::make_scope_guard([&] {
if (ps.code <= pec::trailing_character) {
ipv6_address::array_type bytes;
for (size_t i = 0; i < ipv6_address::num_bytes; ++i)
bytes[i] = prefix[i] | suffix[i];
ipv6_address result{bytes};
consumer.value(result);
}
});
// We need to parse 2-byte hexadecimal numbers (x) and also keep track of
// the current writing position when reading the prefix.
auto read_prefix = [&](uint8_t* bytes, size_t count) {
for (size_t i = 0; i < count; ++i)
prefix[filled_bytes++] = bytes[i];
};
auto prefix_consumer = make_read_ipv6_address_piece_consumer(read_prefix);
// The suffix reader rotates bytes into place, so that we can bitwise-OR
// prefix and suffix for obtaining the full address.
auto read_suffix = [&](uint8_t* bytes, size_t count) {
for (size_t i = 0; i < count; ++i)
suffix[i] = bytes[i];
std::rotate(suffix.begin(), suffix.begin() + count, suffix.end());
filled_bytes += count;
};
auto suffix_consumer = make_read_ipv6_address_piece_consumer(read_suffix);
// Utility function for promoting an IPv4 formatted input.
auto promote_v4 = [&] {
if (filled_bytes == 4) {
ipv4_address::array_type bytes;
memcpy(bytes.data(), prefix.data(), bytes.size());
prefix = ipv6_address{ipv4_address{bytes}}.bytes();
return true;
}
return false;
};
start();
// Either transitions to reading leading "::" or reads the first h16. When
// reading an l32 immediately promotes to IPv4 address and stops.
state(init) {
transition(rd_sep, ':')
fsm_epsilon(read_ipv6_h16_or_l32(ps, prefix_consumer),
maybe_has_l32, hexadecimal_chars)
}
// Checks whether the first call to read_ipv6_h16_or_l32 consumed
// exactly 4 bytes. If so, we have an IPv4-formatted address.
unstable_state(maybe_has_l32) {
epsilon_if(promote_v4(), done)
epsilon(rd_prefix_sep)
}
// Got ":" at a position where it can only be parsed as "::".
state(rd_sep) {
transition(has_sep, ':')
}
// Stops parsing after reading "::" (all-zero address) or proceeds with
// reading the suffix.
term_state(has_sep) {
epsilon(rd_suffix)
}
// Read part of the prefix, i.e., everything before "::".
state(rd_prefix) {
fsm_epsilon_if(remaining_bytes() > 4,
read_ipv6_h16(ps, prefix_consumer),
rd_prefix_sep, hexadecimal_chars)
fsm_epsilon_if(remaining_bytes() == 4,
read_ipv6_h16_or_l32(ps, prefix_consumer),
maybe_done, hexadecimal_chars)
fsm_epsilon_if(remaining_bytes() == 2,
read_ipv6_h16(ps, prefix_consumer),
done, hexadecimal_chars)
}
// Checks whether we've read an l32 in our last call to read_ipv6_h16_or_l32,
// in which case we're done. Otherwise continues to read the last two bytes.
unstable_state(maybe_done) {
epsilon_if(remaining_bytes() == 0, done);
epsilon(rd_prefix_sep)
}
// Waits for ":" after reading an h16 in the prefix.
state(rd_prefix_sep) {
transition(rd_next_prefix, ':')
}
// Waits for either the second ":" or an h16/l32 after reading an ":".
state(rd_next_prefix) {
transition(has_sep, ':')
epsilon(rd_prefix)
}
// Reads a part of the suffix.
state(rd_suffix) {
fsm_epsilon_if(remaining_bytes() >= 4,
read_ipv6_h16_or_l32(ps, suffix_consumer),
rd_next_suffix, hexadecimal_chars)
fsm_epsilon_if(remaining_bytes() == 2,
read_ipv6_h16(ps, suffix_consumer),
rd_next_suffix, hexadecimal_chars)
}
// Reads the ":" separator between h16.
term_state(rd_next_suffix) {
transition(rd_suffix, ':')
}
// Accepts only the end-of-input, since we've read a full IP address.
term_state(done) {
// nop
}
fin();
}
} // namespace parser
} // namespace detail
} // namespace caf
#include "caf/detail/parser/fsm_undef.hpp"
CAF_POP_WARNINGS
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include "caf/fwd.hpp"
namespace caf {
using ip_address = ipv6_address;
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <array>
#include <cstdint>
#include <string>
#include "caf/byte_address.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/fwd.hpp"
namespace caf {
class ipv6_address : public byte_address<ipv6_address>,
detail::comparable<ipv6_address, ipv4_address> {
public:
// -- constants --------------------------------------------------------------
static constexpr size_t num_bytes = 16;
// -- member types -----------------------------------------------------------
using super = byte_address<ipv6_address>;
using array_type = std::array<uint8_t, num_bytes>;
// -- constructors, destructors, and assignment operators --------------------
ipv6_address();
explicit ipv6_address(ipv4_address addr);
ipv6_address(array_type bytes);
// -- comparison -------------------------------------------------------------
using super::compare;
/// Returns a negative number if `*this < other`, zero if `*this == other`
/// and a positive number if `*this > other`.
int compare(ipv4_address other) const noexcept;
// -- properties -------------------------------------------------------------
/// Returns whether this address embeds an IPv4 address.
bool embeds_v4() const noexcept;
/// Returns an embedded IPv4 address.
/// @pre `embeds_v4()`
ipv4_address embedded_v4() const noexcept;
/// Returns whether this is a loopback address.
bool is_loopback() const noexcept;
/// Returns the bytes of the IP address as array.
inline array_type& bytes() noexcept {
return bytes_;
}
/// Returns the bytes of the IP address as array.
inline const array_type& bytes() const noexcept {
return bytes_;
}
/// Alias for `bytes()`.
inline array_type& data() noexcept {
return bytes_;
}
/// Alias for `bytes()`.
inline const array_type& data() const noexcept {
return bytes_;
}
/// Returns the number of bytes of an IPv6 address.
inline size_t size() const noexcept {
return bytes_.size();
}
/// Returns whether this address contains only zeros, i.e., equals `::`.
inline bool zero() const noexcept {
return half_segments_[0] == 0 && half_segments_[1] == 0;
}
// -- inspection -------------------------------------------------------------
template <class Inspector>
friend typename Inspector::result_type inspect(Inspector& f,
ipv6_address& x) {
return f(x.bytes_);
}
friend std::string to_string(ipv6_address x);
private:
// -- member variables -------------------------------------------------------
union {
std::array<uint64_t, 2> half_segments_;
std::array<uint32_t, 4> quad_segments_;
std::array<uint16_t, 8> oct_segments_;
array_type bytes_;
};
};
error parse(string_view str, ipv6_address& dest);
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/ipv6_address.hpp"
#include "caf/detail/append_hex.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/detail/parser/read_ipv6_address.hpp"
#include "caf/error.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace caf {
namespace {
struct ipv6_address_consumer {
ipv6_address& dest;
ipv6_address_consumer(ipv6_address& ref) : dest(ref) {
// nop
}
void value(ipv6_address val) {
dest = val;
}
};
// It's good practice when printing IPv6 addresses to:
// - remove leading zeros in all segments
// - use lowercase hexadecimal characters
// @param pointer to an uint16_t segment converted to uint8_t*
void append_v6_hex(std::string& result, const uint8_t* xs) {
auto tbl = "0123456789abcdef";
size_t j = 0;
std::array<char, (sizeof(uint16_t) * 2) + 1> buf;
buf.fill(0);
for (size_t i = 0; i < sizeof(uint16_t); ++i) {
auto c = xs[i];
buf[j++] = tbl[c >> 4];
buf[j++] = tbl[c & 0x0F];
}
auto pred = [](char c) {
return c != '0';
};
auto first_non_zero = std::find_if(buf.begin(), buf.end(), pred);
CAF_ASSERT(first_non_zero != buf.end());
if (*first_non_zero != '\0')
result += &(*first_non_zero);
else
result += '0';
}
inline uint32_t net_order_32(uint32_t value) {
return detail::to_network_order(value);
}
inline uint64_t net_order_64(uint64_t value) {
return detail::to_network_order(value);
}
std::array<uint32_t, 3> v4_prefix{{0, 0, net_order_32(0x0000FFFFu)}};
} // namespace <anonymous>
// -- constructors, destructors, and assignment operators ----------------------
ipv6_address::ipv6_address() {
half_segments_[0] = 0;
half_segments_[1] = 0;
}
ipv6_address::ipv6_address(ipv4_address addr) {
std::copy(v4_prefix.begin(), v4_prefix.end(), quad_segments_.begin());
quad_segments_.back() = addr.bits();
}
ipv6_address::ipv6_address(array_type bytes) {
memcpy(bytes_.data(), bytes.data(), bytes.size());
}
int ipv6_address::compare(ipv4_address other) const noexcept {
ipv6_address tmp{other};
return compare(tmp);
}
// -- properties ---------------------------------------------------------------
bool ipv6_address::embeds_v4() const noexcept {
using std::begin;
using std::end;
return std::equal(begin(v4_prefix), end(v4_prefix), quad_segments_.begin());
}
ipv4_address ipv6_address::embedded_v4() const noexcept {
ipv4_address result;
result.bits(quad_segments_.back());
return result;
}
bool ipv6_address::is_loopback() const noexcept {
// IPv6 defines "::1" as the loopback address, when embedding a v4 we
// dispatch accordingly.
return embeds_v4()
? embedded_v4().is_loopback()
: half_segments_[0] == 0 && half_segments_[1] == net_order_64(1u);
}
// -- related free functions ---------------------------------------------------
namespace {
using u16_iterator = const uint16_t*;
using u16_range = std::pair<u16_iterator, u16_iterator>;
u16_range longest_streak(u16_iterator first, u16_iterator last) {
auto zero = [](uint16_t x, uint16_t y) {
return x == 0 && y == 0;
};
auto not_zero = [](uint16_t x) {
return x != 0;
};
u16_range result;
result.first = std::adjacent_find(first, last, zero);
if (result.first == last)
return {last, last};
result.second = std::find_if(result.first + 2, last, not_zero);
if (result.second == last)
return result;
auto next_streak = longest_streak(result.second, last);
auto distance = [](u16_range x) {
return std::distance(x.first, x.second);
};
return distance(result) >= distance(next_streak) ? result : next_streak;
}
} // namespace <anonymous>
std::string to_string(ipv6_address x) {
// Shortcut for embedded v4 addresses.
if (x.embeds_v4())
return to_string(x.embedded_v4());
// Shortcut for all-zero addresses.
if (x.zero())
return "::";
// Output buffer.
std::string result;
// Utility for appending chunks to the result.
auto add_chunk = [&](uint16_t x) {
append_v6_hex(result, reinterpret_cast<uint8_t*>(&x));
};
auto add_chunks = [&](u16_iterator i, u16_iterator e) {
if (i != e) {
add_chunk(*i++);
for (; i != e; ++i) {
result += ':';
add_chunk(*i);
}
}
};
// Scan for the longest streak of 0 16-bit chunks for :: shorthand.
auto first = x.oct_segments_.begin();
auto last = x.oct_segments_.end();
auto streak = longest_streak(first, last);
// Put it all together.
if (streak.first == last) {
add_chunks(first, last);
} else {
add_chunks(first, streak.first);
result += "::";
add_chunks(streak.second, last);
}
return result;
}
error parse(string_view str, ipv6_address& dest) {
using namespace detail;
parser::state<string_view::iterator> res{str.begin(), str.end()};
ipv6_address_consumer f{dest};
parser::read_ipv6_address(res, f);
if (res.code == pec::success)
return none;
return make_error(res.code, res.line, res.column);
}
} // namespace caf
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include <initializer_list>
#include "caf/config.hpp"
#define CAF_SUITE ipv6_address
#include "caf/test/dsl.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/ipv6_address.hpp"
using namespace caf;
namespace {
using array_type = ipv6_address::array_type;
void addr_fill(ipv6_address::array_type& arr,
std::initializer_list<uint16_t> chunks) {
union {
uint16_t i;
std::array<uint8_t, 2> a;
} tmp;
size_t p = 0;
for (auto chunk : chunks) {
tmp.i = detail::to_network_order(chunk);
arr[p++] = tmp.a[0];
arr[p++] = tmp.a[1];
}
}
ipv6_address addr(std::initializer_list<uint16_t> prefix,
std::initializer_list<uint16_t> suffix) {
CAF_ASSERT((prefix.size() + suffix.size()) <= 8);
ipv6_address::array_type bytes;
bytes.fill(0);
addr_fill(bytes, suffix);
std::rotate(bytes.begin(), bytes.begin() + suffix.size() * 2, bytes.end());
addr_fill(bytes, prefix);
return ipv6_address{bytes};
}
ipv6_address addr(std::initializer_list<uint16_t> segments) {
ipv6_address::array_type bytes;
bytes.fill(0);
addr_fill(bytes, segments);
return ipv6_address{bytes};
}
} // namespace <anonymous>
CAF_TEST(constructing) {
ipv6_address localhost({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1});
CAF_CHECK_EQUAL(localhost.data(),
array_type({0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}));
CAF_CHECK_EQUAL(localhost, addr({}, {0x01}));
}
CAF_TEST(comparison) {
CAF_CHECK_EQUAL(addr({1, 2, 3}), addr({1, 2, 3}));
CAF_CHECK_NOT_EQUAL(addr({3, 2, 1}), addr({1, 2, 3}));
CAF_CHECK_EQUAL(addr({}, {0xFFFF, 0x7F00, 0x0001}),
ipv4_address({127, 0, 0, 1}));
}
CAF_TEST(from string) {
auto from_string = [](string_view str) {
ipv6_address result;
auto err = parse(str, result);
if (err)
CAF_FAIL("error while parsing " << str << ": " << to_string(err));
return result;
};
CAF_CHECK_EQUAL(from_string("::1"), addr({}, {0x01}));
CAF_CHECK_EQUAL(from_string("::11"), addr({}, {0x11}));
CAF_CHECK_EQUAL(from_string("::112"), addr({}, {0x0112}));
CAF_CHECK_EQUAL(from_string("::1122"), addr({}, {0x1122}));
CAF_CHECK_EQUAL(from_string("::1:2"), addr({}, {0x01, 0x02}));
CAF_CHECK_EQUAL(from_string("::1:2"), addr({}, {0x01, 0x02}));
CAF_CHECK_EQUAL(from_string("1::1"), addr({0x01}, {0x01}));
CAF_CHECK_EQUAL(from_string("1::"), addr({0x01}, {}));
CAF_CHECK_EQUAL(from_string("0.1.0.1"), addr({}, {0xFFFF, 0x01, 0x01}));
CAF_CHECK_EQUAL(from_string("::ffff:127.0.0.1"),
addr({}, {0xFFFF, 0x7F00, 0x0001}));
CAF_CHECK_EQUAL(from_string("1:2:3:4:5:6:7:8"), addr({1,2,3,4,5,6,7,8}));
CAF_CHECK_EQUAL(from_string("1:2:3:4::5:6:7:8"), addr({1,2,3,4,5,6,7,8}));
CAF_CHECK_EQUAL(from_string("1:2:3:4:5:6:0.7.0.8"), addr({1,2,3,4,5,6,7,8}));
auto invalid = [](string_view str) {
ipv6_address result;
auto err = parse(str, result);
return err != none;
};
CAF_CHECK(invalid("1:2:3:4:5:6:7:8:9"));
CAF_CHECK(invalid("1:2:3:4::5:6:7:8:9"));
CAF_CHECK(invalid("1:2:3::4:5:6::7:8:9"));
}
CAF_TEST(to string) {
CAF_CHECK_EQUAL(to_string(addr({}, {0x01})), "::1");
CAF_CHECK_EQUAL(to_string(addr({0x01}, {0x01})), "1::1");
CAF_CHECK_EQUAL(to_string(addr({0x01})), "1::");
CAF_CHECK_EQUAL(to_string(addr({}, {0xFFFF, 0x01, 0x01})), "0.1.0.1");
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment