Skip to content

Commit

Permalink
Tunneling binaries
Browse files Browse the repository at this point in the history
- TCP connections can be send over a QUIC connection tunneled over datagrams now
- I'll write more later
  • Loading branch information
dr7ana committed May 7, 2024
1 parent ffb3223 commit 2fc7a12
Show file tree
Hide file tree
Showing 23 changed files with 1,436 additions and 56 deletions.
16 changes: 15 additions & 1 deletion include/oxen/quic/address.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include <oxenc/bt.h>

#include "formattable.hpp"
#include "ip.hpp"
#include "utils.hpp"
Expand Down Expand Up @@ -292,11 +294,17 @@ namespace oxen::quic
// sockaddr pointer).
void update_socklen(socklen_t len) { _addr.addrlen = len; }

std::string host() const;
std::string host(bool no_format = false) const;

// Convenience method for debugging, etc. This is usually called implicitly by passing the
// Address to fmt to format it.
std::string to_string() const;

std::string bt_encode() const;

void bt_encode(oxenc::bt_dict_producer& btdp) const;

static std::optional<Address> bt_decode(oxenc::bt_dict_consumer& btdc);
};

struct RemoteAddress : public Address
Expand Down Expand Up @@ -381,6 +389,12 @@ namespace oxen::quic
Path invert() const { return {remote, local}; }

std::string to_string() const;

std::string bt_encode() const;

void bt_encode(oxenc::bt_dict_producer& btdp) const;

static std::optional<Path> bt_decode(oxenc::bt_dict_consumer& btdc);
};
} // namespace oxen::quic

Expand Down
3 changes: 2 additions & 1 deletion include/oxen/quic/endpoint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ namespace oxen::quic
// Returns a random value suitable for use as the Endpoint static secret value.
static ustring make_static_secret();

// Receive a packet constructed in the function call or moved into it
void manually_receive_packet(Packet&& pkt);

private:
Expand Down Expand Up @@ -231,7 +232,7 @@ namespace oxen::quic
std::map<ustring, ustring> encoded_transport_params;
std::map<ustring, ustring> path_validation_tokens;

const std::shared_ptr<event_base>& get_loop() { return net.loop(); }
const std::shared_ptr<event_base>& get_loop() { return net.ev_loop(); }

const std::unique_ptr<UDPSocket>& get_socket() { return socket; }

Expand Down
4 changes: 3 additions & 1 deletion include/oxen/quic/network.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ namespace oxen::quic
Network();
~Network();

const std::shared_ptr<::event_base>& loop() const { return _loop->loop(); }
const std::shared_ptr<::event_base>& ev_loop() const { return _loop->loop(); }

const std::shared_ptr<Loop>& loop() const { return _loop; }

bool in_event_loop() const { return _loop->in_event_loop(); }

Expand Down
3 changes: 1 addition & 2 deletions include/oxen/quic/opt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
#include <stdexcept>

#include "address.hpp"
#include "crypto.hpp"
#include "types.hpp"
#include "connection_ids.hpp"

namespace oxen::quic
{
Expand Down
2 changes: 2 additions & 0 deletions include/oxen/quic/stream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ namespace oxen::quic

std::shared_ptr<Stream> get_stream() override;

std::shared_ptr<connection_interface> get_conn_interface();

void close(uint64_t app_err_code = 0);

void set_stream_data_cb(stream_data_callback cb) { data_callback = std::move(cb); }
Expand Down
13 changes: 12 additions & 1 deletion include/oxen/quic/udp.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ extern "C"

#include "address.hpp"
#include "types.hpp"
#include "utils.hpp"

namespace oxen::quic
{
Expand All @@ -33,6 +32,10 @@ namespace oxen::quic
// Simple struct wrapping a raw packet and its corresponding information
struct Packet
{
private:
explicit Packet() = default;

public:
Path path;
ngtcp2_pkt_info pkt_info{};
std::variant<bstring_view, bstring> pkt_data;
Expand All @@ -47,6 +50,10 @@ namespace oxen::quic
pkt_data);
}

bool operator==(const Packet& other) const { return (path == other.path) and (data() == other.data()); }

bool operator!=(const Packet& other) const { return !(*this == other); }

/// Constructs a packet from a path and data view:
Packet(Path p, bstring_view d) : path{std::move(p)}, pkt_data{std::move(d)} {}

Expand All @@ -56,6 +63,10 @@ namespace oxen::quic
/// Constructs a packet from a local address, data, and the IP header; remote addr and ECN
/// data are extracted from the header.
Packet(const Address& local, bstring_view data, msghdr& hdr);

std::string bt_encode() const;

static std::optional<Packet> bt_decode(bstring buf);
};

/// RAII class wrapping a UDP socket; the socket is bound at construction and closed during
Expand Down
3 changes: 3 additions & 0 deletions include/oxen/quic/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,9 @@ namespace oxen::quic
return true;
}

/// Checks rv for being -1 and, if so, raises a system_error from errno. Otherwise returns it.
int check_rv(int rv, std::string_view action);

// Shortcut for a const-preserving `reinterpret_cast`ing c.data() from a std::byte to a uint8_t
// pointer, because we need it all over the place in the ngtcp2 API
template <typename Container>
Expand Down
88 changes: 86 additions & 2 deletions src/address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,13 @@ namespace oxen::quic
return {&in6().sin6_addr};
}

