Commit 45cdea3d authored by Dominik Charousset's avatar Dominik Charousset

Add IPv4 address abstraction

parent 316a7eeb
...@@ -65,6 +65,7 @@ set(LIBCAF_CORE_SRCS ...@@ -65,6 +65,7 @@ set(LIBCAF_CORE_SRCS
src/inbound_path.cpp src/inbound_path.cpp
src/ini_consumer.cpp src/ini_consumer.cpp
src/invoke_result_visitor.cpp src/invoke_result_visitor.cpp
src/ipv4_address.cpp
src/local_actor.cpp src/local_actor.cpp
src/logger.cpp src/logger.cpp
src/mailbox_element.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 <cstring>
#include "caf/config.hpp"
#include "caf/detail/comparable.hpp"
namespace caf {
/// Base type for addresses based on a byte representation such as IP or
/// Ethernet addresses.
template <class Derived>
class byte_address : detail::comparable<Derived> {
public:
// -- element access ---------------------------------------------------------
/// Returns the byte at given index.
uint8_t& operator[](size_t index) noexcept {
return dref().bytes()[index];
}
/// Returns the byte at given index.
const uint8_t& operator[](size_t index) const noexcept {
return dref().bytes()[index];
}
// -- comparison -------------------------------------------------------------
/// Returns a negative number if `*this < other`, zero if `*this == other`
/// and a positive number if `*this > other`.
int compare(const Derived& other) const noexcept {
auto& buf = dref().bytes();
return memcmp(buf.data(), other.bytes().data(), Derived::num_bytes);
}
// -- mutators ---------------------------------------------------------------
/// Masks out lower bytes of the address. For example, calling `mask(1)` on
/// the IPv4 address `192.168.1.1` would produce `192.0.0.0`.
/// @pre `bytes_to_keep < num_bytes`
void mask(int bytes_to_keep) noexcept {
auto& buf = dref().bytes();
memset(buf.data() + bytes_to_keep, 0, Derived::num_bytes - bytes_to_keep);
}
/// Returns a copy of this address with that masks out lower bytes. For
/// example, calling `masked(1)` on the IPv4 address `192.168.1.1` would
/// return `192.0.0.0`.
/// @pre `bytes_to_keep < num_bytes`
Derived masked(int bytes_to_keep) const noexcept {
Derived result{dref()};
result.mask(bytes_to_keep);
return result;
}
// -- bitwise member operators -----------------------------------------------
/// Bitwise ANDs `*this` and `other`.
Derived& operator&=(const Derived& other) {
auto& buf = dref().bytes();
for (size_t index = 0; index < Derived::num_bytes; ++index)
buf[index] &= other[index];
return dref();
}
/// Bitwise ORs `*this` and `other`.
Derived& operator|=(const Derived& other) {
auto& buf = dref().bytes();
for (size_t index = 0; index < Derived::num_bytes; ++index)
buf[index] |= other[index];
return dref();
}
/// Bitwise XORs `*this` and `other`.
Derived& operator^=(const Derived& other) {
auto& buf = dref().bytes();
for (size_t index = 0; index < Derived::num_bytes; ++index)
buf[index] ^= other[index];
return dref();
}
// -- bitwise free operators -------------------------------------------------
friend Derived operator&(const Derived& x, const Derived& y) {
Derived result{x};
result &= y;
return result;
}
friend Derived operator|(const Derived& x, const Derived& y) {
Derived result{x};
result |= y;
return result;
}
friend Derived operator^(const Derived& x, const Derived& y) {
Derived result{x};
result ^= y;
return result;
}
private:
Derived& dref() noexcept {
return *static_cast<Derived*>(this);
}
const Derived& dref() const noexcept {
return *static_cast<const Derived*>(this);
}
};
} // 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 <cstdint>
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/add_ascii.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/pec.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace caf {
namespace detail {
namespace parser {
template <class Iterator, class Sentinel, class Consumer>
void read_ipv4_octet(state<Iterator, Sentinel>& ps, Consumer& consumer) {
uint8_t res = 0;
// Reads the a decimal place.
auto rd_decimal = [&](char c) {
return add_ascii<10>(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, decimal_chars, rd_decimal(ch), pec::integer_overflow)
}
term_state(read) {
transition(read, decimal_chars, rd_decimal(ch), pec::integer_overflow)
}
fin();
}
/// Reads a number, i.e., on success produces either an `int64_t` or a
/// `double`.
template <class Iterator, class Sentinel, class Consumer>
void read_ipv4_address(state<Iterator, Sentinel>& ps, Consumer& consumer) {
int octets = 0;
start();
state(init) {
fsm_epsilon(read_ipv4_octet(ps, consumer), rd_dot, decimal_chars, ++octets)
}
state(rd_dot) {
transition(rd_oct, '.')
}
state(rd_oct) {
fsm_epsilon_if(octets < 3, read_ipv4_octet(ps, consumer), rd_dot,
decimal_chars, ++octets)
fsm_epsilon_if(octets == 3, read_ipv4_octet(ps, consumer), done)
}
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 <array>
#include <cstdint>
#include <string>
#include "caf/byte_address.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/fwd.hpp"
namespace caf {
class ipv4_address : public byte_address<ipv4_address>,
detail::comparable<ipv4_address, ipv6_address> {
public:
// -- constants --------------------------------------------------------------
static constexpr size_t num_bytes = 4;
// -- member types -----------------------------------------------------------
using super = byte_address<ipv4_address>;
using array_type = std::array<uint8_t, num_bytes>;
// -- constructors, destructors, and assignment operators --------------------
ipv4_address();
ipv4_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(const ipv6_address& other) const noexcept;
// -- properties -------------------------------------------------------------
/// Returns whether this is a loopback address.
bool is_loopback() const noexcept;
/// Returns whether this is a multicast address.
bool is_multicast() const noexcept;
/// Returns the bits of the IP address in a single integer.
inline uint32_t bits() const noexcept {
return bits_;
}
/// 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_;
}
constexpr size_t size() const noexcept {
return bytes_.size();
}
private:
// -- member variables -------------------------------------------------------
union {
uint32_t bits_;
array_type bytes_;
};
// -- sanity checks ----------------------------------------------------------
static_assert(sizeof(array_type) == sizeof(uint32_t),
"array<char, 4> has a different size than uint32");
};
// -- related free functions ---------------------------------------------------
/// Returns a human-readable string representation of the address.
/// @relates ipv4_address
std::string to_string(const ipv4_address& x);
/// Tries to parse the content of `str` into `dest`.
/// @relates ipv4_address
error parse(string_view str, ipv4_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/ipv4_address.hpp"
#include "caf/detail/network_order.hpp"
#include "caf/detail/parser/read_ipv4_address.hpp"
#include "caf/error.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace caf {
namespace {
inline uint32_t net_order(uint32_t value) {
return detail::to_network_order(value);
}
struct ipv4_address_consumer {
size_t pos;
ipv4_address& dest;
ipv4_address_consumer(ipv4_address& ref) : pos(0), dest(ref) {
// nop
}
void value(uint8_t octet) {
dest[pos++] = octet;
}
};
} // namespace <anonymous>
// -- constructors, destructors, and assignment operators ----------------------
ipv4_address::ipv4_address() {
bits_ = 0u;
}
ipv4_address::ipv4_address(array_type bytes) {
memcpy(bytes_.data(), bytes.data(), bytes.size());
}
// -- properties ---------------------------------------------------------------
bool ipv4_address::is_loopback() const noexcept {
// All addresses in 127.0.0.0/8 are considered loopback addresses.
return (bits_ & net_order(0xFF000000)) == net_order(0x7F000000);
}
bool ipv4_address::is_multicast() const noexcept {
// All addresses in 224.0.0.0/8 are considered loopback addresses.
return (bits_ & net_order(0xFF000000)) == net_order(0xE0000000);
}
// -- related free functions ---------------------------------------------------
std::string to_string(const ipv4_address& x) {
using std::to_string;
std::string result;
result += to_string(x[0]);
for (size_t i = 1; i < x.data().size(); ++i) {
result += '.';
result += to_string(x[i]);
}
return result;
}
error parse(string_view str, ipv4_address& dest) {
using namespace detail;
parser::state<string_view::iterator> res{str.begin(), str.end()};
ipv4_address_consumer f{dest};
parser::read_ipv4_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 "caf/config.hpp"
#define CAF_SUITE ipv4_address
#include "caf/test/dsl.hpp"
#include "caf/ipv4_address.hpp"
#include "caf/detail/network_order.hpp"
using caf::detail::to_network_order;
using namespace caf;
namespace {
using array_type = ipv4_address::array_type;
ipv4_address addr(uint8_t oct1, uint8_t oct2, uint8_t oct3, uint8_t oct4) {
return ipv4_address({oct1, oct2, oct3, oct4});
}
} // namespace <anonymous>
CAF_TEST(constructing) {
ipv4_address localhost({127, 0, 0, 1});
CAF_CHECK_EQUAL(localhost.bits(), to_network_order(0x7F000001u));
CAF_CHECK_EQUAL(localhost.data(), array_type({127, 0, 0, 1}));
CAF_CHECK_EQUAL(ipv4_address().data(), array_type({0, 0, 0, 0}));
}
CAF_TEST(to and from string) {
ipv4_address x;
auto err = parse("255.255.255.255", x);
CAF_CHECK_EQUAL(err, pec::success);
CAF_CHECK_EQUAL(x.bits(), 0xFFFFFFFF);
CAF_CHECK_EQUAL(to_string(x), "255.255.255.255");
CAF_CHECK_EQUAL(x, addr(255, 255, 255, 255));
}
CAF_TEST(properties) {
CAF_CHECK_EQUAL(addr(127, 0, 0, 1).is_loopback(), true);
CAF_CHECK_EQUAL(addr(127, 0, 0, 254).is_loopback(), true);
CAF_CHECK_EQUAL(addr(127, 0, 1, 1).is_loopback(), true);
CAF_CHECK_EQUAL(addr(128, 0, 0, 1).is_loopback(), false);
CAF_CHECK_EQUAL(addr(224, 0, 0, 1).is_multicast(), true);
CAF_CHECK_EQUAL(addr(224, 0, 0, 254).is_multicast(), true);
CAF_CHECK_EQUAL(addr(224, 0, 1, 1).is_multicast(), true);
CAF_CHECK_EQUAL(addr(225, 0, 0, 1).is_multicast(), false);
CAF_CHECK_EQUAL(addr(1, 2, 3, 4).masked(0), addr(0, 0, 0, 0));
CAF_CHECK_EQUAL(addr(1, 2, 3, 4).masked(1), addr(1, 0, 0, 0));
CAF_CHECK_EQUAL(addr(1, 2, 3, 4).masked(2), addr(1, 2, 0, 0));
CAF_CHECK_EQUAL(addr(1, 2, 3, 4).masked(3), addr(1, 2, 3, 0));
CAF_CHECK_EQUAL(addr(1, 2, 3, 4).masked(4), addr(1, 2, 3, 4));
}
CAF_TEST(operators) {
CAF_CHECK_EQUAL(addr(16, 0, 0, 8) & addr(255, 2, 4, 6), addr(16, 0, 0, 0));
CAF_CHECK_EQUAL(addr(16, 0, 0, 8) | addr(255, 2, 4, 6), addr(255, 2, 4, 14));
CAF_CHECK_EQUAL(addr(16, 0, 0, 8) ^ addr(255, 2, 4, 6), addr(239, 2, 4, 14));
}
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