From 0b5408768950142f71995f1593fbdf64d9c8393e Mon Sep 17 00:00:00 2001 From: Stephen Shelton Date: Fri, 8 May 2020 11:23:21 -0600 Subject: [PATCH] Begin implementing SockAddr --- llarp/CMakeLists.txt | 2 + llarp/ev/ev.cpp | 2 +- llarp/ev/ev_libuv.cpp | 2 +- llarp/net/address_info.cpp | 13 ++ llarp/net/ip_address.cpp | 98 ++++++++++++++ llarp/net/net.cpp | 18 ++- llarp/net/sock_addr.cpp | 199 +++++++++++++++++++++++++++++ llarp/net/sock_addr.hpp | 30 ++++- test/CMakeLists.txt | 3 +- test/net/test_addr.cpp | 13 -- test/net/test_ip_address.cpp | 9 ++ test/net/test_sock_addr.cpp | 41 ++++++ test/test_libabyss.cpp | 3 + test/test_llarp_router_contact.cpp | 7 + 14 files changed, 414 insertions(+), 26 deletions(-) create mode 100644 llarp/net/ip_address.cpp create mode 100644 llarp/net/sock_addr.cpp delete mode 100644 test/net/test_addr.cpp create mode 100644 test/net/test_ip_address.cpp create mode 100644 test/net/test_sock_addr.cpp diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 080746e0b..8f7175f06 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -150,6 +150,8 @@ set(LIB_SRC messages/relay_commit.cpp messages/relay_status.cpp net/address_info.cpp + net/ip_address.cpp + net/sock_addr.cpp net/exit_info.cpp nodedb.cpp path/ihophandler.cpp diff --git a/llarp/ev/ev.cpp b/llarp/ev/ev.cpp index e8ff8c24a..364f5e19b 100644 --- a/llarp/ev/ev.cpp +++ b/llarp/ev/ev.cpp @@ -162,7 +162,7 @@ llarp_tcp_async_try_connect(struct llarp_ev_loop* loop, struct llarp_tcp_connect bool llarp_tcp_serve( - struct llarp_ev_loop* loop, struct llarp_tcp_acceptor* tcp, const struct sockaddr* bindaddr) + struct llarp_ev_loop* loop, struct llarp_tcp_acceptor* tcp, const llarp::SockAddr& bindaddr) { tcp->loop = loop; return loop->tcp_listen(tcp, bindaddr); diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/ev_libuv.cpp index 686e6507d..8790359f5 100644 --- a/llarp/ev/ev_libuv.cpp +++ b/llarp/ev/ev_libuv.cpp @@ -423,7 +423,7 @@ namespace libuv { udp_glue* glue = static_cast(handle->data); if (addr) - glue->RecvFrom(nread, buf, llarp::SockAddr(addr)); + glue->RecvFrom(nread, buf, llarp::SockAddr(*addr)); if (nread <= 0 || glue->m_UDP == nullptr || glue->m_UDP->recvfrom != nullptr) delete[] buf->base; } diff --git a/llarp/net/address_info.cpp b/llarp/net/address_info.cpp index be5b2976a..a8a62bc61 100644 --- a/llarp/net/address_info.cpp +++ b/llarp/net/address_info.cpp @@ -1,4 +1,5 @@ #include +#include #ifndef _WIN32 #include @@ -148,6 +149,18 @@ namespace llarp return bencode_end(buff); } + IpAddress + AddressInfo::toIpAddress() const + { + throw std::runtime_error("FIXME"); + } + + void + AddressInfo::fromIpAddress(const IpAddress& address) + { + throw std::runtime_error("FIXME"); + } + std::ostream& AddressInfo::print(std::ostream& stream, int level, int spaces) const { diff --git a/llarp/net/ip_address.cpp b/llarp/net/ip_address.cpp new file mode 100644 index 000000000..d2e0228bb --- /dev/null +++ b/llarp/net/ip_address.cpp @@ -0,0 +1,98 @@ +#include + +namespace llarp +{ + IpAddress::IpAddress(std::string_view str) + { + throw std::runtime_error("FIXME"); + } + + IpAddress::IpAddress(std::string_view str, std::optional port) + { + throw std::runtime_error("FIXME"); + } + + IpAddress::IpAddress(const SockAddr& addr) + { + throw std::runtime_error("FIXME"); + } + + SockAddr& + IpAddress::operator=(const sockaddr& other) + { + throw std::runtime_error("FIXME"); + } + + std::optional + IpAddress::getPort() const + { + throw std::runtime_error("FIXME"); + } + + void + IpAddress::setPort(std::optional port) + { + throw std::runtime_error("FIXME"); + } + + void + IpAddress::setAddress(std::string_view str) + { + throw std::runtime_error("FIXME"); + } + + void + IpAddress::setAddress(std::string_view str, std::optional port) + { + throw std::runtime_error("FIXME"); + } + + bool + IpAddress::isIPv4() + { + throw std::runtime_error("FIXME"); + } + + bool + IpAddress::isEmpty() const + { + throw std::runtime_error("FIXME"); + } + + SockAddr + IpAddress::createSockAddr() const + { + throw std::runtime_error("FIXME"); + } + + bool + IpAddress::isBogon() const + { + throw std::runtime_error("FIXME"); + } + + std::string + IpAddress::toString() const + { + throw std::runtime_error("FIXME"); + } + + bool + IpAddress::operator<(const IpAddress& other) const + { + throw std::runtime_error("FIXME"); + } + + bool + IpAddress::operator==(const IpAddress& other) const + { + throw std::runtime_error("FIXME"); + } + + std::ostream& + operator<<(std::ostream& out, const IpAddress& address) + { + throw std::runtime_error("FIXME"); + } + +} // namespace llarp diff --git a/llarp/net/net.cpp b/llarp/net/net.cpp index 26d40aa37..ac164411a 100644 --- a/llarp/net/net.cpp +++ b/llarp/net/net.cpp @@ -1,6 +1,7 @@ #include #include +#include #ifdef ANDROID #include @@ -427,14 +428,17 @@ namespace llarp { if (i->ifa_addr->sa_family == af) { - llarp::SockAddr a(*i->ifa_addr); - llarp::IpAddress ip(a); + /* TODO: why the fuck does'n this work? + // llarp::SockAddr a(i->ifa_addr); + // llarp::IpAddress ip(a); if (!ip.isBogon()) { ifname = i->ifa_name; found = true; } + */ + throw std::runtime_error("WTF"); } } }); @@ -540,7 +544,10 @@ namespace llarp sockaddr* sptr = (sockaddr*)&s; if (!llarp_getifaddr(ifname.c_str(), af, sptr)) return std::nullopt; - return IpAddress(SockAddr(sptr)); + // TODO: why the fuck does this not compile? + // llarp::SockAddr saddr = SockAddr(*sptr); + // return llarp::IpAddress(saddr); + throw std::runtime_error("WTF"); } bool @@ -552,7 +559,10 @@ namespace llarp addr.sin_family = AF_INET; addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(0); - result = IpAddress(SockAddr(addr)); + // TODO: why the fuck doesn't this work? + // SockAddr saddr = SockAddr(addr); + // result = IpAddress(saddr); + throw std::runtime_error("WTF"); return true; } if (af == AF_INET6) diff --git a/llarp/net/sock_addr.cpp b/llarp/net/sock_addr.cpp new file mode 100644 index 000000000..e6aa19180 --- /dev/null +++ b/llarp/net/sock_addr.cpp @@ -0,0 +1,199 @@ +#include + +#include +#include +#include + +#include +#include + +namespace llarp +{ + /// shared utility functions + /// + + void + SockAddr::init() + { + llarp::Zero(&m_addr, sizeof(m_addr)); + } + + SockAddr::SockAddr() + { + init(); + } + + SockAddr::SockAddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d) + { + init(); + setIPv4(a, b, c, d); + } + + SockAddr::SockAddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port) + { + init(); + setIPv4(a, b, c, d); + setPort(port); + } + + SockAddr::SockAddr(std::string_view addr) + { + throw std::runtime_error("FIXME"); + } + + SockAddr::SockAddr(const SockAddr&) + { + throw std::runtime_error("FIXME"); + } + + SockAddr& + SockAddr::operator=(const SockAddr&) const + { + throw std::runtime_error("FIXME"); + } + + SockAddr::SockAddr(const sockaddr& addr) + { + throw std::runtime_error("FIXME"); + } + + SockAddr::SockAddr(const sockaddr_in& addr) + { + throw std::runtime_error("FIXME"); + } + + SockAddr::operator const sockaddr*() const + { + throw std::runtime_error("FIXME"); + } + + void + SockAddr::fromString(std::string_view str) + { + if (str.empty()) + throw std::invalid_argument("cannot construct IPv4 from empty string"); + + // NOTE: this potentially involves multiple memory allocations, + // reimplement without split() if it is performance bottleneck + auto splits = split(str, ':'); + + // TODO: having ":port" at the end makes this ambiguous with IPv6 + // come up with a strategy for implementing + if (splits.size() > 2) + throw std::runtime_error("IPv6 not yet supported"); + + // split() shouldn't return an empty list if str is empty (checked above) + assert(splits.size() > 0); + + // splits[0] should be dot-separated IPv4 + auto ipSplits = split(splits[0], '.'); + if (ipSplits.size() != 4) + throw std::invalid_argument(stringify(str, " is not a valid IPv4 address")); + + uint8_t ipBytes[4] = {0}; + + for (int i = 0; i < 4; ++i) + { + auto byteStr = ipSplits[i]; + auto result = std::from_chars(byteStr.data(), byteStr.data() + byteStr.size(), ipBytes[i]); + + if (result.ec == std::errc::invalid_argument) + throw std::runtime_error(stringify(str, " contains invalid number")); + } + + // attempt port before setting IPv4 bytes + if (splits.size() == 2) + { + uint16_t port = 0; + auto portStr = splits[1]; + auto result = std::from_chars(portStr.data(), portStr.data() + portStr.size(), port); + + if (result.ec == std::errc::invalid_argument) + throw std::runtime_error(stringify(str, " contains invalid port")); + + setPort(port); + } + + setIPv4(ipBytes[0], ipBytes[1], ipBytes[2], ipBytes[3]); + } + + std::string + SockAddr::toString() const + { + // TODO: review + if (isEmpty()) + return ""; + + if (m_addr.sin6_family != AF_INET) + throw std::runtime_error("Only IPv4 supported"); + + uint8_t* ip6 = m_addr.sin6_addr.s6_addr; + + // ensure SIIT + if (not ip6[10] == 0xff or not ip6[11]) + throw std::runtime_error("Only SIIT address supported"); + + constexpr auto MaxIPv4PlusPortStringSize = 22; + std::string str; + str.reserve(MaxIPv4PlusPortStringSize); + + // TODO: ensure these don't each incur a memory allocation + str.append(std::to_string(ip6[12])); + str.append(1, '.'); + str.append(std::to_string(ip6[13])); + str.append(1, '.'); + str.append(std::to_string(ip6[14])); + str.append(1, '.'); + str.append(std::to_string(ip6[15])); + + str.append(1, ':'); + str.append(std::to_string(m_addr.sin6_port)); + + return str; + } + + bool + SockAddr::isEmpty() const + { + return m_empty; + } + + void + SockAddr::setIPv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d) + { + m_addr.sin6_family = AF_INET; + + uint8_t* ip6 = m_addr.sin6_addr.s6_addr; + llarp::Zero(ip6, sizeof(m_addr.sin6_addr.s6_addr)); + + // SIIT (represent IPv4 with IPv6) + ip6[10] = 0xff; + ip6[11] = 0xff; + + ip6[12] = a; + ip6[13] = b; + ip6[14] = c; + ip6[15] = d; + + m_empty = false; + } + + void + SockAddr::setPort(uint16_t port) + { + m_addr.sin6_port = htons(port); + } + + uint16_t + SockAddr::getPort() const + { + return ntohs(m_addr.sin6_port); + } + + std::ostream& + operator<<(std::ostream& out, const SockAddr& address) + { + throw std::runtime_error("FIXME"); + } + +} // namespace llarp diff --git a/llarp/net/sock_addr.hpp b/llarp/net/sock_addr.hpp index ae96d4670..8e0d08cb2 100644 --- a/llarp/net/sock_addr.hpp +++ b/llarp/net/sock_addr.hpp @@ -9,31 +9,49 @@ namespace llarp { /// A simple SockAddr wrapper which provides a sockaddr_in (IPv4). Memory management is handled /// in constructor and destructor (if needed) and copying is disabled. - /// - /// This is immutable other than assignment operator. struct SockAddr { SockAddr(); SockAddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d); SockAddr(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint16_t port); - SockAddr(uint32_t addr); SockAddr(std::string_view addr); SockAddr(const SockAddr&); SockAddr& operator=(const SockAddr&) const; - SockAddr(const sockaddr* addr); SockAddr(const sockaddr& addr); - SockAddr(const sockaddr_in* addr); SockAddr(const sockaddr_in& addr); operator const sockaddr*() const; + void + fromString(std::string_view str); + + std::string + toString() const; + + /// Returns true if this is an empty SockAddr, defined by having no IP address set. An empty IP + /// address with a valid port is still considered empty. + /// + /// @return true if this is empty, false otherwise bool isEmpty() const; + void + setIPv4(uint8_t a, uint8_t b, uint8_t c, uint8_t d); + + void + setPort(uint16_t port); + + uint16_t + getPort() const; + private: - sockaddr_in addr; + bool m_empty = true; + sockaddr_in6 m_addr; + + void + init(); }; std::ostream& diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 812e599ee..3b27a818a 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -82,7 +82,8 @@ add_executable(${CATCH_EXE} util/test_llarp_util_decaying_hashset.cpp config/test_llarp_config_definition.cpp config/test_llarp_config_output.cpp - net/test_addr.cpp + net/test_ip_address.cpp + net/test_sock_addr.cpp check_main.cpp) target_link_libraries(${CATCH_EXE} PUBLIC ${STATIC_LIB} Catch2::Catch2) diff --git a/test/net/test_addr.cpp b/test/net/test_addr.cpp deleted file mode 100644 index 231982667..000000000 --- a/test/net/test_addr.cpp +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include - -TEST_CASE("Addr FromString", "[addr]") -{ - llarp::Addr addr; - bool success = false; - CHECK_NOTHROW(success = addr.FromString("127.0.0.1:53")); - CHECK(success == true); - - CHECK(addr.port() == 53); - CHECK(addr.ToString() == "127.0.0.1:53"); -} diff --git a/test/net/test_ip_address.cpp b/test/net/test_ip_address.cpp new file mode 100644 index 000000000..1366ebb76 --- /dev/null +++ b/test/net/test_ip_address.cpp @@ -0,0 +1,9 @@ +#include + +#include + +TEST_CASE("IpAddress empty constructor", "[IpAdress]") +{ + llarp::IpAddress address; + CHECK(address.isEmpty() == true); +} diff --git a/test/net/test_sock_addr.cpp b/test/net/test_sock_addr.cpp new file mode 100644 index 000000000..3f1467074 --- /dev/null +++ b/test/net/test_sock_addr.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +#include + +TEST_CASE("SockAddr from sockaddr", "[SockAddr]") +{ + sockaddr sa; + + // check that this compiles (taking sockaddr by ref) + CHECK_NOTHROW(llarp::SockAddr(sa)); + + sockaddr* saptr = &sa; + + // check that this compiles (taking sockaddr by ref from cast from ptr) + // this was giving odd compilation errors from within net/net.cpp + llarp::SockAddr addr(*saptr); + + llarp::IpAddress ip(addr); +} + +TEST_CASE("SockAddr from IPv4", "[SockAddr]") +{ + llarp::SockAddr addr(1, 2, 3, 4); + CHECK(addr.toString() == "1.2.3.4:0"); +} + +TEST_CASE("SockAddr test port", "[SockAddr]") +{ + llarp::SockAddr addr; + addr.setPort(42); + CHECK(addr.getPort() == 42); +} + +TEST_CASE("SockAddr fromString", "[SockAddr]") +{ + llarp::SockAddr addr; + CHECK_NOTHROW(addr.fromString("1.2.3.4")); + CHECK(addr.toString() == "1.2.3.4:0"); +} diff --git a/test/test_libabyss.cpp b/test/test_libabyss.cpp index 86a5da728..498130cd5 100644 --- a/test/test_libabyss.cpp +++ b/test/test_libabyss.cpp @@ -32,6 +32,8 @@ struct AbyssTestBase : public ::testing::Test void Start() { + throw std::runtime_error("FIXME (replace libabyss with lokimq)"); + /* loop = llarp_make_ev_loop(); logic = std::make_shared< llarp::Logic >(); loop->set_logic(logic); @@ -50,6 +52,7 @@ struct AbyssTestBase : public ::testing::Test } std::this_thread::sleep_for(1s); } + */ } void diff --git a/test/test_llarp_router_contact.cpp b/test/test_llarp_router_contact.cpp index fa9962cc7..2cb523df0 100644 --- a/test/test_llarp_router_contact.cpp +++ b/test/test_llarp_router_contact.cpp @@ -30,6 +30,12 @@ struct RCTest : public test::LlarpTest<> TEST_F(RCTest, TestSignVerify) { + // TODO: RouterContact no longer takes a netmask (the nuint32_t below) + // This was previously used in a call to IsBogonRange, but this wasn't actually + // implemented anyway + throw std::runtime_error("FIXME: RouterContact doesn't take a netmask anymore"); + + /* NetID netid(DEF_VALUE); RC_t rc; SecKey_t encr; @@ -46,4 +52,5 @@ TEST_F(RCTest, TestSignVerify) ASSERT_TRUE(rc.Sign(sign)); ASSERT_TRUE(rc.Verify(time_now_ms())); + */ }