std::string Address::host() const
std::string Address::host(bool no_format) const
{
char buf[INET6_ADDRSTRLEN] = {};
if (is_ipv6())
{
inet_ntop(AF_INET6, &reinterpret_cast<const sockaddr_in6&>(_sock_addr).sin6_addr, buf, sizeof(buf));
return "[{}]"_format(buf);
return no_format ? "{}"_format(buf) : "[{}]"_format(buf);
}
inet_ntop(AF_INET, &reinterpret_cast<const sockaddr_in&>(_sock_addr).sin_addr, buf, sizeof(buf));
return "{}"_format(buf);
Expand All @@ -227,6 +227,90 @@ namespace oxen::quic
return "{}:{}"_format(host(), port());
}

std::string Address::bt_encode() const
{
oxenc::bt_dict_producer btdp;
bt_encode(btdp);

return std::move(btdp).str();
}

void Address::bt_encode(oxenc::bt_dict_producer& btdp) const
{
btdp.append("h", host(true));
btdp.append("p", port());
}

std::optional<Address> Address::bt_decode(oxenc::bt_dict_consumer& btdc)
{
std::optional<Address> a = std::nullopt;
std::string host;
uint16_t port;

try
{
host = btdc.require<std::string>("h");
port = btdc.require<uint16_t>("p");

a = Address{std::move(host), port};
}
catch (const std::exception& e)
{
log::critical(log_cat, "Exception parsing Address: {}", e.what());
throw;
}

return a;
}

std::string Path::bt_encode() const
{
oxenc::bt_dict_producer btdp;
bt_encode(btdp);

return std::move(btdp).str();
}

void Path::bt_encode(oxenc::bt_dict_producer& btdp) const
{
{
auto subdict = btdp.append_dict("l");
local.bt_encode(subdict);
}

{
auto subdict = btdp.append_dict("r");
remote.bt_encode(subdict);
}
}

std::optional<Path> Path::bt_decode(oxenc::bt_dict_consumer& btdc)
{
std::optional<Path> p = std::nullopt;

try
{
{
auto [_, subdict] = btdc.next_dict_consumer();
// this constructor will throw for failures
p->local = *Address::bt_decode(subdict);
}

{
auto [_, subdict] = btdc.next_dict_consumer();
// this constructor will throw for failures
p->remote = *Address::bt_decode(subdict);
}
}
catch (const std::exception& e)
{
log::critical(log_cat, "Exception parsing Path: {}", e.what());
throw;
}

return p;
}

void Path::set_new_remote(const ngtcp2_addr& new_remote)
{
memcpy(_path.remote.addr, new_remote.addr, new_remote.addrlen);
Expand Down
5 changes: 5 additions & 0 deletions src/stream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,11 @@ namespace oxen::quic
{
return endpoint.call_get([this]() { return _paused; });
}

std::shared_ptr<connection_interface> Stream::get_conn_interface()
{
return std::static_pointer_cast<connection_interface>(_conn->shared_from_this());
}

bool Stream::available() const
{
Expand Down
66 changes: 45 additions & 21 deletions src/udp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,13 @@ extern "C"
#endif
}

#include <oxenc/bt.h>

#include <system_error>

#include "internal.hpp"
#include "udp.hpp"
#include "utils.hpp"

#ifdef _WIN32

Expand Down Expand Up @@ -60,27 +63,6 @@ namespace oxen::quic
#endif
#endif

/// Checks rv for being -1 and, if so, raises a system_error from errno. Otherwise returns it.
static int check_rv(int rv, std::string_view action)
{
std::optional<std::error_code> ec;
#ifdef _WIN32
if (rv == SOCKET_ERROR)
ec.emplace(WSAGetLastError(), std::system_category());
#else
if (rv == -1)
ec.emplace(errno, std::system_category());

#endif
if (ec)
{
log::error(log_cat, "Got error {} ({}) during {}", ec->value(), ec->message(), action);
throw std::system_error{*ec};
}

return rv;
}

// Same as above, but just logs, doesn't throw.
static void log_rv_error(int rv, std::string_view action)
{
Expand Down Expand Up @@ -784,6 +766,48 @@ namespace oxen::quic
event_add(wev_.get(), nullptr);
}

std::string Packet::bt_encode() const
{
oxenc::bt_dict_producer btdp;

btdp.append("d", data());

{
auto subdict = btdp.append_dict("p");
path.bt_encode(subdict);
}

return std::move(btdp).str();
}

std::optional<Packet> Packet::bt_decode(bstring buf)
{
std::optional<Packet> p = std::nullopt;

oxenc::bt_dict_consumer btdc{buf};

bstring data;
Path path;

try
{
data = btdc.require<bstring>("d");

{
auto [_, subdict] = btdc.next_dict_consumer();
path = *Path::bt_decode(subdict);
}

p = Packet{std::move(path), std::move(data)};
}
catch (const std::exception& e)
{
log::critical(log_cat, "Exception parsing Packet: {}", e.what());
}

return p;
}

Packet::Packet(const Address& local, bstring_view data, msghdr& hdr) :
path{local,
#ifdef _WIN32
Expand Down
20 changes: 20 additions & 0 deletions src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,26 @@ namespace oxen::quic
}
}

int check_rv(int rv, std::string_view action)
{
std::optional<std::error_code> ec;
#ifdef _WIN32
if (rv == SOCKET_ERROR)
ec.emplace(WSAGetLastError(), std::system_category());
#else
if (rv == -1)
ec.emplace(errno, std::system_category());

#endif
if (ec)
{
log::error(log_cat, "Got error {} ({}) during {}", ec->value(), ec->message(), action);
throw std::system_error{*ec};
}

return rv;
}

std::chrono::steady_clock::time_point get_time()
{
return std::chrono::steady_clock::now();
Expand Down
Loading

0 comments on commit 2fc7a12

Please sign in to comment.