mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
temp: everything else
This commit is contained in:
parent
170ecd7add
commit
e3e9b16ddf
|
@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.13...3.24) # 3.13 is buster's version
|
|||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
|
||||
# Has to be set before `project()`, and ignored on non-macos:
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "macOS deployment target (Apple clang only)")
|
||||
|
||||
|
@ -185,6 +186,7 @@ if(NOT TARGET sodium)
|
|||
endif()
|
||||
|
||||
set(warning_flags -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Werror=vla)
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND warning_flags -Wno-unknown-warning-option)
|
||||
endif()
|
||||
|
|
|
@ -25,7 +25,6 @@ cmake \
|
|||
-S "$root" -B "$build" \
|
||||
-G 'Unix Makefiles' \
|
||||
-DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \
|
||||
-DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \
|
||||
-DCMAKE_TOOLCHAIN_FILE="$root/contrib/cross/mingw64.cmake" \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_STATIC_DEPS=ON \
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include <chrono>
|
||||
#include <llarp/config/config.hpp> // for ensure_config
|
||||
#include <llarp/constants/version.hpp>
|
||||
#include <llarp.hpp>
|
||||
|
@ -74,7 +75,7 @@ namespace
|
|||
{
|
||||
llarp::log::info(logcat, "Handling signal {}", sig);
|
||||
if (ctx)
|
||||
ctx->loop->call([sig] { ctx->HandleSignal(sig); });
|
||||
ctx->HandleSignal(sig);
|
||||
else
|
||||
std::cerr << "Received signal " << sig << ", but have no context yet. Ignoring!" << std::endl;
|
||||
}
|
||||
|
@ -504,7 +505,7 @@ namespace
|
|||
do
|
||||
{
|
||||
// do periodic non lokinet related tasks here
|
||||
if (ctx and ctx->IsUp() and not ctx->LooksAlive())
|
||||
if (ctx and (ctx->IsUp() or ctx->IsStopping()) and not ctx->LooksAlive())
|
||||
{
|
||||
auto deadlock_cat = llarp::log::Cat("deadlock");
|
||||
for (const auto& wtf :
|
||||
|
@ -534,7 +535,7 @@ namespace
|
|||
llarp::sys::service_manager->failed();
|
||||
std::abort();
|
||||
}
|
||||
} while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready);
|
||||
} while (ftr.wait_for(std::chrono::milliseconds(100)) != std::future_status::ready);
|
||||
|
||||
main_thread.join();
|
||||
|
||||
|
@ -674,4 +675,4 @@ main(int argc, char* argv[])
|
|||
return 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,6 @@ add_library(lokinet-util
|
|||
util/bencode.cpp
|
||||
util/buffer.cpp
|
||||
util/file.cpp
|
||||
util/json.cpp
|
||||
util/logging/buffer.cpp
|
||||
util/easter_eggs.cpp
|
||||
util/mem.cpp
|
||||
|
@ -76,6 +75,14 @@ if(APPLE)
|
|||
target_sources(lokinet-platform PRIVATE util/nop_service_manager.cpp)
|
||||
endif()
|
||||
|
||||
# set to ON when we want to flip over to the new platform and flow layer.
|
||||
set(use_new_platform_layer OFF)
|
||||
# set to ON when we want to flip over to the refactor routing layer (called the route layer).
|
||||
set(use_new_route_layer OFF)
|
||||
|
||||
set(use_new_onion_layer OFF)
|
||||
|
||||
|
||||
# lokinet-dns is the dns parsing and hooking library that we use to
|
||||
# parse modify and reconstitute dns wire proto, dns queries and RR
|
||||
# should have no concept of dns caching, this is left as an implementation
|
||||
|
@ -87,7 +94,6 @@ add_library(lokinet-dns
|
|||
dns/platform.cpp
|
||||
dns/question.cpp
|
||||
dns/rr.cpp
|
||||
dns/serialize.cpp
|
||||
dns/server.cpp
|
||||
dns/srv_data.cpp)
|
||||
|
||||
|
@ -175,7 +181,18 @@ add_library(lokinet-dht
|
|||
# flows between lokinet snapp endpoints be they .loki or .snode
|
||||
add_library(lokinet-layer-flow
|
||||
STATIC
|
||||
layers/flow/stub.cpp # todo: remove me
|
||||
layers/flow/flow_addr.cpp
|
||||
layers/flow/flow_auth.cpp
|
||||
layers/flow/flow_data_kind.cpp
|
||||
layers/flow/flow_establish.cpp
|
||||
layers/flow/flow_identity.cpp
|
||||
layers/flow/flow_info.cpp
|
||||
layers/flow/flow_layer.cpp
|
||||
layers/flow/flow_state.cpp
|
||||
layers/flow/flow_stats.cpp
|
||||
layers/flow/flow_tag.cpp
|
||||
layers/flow/name_cache.cpp
|
||||
layers/flow/name_resolver.cpp
|
||||
)
|
||||
|
||||
|
||||
|
@ -183,6 +200,13 @@ add_library(lokinet-layer-flow
|
|||
# with onion paths. onion paths anonymize routing layer pdu.
|
||||
add_library(lokinet-layer-onion
|
||||
STATIC
|
||||
layers/onion/onion_layer.cpp
|
||||
layers/onion/onion_stats.cpp
|
||||
)
|
||||
|
||||
add_library(lokinet-deprecated-layer-onion
|
||||
STATIC
|
||||
# old compilation units for onion layer
|
||||
path/ihophandler.cpp
|
||||
path/path_context.cpp
|
||||
path/path.cpp
|
||||
|
@ -194,6 +218,7 @@ add_library(lokinet-layer-onion
|
|||
messages/relay_status.cpp
|
||||
)
|
||||
|
||||
|
||||
# lokinet-layer-wire is a layer 1 analog which splits up
|
||||
# layer 2 frames into layer 1 symbols which in the case of iwp are encrypted udp/ip packets
|
||||
add_library(lokinet-layer-wire
|
||||
|
@ -234,6 +259,7 @@ add_library(lokinet-plainquic
|
|||
add_library(lokinet-context
|
||||
STATIC
|
||||
context.cpp
|
||||
layers/layers.cpp
|
||||
link/link_manager.cpp
|
||||
router/outbound_message_handler.cpp
|
||||
router/outbound_session_maker.cpp
|
||||
|
@ -263,8 +289,14 @@ add_library(lokinet-peerstats
|
|||
|
||||
# lokinet-layer-routing holds logic related to the routing layer
|
||||
# routing layer is anonymized over the onion layer
|
||||
add_library(lokinet-layer-routing
|
||||
add_library(lokinet-layer-route
|
||||
STATIC
|
||||
layers/route/route_layer.cpp
|
||||
)
|
||||
|
||||
add_library(lokinet-deprecated-layer-route
|
||||
STATIC
|
||||
# old compilation units for route layer
|
||||
routing/dht_message.cpp
|
||||
routing/message_parser.cpp
|
||||
routing/path_confirm_message.cpp
|
||||
|
@ -274,22 +306,22 @@ add_library(lokinet-layer-routing
|
|||
)
|
||||
|
||||
# kitchen sink to be removed after refactor
|
||||
add_library(lokinet-service-deprecated-kitchensink
|
||||
add_library(lokinet-deprecated-layer-kitchensink
|
||||
STATIC
|
||||
endpoint_base.cpp
|
||||
handlers/exit.cpp
|
||||
handlers/tun.cpp
|
||||
exit/context.cpp
|
||||
exit/endpoint.cpp
|
||||
exit/exit_messages.cpp
|
||||
exit/policy.cpp
|
||||
exit/session.cpp
|
||||
handlers/exit.cpp
|
||||
handlers/tun.cpp
|
||||
service/context.cpp
|
||||
service/name.cpp
|
||||
service/address.cpp
|
||||
service/async_key_exchange.cpp
|
||||
service/auth.cpp
|
||||
service/convotag.cpp
|
||||
service/context.cpp
|
||||
service/endpoint_state.cpp
|
||||
service/endpoint_util.cpp
|
||||
service/endpoint.cpp
|
||||
|
@ -311,7 +343,13 @@ add_library(lokinet-service-deprecated-kitchensink
|
|||
|
||||
add_library(lokinet-layer-platform
|
||||
STATIC
|
||||
layers/platform/stub.cpp # todo: remove me
|
||||
layers/platform/addr_mapper.cpp
|
||||
layers/platform/dns_bridge.cpp
|
||||
layers/platform/ethertype.cpp
|
||||
layers/platform/os_traffic.cpp
|
||||
layers/platform/platform_addr.cpp
|
||||
layers/platform/platform_layer.cpp
|
||||
layers/platform/platform_stats.cpp
|
||||
)
|
||||
|
||||
|
||||
|
@ -371,7 +409,7 @@ if(TARGET lokinet-dns-systemd)
|
|||
)
|
||||
endif()
|
||||
|
||||
lokinet_link_lib(lokinet-platform lokinet-util)
|
||||
lokinet_link_lib(lokinet-platform lokinet-util lokinet-config)
|
||||
|
||||
lokinet_link_lib(lokinet-config
|
||||
lokinet-util
|
||||
|
@ -447,42 +485,54 @@ function(link_lokinet_layers)
|
|||
endif()
|
||||
endfunction()
|
||||
|
||||
link_lokinet_layers(
|
||||
lokinet-layer-platform
|
||||
lokinet-layer-flow
|
||||
lokinet-layer-routing
|
||||
lokinet-layer-onion
|
||||
lokinet-layer-link
|
||||
lokinet-layer-wire
|
||||
)
|
||||
|
||||
# set me to OFF to disable old codepath
|
||||
set(use_old_impl ON)
|
||||
if(use_old_impl)
|
||||
# flow layer deprecated-kitchensink (remove me after refactor)
|
||||
lokinet_link_lib(lokinet-service-deprecated-kitchensink
|
||||
lokinet-dns
|
||||
lokinet-nodedb
|
||||
lokinet-context
|
||||
lokinet-plainquic
|
||||
lokinet-layer-routing
|
||||
lokinet-layer-onion
|
||||
lokinet-dht
|
||||
lokinet-platform
|
||||
lokinet-rpc
|
||||
)
|
||||
target_link_libraries(lokinet-layers INTERFACE lokinet-service-deprecated-kitchensink)
|
||||
endif()
|
||||
|
||||
add_library(lokinet-deprecated INTERFACE)
|
||||
# todo: add branching case for only new platform layer
|
||||
target_link_libraries(lokinet-layers INTERFACE
|
||||
lokinet-layer-platform
|
||||
lokinet-layer-flow
|
||||
lokinet-layer-routing
|
||||
lokinet-layer-route
|
||||
lokinet-layer-onion
|
||||
lokinet-layer-link
|
||||
lokinet-layer-wire)
|
||||
|
||||
|
||||
|
||||
lokinet_link_lib(lokinet-deprecated-layer-route
|
||||
lokinet-nodedb
|
||||
lokinet-context
|
||||
lokinet-platform)
|
||||
|
||||
lokinet_link_lib(lokinet-deprecated-layer-onion
|
||||
lokinet-deprecated-layer-route
|
||||
lokinet-nodedb
|
||||
lokinet-context
|
||||
lokinet-platform)
|
||||
|
||||
lokinet_link_lib(lokinet-deprecated-layer-kitchensink
|
||||
lokinet-deprecated-layer-route
|
||||
lokinet-deprecated-layer-onion
|
||||
lokinet-dns
|
||||
lokinet-nodedb
|
||||
lokinet-context
|
||||
lokinet-plainquic
|
||||
lokinet-dht
|
||||
lokinet-platform
|
||||
lokinet-rpc)
|
||||
|
||||
target_link_libraries(lokinet-deprecated INTERFACE lokinet-deprecated-layer-onion lokinet-deprecated-layer-route lokinet-deprecated-layer-kitchensink)
|
||||
target_compile_definitions(lokinet-base INTERFACE -DUSE_DEPRECATED_LAYERS)
|
||||
|
||||
|
||||
link_lokinet_layers(
|
||||
lokinet-layer-platform
|
||||
lokinet-layer-flow
|
||||
lokinet-layer-route
|
||||
lokinet-layer-onion
|
||||
lokinet-layer-link
|
||||
lokinet-layer-wire
|
||||
)
|
||||
|
||||
target_link_libraries(lokinet-layers INTERFACE lokinet-deprecated)
|
||||
|
||||
# per component external deps
|
||||
|
||||
|
@ -521,6 +571,7 @@ target_link_libraries(lokinet-plainquic PUBLIC
|
|||
if(WITH_EMBEDDED_LOKINET)
|
||||
include(GNUInstallDirs)
|
||||
add_library(lokinet-shared SHARED lokinet_shared.cpp)
|
||||
target_include_directories(lokinet-shared PRIVATE ${PROJECT_SOURCE_DIR}/include)
|
||||
target_link_libraries(lokinet-shared PUBLIC lokinet-amalgum)
|
||||
if(WIN32)
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "")
|
||||
|
|
|
@ -6,22 +6,96 @@
|
|||
|
||||
namespace llarp::apple
|
||||
{
|
||||
struct Context;
|
||||
|
||||
/// make layers with platform specific quarks applied.
|
||||
std::unique_ptr<const layers::Layers>
|
||||
make_apple_layers(Context& ctx, Config conf);
|
||||
|
||||
/// router with apple quarks applied.
|
||||
struct AppleRouter : public llarp::Router
|
||||
{
|
||||
AppleRouter(
|
||||
const std::shared_ptr<EventLoop>& loop, std::shared_ptr<VPNPlatform> plat, Context& ctx)
|
||||
: Router{loop, std::move(plat)}, _apple_ctx{ctx}
|
||||
|
||||
{}
|
||||
|
||||
~AppleRouter() override = default;
|
||||
|
||||
Context& _apple_ctx;
|
||||
|
||||
std::unique_ptr<const layers::Layers>
|
||||
create_layers() override
|
||||
{
|
||||
return make_apple_layers(_apple_ctx, *m_Config);
|
||||
}
|
||||
};
|
||||
|
||||
struct Context : public llarp::Context
|
||||
{
|
||||
std::shared_ptr<vpn::Platform>
|
||||
makeVPNPlatform() override
|
||||
{
|
||||
return std::make_shared<VPNPlatform>(
|
||||
*this, m_PacketWriter, m_OnReadable, route_callbacks, callback_context);
|
||||
return std::make_shared<VPNPlatform>(*this, write_packet, route_callbacks, callback_context);
|
||||
}
|
||||
|
||||
std::shared_ptr<AbstractRouter>
|
||||
makeRouter(const std::shared_ptr<EventLoop>& loop) override
|
||||
{
|
||||
auto ptr = std::make_shared<AppleRouter>(loop, makeVPNPlatform(), *this);
|
||||
return std::static_pointer_cast<AbstractRouter>(ptr);
|
||||
}
|
||||
|
||||
// Callbacks that must be set for packet handling *before* calling Setup/Configure/Run; the main
|
||||
// point of these is to get passed through to VPNInterface, which will be called during setup,
|
||||
// after construction.
|
||||
VPNInterface::packet_write_callback m_PacketWriter;
|
||||
VPNInterface::on_readable_callback m_OnReadable;
|
||||
packet_write_callback write_packet;
|
||||
on_readable_callback on_readable;
|
||||
llarp_route_callbacks route_callbacks{};
|
||||
void* callback_context = nullptr;
|
||||
};
|
||||
|
||||
/// applies os traffic io quarks for apple platform
|
||||
class AppleOSTraffic_IO : public layers::platform::OSTraffic_IO_Base
|
||||
{
|
||||
public:
|
||||
using layers::platform::OSTraffic_IO_Base::OSTraffic_IO_Base;
|
||||
|
||||
std::vector<layers::platform::OSTraffic>
|
||||
read_platform_traffic() override
|
||||
{
|
||||
auto vec = layers::platform::OSTraffic_IO_Base::read_platform_traffic();
|
||||
// we collected all reads from the past read cycle. queue reads again.
|
||||
if (auto ptr = std::dynamic_pointer_cast<llarp::apple::AppleVPNInterface>(_netif))
|
||||
ptr->on_readable();
|
||||
|
||||
return vec;
|
||||
}
|
||||
};
|
||||
|
||||
/// applies quarks for apple for the platform layer.
|
||||
class ApplePlatformLayer : public layers::platform::PlatformLayer
|
||||
{
|
||||
protected:
|
||||
std::unique_ptr<layers::platform::OSTraffic_IO_Base>
|
||||
make_io() const override
|
||||
{
|
||||
return std::unique_ptr<layers::platform::OSTraffic_IO_Base>{
|
||||
new AppleOSTraffic_IO{_router.loop()}};
|
||||
}
|
||||
|
||||
public:
|
||||
using layers::platform::PlatformLayer::PlatformLayer;
|
||||
};
|
||||
|
||||
std::unique_ptr<const layers::Layers>
|
||||
make_apple_layers(AbstractRouter& router, Config conf)
|
||||
{
|
||||
auto ptr = layers::make_layers(router, conf);
|
||||
ptr->platform.reset(new ApplePlatformLayer{router, *ptr->flow, conf.network});
|
||||
|
||||
return ptr->freeze();
|
||||
}
|
||||
|
||||
} // namespace llarp::apple
|
||||
|
|
|
@ -21,8 +21,8 @@ namespace
|
|||
std::thread runner;
|
||||
packet_writer_callback packet_writer;
|
||||
start_reading_callback start_reading;
|
||||
|
||||
std::weak_ptr<llarp::apple::VPNInterface> iface;
|
||||
llarp::apple::on_readable_callback on_readable;
|
||||
std::weak_ptr<llarp::apple::AppleVPNInterface> iface;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -103,10 +103,8 @@ llarp_apple_init(llarp_apple_config* appleconf)
|
|||
auto inst = std::make_unique<instance_data>();
|
||||
inst->context.Configure(std::move(config));
|
||||
inst->context.route_callbacks = appleconf->route_callbacks;
|
||||
|
||||
inst->packet_writer = appleconf->packet_writer;
|
||||
inst->start_reading = appleconf->start_reading;
|
||||
|
||||
return inst.release();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
|
@ -123,12 +121,12 @@ llarp_apple_start(void* lokinet, void* callback_context)
|
|||
|
||||
inst->context.callback_context = callback_context;
|
||||
|
||||
inst->context.m_PacketWriter = [inst, callback_context](int af_family, void* data, size_t size) {
|
||||
inst->context.write_packet = [inst, callback_context](int af_family, void* data, size_t size) {
|
||||
inst->packet_writer(af_family, data, size, callback_context);
|
||||
return true;
|
||||
};
|
||||
|
||||
inst->context.m_OnReadable = [inst, callback_context](llarp::apple::VPNInterface& iface) {
|
||||
inst->context.on_readable = [inst, callback_context](llarp::apple::AppleVPNInterface& iface) {
|
||||
inst->iface = iface.weak_from_this();
|
||||
inst->start_reading(callback_context);
|
||||
};
|
||||
|
@ -158,7 +156,6 @@ llarp_apple_start(void* lokinet, void* callback_context)
|
|||
llarp::LogError("Failed to initialize lokinet: ", e.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -177,20 +174,22 @@ llarp_apple_incoming(void* lokinet, const llarp_incoming_packet* packets, size_t
|
|||
auto& inst = *static_cast<instance_data*>(lokinet);
|
||||
|
||||
auto iface = inst.iface.lock();
|
||||
if (!iface)
|
||||
if (not iface)
|
||||
return -1;
|
||||
|
||||
int count = 0;
|
||||
for (size_t i = 0; i < size; i++)
|
||||
{
|
||||
llarp_buffer_t buf{static_cast<const uint8_t*>(packets[i].bytes), packets[i].size};
|
||||
if (iface->OfferReadPacket(buf))
|
||||
count++;
|
||||
else
|
||||
llarp::LogError("invalid IP packet: ", llarp::buffer_printer(buf));
|
||||
}
|
||||
llarp::net::IPPacket pkt{
|
||||
llarp::byte_view_t{reinterpret_cast<const byte_t*>(packets[i].bytes), packets[i].size}};
|
||||
|
||||
iface->MaybeWakeUpperLayers();
|
||||
if (pkt.empty())
|
||||
continue;
|
||||
iface->offer_read(std::move(pkt));
|
||||
count++;
|
||||
}
|
||||
// idempotently wake up the part of lokinet that cares.
|
||||
inst.context.router->get_layers()->platform->wakeup->Trigger();
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,23 +18,22 @@ namespace llarp::apple
|
|||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<llarp::handlers::TunEndpoint> tun;
|
||||
router->hiddenServiceContext().ForEachService([&tun](const auto& /*name*/, const auto ep) {
|
||||
tun = std::dynamic_pointer_cast<llarp::handlers::TunEndpoint>(ep);
|
||||
return !tun;
|
||||
});
|
||||
|
||||
if (!tun)
|
||||
auto dns = router->get_dns();
|
||||
if (not dns)
|
||||
{
|
||||
LogError("Cannot reconfigure to use DNS trampoline: no tun endpoint found (!?)");
|
||||
LogError("Cannot reconfigure to use DNS trampoline: no dns on router");
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable)
|
||||
tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, {dns_trampoline_port}}});
|
||||
else
|
||||
tun->ReconfigureDNS(router->GetConfig()->dns.m_upstreamDNS);
|
||||
const std::vector<SockAddr> upstream_addrs{
|
||||
enable ? std::vector<SockAddr>{SockAddr{127, 0, 0, 1, {dns_trampoline_port}}}
|
||||
: router->GetConfig()->dns.m_upstreamDNS};
|
||||
|
||||
for (auto weak : dns->GetAllResolvers())
|
||||
{
|
||||
if (auto ptr = weak.lock())
|
||||
ptr->ResetResolver(upstream_addrs);
|
||||
}
|
||||
trampoline_active = enable;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,59 +1,33 @@
|
|||
|
||||
#include "vpn_interface.hpp"
|
||||
#include "context.hpp"
|
||||
#include "llarp/net/ip_packet.hpp"
|
||||
#include "llarp/vpn/platform.hpp"
|
||||
#include <cstddef>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
|
||||
namespace llarp::apple
|
||||
{
|
||||
VPNInterface::VPNInterface(
|
||||
Context& ctx,
|
||||
packet_write_callback packet_writer,
|
||||
on_readable_callback on_readable,
|
||||
AbstractRouter* router)
|
||||
: vpn::NetworkInterface{{}}
|
||||
, m_PacketWriter{std::move(packet_writer)}
|
||||
, m_OnReadable{std::move(on_readable)}
|
||||
, _router{router}
|
||||
AppleVPNInterface::AppleVPNInterface(
|
||||
Context& ctx, layers::platform::PlatformLayer& plat, packet_write_callback packet_writer)
|
||||
: vpn::QueuedNetworkInterface{vpn::InterfaceInfo{}, plat.wakeup_send, plat.wakeup_recv}
|
||||
, _apple_ctx{ctx}
|
||||
, _write_packet{std::move(packet_writer)}
|
||||
{
|
||||
ctx.loop->call_soon([this] { m_OnReadable(*this); });
|
||||
}
|
||||
|
||||
bool
|
||||
VPNInterface::OfferReadPacket(const llarp_buffer_t& buf)
|
||||
{
|
||||
llarp::net::IPPacket pkt;
|
||||
if (!pkt.Load(buf))
|
||||
return false;
|
||||
m_ReadQueue.tryPushBack(std::move(pkt));
|
||||
return true;
|
||||
// make the reads happen.
|
||||
ctx.loop->call_soon([this]() { on_readable(); });
|
||||
}
|
||||
|
||||
void
|
||||
VPNInterface::MaybeWakeUpperLayers() const
|
||||
AppleVPNInterface::on_readable()
|
||||
{
|
||||
_router->TriggerPump();
|
||||
}
|
||||
|
||||
int
|
||||
VPNInterface::PollFD() const
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
net::IPPacket
|
||||
VPNInterface::ReadNextPacket()
|
||||
{
|
||||
net::IPPacket pkt{};
|
||||
if (not m_ReadQueue.empty())
|
||||
pkt = m_ReadQueue.popFront();
|
||||
return pkt;
|
||||
_apple_ctx.on_readable(*this);
|
||||
}
|
||||
|
||||
bool
|
||||
VPNInterface::WritePacket(net::IPPacket pkt)
|
||||
AppleVPNInterface::WritePacket(net::IPPacket pkt)
|
||||
{
|
||||
int af_family = pkt.IsV6() ? AF_INET6 : AF_INET;
|
||||
return m_PacketWriter(af_family, pkt.data(), pkt.size());
|
||||
return _write_packet(pkt.family(), pkt.data(), pkt.size());
|
||||
}
|
||||
|
||||
} // namespace llarp::apple
|
||||
|
|
|
@ -3,54 +3,38 @@
|
|||
#include <llarp.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include <llarp/util/thread/queue.hpp>
|
||||
#include <memory>
|
||||
#include <llarp/layers/platform/platform_layer.hpp>
|
||||
|
||||
namespace llarp::apple
|
||||
{
|
||||
struct Context;
|
||||
|
||||
class VPNInterface final : public vpn::NetworkInterface,
|
||||
public std::enable_shared_from_this<VPNInterface>
|
||||
using packet_write_callback = std::function<bool(int af_family, void* data, int size)>;
|
||||
|
||||
class AppleVPNInterface final : public vpn::QueuedNetworkInterface,
|
||||
public std::enable_shared_from_this<AppleVPNInterface>
|
||||
{
|
||||
public:
|
||||
using packet_write_callback = std::function<bool(int af_family, void* data, int size)>;
|
||||
using on_readable_callback = std::function<void(VPNInterface&)>;
|
||||
using on_readable_callback = std::function<void(AppleVPNInterface&)>;
|
||||
|
||||
explicit VPNInterface(
|
||||
explicit AppleVPNInterface(
|
||||
Context& ctx,
|
||||
layers::platform::PlatformLayer& plat,
|
||||
packet_write_callback packet_writer,
|
||||
on_readable_callback on_readable,
|
||||
AbstractRouter* router);
|
||||
|
||||
// Method to call when a packet has arrived to deliver the packet to lokinet
|
||||
bool
|
||||
OfferReadPacket(const llarp_buffer_t& buf);
|
||||
|
||||
int
|
||||
PollFD() const override;
|
||||
|
||||
net::IPPacket
|
||||
ReadNextPacket() override;
|
||||
on_readable_callback on_readable);
|
||||
|
||||
bool
|
||||
WritePacket(net::IPPacket pkt) override;
|
||||
|
||||
/// continue reading packets.
|
||||
void
|
||||
MaybeWakeUpperLayers() const override;
|
||||
on_readable();
|
||||
|
||||
private:
|
||||
Context& _apple_ctx;
|
||||
// Function for us to call when we have a packet to emit. Should return true if the packet was
|
||||
// handed off to the OS successfully.
|
||||
packet_write_callback m_PacketWriter;
|
||||
|
||||
// Called when we are ready to start reading packets
|
||||
on_readable_callback m_OnReadable;
|
||||
|
||||
static inline constexpr auto PacketQueueSize = 1024;
|
||||
|
||||
thread::Queue<net::IPPacket> m_ReadQueue{PacketQueueSize};
|
||||
|
||||
AbstractRouter* const _router;
|
||||
packet_write_callback _write_packet;
|
||||
};
|
||||
|
||||
using on_readable_callback = AppleVPNInterface::on_readable_callback;
|
||||
} // namespace llarp::apple
|
||||
|
|
|
@ -1,23 +1,47 @@
|
|||
#include "vpn_platform.hpp"
|
||||
#include "context.hpp"
|
||||
#include "llarp/layers/platform/platform_layer.hpp"
|
||||
#include "llarp/net/ip_packet.hpp"
|
||||
|
||||
namespace llarp::apple
|
||||
{
|
||||
|
||||
class VPNInterface : public vpn::QueuedNetworkInterface
|
||||
{
|
||||
packet_write_callback _write_packet;
|
||||
|
||||
public:
|
||||
VPNInterface(
|
||||
Context& ctx,
|
||||
const layers::platform::PlatformLayer& plat,
|
||||
packet_write_callback write_packet)
|
||||
: vpn::QueuedNetworkInterface{vpn::InterfaceInfo{}, plat.wakeup_send, plat.wakeup_recv}
|
||||
, _write_packet{std::move(write_packet)}
|
||||
{
|
||||
_recv_wakeup->Trigger();
|
||||
}
|
||||
|
||||
bool
|
||||
WritePacket(net::IPPacket pkt) override
|
||||
{
|
||||
return _write_packet(pkt.family(), pkt.data(), static_cast<int>(pkt.size()));
|
||||
}
|
||||
};
|
||||
|
||||
VPNPlatform::VPNPlatform(
|
||||
Context& ctx,
|
||||
VPNInterface::packet_write_callback packet_writer,
|
||||
VPNInterface::on_readable_callback on_readable,
|
||||
packet_write_callback packet_writer,
|
||||
llarp_route_callbacks route_callbacks,
|
||||
void* callback_context)
|
||||
: m_Context{ctx}
|
||||
, m_RouteManager{ctx, std::move(route_callbacks), callback_context}
|
||||
, m_PacketWriter{std::move(packet_writer)}
|
||||
, m_OnReadable{std::move(on_readable)}
|
||||
{}
|
||||
|
||||
std::shared_ptr<vpn::NetworkInterface>
|
||||
VPNPlatform::ObtainInterface(vpn::InterfaceInfo, AbstractRouter* router)
|
||||
{
|
||||
return std::make_shared<VPNInterface>(m_Context, m_PacketWriter, m_OnReadable, router);
|
||||
const auto& plat = *router->get_layers()->platform;
|
||||
return std::make_shared<VPNInterface>(m_Context, plat, m_PacketWriter);
|
||||
}
|
||||
} // namespace llarp::apple
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include "vpn_interface.hpp"
|
||||
#include "route_manager.hpp"
|
||||
|
||||
|
@ -11,8 +10,7 @@ namespace llarp::apple
|
|||
public:
|
||||
explicit VPNPlatform(
|
||||
Context& ctx,
|
||||
VPNInterface::packet_write_callback packet_writer,
|
||||
VPNInterface::on_readable_callback on_readable,
|
||||
packet_write_callback packet_writer,
|
||||
llarp_route_callbacks route_callbacks,
|
||||
void* callback_context);
|
||||
|
||||
|
@ -28,8 +26,7 @@ namespace llarp::apple
|
|||
private:
|
||||
Context& m_Context;
|
||||
apple::RouteManager m_RouteManager;
|
||||
VPNInterface::packet_write_callback m_PacketWriter;
|
||||
VPNInterface::on_readable_callback m_OnReadable;
|
||||
packet_write_callback m_PacketWriter;
|
||||
};
|
||||
|
||||
} // namespace llarp::apple
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "config.hpp"
|
||||
#include "definition.hpp"
|
||||
#include "ini.hpp"
|
||||
#include "llarp/net/ip_range.hpp"
|
||||
|
||||
#include <llarp/constants/files.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
@ -611,7 +612,7 @@ namespace llarp
|
|||
"Interface name for lokinet traffic. If unset lokinet will look for a free name",
|
||||
"matching 'lokinetN', starting at N=0 (e.g. lokinet0, lokinet1, ...).",
|
||||
},
|
||||
AssignmentAcceptor(m_ifname));
|
||||
AssignmentAcceptor(_ifname));
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
"network",
|
||||
|
@ -622,10 +623,12 @@ namespace llarp
|
|||
"lokinet will attempt to find an unused private range.",
|
||||
},
|
||||
[this](std::string arg) {
|
||||
if (not m_ifaddr.FromString(arg))
|
||||
IPRange range;
|
||||
if (not range.FromString(arg))
|
||||
{
|
||||
throw std::invalid_argument{fmt::format("[network]:ifaddr invalid value: '{}'", arg)};
|
||||
}
|
||||
_ifaddr = range;
|
||||
});
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
|
@ -735,13 +738,7 @@ namespace llarp
|
|||
"https://docs.oxen.io/products-built-on-oxen/lokinet/snapps/hosting-snapps",
|
||||
"and general description of DNS SRV record configuration.",
|
||||
},
|
||||
[this](std::string arg) {
|
||||
llarp::dns::SRVData newSRV;
|
||||
if (not newSRV.fromString(arg))
|
||||
throw std::invalid_argument{fmt::format("Invalid SRV Record string: {}", arg)};
|
||||
|
||||
m_SRVRecords.push_back(std::move(newSRV));
|
||||
});
|
||||
[this](std::string_view arg) { m_SRVRecords.emplace_back(arg); });
|
||||
|
||||
conf.defineOption<int>(
|
||||
"network",
|
||||
|
@ -779,6 +776,26 @@ namespace llarp
|
|||
conf.defineOption<std::string>("network", "enabled", Deprecated);
|
||||
}
|
||||
|
||||
IPRange
|
||||
NetworkConfig::ifaddr(const net::Platform& net_plat) const
|
||||
{
|
||||
if (_ifaddr)
|
||||
return *_ifaddr;
|
||||
if (auto maybe_range = net_plat.FindFreeRange())
|
||||
return *maybe_range;
|
||||
throw std::runtime_error{"cannot infer sensible address for local lokinet ip range"};
|
||||
}
|
||||
|
||||
std::string
|
||||
NetworkConfig::ifname(const net::Platform& net_plat) const
|
||||
{
|
||||
if (_ifname)
|
||||
return *_ifname;
|
||||
if (auto maybe_ifname = net_plat.FindFreeTun())
|
||||
return *maybe_ifname;
|
||||
throw std::runtime_error{"cannot infer sensible interface name for lokinet"};
|
||||
}
|
||||
|
||||
void
|
||||
DnsConfig::defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params)
|
||||
{
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#pragma once
|
||||
#include "ini.hpp"
|
||||
#include "definition.hpp"
|
||||
#include "llarp/net/ip_range.hpp"
|
||||
#include "llarp/net/net.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
@ -109,8 +111,8 @@ namespace llarp
|
|||
std::optional<bool> m_enableProfiling;
|
||||
bool m_saveProfiles;
|
||||
std::set<RouterID> m_strictConnect;
|
||||
std::string m_ifname;
|
||||
IPRange m_ifaddr;
|
||||
std::optional<std::string> _ifname;
|
||||
std::optional<IPRange> _ifaddr;
|
||||
|
||||
std::optional<fs::path> m_keyfile;
|
||||
std::string m_endpointType;
|
||||
|
@ -151,6 +153,16 @@ namespace llarp
|
|||
|
||||
void
|
||||
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
|
||||
|
||||
/// obtain interface name. uses configured value or runtime inferred sensible default if one is
|
||||
/// not provided by config. default is inferred with net_plat.
|
||||
std::string
|
||||
ifname(const net::Platform& net_plat) const;
|
||||
|
||||
/// obtain ip range for network interface. uses configured value or runtime inferred sensible
|
||||
/// default if one is one not provided by config. default is inferred with net_plat.
|
||||
IPRange
|
||||
ifaddr(const net::Platform& net_plat) const;
|
||||
};
|
||||
|
||||
struct DnsConfig
|
||||
|
|
|
@ -97,4 +97,12 @@ namespace llarp::platform
|
|||
/// does this platform support native ipv6 ?
|
||||
// TODO: make windows support ipv6
|
||||
inline constexpr bool supports_ipv6 = not is_windows;
|
||||
|
||||
/// this platform uses reactor style io.
|
||||
/// see https://wikipedia.org/wiki/Reactor_pattern?lang=en
|
||||
inline constexpr bool has_reactor_io = is_linux and not is_android;
|
||||
|
||||
/// this platform users proactor style io.
|
||||
/// see https://wikipedia.org/wiki/Proactor_pattern?lang=en
|
||||
inline constexpr bool has_proactor_io = not has_reactor_io;
|
||||
} // namespace llarp::platform
|
||||
|
|
|
@ -127,22 +127,17 @@ namespace llarp
|
|||
|
||||
loop->run();
|
||||
if (closeWaiter)
|
||||
{
|
||||
closeWaiter->set_value();
|
||||
}
|
||||
Close();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
Context::CloseAsync()
|
||||
{
|
||||
/// already closing
|
||||
if (IsStopping())
|
||||
return;
|
||||
|
||||
loop->call([this]() { HandleSignal(SIGTERM); });
|
||||
closeWaiter = std::make_unique<std::promise<void>>();
|
||||
if (not closeWaiter)
|
||||
closeWaiter = std::make_unique<std::promise<void>>();
|
||||
HandleSignal(SIGTERM);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -155,10 +150,8 @@ namespace llarp
|
|||
Context::Wait()
|
||||
{
|
||||
if (closeWaiter)
|
||||
{
|
||||
closeWaiter->get_future().wait();
|
||||
closeWaiter.reset();
|
||||
}
|
||||
closeWaiter.reset();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -175,7 +168,7 @@ namespace llarp
|
|||
if (router and not router->IsServiceNode())
|
||||
{
|
||||
LogInfo("SIGUSR1: resetting network state");
|
||||
router->Thaw();
|
||||
CallSafe([r = router]() { r->Thaw(); });
|
||||
}
|
||||
}
|
||||
if (sig == SIGHUP)
|
||||
|
@ -196,26 +189,10 @@ namespace llarp
|
|||
{
|
||||
llarp::log::debug(logcat, "Handling SIGINT");
|
||||
/// async stop router on sigint
|
||||
router->Stop();
|
||||
CallSafe([r = router]() { r->Stop(); });
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::Close()
|
||||
{
|
||||
llarp::LogDebug("free config");
|
||||
config.reset();
|
||||
|
||||
llarp::LogDebug("free nodedb");
|
||||
nodedb.reset();
|
||||
|
||||
llarp::LogDebug("free router");
|
||||
router.reset();
|
||||
|
||||
llarp::LogDebug("free loop");
|
||||
loop.reset();
|
||||
}
|
||||
|
||||
Context::Context()
|
||||
{
|
||||
// service_manager is a global and context isnt
|
||||
|
|
|
@ -326,6 +326,7 @@ namespace llarp
|
|||
const RouterID K(askpeer.as_array());
|
||||
pendingExploreLookups().NewTX(
|
||||
peer, whoasked, K, new ExploreNetworkJob(askpeer.as_array(), this));
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -497,7 +498,12 @@ namespace llarp
|
|||
if (not reply.M.empty())
|
||||
{
|
||||
auto path = router->pathContext().GetByUpstream(router->pubkey(), id);
|
||||
return path && path->SendRoutingMessage(reply, router);
|
||||
if (not path)
|
||||
return false;
|
||||
if (not path->SendRoutingMessage(reply, router))
|
||||
return false;
|
||||
router->pathContext().PumpUpstream();
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -517,6 +523,7 @@ namespace llarp
|
|||
asker,
|
||||
asker,
|
||||
new LocalServiceAddressLookup(path, txid, relayOrder, addr, this, askpeer));
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -531,6 +538,7 @@ namespace llarp
|
|||
const TXOwner peer(tellpeer, ++ids);
|
||||
_pendingIntrosetLookups.NewTX(
|
||||
peer, asker, asker, new PublishServiceJob(asker, introset, this, relayOrder));
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -548,6 +556,7 @@ namespace llarp
|
|||
asker,
|
||||
peer,
|
||||
new LocalPublishServiceJob(peer, from, txid, introset, this, relayOrder));
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -563,6 +572,7 @@ namespace llarp
|
|||
const TXOwner peer(askpeer, ++ids);
|
||||
_pendingIntrosetLookups.NewTX(
|
||||
peer, asker, asker, new ServiceAddressLookup(asker, addr, this, relayOrder, handler));
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -577,6 +587,7 @@ namespace llarp
|
|||
const TXOwner peer(askpeer, ++ids);
|
||||
_pendingIntrosetLookups.NewTX(
|
||||
peer, asker, asker, new ServiceAddressLookup(asker, addr, this, 0, handler), 1s);
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -634,6 +645,7 @@ namespace llarp
|
|||
const TXOwner whoasked(OurKey(), txid);
|
||||
_pendingRouterLookups.NewTX(
|
||||
peer, whoasked, target, new LocalRouterLookup(path, txid, target, this));
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -648,6 +660,7 @@ namespace llarp
|
|||
const TXOwner peer(askpeer, ++ids);
|
||||
_pendingRouterLookups.NewTX(
|
||||
peer, asker, target, new RecursiveRouterLookup(asker, target, this, handler));
|
||||
router->TriggerPump();
|
||||
}
|
||||
|
||||
llarp_time_t
|
||||
|
|
30
llarp/dns/bits.hpp
Normal file
30
llarp/dns/bits.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace llarp::dns::bits
|
||||
{
|
||||
|
||||
/// query reply bitmask
|
||||
constexpr uint16_t query_reply = 1 << 15;
|
||||
/// authoritative response bitmask
|
||||
constexpr uint16_t authoritative = 1 << 10;
|
||||
/// recursion desirsed bitmask
|
||||
constexpr uint16_t recursion_desired = 1 << 8;
|
||||
// recursion allowed bitmask
|
||||
constexpr uint16_t recursion_allowed = 1 << 7;
|
||||
/// rcode for nx reply
|
||||
constexpr uint16_t rcode_name_error = 3;
|
||||
/// rcdode for srv fail
|
||||
constexpr uint16_t rcode_servfail = 2;
|
||||
/// recode for no error
|
||||
constexpr uint16_t rcode_no_error = 0;
|
||||
|
||||
constexpr uint16_t qclass_in = 1;
|
||||
|
||||
inline constexpr uint16_t
|
||||
make_rcode(uint16_t rcode)
|
||||
{
|
||||
return (rcode | query_reply | authoritative | recursion_allowed) & (~recursion_desired);
|
||||
}
|
||||
} // namespace llarp::dns::bits
|
|
@ -1,30 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace dns
|
||||
{
|
||||
constexpr uint16_t qTypeSRV = 33;
|
||||
constexpr uint16_t qTypeAAAA = 28;
|
||||
constexpr uint16_t qTypeTXT = 16;
|
||||
constexpr uint16_t qTypeMX = 15;
|
||||
constexpr uint16_t qTypePTR = 12;
|
||||
constexpr uint16_t qTypeCNAME = 5;
|
||||
constexpr uint16_t qTypeNS = 2;
|
||||
constexpr uint16_t qTypeA = 1;
|
||||
|
||||
constexpr uint16_t qClassIN = 1;
|
||||
|
||||
constexpr uint16_t flags_QR = (1 << 15);
|
||||
constexpr uint16_t flags_AA = (1 << 10);
|
||||
constexpr uint16_t flags_TC = (1 << 9);
|
||||
constexpr uint16_t flags_RD = (1 << 8);
|
||||
constexpr uint16_t flags_RA = (1 << 7);
|
||||
constexpr uint16_t flags_RCODENameError = (3);
|
||||
constexpr uint16_t flags_RCODEServFail = (2);
|
||||
constexpr uint16_t flags_RCODENoError = (0);
|
||||
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
|
@ -1,434 +1,169 @@
|
|||
#include "message.hpp"
|
||||
#include <oxenc/endian.h>
|
||||
|
||||
#include "dns.hpp"
|
||||
#include "bits.hpp"
|
||||
#include "llarp/util/str.hpp"
|
||||
#include "srv_data.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <future>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/logging.hpp>
|
||||
#include <llarp/net/ip.hpp>
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <oxenc/endian.h>
|
||||
|
||||
#include <array>
|
||||
#include <numeric>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
MessageHeader::MessageHeader(byte_view_t& raw)
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
bool
|
||||
MessageHeader::Encode(llarp_buffer_t* buf) const
|
||||
if (raw.size() < MessageHeader::Size)
|
||||
throw std::invalid_argument{
|
||||
"dns message buffer too small: {} < {}"_format(raw.size(), MessageHeader::Size)};
|
||||
for (auto* i : {&id, &fields, &qd_count, &an_count, &ns_count, &ar_count})
|
||||
{
|
||||
if (!buf->put_uint16(id))
|
||||
return false;
|
||||
if (!buf->put_uint16(fields))
|
||||
return false;
|
||||
if (!buf->put_uint16(qd_count))
|
||||
return false;
|
||||
if (!buf->put_uint16(an_count))
|
||||
return false;
|
||||
if (!buf->put_uint16(ns_count))
|
||||
return false;
|
||||
return buf->put_uint16(ar_count);
|
||||
*i = oxenc::load_big_to_host<uint16_t>(raw.data());
|
||||
raw = raw.substr(2);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
MessageHeader::Decode(llarp_buffer_t* buf)
|
||||
util::StatusObject
|
||||
MessageHeader::to_json() const
|
||||
{
|
||||
return util::StatusObject{
|
||||
{"id", id},
|
||||
{"fields", fields},
|
||||
{"qd", qd_count},
|
||||
{"an", an_count},
|
||||
{"ns", ns_count},
|
||||
{"ar", ar_count}};
|
||||
}
|
||||
|
||||
MessageHeader&
|
||||
MessageHeader::rcode(uint16_t code)
|
||||
{
|
||||
id = bits::make_rcode(code);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bstring_t
|
||||
MessageHeader::encode_dns() const
|
||||
{
|
||||
bstring_t raw;
|
||||
raw.resize(MessageHeader::Size);
|
||||
auto ptr = reinterpret_cast<uint16_t*>(raw.data());
|
||||
for (uint16_t i : {id, fields, qd_count, an_count, ns_count, ar_count})
|
||||
{
|
||||
if (!buf->read_uint16(id))
|
||||
return false;
|
||||
if (!buf->read_uint16(fields))
|
||||
return false;
|
||||
if (!buf->read_uint16(qd_count))
|
||||
return false;
|
||||
if (!buf->read_uint16(an_count))
|
||||
return false;
|
||||
if (!buf->read_uint16(ns_count))
|
||||
return false;
|
||||
if (!buf->read_uint16(ar_count))
|
||||
return false;
|
||||
return true;
|
||||
oxenc::write_host_as_big(i, ptr);
|
||||
ptr++;
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
MessageHeader::ToJSON() const
|
||||
namespace
|
||||
{
|
||||
/// decode N dns data types and shrinks raw as it reads.
|
||||
/// returns a vector of size N or throws.
|
||||
template <typename T>
|
||||
[[nodiscard]] auto
|
||||
decode_dns(byte_view_t& raw, size_t n)
|
||||
{
|
||||
return util::StatusObject{};
|
||||
}
|
||||
|
||||
Message::Message(Message&& other)
|
||||
: hdr_id(std::move(other.hdr_id))
|
||||
, hdr_fields(std::move(other.hdr_fields))
|
||||
, questions(std::move(other.questions))
|
||||
, answers(std::move(other.answers))
|
||||
, authorities(std::move(other.authorities))
|
||||
, additional(std::move(other.additional))
|
||||
{}
|
||||
|
||||
Message::Message(const Message& other)
|
||||
: hdr_id(other.hdr_id)
|
||||
, hdr_fields(other.hdr_fields)
|
||||
, questions(other.questions)
|
||||
, answers(other.answers)
|
||||
, authorities(other.authorities)
|
||||
, additional(other.additional)
|
||||
{}
|
||||
|
||||
Message::Message(const MessageHeader& hdr) : hdr_id(hdr.id), hdr_fields(hdr.fields)
|
||||
{
|
||||
questions.resize(size_t(hdr.qd_count));
|
||||
answers.resize(size_t(hdr.an_count));
|
||||
authorities.resize(size_t(hdr.ns_count));
|
||||
additional.resize(size_t(hdr.ar_count));
|
||||
}
|
||||
|
||||
Message::Message(const Question& question) : hdr_id{0}, hdr_fields{}
|
||||
{
|
||||
questions.emplace_back(question);
|
||||
}
|
||||
|
||||
bool
|
||||
Message::Encode(llarp_buffer_t* buf) const
|
||||
{
|
||||
MessageHeader hdr;
|
||||
hdr.id = hdr_id;
|
||||
hdr.fields = hdr_fields;
|
||||
hdr.qd_count = questions.size();
|
||||
hdr.an_count = answers.size();
|
||||
hdr.ns_count = 0;
|
||||
hdr.ar_count = 0;
|
||||
|
||||
if (!hdr.Encode(buf))
|
||||
return false;
|
||||
|
||||
for (const auto& question : questions)
|
||||
if (!question.Encode(buf))
|
||||
return false;
|
||||
|
||||
for (const auto& answer : answers)
|
||||
if (!answer.Encode(buf))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Message::Decode(llarp_buffer_t* buf)
|
||||
{
|
||||
for (auto& qd : questions)
|
||||
std::vector<T> ents;
|
||||
while (n > 0)
|
||||
{
|
||||
if (!qd.Decode(buf))
|
||||
{
|
||||
log::error(logcat, "failed to decode question");
|
||||
return false;
|
||||
}
|
||||
log::debug(logcat, "question: {}", qd);
|
||||
ents.emplace_back(raw);
|
||||
--n;
|
||||
}
|
||||
for (auto& an : answers)
|
||||
{
|
||||
if (not an.Decode(buf))
|
||||
{
|
||||
log::debug(logcat, "failed to decode answer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return ents;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
util::StatusObject
|
||||
Message::ToJSON() const
|
||||
Message::Message(byte_view_t& raw) : hdr{raw}
|
||||
{
|
||||
questions = decode_dns<Question>(raw, hdr.qd_count);
|
||||
answers = decode_dns<ResourceRecord>(raw, hdr.an_count);
|
||||
authorities = decode_dns<ResourceRecord>(raw, hdr.ns_count);
|
||||
additional = decode_dns<ResourceRecord>(raw, hdr.ar_count);
|
||||
}
|
||||
|
||||
Message&
|
||||
Message::nx()
|
||||
{
|
||||
hdr.rcode(bits::rcode_name_error);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Message&
|
||||
Message::servfail()
|
||||
{
|
||||
hdr.rcode(bits::rcode_servfail);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bstring_t
|
||||
Message::encode_dns() const
|
||||
{
|
||||
// strip off authorities and additional because we encode them wrong or something so here we
|
||||
// reconstruct the header we write so it is correct.
|
||||
MessageHeader _hdr{hdr};
|
||||
_hdr.qd_count = questions.size();
|
||||
_hdr.an_count = answers.size();
|
||||
_hdr.ns_count = 0;
|
||||
_hdr.ar_count = 0;
|
||||
|
||||
_hdr.fields = bits::make_rcode(answers.empty() ? bits::rcode_name_error : bits::rcode_no_error);
|
||||
|
||||
auto ret = _hdr.encode_dns();
|
||||
for (const auto& question : questions)
|
||||
ret += question.encode_dns();
|
||||
for (const auto& answer : answers)
|
||||
ret += answer.encode_dns();
|
||||
|
||||
return ret;
|
||||
}
|
||||
util::StatusObject
|
||||
Message::to_json() const
|
||||
{
|
||||
std::vector<util::StatusObject> ques;
|
||||
std::vector<util::StatusObject> ans;
|
||||
for (const auto& q : questions)
|
||||
{
|
||||
std::vector<util::StatusObject> ques;
|
||||
std::vector<util::StatusObject> ans;
|
||||
for (const auto& q : questions)
|
||||
{
|
||||
ques.push_back(q.ToJSON());
|
||||
}
|
||||
for (const auto& a : answers)
|
||||
{
|
||||
ans.push_back(a.ToJSON());
|
||||
}
|
||||
return util::StatusObject{{"questions", ques}, {"answers", ans}};
|
||||
ques.push_back(q.to_json());
|
||||
}
|
||||
|
||||
OwnedBuffer
|
||||
Message::ToBuffer() const
|
||||
for (const auto& a : answers)
|
||||
{
|
||||
std::array<byte_t, 1500> tmp;
|
||||
llarp_buffer_t buf{tmp};
|
||||
if (not Encode(&buf))
|
||||
throw std::runtime_error("cannot encode dns message");
|
||||
return OwnedBuffer::copy_used(buf);
|
||||
ans.push_back(a.to_json());
|
||||
}
|
||||
return util::StatusObject{{"questions", ques}, {"answers", ans}};
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddServFail(RR_TTL_t)
|
||||
{
|
||||
if (questions.size())
|
||||
{
|
||||
hdr_fields |= flags_RCODEServFail;
|
||||
// authorative response with recursion available
|
||||
hdr_fields |= flags_QR | flags_AA | flags_RA;
|
||||
// don't allow recursion on this request
|
||||
hdr_fields &= ~flags_RD;
|
||||
}
|
||||
}
|
||||
std::string
|
||||
MessageHeader::ToString() const
|
||||
{
|
||||
return fmt::format(
|
||||
"[dns::MessageHeader id={:x} flags={:x} questions={:x} answers={:x} ns={:x} ar={:x}]"_format(
|
||||
id, fields, qd_count, an_count, ns_count, ar_count));
|
||||
}
|
||||
|
||||
static constexpr uint16_t
|
||||
reply_flags(uint16_t setbits)
|
||||
{
|
||||
return setbits | flags_QR | flags_AA | flags_RA;
|
||||
}
|
||||
std::string
|
||||
Message::ToString() const
|
||||
{
|
||||
return fmt::format(
|
||||
"[dns::Message hdr={} questions={{{}}} answers={{{}}} authorities={{{}}} "
|
||||
"additional={{{}}}]",
|
||||
hdr,
|
||||
fmt::format("{}", fmt::join(questions, ",")),
|
||||
fmt::format("{}", fmt::join(answers, ",")),
|
||||
fmt::format("{}", fmt::join(authorities, ",")),
|
||||
fmt::format("{}", fmt::join(additional, ",")));
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddINReply(llarp::huint128_t ip, bool isV6, RR_TTL_t ttl)
|
||||
{
|
||||
if (questions.size())
|
||||
{
|
||||
hdr_fields = reply_flags(hdr_fields);
|
||||
ResourceRecord rec;
|
||||
rec.rr_name = questions[0].qname;
|
||||
rec.rr_class = qClassIN;
|
||||
rec.ttl = ttl;
|
||||
if (isV6)
|
||||
{
|
||||
rec.rr_type = qTypeAAAA;
|
||||
ip.ToV6(rec.rData);
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto addr = net::TruncateV6(ip);
|
||||
rec.rr_type = qTypeA;
|
||||
rec.rData.resize(4);
|
||||
oxenc::write_host_as_big(addr.h, rec.rData.data());
|
||||
}
|
||||
answers.emplace_back(std::move(rec));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddAReply(std::string name, RR_TTL_t ttl)
|
||||
{
|
||||
if (questions.size())
|
||||
{
|
||||
hdr_fields = reply_flags(hdr_fields);
|
||||
|
||||
const auto& question = questions[0];
|
||||
answers.emplace_back();
|
||||
auto& rec = answers.back();
|
||||
rec.rr_name = question.qname;
|
||||
rec.rr_type = question.qtype;
|
||||
rec.rr_class = qClassIN;
|
||||
rec.ttl = ttl;
|
||||
std::array<byte_t, 512> tmp = {{0}};
|
||||
llarp_buffer_t buf(tmp);
|
||||
if (EncodeNameTo(&buf, name))
|
||||
{
|
||||
buf.sz = buf.cur - buf.base;
|
||||
rec.rData.resize(buf.sz);
|
||||
memcpy(rec.rData.data(), buf.base, buf.sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddNSReply(std::string name, RR_TTL_t ttl)
|
||||
{
|
||||
if (not questions.empty())
|
||||
{
|
||||
hdr_fields = reply_flags(hdr_fields);
|
||||
|
||||
const auto& question = questions[0];
|
||||
answers.emplace_back();
|
||||
auto& rec = answers.back();
|
||||
rec.rr_name = question.qname;
|
||||
rec.rr_type = qTypeNS;
|
||||
rec.rr_class = qClassIN;
|
||||
rec.ttl = ttl;
|
||||
std::array<byte_t, 512> tmp = {{0}};
|
||||
llarp_buffer_t buf(tmp);
|
||||
if (EncodeNameTo(&buf, name))
|
||||
{
|
||||
buf.sz = buf.cur - buf.base;
|
||||
rec.rData.resize(buf.sz);
|
||||
memcpy(rec.rData.data(), buf.base, buf.sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddCNAMEReply(std::string name, RR_TTL_t ttl)
|
||||
{
|
||||
if (questions.size())
|
||||
{
|
||||
hdr_fields = reply_flags(hdr_fields);
|
||||
|
||||
const auto& question = questions[0];
|
||||
answers.emplace_back();
|
||||
auto& rec = answers.back();
|
||||
rec.rr_name = question.qname;
|
||||
rec.rr_type = qTypeCNAME;
|
||||
rec.rr_class = qClassIN;
|
||||
rec.ttl = ttl;
|
||||
std::array<byte_t, 512> tmp = {{0}};
|
||||
llarp_buffer_t buf(tmp);
|
||||
if (EncodeNameTo(&buf, name))
|
||||
{
|
||||
buf.sz = buf.cur - buf.base;
|
||||
rec.rData.resize(buf.sz);
|
||||
memcpy(rec.rData.data(), buf.base, buf.sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddMXReply(std::string name, uint16_t priority, RR_TTL_t ttl)
|
||||
{
|
||||
if (questions.size())
|
||||
{
|
||||
hdr_fields = reply_flags(hdr_fields);
|
||||
|
||||
const auto& question = questions[0];
|
||||
answers.emplace_back();
|
||||
auto& rec = answers.back();
|
||||
rec.rr_name = question.qname;
|
||||
rec.rr_type = qTypeMX;
|
||||
rec.rr_class = qClassIN;
|
||||
rec.ttl = ttl;
|
||||
std::array<byte_t, 512> tmp = {{0}};
|
||||
llarp_buffer_t buf(tmp);
|
||||
buf.put_uint16(priority);
|
||||
if (EncodeNameTo(&buf, name))
|
||||
{
|
||||
buf.sz = buf.cur - buf.base;
|
||||
rec.rData.resize(buf.sz);
|
||||
memcpy(rec.rData.data(), buf.base, buf.sz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddSRVReply(std::vector<SRVData> records, RR_TTL_t ttl)
|
||||
{
|
||||
hdr_fields = reply_flags(hdr_fields);
|
||||
|
||||
const auto& question = questions[0];
|
||||
|
||||
for (const auto& srv : records)
|
||||
{
|
||||
if (not srv.IsValid())
|
||||
{
|
||||
AddNXReply();
|
||||
return;
|
||||
}
|
||||
|
||||
answers.emplace_back();
|
||||
auto& rec = answers.back();
|
||||
rec.rr_name = question.qname;
|
||||
rec.rr_type = qTypeSRV;
|
||||
rec.rr_class = qClassIN;
|
||||
rec.ttl = ttl;
|
||||
|
||||
std::array<byte_t, 512> tmp = {{0}};
|
||||
llarp_buffer_t buf(tmp);
|
||||
|
||||
buf.put_uint16(srv.priority);
|
||||
buf.put_uint16(srv.weight);
|
||||
buf.put_uint16(srv.port);
|
||||
|
||||
std::string target;
|
||||
if (srv.target == "")
|
||||
{
|
||||
// get location of second dot (after service.proto) in qname
|
||||
size_t pos = question.qname.find(".");
|
||||
pos = question.qname.find(".", pos + 1);
|
||||
|
||||
target = question.qname.substr(pos + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
target = srv.target;
|
||||
}
|
||||
|
||||
if (not EncodeNameTo(&buf, target))
|
||||
{
|
||||
AddNXReply();
|
||||
return;
|
||||
}
|
||||
|
||||
buf.sz = buf.cur - buf.base;
|
||||
rec.rData.resize(buf.sz);
|
||||
memcpy(rec.rData.data(), buf.base, buf.sz);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddTXTReply(std::string str, RR_TTL_t ttl)
|
||||
{
|
||||
auto& rec = answers.emplace_back();
|
||||
rec.rr_name = questions[0].qname;
|
||||
rec.rr_class = qClassIN;
|
||||
rec.rr_type = qTypeTXT;
|
||||
rec.ttl = ttl;
|
||||
std::array<byte_t, 1024> tmp{};
|
||||
llarp_buffer_t buf(tmp);
|
||||
while (not str.empty())
|
||||
{
|
||||
const auto left = std::min(str.size(), size_t{256});
|
||||
const auto sub = str.substr(0, left);
|
||||
uint8_t byte = left;
|
||||
*buf.cur = byte;
|
||||
buf.cur++;
|
||||
if (not buf.write(sub.begin(), sub.end()))
|
||||
throw std::length_error("text record too big");
|
||||
str = str.substr(left);
|
||||
}
|
||||
buf.sz = buf.cur - buf.base;
|
||||
rec.rData.resize(buf.sz);
|
||||
std::copy_n(buf.base, buf.sz, rec.rData.data());
|
||||
}
|
||||
|
||||
void
|
||||
Message::AddNXReply(RR_TTL_t)
|
||||
{
|
||||
if (questions.size())
|
||||
{
|
||||
answers.clear();
|
||||
authorities.clear();
|
||||
additional.clear();
|
||||
|
||||
// authorative response with recursion available
|
||||
hdr_fields = reply_flags(hdr_fields);
|
||||
// don't allow recursion on this request
|
||||
hdr_fields &= ~flags_RD;
|
||||
hdr_fields |= flags_RCODENameError;
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
Message::ToString() const
|
||||
{
|
||||
return fmt::format(
|
||||
"[DNSMessage id={:x} fields={:x} questions={{{}}} answers={{{}}} authorities={{{}}} "
|
||||
"additional={{{}}}]",
|
||||
hdr_id,
|
||||
hdr_fields,
|
||||
fmt::format("{}", fmt::join(questions, ",")),
|
||||
fmt::format("{}", fmt::join(answers, ",")),
|
||||
fmt::format("{}", fmt::join(authorities, ",")),
|
||||
fmt::format("{}", fmt::join(additional, ",")));
|
||||
}
|
||||
|
||||
std::optional<Message>
|
||||
MaybeParseDNSMessage(llarp_buffer_t buf)
|
||||
{
|
||||
MessageHeader hdr{};
|
||||
if (not hdr.Decode(&buf))
|
||||
return std::nullopt;
|
||||
|
||||
Message msg{hdr};
|
||||
if (not msg.Decode(&buf))
|
||||
return std::nullopt;
|
||||
return msg;
|
||||
}
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,113 +1,181 @@
|
|||
#pragma once
|
||||
|
||||
#include "serialize.hpp"
|
||||
#include <cstdint>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include "rr.hpp"
|
||||
#include "question.hpp"
|
||||
#include "srv_data.hpp"
|
||||
#include "bits.hpp"
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
|
||||
struct MessageHeader
|
||||
{
|
||||
static constexpr size_t Size = 12;
|
||||
|
||||
MessageHeader() = default;
|
||||
MessageHeader(const MessageHeader&) = default;
|
||||
MessageHeader(MessageHeader&&) = default;
|
||||
|
||||
MessageHeader&
|
||||
operator=(const MessageHeader&) = default;
|
||||
MessageHeader&
|
||||
operator=(MessageHeader&&) = default;
|
||||
|
||||
/// construct from binary view.
|
||||
/// raw is modified to contain any remaining data.
|
||||
explicit MessageHeader(byte_view_t& raw);
|
||||
|
||||
uint16_t id;
|
||||
uint16_t fields;
|
||||
uint16_t qd_count;
|
||||
uint16_t an_count;
|
||||
uint16_t ns_count;
|
||||
uint16_t ar_count;
|
||||
|
||||
bstring_t
|
||||
encode_dns() const;
|
||||
|
||||
util::StatusObject
|
||||
to_json() const;
|
||||
|
||||
/// set rcode.
|
||||
MessageHeader&
|
||||
rcode(uint16_t i);
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
|
||||
bool
|
||||
operator==(const MessageHeader& other) const
|
||||
{
|
||||
return id == other.id && fields == other.fields && qd_count == other.qd_count
|
||||
&& an_count == other.an_count && ns_count == other.ns_count && ar_count == other.ar_count;
|
||||
}
|
||||
};
|
||||
|
||||
struct Message
|
||||
{
|
||||
/// constructs a dns message from a raw dns wire protocol message.
|
||||
/// raw is modified to contain any remaining data.
|
||||
explicit Message(byte_view_t& raw);
|
||||
|
||||
Message() = default;
|
||||
Message(const Message&) = default;
|
||||
Message(Message&&) = default;
|
||||
|
||||
Message&
|
||||
operator=(const Message&) = default;
|
||||
Message&
|
||||
operator=(Message&&) = default;
|
||||
|
||||
/// mark as not found.
|
||||
Message&
|
||||
nx();
|
||||
|
||||
/// mark as servfail.
|
||||
Message&
|
||||
servfail();
|
||||
|
||||
/// encodes this dns message to dns wire protocol.
|
||||
bstring_t
|
||||
encode_dns() const;
|
||||
|
||||
util::StatusObject
|
||||
to_json() const;
|
||||
|
||||
// Wrapper around Encode that encodes into a new buffer and returns it
|
||||
[[nodiscard]] inline OwnedBuffer
|
||||
ToBuffer() const
|
||||
{
|
||||
const auto data = encode_dns();
|
||||
return OwnedBuffer{data.data(), data.size()};
|
||||
}
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
|
||||
MessageHeader hdr;
|
||||
std::vector<Question> questions;
|
||||
std::vector<ResourceRecord> answers;
|
||||
std::vector<ResourceRecord> authorities;
|
||||
std::vector<ResourceRecord> additional;
|
||||
|
||||
template <typename... Args_t>
|
||||
void
|
||||
add_reply(RRType rr_type, Args_t&&... args)
|
||||
{
|
||||
auto qname = questions[0].qname();
|
||||
answers.emplace_back(qname, rr_type, args...);
|
||||
}
|
||||
|
||||
inline void
|
||||
AddNSReply(std::string_view txt, uint32_t ttl = 1) [[deprecated("use rr_ns")]]
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
inline void
|
||||
AddTXTReply(std::string_view txt, uint32_t ttl = 1) [[deprecated("use rr_txt")]]
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
inline void
|
||||
AddMXReply(std::string_view name, uint32_t ttl = 1) [[deprecated("use rr_mx")]]
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
inline void
|
||||
AddCNAMEReply(std::string_view name, uint32_t ttl = 1) [[deprecated("use rr_cname")]]
|
||||
{
|
||||
add_reply(RRType::CNAME, RData{split_dns_name(name)}, ttl);
|
||||
}
|
||||
|
||||
inline void
|
||||
AddPTRReply(std::string_view name, uint32_t ttl = 1) [[deprecated("use rr_ptr")]]
|
||||
{
|
||||
add_reply(RRType::PTR, RData{split_dns_name(name)}, ttl);
|
||||
}
|
||||
|
||||
inline void
|
||||
AddAReply(net::ipv4addr_t ip, uint32_t ttl = 1) [[deprecated("use rr_cname")]]
|
||||
{
|
||||
add_reply(RRType::A, RData{ip}, ttl);
|
||||
}
|
||||
|
||||
inline void
|
||||
AddSRVReply(const std::vector<dns::SRVData>& recs, uint32_t ttl = 1)
|
||||
[[deprecated("use rr_srv")]]
|
||||
{
|
||||
for (const auto& rec : recs)
|
||||
add_reply(RRType::SRV, RData{rec.encode_dns(questions[0].qname())}, ttl);
|
||||
}
|
||||
|
||||
inline void
|
||||
rr_add_ipaddr(net::ipaddr_t ip, uint32_t ttl = 1)
|
||||
{
|
||||
add_reply(
|
||||
std::holds_alternative<net::ipv4addr_t>(ip) ? RRType::A : RRType::AAAA,
|
||||
var::visit([](auto&& ip) { return RData{ip}; }, ip),
|
||||
ttl);
|
||||
}
|
||||
|
||||
inline void
|
||||
AddINReply(std::string_view addr, uint32_t ttl = 1) [[deprecated("use rr_in")]]
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace dns
|
||||
{
|
||||
struct SRVData;
|
||||
|
||||
using MsgID_t = uint16_t;
|
||||
using Fields_t = uint16_t;
|
||||
using Count_t = uint16_t;
|
||||
|
||||
struct MessageHeader : public Serialize
|
||||
{
|
||||
static constexpr size_t Size = 12;
|
||||
|
||||
MessageHeader() = default;
|
||||
|
||||
MsgID_t id;
|
||||
Fields_t fields;
|
||||
Count_t qd_count;
|
||||
Count_t an_count;
|
||||
Count_t ns_count;
|
||||
Count_t ar_count;
|
||||
|
||||
bool
|
||||
Encode(llarp_buffer_t* buf) const override;
|
||||
|
||||
bool
|
||||
Decode(llarp_buffer_t* buf) override;
|
||||
|
||||
util::StatusObject
|
||||
ToJSON() const override;
|
||||
|
||||
bool
|
||||
operator==(const MessageHeader& other) const
|
||||
{
|
||||
return id == other.id && fields == other.fields && qd_count == other.qd_count
|
||||
&& an_count == other.an_count && ns_count == other.ns_count
|
||||
&& ar_count == other.ar_count;
|
||||
}
|
||||
};
|
||||
|
||||
struct Message : public Serialize
|
||||
{
|
||||
explicit Message(const MessageHeader& hdr);
|
||||
explicit Message(const Question& question);
|
||||
|
||||
Message(Message&& other);
|
||||
Message(const Message& other);
|
||||
|
||||
util::StatusObject
|
||||
ToJSON() const override;
|
||||
|
||||
void
|
||||
AddNXReply(RR_TTL_t ttl = 1);
|
||||
|
||||
void
|
||||
AddServFail(RR_TTL_t ttl = 30);
|
||||
|
||||
void
|
||||
AddMXReply(std::string name, uint16_t priority, RR_TTL_t ttl = 1);
|
||||
|
||||
void
|
||||
AddCNAMEReply(std::string name, RR_TTL_t ttl = 1);
|
||||
|
||||
void
|
||||
AddINReply(llarp::huint128_t addr, bool isV6, RR_TTL_t ttl = 1);
|
||||
|
||||
void
|
||||
AddAReply(std::string name, RR_TTL_t ttl = 1);
|
||||
|
||||
void
|
||||
AddSRVReply(std::vector<SRVData> records, RR_TTL_t ttl = 1);
|
||||
|
||||
void
|
||||
AddNSReply(std::string name, RR_TTL_t ttl = 1);
|
||||
|
||||
void
|
||||
AddTXTReply(std::string value, RR_TTL_t ttl = 1);
|
||||
|
||||
bool
|
||||
Encode(llarp_buffer_t* buf) const override;
|
||||
|
||||
bool
|
||||
Decode(llarp_buffer_t* buf) override;
|
||||
|
||||
// Wrapper around Encode that encodes into a new buffer and returns it
|
||||
[[nodiscard]] OwnedBuffer
|
||||
ToBuffer() const;
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
|
||||
MsgID_t hdr_id;
|
||||
Fields_t hdr_fields;
|
||||
std::vector<Question> questions;
|
||||
std::vector<ResourceRecord> answers;
|
||||
std::vector<ResourceRecord> authorities;
|
||||
std::vector<ResourceRecord> additional;
|
||||
};
|
||||
|
||||
std::optional<Message>
|
||||
MaybeParseDNSMessage(llarp_buffer_t buf);
|
||||
} // namespace dns
|
||||
|
||||
template <>
|
||||
constexpr inline bool IsToStringFormattable<llarp::dns::Message> = true;
|
||||
|
||||
template <>
|
||||
constexpr inline bool IsToStringFormattable<llarp::dns::MessageHeader> = true;
|
||||
} // namespace llarp
|
||||
|
|
|
@ -1,139 +1,173 @@
|
|||
#include "name.hpp"
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/net/ip.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include "llarp/util/types.hpp"
|
||||
#include <fmt/core.h>
|
||||
#include <oxenc/hex.h>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
namespace
|
||||
{
|
||||
std::optional<std::string>
|
||||
DecodeName(llarp_buffer_t* buf, bool trimTrailingDot)
|
||||
template <typename Label_t>
|
||||
std::vector<Label_t>
|
||||
decode(byte_view_t& bstr)
|
||||
{
|
||||
if (buf->size_left() < 1)
|
||||
return std::nullopt;
|
||||
auto result = std::make_optional<std::string>();
|
||||
auto& name = *result;
|
||||
size_t l;
|
||||
std::vector<Label_t> labels;
|
||||
size_t label_len{};
|
||||
do
|
||||
{
|
||||
l = *buf->cur;
|
||||
buf->cur++;
|
||||
if (l)
|
||||
{
|
||||
if (buf->size_left() < l)
|
||||
return std::nullopt;
|
||||
if (bstr.empty())
|
||||
throw std::invalid_argument{"unexpected end of data"};
|
||||
label_len = bstr[0];
|
||||
auto label_size = label_len + 1;
|
||||
if (bstr.size() < label_size)
|
||||
throw std::invalid_argument{
|
||||
"dns label size mismatch: {} < {}"_format(bstr.size(), label_size)};
|
||||
|
||||
name.append((const char*)buf->cur, l);
|
||||
name += '.';
|
||||
}
|
||||
buf->cur = buf->cur + l;
|
||||
} while (l);
|
||||
/// trim off last dot
|
||||
if (trimTrailingDot)
|
||||
name.pop_back();
|
||||
return result;
|
||||
if (label_len)
|
||||
labels.emplace_back(
|
||||
reinterpret_cast<const typename Label_t::value_type*>(&bstr[1]), label_len);
|
||||
bstr = bstr.substr(label_size);
|
||||
} while (label_len > 0);
|
||||
|
||||
// remove trailing empty label.
|
||||
if (labels.back().empty())
|
||||
labels.pop_back();
|
||||
|
||||
return labels;
|
||||
}
|
||||
|
||||
bool
|
||||
EncodeNameTo(llarp_buffer_t* buf, std::string_view name)
|
||||
template <typename Ret_t, typename Val_t>
|
||||
Ret_t
|
||||
encode(std::vector<Val_t> labels)
|
||||
{
|
||||
if (name.size() && name.back() == '.')
|
||||
name.remove_suffix(1);
|
||||
|
||||
for (auto part : llarp::split(name, "."))
|
||||
using char_type_t = typename Ret_t::value_type;
|
||||
Ret_t str;
|
||||
for (const auto& label : labels)
|
||||
{
|
||||
size_t l = part.length();
|
||||
if (l > 63)
|
||||
return false;
|
||||
*(buf->cur) = l;
|
||||
buf->cur++;
|
||||
if (buf->size_left() < l)
|
||||
return false;
|
||||
if (l)
|
||||
{
|
||||
std::memcpy(buf->cur, part.data(), l);
|
||||
buf->cur += l;
|
||||
}
|
||||
else
|
||||
if (label.empty())
|
||||
break;
|
||||
auto label_size = label.size();
|
||||
if (label_size > max_dns_label_size)
|
||||
throw std::invalid_argument{
|
||||
"dns label too big: {} > {}"_format(label_size, max_dns_label_size)};
|
||||
str += static_cast<char_type_t>(label_size);
|
||||
str += std::basic_string_view<char_type_t>{
|
||||
reinterpret_cast<const char_type_t*>(label.data()), label_size};
|
||||
}
|
||||
*buf->cur = 0;
|
||||
buf->cur++;
|
||||
return true;
|
||||
str += char_type_t{};
|
||||
return str;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::optional<huint128_t>
|
||||
DecodePTR(std::string_view name)
|
||||
std::vector<std::string>
|
||||
decode_dns_labels(byte_view_t& bstr)
|
||||
{
|
||||
return decode<std::string>(bstr);
|
||||
}
|
||||
|
||||
std::vector<std::string_view>
|
||||
decode_dns_label_views(byte_view_t& bstr)
|
||||
{
|
||||
return decode<std::string_view>(bstr);
|
||||
}
|
||||
|
||||
bstring_t
|
||||
encode_dns_labels(std::vector<std::string_view> labels)
|
||||
{
|
||||
return encode<bstring_t>(labels);
|
||||
}
|
||||
|
||||
bstring_t
|
||||
encode_dns_labels(std::vector<std::string> labels)
|
||||
{
|
||||
return encode<bstring_t>(labels);
|
||||
}
|
||||
|
||||
bstring_t
|
||||
encode_dns_name(std::string_view name)
|
||||
{
|
||||
return encode_dns_labels(split_dns_name(name));
|
||||
}
|
||||
|
||||
std::optional<huint128_t>
|
||||
DecodePTR(std::string_view name)
|
||||
{
|
||||
bool isV6 = false;
|
||||
auto pos = name.find(".in-addr.arpa");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
bool isV6 = false;
|
||||
auto pos = name.find(".in-addr.arpa");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos = name.find(".ip6.arpa");
|
||||
isV6 = true;
|
||||
}
|
||||
if (pos == std::string::npos)
|
||||
return std::nullopt;
|
||||
name = name.substr(0, pos + 1);
|
||||
const auto numdots = std::count(name.begin(), name.end(), '.');
|
||||
if (numdots == 4 && !isV6)
|
||||
{
|
||||
std::array<uint8_t, 4> q;
|
||||
for (int i = 3; i >= 0; i--)
|
||||
{
|
||||
pos = name.find('.');
|
||||
if (!llarp::parse_int(name.substr(0, pos), q[i]))
|
||||
return std::nullopt;
|
||||
name.remove_prefix(pos + 1);
|
||||
}
|
||||
return net::ExpandV4(llarp::ipaddr_ipv4_bits(q[0], q[1], q[2], q[3]));
|
||||
}
|
||||
if (numdots == 32 && name.size() == 64 && isV6)
|
||||
{
|
||||
// We're going to convert from nybbles a.b.c.d.e.f.0.1.2.3.[...] into hex string
|
||||
// "badcfe1032...", then decode the hex string to bytes.
|
||||
std::array<char, 32> in;
|
||||
auto in_pos = in.data();
|
||||
for (size_t i = 0; i < 64; i += 4)
|
||||
{
|
||||
if (not(oxenc::is_hex_digit(name[i]) and name[i + 1] == '.'
|
||||
and oxenc::is_hex_digit(name[i + 2]) and name[i + 3] == '.'))
|
||||
return std::nullopt;
|
||||
|
||||
// Flip the nybbles because the smallest one is first
|
||||
*in_pos++ = name[i + 2];
|
||||
*in_pos++ = name[i];
|
||||
}
|
||||
assert(in_pos == in.data() + in.size());
|
||||
huint128_t ip;
|
||||
static_assert(in.size() == 2 * sizeof(ip.h));
|
||||
// our string right now is the little endian representation, so load it as such on little
|
||||
// endian, or in reverse on big endian.
|
||||
if constexpr (oxenc::little_endian)
|
||||
oxenc::from_hex(in.begin(), in.end(), reinterpret_cast<uint8_t*>(&ip.h));
|
||||
else
|
||||
oxenc::from_hex(in.rbegin(), in.rend(), reinterpret_cast<uint8_t*>(&ip.h));
|
||||
|
||||
return ip;
|
||||
}
|
||||
pos = name.find(".ip6.arpa");
|
||||
isV6 = true;
|
||||
}
|
||||
if (pos == std::string::npos)
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool
|
||||
NameIsReserved(std::string_view name)
|
||||
name = name.substr(0, pos + 1);
|
||||
const auto numdots = std::count(name.begin(), name.end(), '.');
|
||||
if (numdots == 4 && !isV6)
|
||||
{
|
||||
const std::vector<std::string_view> reserved_names = {
|
||||
".snode.loki"sv, ".loki.loki"sv, ".snode.loki."sv, ".loki.loki."sv};
|
||||
for (const auto& reserved : reserved_names)
|
||||
std::array<uint8_t, 4> q;
|
||||
for (int i = 3; i >= 0; i--)
|
||||
{
|
||||
if (ends_with(name, reserved)) // subdomain foo.loki.loki
|
||||
return true;
|
||||
if (name == reserved.substr(1)) // loki.loki itself
|
||||
return true;
|
||||
pos = name.find('.');
|
||||
if (!llarp::parse_int(name.substr(0, pos), q[i]))
|
||||
return std::nullopt;
|
||||
name.remove_prefix(pos + 1);
|
||||
}
|
||||
return false;
|
||||
return net::ExpandV4(llarp::ipaddr_ipv4_bits(q[0], q[1], q[2], q[3]));
|
||||
}
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
if (numdots == 32 && name.size() == 64 && isV6)
|
||||
{
|
||||
// We're going to convert from nybbles a.b.c.d.e.f.0.1.2.3.[...] into hex string
|
||||
// "badcfe1032...", then decode the hex string to bytes.
|
||||
std::array<char, 32> in;
|
||||
auto in_pos = in.data();
|
||||
for (size_t i = 0; i < 64; i += 4)
|
||||
{
|
||||
if (not(oxenc::is_hex_digit(name[i]) and name[i + 1] == '.'
|
||||
and oxenc::is_hex_digit(name[i + 2]) and name[i + 3] == '.'))
|
||||
return std::nullopt;
|
||||
|
||||
// Flip the nybbles because the smallest one is first
|
||||
*in_pos++ = name[i + 2];
|
||||
*in_pos++ = name[i];
|
||||
}
|
||||
assert(in_pos == in.data() + in.size());
|
||||
huint128_t ip;
|
||||
static_assert(in.size() == 2 * sizeof(ip.h));
|
||||
// our string right now is the little endian representation, so load it as such on little
|
||||
// endian, or in reverse on big endian.
|
||||
if constexpr (oxenc::little_endian)
|
||||
oxenc::from_hex(in.begin(), in.end(), reinterpret_cast<uint8_t*>(&ip.h));
|
||||
else
|
||||
oxenc::from_hex(in.rbegin(), in.rend(), reinterpret_cast<uint8_t*>(&ip.h));
|
||||
|
||||
return ip;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool
|
||||
NameIsReserved(std::string_view name)
|
||||
{
|
||||
constexpr std::array reserved_names = {
|
||||
".snode.snode"sv, ".snode.loki"sv, ".loki.snode"sv, ".loki.loki"sv};
|
||||
|
||||
for (const auto& reserved : reserved_names)
|
||||
{
|
||||
if (ends_with(name, reserved)) // subdomain foo.loki.loki
|
||||
return true;
|
||||
if (name == reserved.substr(1)) // loki.loki itself
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,28 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <llarp/net/net_int.hpp>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
|
||||
/// the max size of one dns label in bytes.
|
||||
constexpr auto max_dns_label_size = 63;
|
||||
|
||||
/// return true if this tld is a lokinet tld.
|
||||
inline bool
|
||||
is_lokinet_tld(std::string_view str)
|
||||
{
|
||||
/// decode name from buffer; return nullopt on failure
|
||||
std::optional<std::string>
|
||||
DecodeName(llarp_buffer_t* buf, bool trimTrailingDot = false);
|
||||
constexpr std::array lokinet_tlds = {"loki"sv, "snode"sv};
|
||||
for (const auto& tld : lokinet_tlds)
|
||||
{
|
||||
if (string_iequal(tld, str))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// encode name to buffer
|
||||
bool
|
||||
EncodeNameTo(llarp_buffer_t* buf, std::string_view name);
|
||||
std::optional<huint128_t>
|
||||
DecodePTR(std::string_view name);
|
||||
|
||||
std::optional<huint128_t>
|
||||
DecodePTR(std::string_view name);
|
||||
bool
|
||||
NameIsReserved(std::string_view name);
|
||||
|
||||
bool
|
||||
NameIsReserved(std::string_view name);
|
||||
/// encode a series of dns labels into dns wire protocol.
|
||||
bstring_t
|
||||
encode_dns_labels(std::vector<std::string_view> labels);
|
||||
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
/// encode a series of dns labels into dns wire protocol.
|
||||
bstring_t
|
||||
encode_dns_labels(std::vector<std::string> labels);
|
||||
|
||||
/// encode a dns name in dotted form and encode it to dns wire protocol.
|
||||
bstring_t
|
||||
encode_dns_name(std::string_view name);
|
||||
|
||||
/// decode dns lables from dns wire protocol
|
||||
std::vector<std::string>
|
||||
decode_dns_labels(byte_view_t& buf);
|
||||
|
||||
/// decode dns lables from dns wire protocol, no copy version.
|
||||
std::vector<std::string_view>
|
||||
decode_dns_label_views(byte_view_t& buf);
|
||||
|
||||
/// named helper functiopn to split a dns name into labels.
|
||||
template <typename T>
|
||||
auto
|
||||
split_dns_name(const T& name)
|
||||
{
|
||||
return split(name, ".");
|
||||
}
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,129 +1,159 @@
|
|||
#include "question.hpp"
|
||||
#include "bits.hpp"
|
||||
#include "llarp/dns/name.hpp"
|
||||
#include <llarp/util/underlying.hpp>
|
||||
|
||||
#include <oxenc/endian.h>
|
||||
#include <llarp/util/logging.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include "dns.hpp"
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
|
||||
namespace llarp
|
||||
#include <oxen/log/format.hpp>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
Question::Question(std::vector<std::string_view> labels, RRType type)
|
||||
: qtype{to_underlying(type)}, qclass{bits::qclass_in}
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
Question::Question(Question&& other)
|
||||
: qname(std::move(other.qname))
|
||||
, qtype(std::move(other.qtype))
|
||||
, qclass(std::move(other.qclass))
|
||||
{}
|
||||
Question::Question(const Question& other)
|
||||
: qname(other.qname), qtype(other.qtype), qclass(other.qclass)
|
||||
{}
|
||||
|
||||
Question::Question(std::string name, QType_t type)
|
||||
: qname{std::move(name)}, qtype{type}, qclass{qClassIN}
|
||||
if (labels.empty())
|
||||
throw std::invalid_argument{"dns labels are empty"};
|
||||
bool prev_was_empty{labels.front().empty()};
|
||||
for (const auto& label : labels)
|
||||
{
|
||||
if (qname.empty())
|
||||
throw std::invalid_argument{"qname cannot be empty"};
|
||||
bool _empty = label.empty();
|
||||
|
||||
if (_empty and prev_was_empty)
|
||||
throw std::invalid_argument{"more than one empty label given in a row"};
|
||||
|
||||
prev_was_empty = _empty;
|
||||
|
||||
if (label.size() > max_dns_label_size)
|
||||
throw std::invalid_argument{
|
||||
fmt::format("dns label too big: {} > {}", label.size(), max_dns_label_size)};
|
||||
_qname_labels.emplace_back(label);
|
||||
}
|
||||
if (_qname_labels.back().empty())
|
||||
_qname_labels.pop_back();
|
||||
}
|
||||
|
||||
bstring_t
|
||||
Question::encode_dns() const
|
||||
{
|
||||
bstring_t ret(size_t{4}, uint8_t{});
|
||||
oxenc::write_host_as_big<uint16_t>(qtype, &ret[0]);
|
||||
oxenc::write_host_as_big<uint16_t>(bits::qclass_in, &ret[2]);
|
||||
return encode_dns_labels(_qname_labels) + ret;
|
||||
}
|
||||
|
||||
Question::Question(byte_view_t& str)
|
||||
{
|
||||
_qname_labels = decode_dns_labels(str);
|
||||
|
||||
if (str.size() < 4)
|
||||
throw std::invalid_argument{"data too small"};
|
||||
|
||||
qtype = oxenc::load_big_to_host<uint16_t>(&str[0]);
|
||||
qclass = oxenc::load_big_to_host<uint16_t>(&str[2]);
|
||||
|
||||
str = str.substr(4);
|
||||
}
|
||||
|
||||
bool
|
||||
Question::valid() const
|
||||
{
|
||||
return qclass == bits::qclass_in;
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
Question::to_json() const
|
||||
{
|
||||
return util::StatusObject{{"qname", qname()}, {"qtype", qtype}, {"qclass", qclass}};
|
||||
}
|
||||
|
||||
bool
|
||||
Question::IsName(const std::string& other) const
|
||||
{
|
||||
auto name = qname();
|
||||
return other == name or (other + ".") == name;
|
||||
}
|
||||
|
||||
bool
|
||||
Question::IsLocalhost() const
|
||||
{
|
||||
auto sz = _qname_labels.size();
|
||||
|
||||
return sz >= 2 and is_lokinet_tld(_qname_labels.back())
|
||||
and _qname_labels[sz - 2] == "localhost";
|
||||
}
|
||||
|
||||
bool
|
||||
Question::HasSubdomains() const
|
||||
{
|
||||
return _qname_labels.size() > 2;
|
||||
}
|
||||
|
||||
std::string
|
||||
Question::Subdomains() const
|
||||
{
|
||||
if (not HasSubdomains())
|
||||
return "";
|
||||
|
||||
auto it = _qname_labels.rbegin();
|
||||
++it;
|
||||
++it;
|
||||
|
||||
std::string ret{*it};
|
||||
++it;
|
||||
|
||||
for (auto itr = it; itr != _qname_labels.rend(); ++itr)
|
||||
{
|
||||
ret = fmt::format("{}.{}", *itr, ret);
|
||||
}
|
||||
|
||||
bool
|
||||
Question::Encode(llarp_buffer_t* buf) const
|
||||
{
|
||||
if (!EncodeNameTo(buf, qname))
|
||||
return false;
|
||||
if (!buf->put_uint16(qtype))
|
||||
return false;
|
||||
return buf->put_uint16(qclass);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
Question::Decode(llarp_buffer_t* buf)
|
||||
{
|
||||
if (auto name = DecodeName(buf))
|
||||
qname = *std::move(name);
|
||||
else
|
||||
{
|
||||
log::error(logcat, "failed to decode name");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint16(qtype))
|
||||
{
|
||||
log::error(logcat, "failed to decode type");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint16(qclass))
|
||||
{
|
||||
log::error(logcat, "failed to decode class");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::string
|
||||
Question::qname() const
|
||||
{
|
||||
return fmt::format("{}", fmt::join(_qname_labels, "."));
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
Question::ToJSON() const
|
||||
{
|
||||
return util::StatusObject{{"qname", qname}, {"qtype", qtype}, {"qclass", qclass}};
|
||||
}
|
||||
std::string_view
|
||||
Question::tld() const
|
||||
{
|
||||
if (_qname_labels.size() < 2)
|
||||
return "";
|
||||
return _qname_labels[_qname_labels.size() - 1];
|
||||
}
|
||||
|
||||
bool
|
||||
Question::IsName(const std::string& other) const
|
||||
{
|
||||
// does other have a . at the end?
|
||||
if (other.find_last_of('.') == (other.size() - 1))
|
||||
return other == qname;
|
||||
// no, add it and retry
|
||||
return IsName(other + ".");
|
||||
}
|
||||
std::string
|
||||
Question::Domain() const
|
||||
{
|
||||
auto itr = _qname_labels.rbegin();
|
||||
if (_qname_labels.size() > 1)
|
||||
++itr;
|
||||
return *itr;
|
||||
}
|
||||
|
||||
bool
|
||||
Question::IsLocalhost() const
|
||||
{
|
||||
return (qname == "localhost.loki." or llarp::ends_with(qname, ".localhost.loki."));
|
||||
}
|
||||
std::string
|
||||
Question::ToString() const
|
||||
{
|
||||
return fmt::format("[DNSQuestion qname={} qtype={:x} qclass={:x}]", qname(), qtype, qclass);
|
||||
}
|
||||
|
||||
bool
|
||||
Question::HasSubdomains() const
|
||||
{
|
||||
const auto parts = split(qname, ".", true);
|
||||
return parts.size() >= 3;
|
||||
}
|
||||
|
||||
std::string
|
||||
Question::Subdomains() const
|
||||
{
|
||||
if (qname.size() < 2)
|
||||
return "";
|
||||
|
||||
size_t pos;
|
||||
|
||||
pos = qname.rfind('.', qname.size() - 2);
|
||||
if (pos == std::string::npos or pos == 0)
|
||||
return "";
|
||||
|
||||
pos = qname.rfind('.', pos - 1);
|
||||
if (pos == std::string::npos or pos == 0)
|
||||
return "";
|
||||
|
||||
return qname.substr(0, pos);
|
||||
}
|
||||
|
||||
std::string
|
||||
Question::Name() const
|
||||
{
|
||||
return qname.substr(0, qname.find_last_of('.'));
|
||||
}
|
||||
|
||||
bool
|
||||
Question::HasTLD(const std::string& tld) const
|
||||
{
|
||||
return qname.find(tld) != std::string::npos
|
||||
&& qname.rfind(tld) == (qname.size() - tld.size()) - 1;
|
||||
}
|
||||
|
||||
std::string
|
||||
Question::ToString() const
|
||||
{
|
||||
return fmt::format("[DNSQuestion qname={} qtype={:x} qclass={:x}]", qname, qtype, qclass);
|
||||
}
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
std::vector<std::string_view>
|
||||
Question::view_dns_labels() const
|
||||
{
|
||||
std::vector<std::string_view> view;
|
||||
for (const auto& label : _qname_labels)
|
||||
view.emplace_back(label);
|
||||
return view;
|
||||
}
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,72 +1,99 @@
|
|||
#pragma once
|
||||
|
||||
#include "serialize.hpp"
|
||||
#include "name.hpp"
|
||||
#include "rr.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <llarp/net/net_int.hpp>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include <llarp/util/status.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
|
||||
struct Question
|
||||
{
|
||||
Question() = default;
|
||||
Question(const Question&) = default;
|
||||
Question(Question&&) = default;
|
||||
|
||||
Question&
|
||||
operator=(const Question&) = default;
|
||||
Question&
|
||||
operator=(Question&&) = default;
|
||||
|
||||
explicit Question(byte_view_t& raw);
|
||||
|
||||
Question(std::string name, RRType type) : Question{split_dns_name(name), type}
|
||||
{}
|
||||
|
||||
Question(std::vector<std::string_view> labels, RRType type);
|
||||
|
||||
/// encode to dns wire proto.
|
||||
bstring_t
|
||||
encode_dns() const;
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
|
||||
/// construct the full domain name including trailing dot.
|
||||
std::string
|
||||
qname() const;
|
||||
|
||||
/// determine if we match a name
|
||||
bool
|
||||
IsName(const std::string& other) const;
|
||||
|
||||
/// is the name [something.]localhost.loki. ?
|
||||
bool
|
||||
IsLocalhost() const;
|
||||
|
||||
/// return true if we have subdomains in ths question
|
||||
bool
|
||||
HasSubdomains() const;
|
||||
|
||||
/// get subdomain(s), if any, from qname
|
||||
std::string
|
||||
Subdomains() const;
|
||||
|
||||
/// get the dns label before the gtld, including the gtld and no trailing full stop.
|
||||
std::string
|
||||
Domain() const;
|
||||
|
||||
/// get the tld for the question's qname.
|
||||
std::string_view
|
||||
tld() const;
|
||||
|
||||
inline bool
|
||||
HasTLD(std::string_view _tld) const [[deprecated]]
|
||||
{
|
||||
return tld() == _tld;
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
to_json() const;
|
||||
|
||||
/// return true of this question has sane data.
|
||||
bool
|
||||
valid() const;
|
||||
|
||||
/// get a view over all the dns labels for this question.
|
||||
std::vector<std::string_view>
|
||||
view_dns_labels() const;
|
||||
|
||||
private:
|
||||
std::vector<std::string> _qname_labels;
|
||||
};
|
||||
} // namespace llarp::dns
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace dns
|
||||
{
|
||||
using QType_t = uint16_t;
|
||||
using QClass_t = uint16_t;
|
||||
|
||||
struct Question : public Serialize
|
||||
{
|
||||
Question() = default;
|
||||
|
||||
explicit Question(std::string name, QType_t type);
|
||||
|
||||
Question(Question&& other);
|
||||
Question(const Question& other);
|
||||
bool
|
||||
Encode(llarp_buffer_t* buf) const override;
|
||||
|
||||
bool
|
||||
Decode(llarp_buffer_t* buf) override;
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
|
||||
bool
|
||||
operator==(const Question& other) const
|
||||
{
|
||||
return qname == other.qname && qtype == other.qtype && qclass == other.qclass;
|
||||
}
|
||||
|
||||
std::string qname;
|
||||
QType_t qtype;
|
||||
QClass_t qclass;
|
||||
|
||||
/// determine if we match a name
|
||||
bool
|
||||
IsName(const std::string& other) const;
|
||||
|
||||
/// is the name [something.]localhost.loki. ?
|
||||
bool
|
||||
IsLocalhost() const;
|
||||
|
||||
/// return true if we have subdomains in ths question
|
||||
bool
|
||||
HasSubdomains() const;
|
||||
|
||||
/// get subdomain(s), if any, from qname
|
||||
std::string
|
||||
Subdomains() const;
|
||||
|
||||
/// return qname with no trailing .
|
||||
std::string
|
||||
Name() const;
|
||||
|
||||
/// determine if we are using this TLD
|
||||
bool
|
||||
HasTLD(const std::string& tld) const;
|
||||
|
||||
util::StatusObject
|
||||
ToJSON() const override;
|
||||
};
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
|
||||
template <>
|
||||
constexpr inline bool llarp::IsToStringFormattable<llarp::dns::Question> = true;
|
||||
template <>
|
||||
constexpr inline bool IsToStringFormattable<dns::Question> = true;
|
||||
}
|
||||
|
|
335
llarp/dns/rr.cpp
335
llarp/dns/rr.cpp
|
@ -1,125 +1,252 @@
|
|||
#include "rr.hpp"
|
||||
#include "dns.hpp"
|
||||
#include "bits.hpp"
|
||||
#include "llarp/dns/name.hpp"
|
||||
#include "llarp/dns/question.hpp"
|
||||
#include "llarp/util/time.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <llarp/util/formattable.hpp>
|
||||
#include <llarp/util/mem.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <llarp/util/logging.hpp>
|
||||
#include <llarp/util/underlying.hpp>
|
||||
|
||||
namespace llarp
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <oxenc/endian.h>
|
||||
#include <oxenc/hex.h>
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
namespace
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
ResourceRecord::ResourceRecord(const ResourceRecord& other)
|
||||
: rr_name(other.rr_name)
|
||||
, rr_type(other.rr_type)
|
||||
, rr_class(other.rr_class)
|
||||
, ttl(other.ttl)
|
||||
, rData(other.rData)
|
||||
{}
|
||||
|
||||
ResourceRecord::ResourceRecord(ResourceRecord&& other)
|
||||
: rr_name(std::move(other.rr_name))
|
||||
, rr_type(std::move(other.rr_type))
|
||||
, rr_class(std::move(other.rr_class))
|
||||
, ttl(std::move(other.ttl))
|
||||
, rData(std::move(other.rData))
|
||||
{}
|
||||
|
||||
ResourceRecord::ResourceRecord(std::string name, RRType_t type, RR_RData_t data)
|
||||
: rr_name{std::move(name)}
|
||||
, rr_type{type}
|
||||
, rr_class{qClassIN}
|
||||
, ttl{1}
|
||||
, rData{std::move(data)}
|
||||
{}
|
||||
|
||||
bool
|
||||
ResourceRecord::Encode(llarp_buffer_t* buf) const
|
||||
template <typename T>
|
||||
[[nodiscard]] auto
|
||||
encode_rdata(const T& rdata)
|
||||
{
|
||||
if (not EncodeNameTo(buf, rr_name))
|
||||
return false;
|
||||
if (!buf->put_uint16(rr_type))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!buf->put_uint16(rr_class))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!buf->put_uint32(ttl))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!EncodeRData(buf, rData))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
const std::size_t sz = rdata.size();
|
||||
if (sz > 65536)
|
||||
throw std::invalid_argument{"rdata too big: {} > {}"_format(sz, 65536)};
|
||||
uint16_t len{static_cast<uint16_t>(sz)};
|
||||
bstring_t vec;
|
||||
vec.resize(sz + 2);
|
||||
auto* ptr = vec.data();
|
||||
oxenc::write_host_as_big(len, ptr);
|
||||
ptr += 2;
|
||||
std::copy(rdata.begin(), rdata.end(), ptr);
|
||||
return vec;
|
||||
}
|
||||
|
||||
bool
|
||||
ResourceRecord::Decode(llarp_buffer_t* buf)
|
||||
[[nodiscard]] byte_view_t
|
||||
decode_rdata(byte_view_t& raw)
|
||||
{
|
||||
uint16_t discard;
|
||||
if (!buf->read_uint16(discard))
|
||||
return false;
|
||||
if (!buf->read_uint16(rr_type))
|
||||
{
|
||||
log::debug(logcat, "failed to decode rr type");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint16(rr_class))
|
||||
{
|
||||
log::debug(logcat, "failed to decode rr class");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint32(ttl))
|
||||
{
|
||||
log::debug(logcat, "failed to decode ttl");
|
||||
return false;
|
||||
}
|
||||
if (!DecodeRData(buf, rData))
|
||||
{
|
||||
log::debug(logcat, "failed to decode rr rdata {}", *this);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
if (raw.size() < 2)
|
||||
throw std::invalid_argument{
|
||||
("resource record data too small to be a resource record: {} < 2"_format(raw.size()))};
|
||||
size_t len = oxenc::load_big_to_host<uint16_t>(raw.data());
|
||||
raw = raw.substr(2);
|
||||
if (raw.size() < len)
|
||||
throw std::invalid_argument{
|
||||
"resource record data buffer too small: {} < {}"_format(raw.size(), len)};
|
||||
byte_view_t ret{raw.data(), len};
|
||||
raw = raw.substr(len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
ResourceRecord::ToJSON() const
|
||||
[[nodiscard]] byte_view_t
|
||||
decode_rdata(bstring_t&& raw)
|
||||
{
|
||||
return util::StatusObject{
|
||||
{"name", rr_name},
|
||||
{"type", rr_type},
|
||||
{"class", rr_class},
|
||||
{"ttl", ttl},
|
||||
{"rdata", std::string{reinterpret_cast<const char*>(rData.data()), rData.size()}}};
|
||||
byte_view_t view{raw};
|
||||
return decode_rdata(view);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string
|
||||
ResourceRecord::ToString() const
|
||||
ResourceRecord::ResourceRecord(std::string name, RRType type, RData data, uint32_t ttl)
|
||||
: rr_name_labels{}, rr_type{type}, rr_ttl{ttl}, rr_data{std::move(data)}
|
||||
{
|
||||
auto parts = split(name, ".");
|
||||
rr_name_labels.insert(rr_name_labels.cbegin(), parts.begin(), parts.end());
|
||||
}
|
||||
|
||||
bstring_t
|
||||
ResourceRecord::encode_dns() const
|
||||
{
|
||||
bstring_t ret;
|
||||
ret.resize(8);
|
||||
|
||||
auto ptr = ret.data();
|
||||
oxenc::write_host_as_big(static_cast<std::underlying_type_t<decltype(rr_type)>>(rr_type), ptr);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(bits::qclass_in, ptr);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(rr_ttl, ptr);
|
||||
ptr += 4;
|
||||
|
||||
ret += rr_data.data();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::optional<RRType>
|
||||
get_rr_type(uint16_t i)
|
||||
{
|
||||
const std::unordered_set<std::underlying_type_t<RRType>> mapping = {
|
||||
to_underlying(RRType::SRV),
|
||||
to_underlying(RRType::A),
|
||||
to_underlying(RRType::AAAA),
|
||||
to_underlying(RRType::CNAME),
|
||||
to_underlying(RRType::MX),
|
||||
to_underlying(RRType::NS),
|
||||
to_underlying(RRType::PTR),
|
||||
to_underlying(RRType::TXT)};
|
||||
if (mapping.count(i))
|
||||
return RRType{i};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string
|
||||
ToString(RRType t)
|
||||
{
|
||||
std::string ret;
|
||||
switch (t)
|
||||
{
|
||||
return fmt::format(
|
||||
"[RR name={} type={} class={} ttl={} rdata-size={}]",
|
||||
rr_name,
|
||||
rr_type,
|
||||
rr_class,
|
||||
ttl,
|
||||
rData.size());
|
||||
case RRType::SRV:
|
||||
ret = "SRV";
|
||||
break;
|
||||
case RRType::A:
|
||||
ret = "A";
|
||||
break;
|
||||
case RRType::AAAA:
|
||||
ret = "AAAA";
|
||||
break;
|
||||
case RRType::CNAME:
|
||||
ret = "CNAME";
|
||||
break;
|
||||
case RRType::MX:
|
||||
ret = "MX";
|
||||
break;
|
||||
case RRType::NS:
|
||||
ret = "NS";
|
||||
break;
|
||||
case RRType::PTR:
|
||||
ret = "PTR";
|
||||
break;
|
||||
case RRType::TXT:
|
||||
ret = "TXT";
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool
|
||||
ResourceRecord::HasCNameForTLD(const std::string& tld) const
|
||||
{
|
||||
if (rr_type != qTypeCNAME)
|
||||
return false;
|
||||
llarp_buffer_t buf(rData);
|
||||
if (auto name = DecodeName(&buf))
|
||||
return name->rfind(tld) == name->size() - tld.size() - 1;
|
||||
ResourceRecord::ResourceRecord(bstring_t&& raw)
|
||||
{
|
||||
if (raw.size() < 12)
|
||||
throw std::invalid_argument{"resource record too small: {} < {}"_format(raw.size(), 12)};
|
||||
auto t = oxenc::load_big_to_host<uint16_t>(raw.data());
|
||||
auto maybe_type = get_rr_type(t);
|
||||
if (not maybe_type)
|
||||
throw std::invalid_argument{"unknown RR type: {:x}"_format(t)};
|
||||
|
||||
rr_type = *maybe_type;
|
||||
|
||||
raw = raw.substr(2);
|
||||
if (auto qc = oxenc::load_big_to_host<uint16_t>(raw.data()); qc != bits::qclass_in)
|
||||
throw std::invalid_argument{
|
||||
"resource record does not have qclass IN, has {:x} instead"_format(qc)};
|
||||
|
||||
raw = raw.substr(2);
|
||||
rr_ttl = oxenc::load_big_to_host<uint32_t>(raw.data());
|
||||
raw = raw.substr(4);
|
||||
|
||||
rr_data = RData{std::move(raw)};
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
ResourceRecord::to_json() const
|
||||
{
|
||||
return util::StatusObject{
|
||||
{"name", "{{}}"_format(fmt::join(rr_name_labels, "."))},
|
||||
{"type", rr_type},
|
||||
{"ttl", rr_ttl},
|
||||
{"rr_data", rr_data.to_json()}};
|
||||
}
|
||||
|
||||
std::string
|
||||
ResourceRecord::ToString() const
|
||||
{
|
||||
return fmt::format(
|
||||
"[RR name={{}} type={} ttl={} rr_data={}]",
|
||||
fmt::join(rr_name_labels, "."),
|
||||
rr_type,
|
||||
rr_ttl,
|
||||
rr_data);
|
||||
}
|
||||
|
||||
bool
|
||||
ResourceRecord::HasCNameForTLD(const std::string& tld) const
|
||||
{
|
||||
if (rr_type != RRType::CNAME)
|
||||
return false;
|
||||
}
|
||||
if (auto labels = rr_data.view_dns_labels(); labels.size() > 1)
|
||||
return labels[labels.size() - 1] == tld;
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
RData::RData(std::vector<std::string_view> dns_labels)
|
||||
: _raw{encode_rdata(encode_dns_labels(dns_labels))}
|
||||
{}
|
||||
|
||||
RData::RData(net::ipv4addr_t ip) : _raw{}
|
||||
{
|
||||
_raw.resize(4);
|
||||
std::memcpy(_raw.data(), &ip.n, 4);
|
||||
}
|
||||
|
||||
RData::RData(net::ipv6addr_t ip) : _raw{}
|
||||
{
|
||||
_raw.resize(16);
|
||||
std::memcpy(_raw.data(), &ip.n, 16);
|
||||
}
|
||||
|
||||
RData::RData(bstring_t&& raw) : _raw{raw}
|
||||
{}
|
||||
|
||||
RData::RData(uint16_t priority, std::vector<std::string_view> dns_labels)
|
||||
{
|
||||
_raw.resize(2);
|
||||
oxenc::write_host_as_big(priority, _raw.data());
|
||||
_raw += encode_dns_labels(dns_labels);
|
||||
}
|
||||
|
||||
nlohmann::json
|
||||
RData::to_json() const
|
||||
{
|
||||
return {{"rdata_hex", oxenc::to_hex(_raw.begin(), _raw.end())}};
|
||||
}
|
||||
|
||||
std::vector<std::string_view>
|
||||
RData::view_dns_labels() const
|
||||
{
|
||||
byte_view_t view{_raw};
|
||||
return decode_dns_label_views(view);
|
||||
}
|
||||
|
||||
std::string
|
||||
RData::ToString() const
|
||||
{
|
||||
return fmt::format("[RData hex={}]", oxenc::to_hex(_raw.begin(), _raw.end()));
|
||||
}
|
||||
|
||||
ResourceRecord::ResourceRecord(byte_view_t& raw) : ResourceRecord{bstring_t{decode_rdata(raw)}}
|
||||
{}
|
||||
} // namespace llarp::dns
|
||||
|
|
147
llarp/dns/rr.hpp
147
llarp/dns/rr.hpp
|
@ -1,52 +1,139 @@
|
|||
#pragma once
|
||||
|
||||
#include "name.hpp"
|
||||
#include "serialize.hpp"
|
||||
#include <llarp/net/net_int.hpp>
|
||||
#include <llarp/util/status.hpp>
|
||||
#include <llarp/util/underlying.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
|
||||
/// resource record types enum.
|
||||
enum class RRType : uint16_t
|
||||
{
|
||||
using RRClass_t = uint16_t;
|
||||
using RRType_t = uint16_t;
|
||||
using RR_RData_t = std::vector<byte_t>;
|
||||
using RR_TTL_t = uint32_t;
|
||||
SRV = 33,
|
||||
AAAA = 28,
|
||||
TXT = 16,
|
||||
MX = 15,
|
||||
PTR = 12,
|
||||
CNAME = 5,
|
||||
NS = 2,
|
||||
A = 1
|
||||
};
|
||||
|
||||
struct ResourceRecord : public Serialize
|
||||
std::optional<RRType>
|
||||
get_rr_type(uint16_t i);
|
||||
|
||||
std::string
|
||||
ToString(RRType t);
|
||||
|
||||
class RData
|
||||
{
|
||||
bstring_t _raw;
|
||||
|
||||
public:
|
||||
RData() = default;
|
||||
RData(const RData&) = default;
|
||||
RData(RData&&) = default;
|
||||
|
||||
RData&
|
||||
operator=(const RData&) = default;
|
||||
RData&
|
||||
operator=(RData&&) = default;
|
||||
|
||||
/// construct from view, modifies view to contain remaining unread data.
|
||||
explicit RData(byte_view_t& view);
|
||||
|
||||
/// construct rdata holding dns labels.
|
||||
explicit RData(std::vector<std::string_view> dns_labels);
|
||||
/// construct holding ipv4 address
|
||||
explicit RData(net::ipv4addr_t ip);
|
||||
/// construct holding ipv6 address
|
||||
explicit RData(net::ipv6addr_t ip);
|
||||
|
||||
/// mx record constructor.
|
||||
explicit RData(uint16_t priority, std::vector<std::string_view> dns_labels);
|
||||
|
||||
/// construct from raw buffer.
|
||||
explicit RData(bstring_t&& raw);
|
||||
|
||||
[[nodiscard]] constexpr const auto&
|
||||
data() const
|
||||
{
|
||||
ResourceRecord() = default;
|
||||
ResourceRecord(const ResourceRecord& other);
|
||||
ResourceRecord(ResourceRecord&& other);
|
||||
return _raw;
|
||||
}
|
||||
|
||||
explicit ResourceRecord(std::string name, RRType_t type, RR_RData_t rdata);
|
||||
std::vector<std::string_view>
|
||||
view_dns_labels() const;
|
||||
|
||||
bool
|
||||
Encode(llarp_buffer_t* buf) const override;
|
||||
inline std::string
|
||||
as_dns_name() const
|
||||
{
|
||||
std::string name;
|
||||
|
||||
bool
|
||||
Decode(llarp_buffer_t* buf) override;
|
||||
for (auto part : view_dns_labels())
|
||||
name += fmt::format("{}.", part);
|
||||
|
||||
util::StatusObject
|
||||
ToJSON() const override;
|
||||
return name.substr(0, name.size() - 2);
|
||||
}
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
util::StatusObject
|
||||
to_json() const;
|
||||
|
||||
bool
|
||||
HasCNameForTLD(const std::string& tld) const;
|
||||
std::string
|
||||
ToString() const;
|
||||
};
|
||||
|
||||
std::string rr_name;
|
||||
RRType_t rr_type;
|
||||
RRClass_t rr_class;
|
||||
RR_TTL_t ttl;
|
||||
RR_RData_t rData;
|
||||
};
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
class ResourceRecord
|
||||
{
|
||||
explicit ResourceRecord(bstring_t&&);
|
||||
|
||||
public:
|
||||
ResourceRecord() = default;
|
||||
|
||||
ResourceRecord(std::string name, RRType type, RData rdata, uint32_t ttl);
|
||||
|
||||
/// construct from raw bytes, modifies raw.
|
||||
explicit ResourceRecord(byte_view_t& raw);
|
||||
|
||||
bstring_t
|
||||
encode_dns() const;
|
||||
|
||||
util::StatusObject
|
||||
to_json() const;
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
|
||||
bool
|
||||
HasCNameForTLD(const std::string& tld) const;
|
||||
|
||||
std::vector<std::string> rr_name_labels;
|
||||
RRType rr_type;
|
||||
uint32_t rr_ttl;
|
||||
RData rr_data;
|
||||
};
|
||||
|
||||
[[deprecated]] constexpr auto qTypePTR = to_underlying(RRType::PTR);
|
||||
[[deprecated]] constexpr auto qTypeA = to_underlying(RRType::A);
|
||||
[[deprecated]] constexpr auto qTypeAAAA = to_underlying(RRType::AAAA);
|
||||
[[deprecated]] constexpr auto qTypeSRV = to_underlying(RRType::SRV);
|
||||
[[deprecated]] constexpr auto qTypeCNAME = to_underlying(RRType::CNAME);
|
||||
[[deprecated]] constexpr auto qTypeMX = to_underlying(RRType::MX);
|
||||
[[deprecated]] constexpr auto qTypeNS = to_underlying(RRType::NS);
|
||||
} // namespace llarp::dns
|
||||
|
||||
template <>
|
||||
constexpr inline bool llarp::IsToStringFormattable<llarp::dns::ResourceRecord> = true;
|
||||
|
||||
template <>
|
||||
constexpr inline bool llarp::IsToStringFormattable<llarp::dns::RData> = true;
|
||||
|
||||
template <>
|
||||
constexpr inline bool llarp::IsToStringFormattable<llarp::dns::RRType> = true;
|
||||
|
|
|
@ -1,44 +1,37 @@
|
|||
#include "serialize.hpp"
|
||||
#include <oxenc/endian.h>
|
||||
#include <cstdint>
|
||||
#include <llarp/net/net_int.hpp>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include "llarp/dns/rr.hpp"
|
||||
#include "llarp/util/str.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace dns
|
||||
|
||||
dns::RR_RData_t
|
||||
encode_rdata(bstring_view rdata)
|
||||
{
|
||||
Serialize::~Serialize() = default;
|
||||
if (rdata.size() > 65536)
|
||||
throw std::invalid_argument{"rdata too big: {} > {}"_format(rdata.size(), 65536)};
|
||||
uint16_t len{static_cast<uint16_t>(rdata.size())};
|
||||
dns::RR_RData_t vec(rdata.size() + 2);
|
||||
oxenc::write_host_as_big(len, vec.data());
|
||||
std::copy_n(rdata.data(), rdata.size(), vec.data() + 2);
|
||||
return vec;
|
||||
}
|
||||
|
||||
bool
|
||||
EncodeRData(llarp_buffer_t* buf, const std::vector<byte_t>& v)
|
||||
{
|
||||
if (v.size() > 65536)
|
||||
return false;
|
||||
uint16_t len = v.size();
|
||||
if (!buf->put_uint16(len))
|
||||
return false;
|
||||
if (buf->size_left() < len)
|
||||
return false;
|
||||
memcpy(buf->cur, v.data(), len);
|
||||
buf->cur += len;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
DecodeRData(llarp_buffer_t* buf, std::vector<byte_t>& v)
|
||||
{
|
||||
uint16_t len;
|
||||
if (!buf->read_uint16(len))
|
||||
return false;
|
||||
size_t left = buf->size_left();
|
||||
if (left < len)
|
||||
return false;
|
||||
v.resize(size_t(len));
|
||||
if (len)
|
||||
{
|
||||
memcpy(v.data(), buf->cur, len);
|
||||
buf->cur += len;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dns
|
||||
std::optional<bstring_view>
|
||||
maybe_decode_rdata(const RR_RData_t& vec)
|
||||
{
|
||||
if (vec.size() < 2)
|
||||
return std::nullopt;
|
||||
auto len = oxenc::load_big_to_host<uint16_t>(vec.data());
|
||||
if (vec.size() - 2 < len)
|
||||
return std::nullopt;
|
||||
return bstring_view{vec.data() + 2, size_t{len}};
|
||||
}
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
|
|
|
@ -3,34 +3,5 @@
|
|||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/status.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace dns
|
||||
{
|
||||
/// base type for serializable dns entities
|
||||
struct Serialize
|
||||
{
|
||||
virtual ~Serialize() = 0;
|
||||
|
||||
/// encode entity to buffer
|
||||
virtual bool
|
||||
Encode(llarp_buffer_t* buf) const = 0;
|
||||
|
||||
/// decode entity from buffer
|
||||
virtual bool
|
||||
Decode(llarp_buffer_t* buf) = 0;
|
||||
|
||||
/// convert this whatever into json
|
||||
virtual util::StatusObject
|
||||
ToJSON() const = 0;
|
||||
};
|
||||
|
||||
bool
|
||||
EncodeRData(llarp_buffer_t* buf, const std::vector<byte_t>& rdata);
|
||||
|
||||
bool
|
||||
DecodeRData(llarp_buffer_t* buf, std::vector<byte_t>& rdata);
|
||||
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
#include "llarp/util/str.hpp"
|
||||
#include "llarp/util/types.hpp"
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
#include "server.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
#include <llarp/constants/apple.hpp>
|
||||
#include "dns.hpp"
|
||||
#include <iterator>
|
||||
#include <llarp/crypto/crypto.hpp>
|
||||
#include <array>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <llarp/ev/udp_handle.hpp>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <unbound.h>
|
||||
#include <uvw.hpp>
|
||||
#include <vector>
|
||||
|
||||
#include "llarp/dns/bits.hpp"
|
||||
#include "llarp/dns/message.hpp"
|
||||
#include "llarp/dns/name.hpp"
|
||||
#include "llarp/net/sock_addr.hpp"
|
||||
#include "llarp/util/buffer.hpp"
|
||||
#include "llarp/util/str.hpp"
|
||||
#include "oxen/log.hpp"
|
||||
#include "sd_platform.hpp"
|
||||
#include "nm_platform.hpp"
|
||||
|
@ -25,36 +35,47 @@ namespace llarp::dns
|
|||
QueryJob_Base::Cancel()
|
||||
{
|
||||
Message reply{m_Query};
|
||||
reply.AddServFail();
|
||||
reply.hdr.rcode(bits::rcode_servfail);
|
||||
SendReply(reply.ToBuffer());
|
||||
}
|
||||
|
||||
QueryJob::QueryJob(
|
||||
std::shared_ptr<PacketSource_Base> source, Message query, SockAddr to_, SockAddr from_)
|
||||
: QueryJob_Base{query}
|
||||
, src{std::move(source)}
|
||||
, resolver{std::move(to_)}
|
||||
, asker{std::move(from_)}
|
||||
|
||||
{}
|
||||
|
||||
void
|
||||
QueryJob::SendReply(OwnedBuffer buf)
|
||||
{
|
||||
src->SendTo(asker, resolver, std::move(buf));
|
||||
}
|
||||
|
||||
/// sucks up udp packets from a bound socket and feeds it to a server
|
||||
class UDPReader : public PacketSource_Base, public std::enable_shared_from_this<UDPReader>
|
||||
{
|
||||
Server& m_DNS;
|
||||
std::shared_ptr<llarp::UDPHandle> m_udp;
|
||||
SockAddr m_LocalAddr;
|
||||
|
||||
public:
|
||||
explicit UDPReader(Server& dns, const EventLoop_ptr& loop, llarp::SockAddr bindaddr)
|
||||
: m_DNS{dns}
|
||||
{
|
||||
m_udp = loop->make_udp([&](auto&, SockAddr src, llarp::OwnedBuffer buf) {
|
||||
if (src == m_LocalAddr)
|
||||
return;
|
||||
if (not m_DNS.MaybeHandlePacket(shared_from_this(), m_LocalAddr, src, std::move(buf)))
|
||||
{
|
||||
log::warning(logcat, "did not handle dns packet from {} to {}", src, m_LocalAddr);
|
||||
}
|
||||
});
|
||||
m_udp->listen(bindaddr);
|
||||
if (auto maybe_addr = BoundOn())
|
||||
{
|
||||
m_LocalAddr = *maybe_addr;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error{"cannot find which address our dns socket is bound on"};
|
||||
m_udp = loop->make_udp(
|
||||
[&](auto&, SockAddr src, llarp::OwnedBuffer buf) {
|
||||
auto laddr = m_udp->LocalAddr();
|
||||
|
||||
if (src == laddr)
|
||||
return;
|
||||
if (not m_DNS.MaybeHandlePacket(shared_from_this(), laddr, src, std::move(buf)))
|
||||
{
|
||||
log::warning(logcat, "did not handle dns packet from {} to {}", src, laddr);
|
||||
}
|
||||
},
|
||||
bindaddr);
|
||||
}
|
||||
|
||||
std::optional<SockAddr>
|
||||
|
@ -66,7 +87,7 @@ namespace llarp::dns
|
|||
bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr&) const override
|
||||
{
|
||||
return to != m_LocalAddr;
|
||||
return to != m_udp->LocalAddr();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -153,8 +174,9 @@ namespace llarp::dns
|
|||
auto* query = static_cast<Query*>(data);
|
||||
if (err)
|
||||
{
|
||||
// some kind of error from upstream
|
||||
// we cant do anything about this kind of failure so cancel query.
|
||||
log::warning(logcat, "Upstream DNS failure: {}", ub_strerror(err));
|
||||
// todo: handle exceptions.
|
||||
query->Cancel();
|
||||
return;
|
||||
}
|
||||
|
@ -162,16 +184,20 @@ namespace llarp::dns
|
|||
log::trace(logcat, "queueing dns response from libunbound to userland");
|
||||
|
||||
// rewrite response
|
||||
OwnedBuffer pkt{(const byte_t*)result->answer_packet, (size_t)result->answer_len};
|
||||
llarp_buffer_t buf{pkt};
|
||||
MessageHeader hdr;
|
||||
hdr.Decode(&buf);
|
||||
hdr.id = query->Underlying().hdr_id;
|
||||
buf.cur = buf.base;
|
||||
hdr.Encode(&buf);
|
||||
|
||||
// send reply
|
||||
query->SendReply(std::move(pkt));
|
||||
try
|
||||
{
|
||||
OwnedBuffer pkt{(const byte_t*)result->answer_packet, (size_t)result->answer_len};
|
||||
auto view = pkt.view();
|
||||
MessageHeader hdr{view};
|
||||
hdr.id = query->Underlying().hdr.id;
|
||||
auto hdr_buff = hdr.encode_dns();
|
||||
std::copy_n(hdr_buff.data(), hdr_buff.size(), pkt.buf.get());
|
||||
query->SendReply(std::move(pkt));
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
log::error(logcat, "failed to rewrite response: {}", ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -488,7 +514,7 @@ namespace llarp::dns
|
|||
for (const auto& q : query.questions)
|
||||
{
|
||||
// dont process .loki or .snode
|
||||
if (q.HasTLD(".loki") or q.HasTLD(".snode"))
|
||||
if (is_lokinet_tld(q.tld()))
|
||||
{
|
||||
log::warning(
|
||||
logcat,
|
||||
|
@ -530,7 +556,7 @@ namespace llarp::dns
|
|||
const auto& q = query.questions[0];
|
||||
if (auto err = ub_resolve_async(
|
||||
m_ctx,
|
||||
q.Name().c_str(),
|
||||
q.qname().c_str(),
|
||||
q.qtype,
|
||||
q.qclass,
|
||||
tmp.get(),
|
||||
|
@ -577,11 +603,11 @@ namespace llarp::dns
|
|||
}
|
||||
} // namespace libunbound
|
||||
|
||||
Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif)
|
||||
: m_Loop{std::move(loop)}
|
||||
Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf)
|
||||
: m_netif_index{}
|
||||
, m_Loop{std::move(loop)}
|
||||
, m_Config{std::move(conf)}
|
||||
, m_Platform{CreatePlatform()}
|
||||
, m_NetIfIndex{std::move(netif)}
|
||||
{}
|
||||
|
||||
std::vector<std::weak_ptr<Resolver_Base>>
|
||||
|
@ -591,8 +617,10 @@ namespace llarp::dns
|
|||
}
|
||||
|
||||
void
|
||||
Server::Start()
|
||||
Server::Start(unsigned int index)
|
||||
{
|
||||
m_netif_index = index;
|
||||
log::debug(logcat, "starting dns server for if_index={}", m_netif_index);
|
||||
// set up udp sockets
|
||||
for (const auto& addr : m_Config.m_bind)
|
||||
{
|
||||
|
@ -602,7 +630,12 @@ namespace llarp::dns
|
|||
|
||||
// add default resolver as needed
|
||||
if (auto ptr = MakeDefaultResolver())
|
||||
{
|
||||
log::debug(logcat, "adding default resolver: '{}'", ptr->ResolverName());
|
||||
AddResolver(ptr);
|
||||
}
|
||||
else
|
||||
log::debug(logcat, "no default resolver made");
|
||||
}
|
||||
|
||||
std::shared_ptr<I_Platform>
|
||||
|
@ -620,6 +653,7 @@ namespace llarp::dns
|
|||
std::shared_ptr<PacketSource_Base>
|
||||
Server::MakePacketSourceOn(const llarp::SockAddr& addr, const llarp::DnsConfig&)
|
||||
{
|
||||
log::debug(logcat, "make udp dns socket on {}", addr);
|
||||
return std::make_shared<UDPReader>(*this, m_Loop, addr);
|
||||
}
|
||||
|
||||
|
@ -685,6 +719,12 @@ namespace llarp::dns
|
|||
void
|
||||
Server::AddPacketSource(std::shared_ptr<PacketSource_Base> pkt)
|
||||
{
|
||||
std::optional<std::string> maybe_addr;
|
||||
if (auto maybe_sockaddr = pkt->BoundOn())
|
||||
maybe_addr = fmt::format("bound to {}", maybe_sockaddr->ToString());
|
||||
|
||||
log::debug(logcat, "adding owned packet source {}", maybe_addr.value_or("not bound to socket"));
|
||||
|
||||
AddPacketSource(std::weak_ptr<PacketSource_Base>{pkt});
|
||||
m_OwnedPacketSources.push_back(std::move(pkt));
|
||||
}
|
||||
|
@ -713,7 +753,7 @@ namespace llarp::dns
|
|||
Server::SetDNSMode(bool all_queries)
|
||||
{
|
||||
if (auto maybe_addr = FirstBoundPacketSourceAddr())
|
||||
m_Platform->set_resolver(m_NetIfIndex, *maybe_addr, all_queries);
|
||||
m_Platform->set_resolver(m_netif_index, *maybe_addr, all_queries);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -730,45 +770,50 @@ namespace llarp::dns
|
|||
return false;
|
||||
}
|
||||
|
||||
auto maybe = MaybeParseDNSMessage(buf);
|
||||
if (not maybe)
|
||||
try
|
||||
{
|
||||
log::warning(logcat, "invalid dns message format from {} to dns listener on {}", from, to);
|
||||
return false;
|
||||
}
|
||||
auto view = buf.view();
|
||||
Message msg{view};
|
||||
|
||||
auto& msg = *maybe;
|
||||
// we don't provide a DoH resolver because it requires verified TLS
|
||||
// TLS needs X509/ASN.1-DER and opting into the Root CA Cabal
|
||||
// thankfully mozilla added a backdoor that allows ISPs to turn it off
|
||||
// so we disable DoH for firefox using mozilla's ISP backdoor
|
||||
// see: https://github.com/oxen-io/lokinet/issues/832
|
||||
for (const auto& q : msg.questions)
|
||||
{
|
||||
// is this firefox looking for their backdoor record?
|
||||
if (q.IsName("use-application-dns.net"))
|
||||
// we don't provide a DoH resolver because it requires verified TLS
|
||||
// TLS needs X509/ASN.1-DER and opting into the Root CA Cabal
|
||||
// thankfully mozilla added a backdoor that allows ISPs to turn it off
|
||||
// so we disable DoH for firefox using mozilla's ISP backdoor
|
||||
// see: https://github.com/oxen-io/lokinet/issues/832
|
||||
for (const auto& q : msg.questions)
|
||||
{
|
||||
// yea it is, let's turn off DoH because god is dead.
|
||||
msg.AddNXReply();
|
||||
// press F to pay respects and send it back where it came from
|
||||
ptr->SendTo(from, to, msg.ToBuffer());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
if (auto res_ptr = resolver.lock())
|
||||
{
|
||||
log::trace(
|
||||
logcat, "check resolver {} for dns from {} to {}", res_ptr->ResolverName(), from, to);
|
||||
if (res_ptr->MaybeHookDNS(ptr, msg, to, from))
|
||||
// is this firefox looking for their backdoor record?
|
||||
if (q.IsName("use-application-dns.net"))
|
||||
{
|
||||
log::trace(
|
||||
logcat, "resolver {} handling dns from {} to {}", res_ptr->ResolverName(), from, to);
|
||||
// yea it is, let's turn off DoH because god is dead.
|
||||
// press F to pay respects and send it back where it came from
|
||||
ptr->SendTo(from, to, msg.nx().ToBuffer());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
if (auto res_ptr = resolver.lock())
|
||||
{
|
||||
log::trace(
|
||||
logcat, "check resolver {} for dns from {} to {}", res_ptr->ResolverName(), from, to);
|
||||
if (res_ptr->MaybeHookDNS(ptr, msg, to, from))
|
||||
{
|
||||
log::trace(
|
||||
logcat,
|
||||
"resolver {} handling dns from {} to {}",
|
||||
res_ptr->ResolverName(),
|
||||
from,
|
||||
to);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
log::error(logcat, "failed to handle dns packet: {}", ex.what());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "llarp/net/sock_addr.hpp"
|
||||
#include "message.hpp"
|
||||
#include "platform.hpp"
|
||||
#include <llarp/config/config.hpp>
|
||||
|
@ -124,19 +125,11 @@ namespace llarp::dns
|
|||
const SockAddr asker;
|
||||
|
||||
public:
|
||||
explicit QueryJob(
|
||||
std::shared_ptr<PacketSource_Base> source,
|
||||
const Message& query,
|
||||
const SockAddr& to_,
|
||||
const SockAddr& from_)
|
||||
: QueryJob_Base{query}, src{source}, resolver{to_}, asker{from_}
|
||||
{}
|
||||
QueryJob(
|
||||
std::shared_ptr<PacketSource_Base> source, Message query, SockAddr to_, SockAddr from_);
|
||||
|
||||
void
|
||||
SendReply(llarp::OwnedBuffer replyBuf) override
|
||||
{
|
||||
src->SendTo(asker, resolver, std::move(replyBuf));
|
||||
}
|
||||
SendReply(llarp::OwnedBuffer replyBuf) override;
|
||||
};
|
||||
|
||||
/// handler of dns query hooking
|
||||
|
@ -214,10 +207,12 @@ namespace llarp::dns
|
|||
virtual std::shared_ptr<I_Platform>
|
||||
CreatePlatform() const;
|
||||
|
||||
unsigned int m_netif_index;
|
||||
|
||||
public:
|
||||
virtual ~Server() = default;
|
||||
|
||||
explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif_index);
|
||||
Server(EventLoop_ptr loop, llarp::DnsConfig conf);
|
||||
|
||||
/// returns all sockaddr we have from all of our PacketSources
|
||||
std::vector<SockAddr>
|
||||
|
@ -239,9 +234,10 @@ namespace llarp::dns
|
|||
virtual std::shared_ptr<PacketSource_Base>
|
||||
MakePacketSourceOn(const SockAddr& bindaddr, const llarp::DnsConfig& conf);
|
||||
|
||||
/// sets up all internal binds and such and begins operation
|
||||
/// sets up all internal binds and such and begins operation.
|
||||
/// we pass in the network interface index of the tun interface we are associated with here.
|
||||
virtual void
|
||||
Start();
|
||||
Start(unsigned int index);
|
||||
|
||||
/// stops all operation
|
||||
virtual void
|
||||
|
@ -278,7 +274,6 @@ namespace llarp::dns
|
|||
std::shared_ptr<I_Platform> m_Platform;
|
||||
|
||||
private:
|
||||
const unsigned int m_NetIfIndex;
|
||||
std::set<std::shared_ptr<Resolver_Base>, ComparePtr<std::shared_ptr<Resolver_Base>>>
|
||||
m_OwnedResolvers;
|
||||
std::set<std::weak_ptr<Resolver_Base>, CompareWeakPtr<Resolver_Base>> m_Resolvers;
|
||||
|
|
|
@ -1,11 +1,20 @@
|
|||
#include "srv_data.hpp"
|
||||
#include <cstdint>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <llarp/util/logging.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include <oxenc/bt_serialize.h>
|
||||
#include <oxenc/endian.h>
|
||||
#include "llarp/dns/name.hpp"
|
||||
#include "llarp/dns/question.hpp"
|
||||
#include "llarp/util/bencode.h"
|
||||
#include "llarp/util/buffer.hpp"
|
||||
#include "llarp/util/types.hpp"
|
||||
|
||||
namespace llarp::dns
|
||||
|
@ -13,105 +22,104 @@ namespace llarp::dns
|
|||
static auto logcat = log::Cat("dns");
|
||||
|
||||
bool
|
||||
SRVData::IsValid() const
|
||||
SRVData::valid() const
|
||||
{
|
||||
// if target is of first two forms outlined above
|
||||
if (target == "." or target.size() == 0)
|
||||
{
|
||||
if (empty_target() or target_full_stop())
|
||||
return true;
|
||||
}
|
||||
|
||||
// check target size is not absurd
|
||||
if (target.size() > TARGET_MAX_SIZE)
|
||||
{
|
||||
log::warning(logcat, "SRVData target larger than max size ({})", TARGET_MAX_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// does target end in .loki?
|
||||
size_t pos = target.find(".loki");
|
||||
if (pos != std::string::npos && pos == (target.size() - 5))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
for (auto suffix : {".loki"sv, ".loki."sv, ".snode"sv, ".snode."sv})
|
||||
if (ends_with(target, suffix))
|
||||
return true;
|
||||
|
||||
// does target end in .snode?
|
||||
pos = target.find(".snode");
|
||||
if (pos != std::string::npos && pos == (target.size() - 6))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// if we're here, target is invalid
|
||||
log::warning(logcat, "SRVData invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
SRVTuple
|
||||
SRVData::toTuple() const
|
||||
SRVData::SRVData(SRVTuple tuple_) : _tuple{std::move(tuple_)}
|
||||
{}
|
||||
|
||||
SRVData::SRVData(std::string_view str) : SRVData{}
|
||||
{
|
||||
return std::make_tuple(service_proto, priority, weight, port, target);
|
||||
from_string(std::move(str));
|
||||
}
|
||||
|
||||
SRVData
|
||||
SRVData::fromTuple(SRVTuple tuple)
|
||||
void
|
||||
SRVData::from_string(std::string_view str)
|
||||
{
|
||||
SRVData s;
|
||||
|
||||
std::tie(s.service_proto, s.priority, s.weight, s.port, s.target) = std::move(tuple);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool
|
||||
SRVData::fromString(std::string_view srvString)
|
||||
{
|
||||
log::debug(logcat, "SRVData::fromString(\"{}\")", srvString);
|
||||
log::debug(logcat, "SRVData::fromString(\"{}\")", str);
|
||||
|
||||
// split on spaces, discard trailing empty strings
|
||||
auto splits = split(srvString, " ", false);
|
||||
auto splits = split(str, " ", false);
|
||||
|
||||
if (splits.size() != 5 && splits.size() != 4)
|
||||
{
|
||||
log::warning(logcat, "SRV record should have either 4 or 5 space-separated parts");
|
||||
return false;
|
||||
}
|
||||
throw std::invalid_argument{"SRV record should have either 4 or 5 space-separated parts"};
|
||||
|
||||
service_proto = splits[0];
|
||||
|
||||
if (not parse_int(splits[1], priority))
|
||||
{
|
||||
log::warning(logcat, "SRV record failed to parse \"{}\" as uint16_t (priority)", splits[1]);
|
||||
return false;
|
||||
}
|
||||
throw std::invalid_argument{
|
||||
"SRV record failed to parse \"{}\" as uint16_t (priority)"_format(splits[1])};
|
||||
|
||||
if (not parse_int(splits[2], weight))
|
||||
{
|
||||
log::warning(logcat, "SRV record failed to parse \"{}\" as uint16_t (weight)", splits[2]);
|
||||
return false;
|
||||
}
|
||||
throw std::invalid_argument{
|
||||
"SRV record failed to parse \"{}\" as uint16_t (weight)"_format(splits[2])};
|
||||
|
||||
if (not parse_int(splits[3], port))
|
||||
{
|
||||
log::warning(logcat, "SRV record failed to parse \"{}\" as uint16_t (port)", splits[3]);
|
||||
return false;
|
||||
}
|
||||
throw std::invalid_argument{
|
||||
"SRV record failed to parse \"{}\" as uint16_t (port)"_format(splits[3])};
|
||||
|
||||
if (splits.size() == 5)
|
||||
target = splits[4];
|
||||
else
|
||||
target = "";
|
||||
target = "@";
|
||||
|
||||
return IsValid();
|
||||
if (not valid())
|
||||
throw std::invalid_argument{"srv record is invalid"};
|
||||
}
|
||||
|
||||
bool
|
||||
SRVData::BEncode(llarp_buffer_t* buf) const
|
||||
{
|
||||
const std::string data = oxenc::bt_serialize(toTuple());
|
||||
auto data = bt_serizalize();
|
||||
return buf->write(data.begin(), data.end());
|
||||
}
|
||||
|
||||
bstring_t
|
||||
SRVData::bt_serizalize() const
|
||||
{
|
||||
auto data = oxenc::bt_serialize(tuple);
|
||||
return bstring_t{reinterpret_cast<const byte_t*>(data.data()), data.size()};
|
||||
}
|
||||
|
||||
void
|
||||
SRVData::bt_deserialize(byte_view_t& raw)
|
||||
{
|
||||
oxenc::bt_list_consumer bt{
|
||||
std::string_view{reinterpret_cast<const char*>(raw.data()), raw.size()}};
|
||||
bt.consume_list(_tuple);
|
||||
auto rem = bt.current_buffer();
|
||||
if (not valid())
|
||||
throw std::invalid_argument{"invalid srv data"};
|
||||
raw = raw.substr(raw.size() - rem.size());
|
||||
}
|
||||
|
||||
SRVData&
|
||||
SRVData::operator=(SRVData&& other)
|
||||
{
|
||||
_tuple = std::move(other._tuple);
|
||||
return *this;
|
||||
}
|
||||
|
||||
SRVData&
|
||||
SRVData::operator=(const SRVData& other)
|
||||
{
|
||||
_tuple = other.tuple;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
SRVData::BDecode(llarp_buffer_t* buf)
|
||||
{
|
||||
|
@ -119,14 +127,11 @@ namespace llarp::dns
|
|||
if (not bencode_discard(buf))
|
||||
return false;
|
||||
byte_t* end = buf->cur;
|
||||
std::string_view srvString{
|
||||
reinterpret_cast<char*>(begin), static_cast<std::size_t>(end - begin)};
|
||||
try
|
||||
{
|
||||
SRVTuple tuple{};
|
||||
oxenc::bt_deserialize(srvString, tuple);
|
||||
*this = fromTuple(std::move(tuple));
|
||||
return IsValid();
|
||||
byte_view_t b{begin, static_cast<size_t>(end - begin)};
|
||||
bt_deserialize(b);
|
||||
return valid();
|
||||
}
|
||||
catch (const oxenc::bt_deserialize_invalid&)
|
||||
{
|
||||
|
@ -144,4 +149,39 @@ namespace llarp::dns
|
|||
{"port", port},
|
||||
{"target", target}};
|
||||
}
|
||||
|
||||
bstring_t
|
||||
SRVData::encode_dns(std::string rootname) const
|
||||
{
|
||||
bstring_t buf;
|
||||
buf.resize(6);
|
||||
auto ptr = buf.data();
|
||||
oxenc::write_host_as_big(priority, ptr);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(weight, ptr);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(port, ptr);
|
||||
ptr += 2;
|
||||
buf += encode_dns_labels(target_dns_labels(std::move(rootname)));
|
||||
return buf;
|
||||
}
|
||||
|
||||
std::vector<std::string_view>
|
||||
SRVData::target_dns_labels(std::string_view rootname) const
|
||||
{
|
||||
return split(empty_target() ? rootname : target, ".");
|
||||
}
|
||||
|
||||
bool
|
||||
SRVData::empty_target() const
|
||||
{
|
||||
return target.empty() or target == "@";
|
||||
}
|
||||
|
||||
bool
|
||||
SRVData::target_full_stop() const
|
||||
{
|
||||
return target == ".";
|
||||
}
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
#pragma once
|
||||
|
||||
#include "llarp/util/str.hpp"
|
||||
#include "name.hpp"
|
||||
#include "serialize.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "llarp/util/status.hpp"
|
||||
|
||||
|
@ -12,63 +15,10 @@ namespace llarp::dns
|
|||
{
|
||||
using SRVTuple = std::tuple<std::string, uint16_t, uint16_t, uint16_t, std::string>;
|
||||
|
||||
struct SRVData
|
||||
class SRVData
|
||||
{
|
||||
static constexpr size_t TARGET_MAX_SIZE = 200;
|
||||
|
||||
std::string service_proto; // service and protocol may as well be together
|
||||
|
||||
uint16_t priority;
|
||||
uint16_t weight;
|
||||
uint16_t port;
|
||||
|
||||
// target string for the SRV record to point to
|
||||
// options:
|
||||
// empty - refer to query name
|
||||
// dot - authoritative "no such service available"
|
||||
// any other .loki or .snode - target is that .loki or .snode
|
||||
std::string target;
|
||||
|
||||
// do some basic validation on the target string
|
||||
// note: this is not a conclusive, regex solution,
|
||||
// but rather some sanity/safety checks
|
||||
bool
|
||||
IsValid() const;
|
||||
|
||||
SRVTuple
|
||||
toTuple() const;
|
||||
|
||||
auto
|
||||
toTupleRef() const
|
||||
{
|
||||
return std::tie(service_proto, priority, weight, port, target);
|
||||
}
|
||||
|
||||
/// so we can put SRVData in a std::set
|
||||
bool
|
||||
operator<(const SRVData& other) const
|
||||
{
|
||||
return toTupleRef() < other.toTupleRef();
|
||||
}
|
||||
|
||||
bool
|
||||
operator==(const SRVData& other) const
|
||||
{
|
||||
return toTupleRef() == other.toTupleRef();
|
||||
}
|
||||
|
||||
bool
|
||||
BEncode(llarp_buffer_t*) const;
|
||||
|
||||
bool
|
||||
BDecode(llarp_buffer_t*);
|
||||
|
||||
util::StatusObject
|
||||
ExtractStatus() const;
|
||||
|
||||
static SRVData
|
||||
fromTuple(SRVTuple tuple);
|
||||
|
||||
/* bind-like formatted string for SRV records in config file
|
||||
*
|
||||
* format:
|
||||
|
@ -76,17 +26,99 @@ namespace llarp::dns
|
|||
*
|
||||
* exactly one space character between parts.
|
||||
*
|
||||
* target can be empty, in which case the space after port should
|
||||
* be omitted. if this is the case, the target is
|
||||
* interpreted as the .loki or .snode of the current context.
|
||||
*
|
||||
* if target is not empty, it must be either
|
||||
* - simply a full stop (dot/period) OR
|
||||
* - a name within the .loki or .snode subdomains. a target
|
||||
* specified in this manner must not end with a full stop.
|
||||
* - a full .loki or .snode address ending with a full stop.
|
||||
* - the literal character '@' in which the primary flow layer address
|
||||
* be it .loki or .snode is used.
|
||||
*
|
||||
* if empty target is provided the trailing space after port MUST be omitted.
|
||||
* when the target is empty it is treated like '@'.
|
||||
*/
|
||||
void
|
||||
from_string(std::string_view srvString);
|
||||
|
||||
SRVTuple _tuple;
|
||||
|
||||
public:
|
||||
explicit SRVData(SRVTuple tup);
|
||||
explicit SRVData(std::string_view str);
|
||||
|
||||
SRVData() = default;
|
||||
SRVData(const SRVData&) = default;
|
||||
SRVData(SRVData&&) = default;
|
||||
|
||||
SRVData&
|
||||
operator=(SRVData&& other);
|
||||
SRVData&
|
||||
operator=(const SRVData& other);
|
||||
|
||||
std::string& service_proto{
|
||||
std::get<0>(_tuple)}; // service and protocol may as well be together
|
||||
|
||||
uint16_t& priority{std::get<1>(_tuple)};
|
||||
uint16_t& weight{std::get<2>(_tuple)};
|
||||
uint16_t& port{std::get<3>(_tuple)};
|
||||
|
||||
// target string for the SRV record to point to
|
||||
// options:
|
||||
// empty - dns query's qname
|
||||
// @ - dns query's qname
|
||||
// dot - authoritative "no such service available"
|
||||
// any other .loki or .snode - target is that .loki or .snode
|
||||
std::string& target{std::get<4>(_tuple)};
|
||||
|
||||
const SRVTuple& tuple{_tuple};
|
||||
|
||||
// do some basic validation on the target string
|
||||
// note: this is not a conclusive, regex solution,
|
||||
// but rather some sanity/safety checks
|
||||
bool
|
||||
fromString(std::string_view srvString);
|
||||
valid() const;
|
||||
|
||||
/// return if the target refers to the query's qname.
|
||||
bool
|
||||
empty_target() const;
|
||||
|
||||
/// returns true if the target is authoritative "no such service"
|
||||
bool
|
||||
target_full_stop() const;
|
||||
|
||||
/// so we can put SRVData in a std::set
|
||||
constexpr bool
|
||||
operator<(const SRVData& other) const
|
||||
{
|
||||
return tuple < other.tuple;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator==(const SRVData& other) const
|
||||
{
|
||||
return tuple == other.tuple;
|
||||
}
|
||||
|
||||
bool
|
||||
BEncode(llarp_buffer_t*) const;
|
||||
|
||||
bstring_t
|
||||
bt_serizalize() const;
|
||||
|
||||
void
|
||||
bt_deserialize(byte_view_t& raw);
|
||||
|
||||
bool
|
||||
BDecode(llarp_buffer_t*);
|
||||
|
||||
util::StatusObject
|
||||
ExtractStatus() const;
|
||||
|
||||
std::vector<std::string_view>
|
||||
target_dns_labels(std::string_view rootname) const;
|
||||
|
||||
bstring_t
|
||||
encode_dns(std::string rootname) const;
|
||||
};
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,20 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "llarp/service/address.hpp"
|
||||
#include "llarp/service/convotag.hpp"
|
||||
#include "llarp/service/protocol_type.hpp"
|
||||
#include "router_id.hpp"
|
||||
#include "llarp/ev/ev.hpp"
|
||||
#include "llarp/dns/srv_data.hpp"
|
||||
#include "router_contact.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
#include <set>
|
||||
#include "oxenc/variant.h"
|
||||
|
||||
#include <oxenc/variant.h>
|
||||
#include <llarp/config/config.hpp>
|
||||
#include <llarp/service/address.hpp>
|
||||
#include <llarp/service/convotag.hpp>
|
||||
#include <llarp/service/protocol_type.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -28,7 +32,7 @@ namespace llarp
|
|||
class Server;
|
||||
}
|
||||
|
||||
class EndpointBase
|
||||
class [[deprecated]] EndpointBase
|
||||
{
|
||||
std::unordered_set<dns::SRVData> m_SRVRecords;
|
||||
|
||||
|
@ -119,6 +123,8 @@ namespace llarp
|
|||
virtual std::optional<service::ConvoTag>
|
||||
GetBestConvoTagFor(AddressVariant_t addr) const = 0;
|
||||
|
||||
/// MUST call MarkAddressOutbound first if we are initiating the flow to the remote.
|
||||
// TODO: this requirement is stupid.
|
||||
virtual bool
|
||||
EnsurePathTo(
|
||||
AddressVariant_t addr,
|
||||
|
@ -129,12 +135,20 @@ namespace llarp
|
|||
LookupNameAsync(
|
||||
std::string name, std::function<void(std::optional<AddressVariant_t>)> resultHandler) = 0;
|
||||
|
||||
virtual bool
|
||||
LookupRC(RouterID rid, RouterLookupHandler handler) = 0;
|
||||
|
||||
virtual const EventLoop_ptr&
|
||||
Loop() = 0;
|
||||
|
||||
virtual bool
|
||||
SendToOrQueue(
|
||||
service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t) = 0;
|
||||
SendToOrQueue(service::ConvoTag tag, std::vector<byte_t> data, service::ProtocolType t) = 0;
|
||||
|
||||
bool
|
||||
SendToOrQueue(service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t)
|
||||
{
|
||||
return SendToOrQueue(std::move(tag), payload.copy(), t);
|
||||
}
|
||||
|
||||
/// lookup srv records async
|
||||
virtual void
|
||||
|
@ -143,8 +157,32 @@ namespace llarp
|
|||
std::string service,
|
||||
std::function<void(std::vector<dns::SRVData>)> resultHandler) = 0;
|
||||
|
||||
/// will mark a remote as something we want to initiate a flow to.
|
||||
/// making this explicit prevents us from doing weird things when we are the recipient.
|
||||
virtual void
|
||||
MarkAddressOutbound(AddressVariant_t remote) = 0;
|
||||
|
||||
virtual bool
|
||||
Configure(const NetworkConfig&, const DnsConfig&) = 0;
|
||||
|
||||
virtual std::string_view
|
||||
endpoint_name() const = 0;
|
||||
|
||||
/// called to do any deferred startup items after configure.
|
||||
virtual void
|
||||
start_endpoint(){};
|
||||
|
||||
/// initiate shutdown of all periodic operations.
|
||||
virtual void
|
||||
stop_endpoint(){};
|
||||
|
||||
/// called in a periodic event loop timer.
|
||||
virtual void
|
||||
periodic_tick(){};
|
||||
|
||||
/// called in an idempotent event loop handler after reading io each tick.
|
||||
virtual void
|
||||
event_loop_pump(){};
|
||||
};
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp/net/sock_addr.hpp>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/time.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
|
@ -9,6 +10,7 @@
|
|||
#include <deque>
|
||||
#include <list>
|
||||
#include <future>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace uvw
|
||||
|
@ -18,7 +20,6 @@ namespace uvw
|
|||
|
||||
namespace llarp
|
||||
{
|
||||
struct SockAddr;
|
||||
struct UDPHandle;
|
||||
|
||||
namespace vpn
|
||||
|
@ -44,9 +45,9 @@ namespace llarp
|
|||
/// callback to fire again if already triggered depending on the underlying implementation).
|
||||
virtual ~EventLoopWakeup() = default;
|
||||
|
||||
/// trigger this task to run on the next event loop iteration; does nothing if already
|
||||
/// triggered.
|
||||
virtual void
|
||||
/// trigger this task to run on the next event loop iteration idempotently.
|
||||
/// returns true if we already triggered it before on this event loop cycle.
|
||||
virtual bool
|
||||
Trigger() = 0;
|
||||
};
|
||||
|
||||
|
@ -193,7 +194,7 @@ namespace llarp
|
|||
|
||||
// Constructs a UDP socket that can be used for sending and/or receiving
|
||||
virtual std::shared_ptr<UDPHandle>
|
||||
make_udp(UDPReceiveFunc on_recv) = 0;
|
||||
make_udp(UDPReceiveFunc on_recv, const std::optional<SockAddr>& laddr = std::nullopt) = 0;
|
||||
|
||||
/// Make a thread-safe event loop waker (an "async" in libuv terminology) on this event loop;
|
||||
/// you can call `->Trigger()` on the returned shared pointer to fire the callback at the next
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "libuv.hpp"
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
@ -9,6 +10,13 @@
|
|||
#include <llarp/vpn/platform.hpp>
|
||||
|
||||
#include <uvw.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
#include "llarp/ev/ev.hpp"
|
||||
#include "llarp/net/net_int.hpp"
|
||||
#include "llarp/net/sock_addr.hpp"
|
||||
#include "llarp/util/buffer.hpp"
|
||||
#include "uvw/udp.h"
|
||||
#include "uvw/util.h"
|
||||
|
||||
namespace llarp::uv
|
||||
{
|
||||
|
@ -21,18 +29,24 @@ namespace llarp::uv
|
|||
class UVWakeup final : public EventLoopWakeup
|
||||
{
|
||||
std::shared_ptr<uvw::AsyncHandle> async;
|
||||
std::atomic_flag _triggered = ATOMIC_FLAG_INIT;
|
||||
|
||||
public:
|
||||
UVWakeup(uvw::Loop& loop, std::function<void()> callback)
|
||||
: async{loop.resource<uvw::AsyncHandle>()}
|
||||
{
|
||||
async->on<uvw::AsyncEvent>([f = std::move(callback)](auto&, auto&) { f(); });
|
||||
async->on<uvw::AsyncEvent>([this, f = std::move(callback)](auto&, auto&) {
|
||||
_triggered.clear();
|
||||
f();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
Trigger() override
|
||||
{
|
||||
bool ret = _triggered.test_and_set();
|
||||
async->send();
|
||||
return ret;
|
||||
}
|
||||
|
||||
~UVWakeup() override
|
||||
|
@ -64,19 +78,23 @@ namespace llarp::uv
|
|||
|
||||
struct UDPHandle final : llarp::UDPHandle
|
||||
{
|
||||
UDPHandle(uvw::Loop& loop, ReceiveFunc rf);
|
||||
|
||||
bool
|
||||
listen(const SockAddr& addr) override;
|
||||
UDPHandle(uvw::Loop& loop, ReceiveFunc rf, std::optional<SockAddr> laddr);
|
||||
|
||||
bool
|
||||
send(const SockAddr& dest, const llarp_buffer_t& buf) override;
|
||||
|
||||
std::optional<SockAddr>
|
||||
SockAddr
|
||||
LocalAddr() const override
|
||||
{
|
||||
auto addr = handle->sock<uvw::IPv4>();
|
||||
return SockAddr{addr.ip, huint16_t{static_cast<uint16_t>(addr.port)}};
|
||||
if (not maybe_laddr)
|
||||
{
|
||||
auto addr = handle->sock<uvw::IPv4>();
|
||||
if (addr.ip.empty())
|
||||
addr = handle->sock<uvw::IPv6>();
|
||||
|
||||
maybe_laddr = SockAddr{addr.ip, huint16_t{static_cast<uint16_t>(addr.port)}};
|
||||
}
|
||||
return *maybe_laddr;
|
||||
}
|
||||
|
||||
std::optional<int>
|
||||
|
@ -96,9 +114,7 @@ namespace llarp::uv
|
|||
|
||||
private:
|
||||
std::shared_ptr<uvw::UDPHandle> handle;
|
||||
|
||||
void
|
||||
reset_handle(uvw::Loop& loop);
|
||||
mutable std::optional<SockAddr> maybe_laddr;
|
||||
};
|
||||
|
||||
void
|
||||
|
@ -138,6 +154,7 @@ namespace llarp::uv
|
|||
m_nextID.store(0);
|
||||
if (!(m_WakeUp = m_Impl->resource<uvw::AsyncHandle>()))
|
||||
throw std::runtime_error{"Failed to create libuv async"};
|
||||
|
||||
m_WakeUp->on<uvw::AsyncEvent>([this](const auto&, auto&) { tick_event_loop(); });
|
||||
}
|
||||
|
||||
|
@ -165,10 +182,10 @@ namespace llarp::uv
|
|||
}
|
||||
|
||||
std::shared_ptr<llarp::UDPHandle>
|
||||
Loop::make_udp(UDPReceiveFunc on_recv)
|
||||
Loop::make_udp(UDPReceiveFunc on_recv, const std::optional<SockAddr>& laddr)
|
||||
{
|
||||
return std::static_pointer_cast<llarp::UDPHandle>(
|
||||
std::make_shared<llarp::uv::UDPHandle>(*m_Impl, std::move(on_recv)));
|
||||
std::make_shared<llarp::uv::UDPHandle>(*m_Impl, std::move(on_recv), laddr));
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -241,41 +258,36 @@ namespace llarp::uv
|
|||
std::shared_ptr<llarp::vpn::NetworkInterface> netif,
|
||||
std::function<void(llarp::net::IPPacket)> handler)
|
||||
{
|
||||
#ifdef __linux__
|
||||
using event_t = uvw::PollEvent;
|
||||
auto handle = m_Impl->resource<uvw::PollHandle>(netif->PollFD());
|
||||
#else
|
||||
// we use a uv_prepare_t because it fires before blocking for new io events unconditionally
|
||||
// we want to match what linux does, using a uv_check_t does not suffice as the order of
|
||||
// operations is not what we need.
|
||||
using event_t = uvw::PrepareEvent;
|
||||
auto handle = m_Impl->resource<uvw::PrepareHandle>();
|
||||
#endif
|
||||
|
||||
if (!handle)
|
||||
return false;
|
||||
|
||||
handle->on<event_t>([netif = std::move(netif), handler = std::move(handler)](
|
||||
const event_t&, [[maybe_unused]] auto& handle) {
|
||||
auto reader = [netif, handler = std::move(handler), this](const auto&, auto&) {
|
||||
for (auto pkt = netif->ReadNextPacket(); true; pkt = netif->ReadNextPacket())
|
||||
{
|
||||
if (pkt.empty())
|
||||
return;
|
||||
if (handler)
|
||||
handler(std::move(pkt));
|
||||
// on windows/apple, vpn packet io does not happen as an io action that wakes up the event
|
||||
// loop thus, we must manually wake up the event loop when we get a packet on our interface.
|
||||
// on linux/android this is a nop
|
||||
netif->MaybeWakeUpperLayers();
|
||||
// on protactor style platforms we want to emulate the reactor style pattern so we wakeup
|
||||
// the event loop when we get packets.
|
||||
if constexpr (platform::has_proactor_io)
|
||||
wakeup();
|
||||
}
|
||||
});
|
||||
|
||||
#ifdef __linux__
|
||||
handle->start(uvw::PollHandle::Event::READABLE);
|
||||
#else
|
||||
handle->start();
|
||||
#endif
|
||||
};
|
||||
|
||||
if constexpr (llarp::platform::has_reactor_io)
|
||||
{
|
||||
auto handle = m_Impl->resource<uvw::PollHandle>(netif->PollFD());
|
||||
if (not handle)
|
||||
return false;
|
||||
handle->on<uvw::PollEvent>(std::move(reader));
|
||||
handle->start(uvw::PollHandle::Event::READABLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto handle = m_Impl->resource<uvw::PrepareHandle>();
|
||||
if (not handle)
|
||||
return false;
|
||||
handle->on<uvw::PrepareEvent>(std::move(reader));
|
||||
handle->start();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -294,44 +306,30 @@ namespace llarp::uv
|
|||
FlushLogic();
|
||||
}
|
||||
m_LogicCalls.pushBack(f);
|
||||
m_WakeUp->send();
|
||||
}
|
||||
|
||||
// Sets `handle` to a new uvw UDP handle, first initiating a close and then disowning the handle
|
||||
// if already set, allocating the resource, and setting the receive event on it.
|
||||
void
|
||||
UDPHandle::reset_handle(uvw::Loop& loop)
|
||||
llarp::uv::UDPHandle::UDPHandle(uvw::Loop& loop, ReceiveFunc rf, std::optional<SockAddr> laddr)
|
||||
: llarp::UDPHandle{std::move(rf)}, handle{loop.resource<uvw::UDPHandle>()}
|
||||
{
|
||||
if (handle)
|
||||
handle->close();
|
||||
handle = loop.resource<uvw::UDPHandle>();
|
||||
handle->on<uvw::UDPDataEvent>([this](auto& event, auto& /*handle*/) {
|
||||
std::optional<std::string> maybe_addr_str;
|
||||
if (laddr)
|
||||
maybe_addr_str = laddr->ToString();
|
||||
|
||||
auto err = handle->on<uvw::ErrorEvent>(
|
||||
[addr_str = maybe_addr_str.value_or("all interfaces")](auto& event, auto&) {
|
||||
throw llarp::util::bind_socket_error{
|
||||
fmt::format("failed to bind udp socket on {}: {}", addr_str, event.what())};
|
||||
});
|
||||
if (laddr)
|
||||
handle->bind(*static_cast<const sockaddr*>(*laddr));
|
||||
handle->on<uvw::UDPDataEvent>([this](auto& ev, auto&) {
|
||||
on_recv(
|
||||
*this,
|
||||
SockAddr{event.sender.ip, huint16_t{static_cast<uint16_t>(event.sender.port)}},
|
||||
OwnedBuffer{std::move(event.data), event.length});
|
||||
SockAddr{ev.sender.ip, huint16_t{static_cast<uint16_t>(ev.sender.port)}},
|
||||
OwnedBuffer{std::move(ev.data), ev.length});
|
||||
});
|
||||
}
|
||||
|
||||
llarp::uv::UDPHandle::UDPHandle(uvw::Loop& loop, ReceiveFunc rf) : llarp::UDPHandle{std::move(rf)}
|
||||
{
|
||||
reset_handle(loop);
|
||||
}
|
||||
|
||||
bool
|
||||
UDPHandle::listen(const SockAddr& addr)
|
||||
{
|
||||
if (handle->active())
|
||||
reset_handle(handle->loop());
|
||||
|
||||
auto err = handle->on<uvw::ErrorEvent>([addr](auto& event, auto&) {
|
||||
throw llarp::util::bind_socket_error{
|
||||
fmt::format("failed to bind udp socket on {}: {}", addr, event.what())};
|
||||
});
|
||||
handle->bind(*static_cast<const sockaddr*>(addr));
|
||||
handle->recv();
|
||||
handle->erase(err);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "ev.hpp"
|
||||
#include "llarp/net/sock_addr.hpp"
|
||||
#include "udp_handle.hpp"
|
||||
#include <llarp/util/thread/queue.hpp>
|
||||
#include <llarp/util/meta/memfn.hpp>
|
||||
|
@ -11,6 +12,8 @@
|
|||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::uv
|
||||
|
@ -64,7 +67,7 @@ namespace llarp::uv
|
|||
make_repeater() override;
|
||||
|
||||
virtual std::shared_ptr<llarp::UDPHandle>
|
||||
make_udp(UDPReceiveFunc on_recv) override;
|
||||
make_udp(UDPReceiveFunc on_recv, const std::optional<SockAddr>& laddr = std::nullopt) override;
|
||||
|
||||
void
|
||||
FlushLogic();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "ev.hpp"
|
||||
#include "../util/buffer.hpp"
|
||||
#include "llarp/net/sock_addr.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -8,12 +9,6 @@ namespace llarp
|
|||
{
|
||||
using ReceiveFunc = EventLoop::UDPReceiveFunc;
|
||||
|
||||
// Starts listening for incoming UDP packets on the given address. Returns true on success,
|
||||
// false if the address could not be bound. If you send without calling this first then the
|
||||
// socket will bind to a random high port on 0.0.0.0 (the "all addresses" address).
|
||||
virtual bool
|
||||
listen(const SockAddr& addr) = 0;
|
||||
|
||||
// Sends a packet to the given recipient, immediately. Returns true if the send succeeded,
|
||||
// false it could not be performed (either because of error, or because it would have blocked).
|
||||
// If listen hasn't been called then a random IP/port will be used.
|
||||
|
@ -34,14 +29,14 @@ namespace llarp
|
|||
}
|
||||
|
||||
/// returns the local address we are bound on
|
||||
virtual std::optional<SockAddr>
|
||||
virtual SockAddr
|
||||
LocalAddr() const = 0;
|
||||
|
||||
// Base class destructor
|
||||
virtual ~UDPHandle() = default;
|
||||
|
||||
protected:
|
||||
explicit UDPHandle(ReceiveFunc on_recv) : on_recv{std::move(on_recv)}
|
||||
UDPHandle(ReceiveFunc on_recv) : on_recv{std::move(on_recv)}
|
||||
{
|
||||
// It makes no sense at all to use this with a null receive function:
|
||||
assert(this->on_recv);
|
||||
|
|
|
@ -1,241 +1,233 @@
|
|||
#include "endpoint.hpp"
|
||||
|
||||
#include <llarp/handlers/exit.hpp>
|
||||
#include <llarp/path/path_context.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/quic/tunnel.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::exit
|
||||
{
|
||||
namespace exit
|
||||
Endpoint::Endpoint(
|
||||
AbstractRouter& router,
|
||||
const llarp::PubKey& remoteIdent,
|
||||
const llarp::path::HopHandler_ptr& beginPath,
|
||||
bool rewriteIP,
|
||||
huint128_t ip)
|
||||
: createdAt{router.Now()}
|
||||
, _router{router}
|
||||
, m_remoteSignKey{remoteIdent}
|
||||
, m_CurrentPath{beginPath}
|
||||
, m_IP{ip}
|
||||
, m_LastActive{router.Now()}
|
||||
, m_RewriteSource{rewriteIP}
|
||||
{}
|
||||
|
||||
void
|
||||
Endpoint::Close()
|
||||
{}
|
||||
|
||||
util::StatusObject
|
||||
Endpoint::ExtractStatus() const
|
||||
{
|
||||
Endpoint::Endpoint(
|
||||
const llarp::PubKey& remoteIdent,
|
||||
const llarp::path::HopHandler_ptr& beginPath,
|
||||
bool rewriteIP,
|
||||
huint128_t ip,
|
||||
llarp::handlers::ExitEndpoint* parent)
|
||||
: createdAt{parent->Now()}
|
||||
, m_Parent{parent}
|
||||
, m_remoteSignKey{remoteIdent}
|
||||
, m_CurrentPath{beginPath}
|
||||
, m_IP{ip}
|
||||
, m_RewriteSource{rewriteIP}
|
||||
{
|
||||
m_LastActive = parent->Now();
|
||||
}
|
||||
auto now = _router.Now();
|
||||
util::StatusObject obj{
|
||||
{"identity", m_remoteSignKey.ToString()},
|
||||
{"ip", m_IP.ToString()},
|
||||
{"txRate", m_TxRate},
|
||||
{"rxRate", m_RxRate},
|
||||
{"createdAt", to_json(createdAt)},
|
||||
{"exiting", !m_RewriteSource},
|
||||
{"looksDead", LooksDead(now)},
|
||||
{"expiresSoon", ExpiresSoon(now)},
|
||||
{"expired", IsExpired(now)}};
|
||||
return obj;
|
||||
}
|
||||
|
||||
Endpoint::~Endpoint()
|
||||
{
|
||||
if (m_CurrentPath)
|
||||
m_Parent->DelEndpointInfo(m_CurrentPath->RXID());
|
||||
}
|
||||
bool
|
||||
Endpoint::UpdateLocalPath(const llarp::PathID_t& nextPath)
|
||||
{
|
||||
// const auto & route = _router->get_layer()->route;
|
||||
// TODO: tell route layer "we are over here now" given here is new pathid
|
||||
|
||||
void
|
||||
Endpoint::Close()
|
||||
{
|
||||
m_Parent->RemoveExit(this);
|
||||
}
|
||||
const RouterID us{_router.pubkey()};
|
||||
m_CurrentPath = _router.pathContext().GetByUpstream(us, nextPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
Endpoint::ExtractStatus() const
|
||||
{
|
||||
auto now = m_Parent->Now();
|
||||
util::StatusObject obj{
|
||||
{"identity", m_remoteSignKey.ToString()},
|
||||
{"ip", m_IP.ToString()},
|
||||
{"txRate", m_TxRate},
|
||||
{"rxRate", m_RxRate},
|
||||
{"createdAt", to_json(createdAt)},
|
||||
{"exiting", !m_RewriteSource},
|
||||
{"looksDead", LooksDead(now)},
|
||||
{"expiresSoon", ExpiresSoon(now)},
|
||||
{"expired", IsExpired(now)}};
|
||||
return obj;
|
||||
}
|
||||
void
|
||||
Endpoint::Tick(llarp_time_t now)
|
||||
{
|
||||
(void)now;
|
||||
m_RxRate = 0;
|
||||
m_TxRate = 0;
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::UpdateLocalPath(const llarp::PathID_t& nextPath)
|
||||
bool
|
||||
Endpoint::IsExpired(llarp_time_t now) const
|
||||
{
|
||||
auto path = GetCurrentPath();
|
||||
if (path)
|
||||
{
|
||||
if (!m_Parent->UpdateEndpointPath(m_remoteSignKey, nextPath))
|
||||
return path->Expired(now);
|
||||
}
|
||||
// if we don't have an underlying path we are considered expired
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const
|
||||
{
|
||||
if (m_CurrentPath)
|
||||
return m_CurrentPath->ExpiresSoon(now, dlt);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::LooksDead(llarp_time_t now, llarp_time_t timeout) const
|
||||
{
|
||||
if (ExpiresSoon(now, timeout))
|
||||
return true;
|
||||
auto path = GetCurrentPath();
|
||||
if (not path)
|
||||
return true;
|
||||
auto lastPing = path->LastRemoteActivityAt();
|
||||
if (lastPing == 0s || (now > lastPing && now - lastPing > timeout))
|
||||
return now > m_LastActive && now - m_LastActive > timeout;
|
||||
else if (lastPing > 0s) // NOLINT
|
||||
return now > lastPing && now - lastPing > timeout;
|
||||
return lastPing > 0s;
|
||||
}
|
||||
|
||||
/*
|
||||
bool
|
||||
Endpoint::QueueOutboundTraffic(
|
||||
PathID_t path, std::vector<byte_t> buf, uint64_t counter, service::ProtocolType t)
|
||||
{
|
||||
const service::ConvoTag tag{path.as_array()};
|
||||
if (t == service::ProtocolType::QUIC)
|
||||
{
|
||||
auto quic = m_Parent->GetQUICTunnel();
|
||||
if (not quic)
|
||||
return false;
|
||||
const RouterID us{m_Parent->GetRouter()->pubkey()};
|
||||
m_CurrentPath = m_Parent->GetRouter()->pathContext().GetByUpstream(us, nextPath);
|
||||
m_TxRate += buf.size();
|
||||
quic->receive_packet(tag, std::move(buf));
|
||||
m_LastActive = _router.Now();
|
||||
return true;
|
||||
}
|
||||
// queue overflow
|
||||
if (m_UpstreamQueue.size() > MaxUpstreamQueueSize)
|
||||
return false;
|
||||
|
||||
void
|
||||
Endpoint::Tick(llarp_time_t now)
|
||||
llarp::net::IPPacket pkt{std::move(buf)};
|
||||
if (pkt.empty())
|
||||
return false;
|
||||
|
||||
if (pkt.IsV6() && m_Parent->SupportsV6())
|
||||
{
|
||||
(void)now;
|
||||
m_RxRate = 0;
|
||||
m_TxRate = 0;
|
||||
huint128_t dst;
|
||||
if (m_RewriteSource)
|
||||
dst = m_Parent->GetIfAddr();
|
||||
else
|
||||
dst = pkt.dstv6();
|
||||
pkt.UpdateIPv6Address(m_IP, dst);
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::IsExpired(llarp_time_t now) const
|
||||
else if (pkt.IsV4() && !m_Parent->SupportsV6())
|
||||
{
|
||||
auto path = GetCurrentPath();
|
||||
if (path)
|
||||
{
|
||||
return path->Expired(now);
|
||||
}
|
||||
// if we don't have an underlying path we are considered expired
|
||||
return true;
|
||||
huint32_t dst;
|
||||
if (m_RewriteSource)
|
||||
dst = net::TruncateV6(m_Parent->GetIfAddr());
|
||||
else
|
||||
dst = pkt.dstv4();
|
||||
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(m_IP)), xhtonl(dst));
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::ExpiresSoon(llarp_time_t now, llarp_time_t dlt) const
|
||||
else
|
||||
{
|
||||
if (m_CurrentPath)
|
||||
return m_CurrentPath->ExpiresSoon(now, dlt);
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
m_TxRate += pkt.size();
|
||||
m_UpstreamQueue.emplace(std::move(pkt), counter);
|
||||
m_LastActive = m_Parent->Now();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::LooksDead(llarp_time_t now, llarp_time_t timeout) const
|
||||
bool
|
||||
Endpoint::QueueInboundTraffic(std::vector<byte_t> buf, service::ProtocolType type)
|
||||
{
|
||||
if (type != service::ProtocolType::QUIC)
|
||||
{
|
||||
if (ExpiresSoon(now, timeout))
|
||||
return true;
|
||||
auto path = GetCurrentPath();
|
||||
if (not path)
|
||||
return true;
|
||||
auto lastPing = path->LastRemoteActivityAt();
|
||||
if (lastPing == 0s || (now > lastPing && now - lastPing > timeout))
|
||||
return now > m_LastActive && now - m_LastActive > timeout;
|
||||
else if (lastPing > 0s) // NOLINT
|
||||
return now > lastPing && now - lastPing > timeout;
|
||||
return lastPing > 0s;
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::QueueOutboundTraffic(
|
||||
PathID_t path, std::vector<byte_t> buf, uint64_t counter, service::ProtocolType t)
|
||||
{
|
||||
const service::ConvoTag tag{path.as_array()};
|
||||
if (t == service::ProtocolType::QUIC)
|
||||
{
|
||||
auto quic = m_Parent->GetQUICTunnel();
|
||||
if (not quic)
|
||||
return false;
|
||||
m_TxRate += buf.size();
|
||||
quic->receive_packet(tag, std::move(buf));
|
||||
m_LastActive = m_Parent->Now();
|
||||
return true;
|
||||
}
|
||||
// queue overflow
|
||||
if (m_UpstreamQueue.size() > MaxUpstreamQueueSize)
|
||||
return false;
|
||||
|
||||
llarp::net::IPPacket pkt{std::move(buf)};
|
||||
if (pkt.empty())
|
||||
return false;
|
||||
|
||||
if (pkt.IsV6() && m_Parent->SupportsV6())
|
||||
{
|
||||
huint128_t dst;
|
||||
if (m_RewriteSource)
|
||||
dst = m_Parent->GetIfAddr();
|
||||
else
|
||||
dst = pkt.dstv6();
|
||||
pkt.UpdateIPv6Address(m_IP, dst);
|
||||
}
|
||||
else if (pkt.IsV4() && !m_Parent->SupportsV6())
|
||||
{
|
||||
huint32_t dst;
|
||||
if (m_RewriteSource)
|
||||
dst = net::TruncateV6(m_Parent->GetIfAddr());
|
||||
else
|
||||
dst = pkt.dstv4();
|
||||
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(m_IP)), xhtonl(dst));
|
||||
}
|
||||
huint128_t src;
|
||||
if (m_RewriteSource)
|
||||
src = m_Parent->GetIfAddr();
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_TxRate += pkt.size();
|
||||
m_UpstreamQueue.emplace(std::move(pkt), counter);
|
||||
m_LastActive = m_Parent->Now();
|
||||
return true;
|
||||
src = pkt.srcv6();
|
||||
if (pkt.IsV6())
|
||||
pkt.UpdateIPv6Address(src, m_IP);
|
||||
else
|
||||
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP)));
|
||||
|
||||
buf = pkt.steal();
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::QueueInboundTraffic(std::vector<byte_t> buf, service::ProtocolType type)
|
||||
const uint8_t queue_idx = buf.size() / llarp::routing::ExitPadSize;
|
||||
if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end())
|
||||
m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{});
|
||||
auto& queue = m_DownstreamQueues.at(queue_idx);
|
||||
if (queue.size() == 0)
|
||||
{
|
||||
if (type != service::ProtocolType::QUIC)
|
||||
{
|
||||
llarp::net::IPPacket pkt{std::move(buf)};
|
||||
if (pkt.empty())
|
||||
return false;
|
||||
|
||||
huint128_t src;
|
||||
if (m_RewriteSource)
|
||||
src = m_Parent->GetIfAddr();
|
||||
else
|
||||
src = pkt.srcv6();
|
||||
if (pkt.IsV6())
|
||||
pkt.UpdateIPv6Address(src, m_IP);
|
||||
else
|
||||
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP)));
|
||||
|
||||
buf = pkt.steal();
|
||||
}
|
||||
|
||||
const uint8_t queue_idx = buf.size() / llarp::routing::ExitPadSize;
|
||||
if (m_DownstreamQueues.find(queue_idx) == m_DownstreamQueues.end())
|
||||
m_DownstreamQueues.emplace(queue_idx, InboundTrafficQueue_t{});
|
||||
auto& queue = m_DownstreamQueues.at(queue_idx);
|
||||
if (queue.size() == 0)
|
||||
{
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = type;
|
||||
return queue.back().PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
auto& msg = queue.back();
|
||||
if (msg.Size() + buf.size() > llarp::routing::ExitPadSize)
|
||||
{
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = type;
|
||||
return queue.back().PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
msg.protocol = type;
|
||||
return msg.PutBuffer(std::move(buf), m_Counter++);
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = type;
|
||||
return queue.back().PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::Flush()
|
||||
auto& msg = queue.back();
|
||||
if (msg.Size() + buf.size() > llarp::routing::ExitPadSize)
|
||||
{
|
||||
// flush upstream queue
|
||||
while (m_UpstreamQueue.size())
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = type;
|
||||
return queue.back().PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
msg.protocol = type;
|
||||
return msg.PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::Flush()
|
||||
{
|
||||
// flush upstream queue
|
||||
while (m_UpstreamQueue.size())
|
||||
{
|
||||
m_Parent->QueueOutboundTraffic(
|
||||
const_cast<net::IPPacket&>(m_UpstreamQueue.top().pkt).steal());
|
||||
m_UpstreamQueue.pop();
|
||||
}
|
||||
// flush downstream queue
|
||||
auto path = GetCurrentPath();
|
||||
bool sent = path != nullptr;
|
||||
if (path)
|
||||
{
|
||||
for (auto& item : m_DownstreamQueues)
|
||||
{
|
||||
m_Parent->QueueOutboundTraffic(
|
||||
const_cast<net::IPPacket&>(m_UpstreamQueue.top().pkt).steal());
|
||||
m_UpstreamQueue.pop();
|
||||
}
|
||||
// flush downstream queue
|
||||
auto path = GetCurrentPath();
|
||||
bool sent = path != nullptr;
|
||||
if (path)
|
||||
{
|
||||
for (auto& item : m_DownstreamQueues)
|
||||
auto& queue = item.second;
|
||||
while (queue.size())
|
||||
{
|
||||
auto& queue = item.second;
|
||||
while (queue.size())
|
||||
auto& msg = queue.front();
|
||||
msg.S = path->NextSeqNo();
|
||||
if (path->SendRoutingMessage(msg, m_Parent->GetRouter()))
|
||||
{
|
||||
auto& msg = queue.front();
|
||||
msg.S = path->NextSeqNo();
|
||||
if (path->SendRoutingMessage(msg, m_Parent->GetRouter()))
|
||||
{
|
||||
m_RxRate += msg.Size();
|
||||
sent = true;
|
||||
}
|
||||
queue.pop_front();
|
||||
m_RxRate += msg.Size();
|
||||
sent = true;
|
||||
}
|
||||
queue.pop_front();
|
||||
}
|
||||
}
|
||||
for (auto& item : m_DownstreamQueues)
|
||||
item.second.clear();
|
||||
return sent;
|
||||
}
|
||||
} // namespace exit
|
||||
} // namespace llarp
|
||||
for (auto& item : m_DownstreamQueues)
|
||||
item.second.clear();
|
||||
return sent;
|
||||
}
|
||||
*/
|
||||
|
||||
} // namespace llarp::exit
|
||||
|
|
|
@ -11,133 +11,128 @@
|
|||
|
||||
namespace llarp
|
||||
{
|
||||
namespace handlers
|
||||
{
|
||||
// forward declare
|
||||
struct ExitEndpoint;
|
||||
} // namespace handlers
|
||||
struct AbstractRouter;
|
||||
}
|
||||
|
||||
namespace exit
|
||||
namespace llarp::exit
|
||||
{
|
||||
/// persistant exit state for 1 identity on the exit node
|
||||
struct Endpoint
|
||||
{
|
||||
/// persistant exit state for 1 identity on the exit node
|
||||
struct Endpoint
|
||||
static constexpr size_t MaxUpstreamQueueSize = 256;
|
||||
|
||||
Endpoint(
|
||||
AbstractRouter& router,
|
||||
const llarp::PubKey& remoteIdent,
|
||||
const llarp::path::HopHandler_ptr& path,
|
||||
bool rewriteIP,
|
||||
huint128_t ip);
|
||||
|
||||
/// close ourselves
|
||||
void
|
||||
Close();
|
||||
|
||||
/// implement istateful
|
||||
util::StatusObject
|
||||
ExtractStatus() const;
|
||||
|
||||
/// return true if we are expired right now
|
||||
bool
|
||||
IsExpired(llarp_time_t now) const;
|
||||
|
||||
bool
|
||||
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 5s) const;
|
||||
|
||||
/// return true if this endpoint looks dead right now
|
||||
bool
|
||||
LooksDead(llarp_time_t now, llarp_time_t timeout = 10s) const;
|
||||
|
||||
/// tick ourself, reset tx/rx rates
|
||||
void
|
||||
Tick(llarp_time_t now);
|
||||
|
||||
/// queue traffic from service node / internet to be transmitted
|
||||
bool
|
||||
QueueInboundTraffic(std::vector<byte_t> data, service::ProtocolType t);
|
||||
|
||||
/// flush inbound and outbound traffic queues
|
||||
bool
|
||||
Flush();
|
||||
|
||||
/// queue outbound traffic on tun
|
||||
/// does ip rewrite here
|
||||
// bool
|
||||
// QueueOutboundTraffic(
|
||||
// PathID_t txid, std::vector<byte_t> data, uint64_t counter, service::ProtocolType t);
|
||||
|
||||
/// update local path id and cascade information to parent
|
||||
/// return true if success
|
||||
bool
|
||||
UpdateLocalPath(const llarp::PathID_t& nextPath);
|
||||
|
||||
llarp::path::HopHandler_ptr
|
||||
GetCurrentPath() const
|
||||
{
|
||||
static constexpr size_t MaxUpstreamQueueSize = 256;
|
||||
return m_CurrentPath;
|
||||
}
|
||||
|
||||
explicit Endpoint(
|
||||
const llarp::PubKey& remoteIdent,
|
||||
const llarp::path::HopHandler_ptr& path,
|
||||
bool rewriteIP,
|
||||
huint128_t ip,
|
||||
llarp::handlers::ExitEndpoint* parent);
|
||||
const llarp::PubKey&
|
||||
PubKey() const
|
||||
{
|
||||
return m_remoteSignKey;
|
||||
}
|
||||
|
||||
~Endpoint();
|
||||
uint64_t
|
||||
TxRate() const
|
||||
{
|
||||
return m_TxRate;
|
||||
}
|
||||
|
||||
/// close ourselves
|
||||
void
|
||||
Close();
|
||||
uint64_t
|
||||
RxRate() const
|
||||
{
|
||||
return m_RxRate;
|
||||
}
|
||||
|
||||
/// implement istateful
|
||||
util::StatusObject
|
||||
ExtractStatus() const;
|
||||
huint128_t
|
||||
LocalIP() const
|
||||
{
|
||||
return m_IP;
|
||||
}
|
||||
|
||||
/// return true if we are expired right now
|
||||
bool
|
||||
IsExpired(llarp_time_t now) const;
|
||||
const llarp_time_t createdAt;
|
||||
|
||||
private:
|
||||
AbstractRouter& _router;
|
||||
llarp::PubKey m_remoteSignKey;
|
||||
llarp::path::HopHandler_ptr m_CurrentPath;
|
||||
llarp::huint128_t m_IP;
|
||||
uint64_t m_TxRate, m_RxRate;
|
||||
llarp_time_t m_LastActive;
|
||||
bool m_RewriteSource;
|
||||
using InboundTrafficQueue_t = std::deque<llarp::routing::TransferTrafficMessage>;
|
||||
using TieredQueue = std::map<uint8_t, InboundTrafficQueue_t>;
|
||||
// maps number of fragments the message will fit in to the queue for it
|
||||
TieredQueue m_DownstreamQueues;
|
||||
|
||||
struct UpstreamBuffer
|
||||
{
|
||||
UpstreamBuffer(llarp::net::IPPacket p, uint64_t c) : pkt{std::move(p)}, counter(c)
|
||||
{}
|
||||
|
||||
llarp::net::IPPacket pkt;
|
||||
uint64_t counter;
|
||||
|
||||
bool
|
||||
ExpiresSoon(llarp_time_t now, llarp_time_t dlt = 5s) const;
|
||||
|
||||
/// return true if this endpoint looks dead right now
|
||||
bool
|
||||
LooksDead(llarp_time_t now, llarp_time_t timeout = 10s) const;
|
||||
|
||||
/// tick ourself, reset tx/rx rates
|
||||
void
|
||||
Tick(llarp_time_t now);
|
||||
|
||||
/// queue traffic from service node / internet to be transmitted
|
||||
bool
|
||||
QueueInboundTraffic(std::vector<byte_t> data, service::ProtocolType t);
|
||||
|
||||
/// flush inbound and outbound traffic queues
|
||||
bool
|
||||
Flush();
|
||||
|
||||
/// queue outbound traffic
|
||||
/// does ip rewrite here
|
||||
bool
|
||||
QueueOutboundTraffic(
|
||||
PathID_t txid, std::vector<byte_t> data, uint64_t counter, service::ProtocolType t);
|
||||
|
||||
/// update local path id and cascade information to parent
|
||||
/// return true if success
|
||||
bool
|
||||
UpdateLocalPath(const llarp::PathID_t& nextPath);
|
||||
|
||||
llarp::path::HopHandler_ptr
|
||||
GetCurrentPath() const
|
||||
operator<(const UpstreamBuffer& other) const
|
||||
{
|
||||
return m_CurrentPath;
|
||||
return counter < other.counter;
|
||||
}
|
||||
|
||||
const llarp::PubKey&
|
||||
PubKey() const
|
||||
{
|
||||
return m_remoteSignKey;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
TxRate() const
|
||||
{
|
||||
return m_TxRate;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
RxRate() const
|
||||
{
|
||||
return m_RxRate;
|
||||
}
|
||||
|
||||
huint128_t
|
||||
LocalIP() const
|
||||
{
|
||||
return m_IP;
|
||||
}
|
||||
|
||||
const llarp_time_t createdAt;
|
||||
|
||||
private:
|
||||
llarp::handlers::ExitEndpoint* m_Parent;
|
||||
llarp::PubKey m_remoteSignKey;
|
||||
llarp::path::HopHandler_ptr m_CurrentPath;
|
||||
llarp::huint128_t m_IP;
|
||||
uint64_t m_TxRate, m_RxRate;
|
||||
llarp_time_t m_LastActive;
|
||||
bool m_RewriteSource;
|
||||
using InboundTrafficQueue_t = std::deque<llarp::routing::TransferTrafficMessage>;
|
||||
using TieredQueue = std::map<uint8_t, InboundTrafficQueue_t>;
|
||||
// maps number of fragments the message will fit in to the queue for it
|
||||
TieredQueue m_DownstreamQueues;
|
||||
|
||||
struct UpstreamBuffer
|
||||
{
|
||||
UpstreamBuffer(llarp::net::IPPacket p, uint64_t c) : pkt{std::move(p)}, counter(c)
|
||||
{}
|
||||
|
||||
llarp::net::IPPacket pkt;
|
||||
uint64_t counter;
|
||||
|
||||
bool
|
||||
operator<(const UpstreamBuffer& other) const
|
||||
{
|
||||
return counter < other.counter;
|
||||
}
|
||||
};
|
||||
|
||||
using UpstreamQueue_t = std::priority_queue<UpstreamBuffer>;
|
||||
UpstreamQueue_t m_UpstreamQueue;
|
||||
uint64_t m_Counter;
|
||||
};
|
||||
} // namespace exit
|
||||
} // namespace llarp
|
||||
|
||||
using UpstreamQueue_t = std::priority_queue<UpstreamBuffer>;
|
||||
UpstreamQueue_t m_UpstreamQueue;
|
||||
uint64_t m_Counter;
|
||||
};
|
||||
|
||||
} // namespace llarp::exit
|
||||
|
|
|
@ -27,8 +27,7 @@ namespace llarp
|
|||
{
|
||||
std::array<byte_t, 1024> tmp;
|
||||
llarp_buffer_t buf(tmp);
|
||||
ObtainExitMessage copy;
|
||||
copy = *this;
|
||||
ObtainExitMessage copy{*this};
|
||||
copy.Z.Zero();
|
||||
if (!copy.BEncode(&buf))
|
||||
{
|
||||
|
@ -140,8 +139,7 @@ namespace llarp
|
|||
{
|
||||
std::array<byte_t, 512> tmp;
|
||||
llarp_buffer_t buf(tmp);
|
||||
GrantExitMessage copy;
|
||||
copy = *this;
|
||||
GrantExitMessage copy{*this};
|
||||
copy.Z.Zero();
|
||||
if (!copy.BEncode(&buf))
|
||||
return false;
|
||||
|
@ -231,8 +229,8 @@ namespace llarp
|
|||
{
|
||||
std::array<byte_t, 512> tmp;
|
||||
llarp_buffer_t buf(tmp);
|
||||
RejectExitMessage copy;
|
||||
copy = *this;
|
||||
RejectExitMessage copy{*this};
|
||||
|
||||
copy.Z.Zero();
|
||||
if (!copy.BEncode(&buf))
|
||||
return false;
|
||||
|
@ -289,8 +287,8 @@ namespace llarp
|
|||
{
|
||||
std::array<byte_t, 512> tmp;
|
||||
llarp_buffer_t buf(tmp);
|
||||
UpdateExitMessage copy;
|
||||
copy = *this;
|
||||
UpdateExitMessage copy{*this};
|
||||
|
||||
copy.Z.Zero();
|
||||
if (!copy.BEncode(&buf))
|
||||
return false;
|
||||
|
@ -389,8 +387,8 @@ namespace llarp
|
|||
{
|
||||
std::array<byte_t, 512> tmp;
|
||||
llarp_buffer_t buf(tmp);
|
||||
CloseExitMessage copy;
|
||||
copy = *this;
|
||||
CloseExitMessage copy{*this};
|
||||
|
||||
copy.Z.Zero();
|
||||
if (!copy.BEncode(&buf))
|
||||
return false;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <llarp/crypto/types.hpp>
|
||||
#include "policy.hpp"
|
||||
#include <llarp/routing/message.hpp>
|
||||
|
||||
#include <ratio>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
|
@ -12,15 +14,23 @@ namespace llarp
|
|||
{
|
||||
struct ObtainExitMessage final : public IMessage
|
||||
{
|
||||
std::vector<llarp::exit::Policy> B;
|
||||
uint64_t E{0};
|
||||
llarp::PubKey I;
|
||||
uint64_t T{0};
|
||||
std::vector<llarp::exit::Policy> W;
|
||||
uint64_t X{0};
|
||||
llarp::Signature Z;
|
||||
std::vector<llarp::exit::Policy> blacklist;
|
||||
uint64_t wants_exit{0};
|
||||
llarp::PubKey source_identity;
|
||||
uint64_t txid{0};
|
||||
std::vector<llarp::exit::Policy> whitelist;
|
||||
uint64_t request_expires_at{0};
|
||||
llarp::Signature sig;
|
||||
|
||||
ObtainExitMessage() : IMessage()
|
||||
decltype(blacklist)& B{blacklist};
|
||||
decltype(wants_exit)& E{wants_exit};
|
||||
decltype(source_identity)& I{source_identity};
|
||||
decltype(txid)& T{txid};
|
||||
decltype(whitelist)& W{whitelist};
|
||||
decltype(request_expires_at)& X{request_expires_at};
|
||||
decltype(sig)& Z{sig};
|
||||
|
||||
ObtainExitMessage() : IMessage{}
|
||||
{}
|
||||
|
||||
~ObtainExitMessage() override = default;
|
||||
|
@ -28,13 +38,14 @@ namespace llarp
|
|||
void
|
||||
Clear() override
|
||||
{
|
||||
B.clear();
|
||||
E = 0;
|
||||
I.Zero();
|
||||
T = 0;
|
||||
W.clear();
|
||||
X = 0;
|
||||
Z.Zero();
|
||||
IMessage::Clear();
|
||||
blacklist.clear();
|
||||
wants_exit = 0;
|
||||
source_identity.Zero();
|
||||
txid = 0;
|
||||
whitelist.clear();
|
||||
request_expires_at = 0;
|
||||
sig.Zero();
|
||||
}
|
||||
|
||||
/// populates I and signs
|
||||
|
@ -52,15 +63,22 @@ namespace llarp
|
|||
|
||||
bool
|
||||
HandleMessage(IMessageHandler* h, AbstractRouter* r) const override;
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
};
|
||||
|
||||
struct GrantExitMessage final : public IMessage
|
||||
{
|
||||
using Nonce_t = llarp::AlignedBuffer<16>;
|
||||
|
||||
uint64_t T;
|
||||
Nonce_t Y;
|
||||
llarp::Signature Z;
|
||||
uint64_t txid;
|
||||
Nonce_t nounce;
|
||||
llarp::Signature sig;
|
||||
|
||||
decltype(txid)& T{txid};
|
||||
decltype(nounce)& Y{nounce};
|
||||
decltype(sig)& Z{sig};
|
||||
|
||||
bool
|
||||
BEncode(llarp_buffer_t* buf) const override;
|
||||
|
@ -80,7 +98,8 @@ namespace llarp
|
|||
void
|
||||
Clear() override
|
||||
{
|
||||
T = 0;
|
||||
IMessage::Clear();
|
||||
txid = 0;
|
||||
Y.Zero();
|
||||
Z.Zero();
|
||||
}
|
||||
|
@ -89,20 +108,27 @@ namespace llarp
|
|||
struct RejectExitMessage final : public IMessage
|
||||
{
|
||||
using Nonce_t = llarp::AlignedBuffer<16>;
|
||||
uint64_t B;
|
||||
std::vector<llarp::exit::Policy> R;
|
||||
uint64_t T;
|
||||
Nonce_t Y;
|
||||
llarp::Signature Z;
|
||||
uint64_t backoff;
|
||||
std::vector<llarp::exit::Policy> rejected_policies;
|
||||
uint64_t txid;
|
||||
Nonce_t nonce;
|
||||
llarp::Signature sig;
|
||||
|
||||
decltype(backoff)& B{backoff};
|
||||
decltype(rejected_policies)& R{rejected_policies};
|
||||
decltype(txid)& T{txid};
|
||||
decltype(nonce)& Y{nonce};
|
||||
decltype(sig)& Z{sig};
|
||||
|
||||
void
|
||||
Clear() override
|
||||
{
|
||||
B = 0;
|
||||
R.clear();
|
||||
T = 0;
|
||||
Y.Zero();
|
||||
Z.Zero();
|
||||
IMessage::Clear();
|
||||
backoff = 0;
|
||||
rejected_policies.clear();
|
||||
txid = 0;
|
||||
nonce.Zero();
|
||||
sig.Zero();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -124,9 +150,13 @@ namespace llarp
|
|||
struct UpdateExitVerifyMessage final : public IMessage
|
||||
{
|
||||
using Nonce_t = llarp::AlignedBuffer<16>;
|
||||
uint64_t T;
|
||||
Nonce_t Y;
|
||||
llarp::Signature Z;
|
||||
uint64_t txid;
|
||||
Nonce_t nounce;
|
||||
llarp::Signature sig;
|
||||
|
||||
decltype(txid)& T{txid};
|
||||
decltype(nounce)& Y{nounce};
|
||||
decltype(sig)& Z{sig};
|
||||
|
||||
~UpdateExitVerifyMessage() override = default;
|
||||
|
||||
|
@ -151,10 +181,15 @@ namespace llarp
|
|||
struct UpdateExitMessage final : public IMessage
|
||||
{
|
||||
using Nonce_t = llarp::AlignedBuffer<16>;
|
||||
llarp::PathID_t P;
|
||||
uint64_t T;
|
||||
Nonce_t Y;
|
||||
llarp::Signature Z;
|
||||
llarp::PathID_t path_id;
|
||||
uint64_t txid;
|
||||
Nonce_t nounce;
|
||||
llarp::Signature sig;
|
||||
|
||||
decltype(path_id)& P{path_id};
|
||||
decltype(txid)& T{txid};
|
||||
decltype(nounce)& Y{nounce};
|
||||
decltype(sig)& Z{sig};
|
||||
|
||||
bool
|
||||
Sign(const llarp::SecretKey& sk);
|
||||
|
@ -174,6 +209,7 @@ namespace llarp
|
|||
void
|
||||
Clear() override
|
||||
{
|
||||
IMessage::Clear();
|
||||
P.Zero();
|
||||
T = 0;
|
||||
Y.Zero();
|
||||
|
@ -185,8 +221,10 @@ namespace llarp
|
|||
{
|
||||
using Nonce_t = llarp::AlignedBuffer<16>;
|
||||
|
||||
Nonce_t Y;
|
||||
llarp::Signature Z;
|
||||
Nonce_t nounce;
|
||||
llarp::Signature sig;
|
||||
decltype(nounce)& Y{nounce};
|
||||
decltype(sig)& Z{sig};
|
||||
|
||||
bool
|
||||
BEncode(llarp_buffer_t* buf) const override;
|
||||
|
@ -206,6 +244,7 @@ namespace llarp
|
|||
void
|
||||
Clear() override
|
||||
{
|
||||
IMessage::Clear();
|
||||
Y.Zero();
|
||||
Z.Zero();
|
||||
}
|
||||
|
|
|
@ -114,7 +114,7 @@ namespace llarp
|
|||
bool
|
||||
IsReady() const;
|
||||
|
||||
const llarp::RouterID
|
||||
llarp::RouterID
|
||||
Endpoint() const
|
||||
{
|
||||
return m_ExitRouter;
|
||||
|
@ -218,8 +218,7 @@ namespace llarp
|
|||
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const override
|
||||
{
|
||||
// TODO: set expiration time
|
||||
msg.X = 0;
|
||||
msg.E = 1;
|
||||
msg.wants_exit = 1;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -247,8 +246,7 @@ namespace llarp
|
|||
PopulateRequest(llarp::routing::ObtainExitMessage& msg) const override
|
||||
{
|
||||
// TODO: set expiration time
|
||||
msg.X = 0;
|
||||
msg.E = 0;
|
||||
msg.wants_exit = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
#include "exit.hpp"
|
||||
|
||||
#include <llarp/dns/dns.hpp>
|
||||
#include <llarp/dns/server.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/path/path_context.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <llarp/util/bits.hpp>
|
||||
#include <llarp/util/underlying.hpp>
|
||||
|
||||
#include <llarp/quic/tunnel.hpp>
|
||||
#include <llarp/router/i_rc_lookup_handler.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <llarp/service/protocol_type.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -97,7 +98,7 @@ namespace llarp
|
|||
|
||||
bool
|
||||
ExitEndpoint::SendToOrQueue(
|
||||
service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType type)
|
||||
service::ConvoTag tag, std::vector<byte_t> payload, service::ProtocolType type)
|
||||
{
|
||||
if (auto maybeAddr = GetEndpointWithConvoTag(tag))
|
||||
{
|
||||
|
@ -109,7 +110,7 @@ namespace llarp
|
|||
{
|
||||
if (not itr->second->LooksDead(Now()))
|
||||
{
|
||||
if (itr->second->QueueInboundTraffic(payload.copy(), type))
|
||||
if (itr->second->QueueInboundTraffic(std::move(payload), type))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -117,10 +118,10 @@ namespace llarp
|
|||
if (not m_Router->PathToRouterAllowed(*rid))
|
||||
return false;
|
||||
|
||||
ObtainSNodeSession(*rid, [pkt = payload.copy(), type](auto session) mutable {
|
||||
ObtainSNodeSession(*rid, [payload, type](auto session) mutable {
|
||||
if (session and session->IsReady())
|
||||
{
|
||||
session->SendPacketToRemote(std::move(pkt), type);
|
||||
session->SendPacketToRemote(std::move(payload), type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -189,19 +190,25 @@ namespace llarp
|
|||
{
|
||||
if (msg.questions.size() == 0)
|
||||
return false;
|
||||
const auto& question = msg.questions[0];
|
||||
|
||||
if (not(question.tld() == "snode" or question.tld() == "loki"))
|
||||
return false;
|
||||
|
||||
const auto& qtype = question.qtype;
|
||||
// always hook ptr for ranges we own
|
||||
if (msg.questions[0].qtype == dns::qTypePTR)
|
||||
if (qtype == to_underlying(dns::RRType::PTR))
|
||||
{
|
||||
if (auto ip = dns::DecodePTR(msg.questions[0].qname))
|
||||
if (auto ip = dns::DecodePTR(msg.questions[0].qname()))
|
||||
return m_OurRange.Contains(*ip);
|
||||
return false;
|
||||
}
|
||||
if (msg.questions[0].qtype == dns::qTypeA || msg.questions[0].qtype == dns::qTypeCNAME
|
||||
|| msg.questions[0].qtype == dns::qTypeAAAA)
|
||||
if (qtype == to_underlying(dns::RRType::A) or qtype == to_underlying(dns::RRType::AAAA)
|
||||
or qtype == to_underlying(dns::RRType::CNAME))
|
||||
{
|
||||
if (msg.questions[0].IsName("localhost.loki"))
|
||||
if (question.tld() == "snode")
|
||||
return true;
|
||||
if (msg.questions[0].HasTLD(".snode"))
|
||||
if (question.tld() == "loki")
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -226,94 +233,109 @@ namespace llarp
|
|||
bool
|
||||
ExitEndpoint::HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)> reply)
|
||||
{
|
||||
if (msg.questions[0].qtype == dns::qTypePTR)
|
||||
auto name = msg.questions[0].qname();
|
||||
if (msg.questions[0].qtype == to_underlying(dns::RRType::PTR))
|
||||
{
|
||||
auto ip = dns::DecodePTR(msg.questions[0].qname);
|
||||
auto ip = dns::DecodePTR(name);
|
||||
if (not ip)
|
||||
return false;
|
||||
if (ip == m_IfAddr)
|
||||
{
|
||||
RouterID us = GetRouter()->pubkey();
|
||||
msg.AddAReply(us.ToString(), 300);
|
||||
msg.answers.emplace_back(
|
||||
name, dns::RRType::PTR, dns::RData{dns::split_dns_name(us.ToString())}, 300);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto itr = m_IPToKey.find(*ip);
|
||||
if (itr != m_IPToKey.end() && m_SNodeKeys.find(itr->second) != m_SNodeKeys.end())
|
||||
{
|
||||
RouterID them = itr->second;
|
||||
msg.AddAReply(them.ToString());
|
||||
msg.answers.emplace_back(
|
||||
name,
|
||||
dns::RRType::PTR,
|
||||
dns::RData{dns::split_dns_name(itr->second.ToString())},
|
||||
300);
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypeCNAME)
|
||||
else if (msg.questions[0].qtype == to_underlying(dns::RRType::CNAME))
|
||||
{
|
||||
if (msg.questions[0].IsName("random.snode"))
|
||||
{
|
||||
RouterID random;
|
||||
if (GetRouter()->GetRandomGoodRouter(random))
|
||||
msg.AddCNAMEReply(random.ToString(), 1);
|
||||
msg.answers.emplace_back(
|
||||
name, dns::RRType::CNAME, dns::RData{dns::split_dns_name(random.ToString())}, 1);
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
else if (msg.questions[0].IsName("localhost.loki"))
|
||||
{
|
||||
RouterID us = m_Router->pubkey();
|
||||
msg.AddAReply(us.ToString(), 1);
|
||||
msg.answers.emplace_back(
|
||||
name, dns::RRType::PTR, dns::RData{dns::split_dns_name(us.ToString())}, 1);
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypeA || msg.questions[0].qtype == dns::qTypeAAAA)
|
||||
else if (msg.questions[0].qtype == to_underlying(dns::RRType::A))
|
||||
{
|
||||
const bool isV6 = msg.questions[0].qtype == dns::qTypeAAAA;
|
||||
const bool isV4 = msg.questions[0].qtype == dns::qTypeA;
|
||||
if (msg.questions[0].IsName("random.snode"))
|
||||
{
|
||||
RouterID random;
|
||||
if (GetRouter()->GetRandomGoodRouter(random))
|
||||
{
|
||||
msg.AddCNAMEReply(random.ToString(), 1);
|
||||
auto ip = ObtainServiceNodeIP(random);
|
||||
msg.AddINReply(ip, false);
|
||||
msg.answers.emplace_back(
|
||||
name, dns::RRType::CNAME, dns::RData{dns::split_dns_name(random.ToString())}, 1);
|
||||
auto rdata = var::visit(
|
||||
[](auto&& ip) { return dns::RData{ip}; },
|
||||
net::maybe_truncate_ip(ToNet(ObtainServiceNodeIP(random))));
|
||||
msg.answers.emplace_back(name, dns::RRType::A, std::move(rdata), 1);
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
reply(msg);
|
||||
return true;
|
||||
}
|
||||
if (msg.questions[0].IsName("localhost.loki"))
|
||||
{
|
||||
msg.AddINReply(GetIfAddr(), isV6);
|
||||
auto rdata = var::visit(
|
||||
[](auto&& ip) { return dns::RData{ip}; }, net::maybe_truncate_ip(ToNet(GetIfAddr())));
|
||||
msg.answers.emplace_back(name, dns::RRType::A, std::move(rdata), 1);
|
||||
|
||||
reply(msg);
|
||||
return true;
|
||||
}
|
||||
// forward dns for snode
|
||||
RouterID r;
|
||||
if (r.FromString(msg.questions[0].Name()))
|
||||
auto name = msg.questions[0].qname();
|
||||
if (ends_with(name, "."))
|
||||
name = name.substr(0, name.size() - 2);
|
||||
|
||||
if (r.FromString(name))
|
||||
{
|
||||
huint128_t ip;
|
||||
PubKey pubKey(r);
|
||||
if (isV4 && SupportsV6())
|
||||
{
|
||||
msg.hdr_fields |= dns::flags_QR | dns::flags_AA | dns::flags_RA;
|
||||
}
|
||||
else if (m_SNodeKeys.find(pubKey) == m_SNodeKeys.end())
|
||||
if (m_SNodeKeys.find(pubKey) == m_SNodeKeys.end())
|
||||
{
|
||||
// we do not have it mapped, async obtain it
|
||||
ObtainSNodeSession(
|
||||
r,
|
||||
[&, msg = std::make_shared<dns::Message>(msg), reply](
|
||||
[this, pubKey, msg = std::make_shared<dns::Message>(msg), reply](
|
||||
std::shared_ptr<exit::BaseSession> session) {
|
||||
auto name = msg->questions[0].qname();
|
||||
if (session && session->IsReady())
|
||||
{
|
||||
msg->AddINReply(m_KeyToIP[pubKey], isV6);
|
||||
auto rdata = var::visit(
|
||||
[](auto&& ip) { return dns::RData{ip}; },
|
||||
net::maybe_truncate_ip(ToNet(m_KeyToIP[pubKey])));
|
||||
msg->answers.emplace_back(name, dns::RRType::A, std::move(rdata), 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->AddNXReply();
|
||||
msg->nx();
|
||||
}
|
||||
reply(*msg);
|
||||
});
|
||||
|
@ -325,15 +347,17 @@ namespace llarp
|
|||
auto itr = m_KeyToIP.find(pubKey);
|
||||
if (itr != m_KeyToIP.end())
|
||||
{
|
||||
ip = itr->second;
|
||||
msg.AddINReply(ip, isV6);
|
||||
auto rdata = var::visit(
|
||||
[](auto&& ip) { return dns::RData{ip}; },
|
||||
net::maybe_truncate_ip(ToNet(itr->second)));
|
||||
msg.answers.emplace_back(name, dns::RRType::A, std::move(rdata), 1);
|
||||
}
|
||||
else // fallback case that should never happen (probably)
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
reply(msg);
|
||||
return true;
|
||||
|
@ -478,9 +502,8 @@ namespace llarp
|
|||
|
||||
GetRouter()->loop()->add_ticker([this] { Flush(); });
|
||||
#ifndef _WIN32
|
||||
m_Resolver = std::make_shared<dns::Server>(
|
||||
m_Router->loop(), m_DNSConf, if_nametoindex(m_ifname.c_str()));
|
||||
m_Resolver->Start();
|
||||
m_Resolver = std::make_shared<dns::Server>(m_Router->loop(), m_DNSConf);
|
||||
m_Resolver->Start(if_nametoindex(m_ifname.c_str()));
|
||||
|
||||
#endif
|
||||
}
|
||||
|
@ -701,7 +724,7 @@ namespace llarp
|
|||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
bool
|
||||
ExitEndpoint::Configure(const NetworkConfig& networkConfig, const DnsConfig& dnsConfig)
|
||||
{
|
||||
/*
|
||||
|
@ -728,14 +751,7 @@ namespace llarp
|
|||
m_ShouldInitTun = false;
|
||||
}
|
||||
|
||||
m_OurRange = networkConfig.m_ifaddr;
|
||||
if (!m_OurRange.addr.h)
|
||||
{
|
||||
const auto maybe = m_Router->Net().FindFreeRange();
|
||||
if (not maybe.has_value())
|
||||
throw std::runtime_error("cannot find free interface range");
|
||||
m_OurRange = *maybe;
|
||||
}
|
||||
m_OurRange = networkConfig.ifaddr(m_Router->Net());
|
||||
const auto host_str = m_OurRange.BaseAddressString();
|
||||
// string, or just a plain char array?
|
||||
m_IfAddr = m_OurRange.addr;
|
||||
|
@ -743,14 +759,8 @@ namespace llarp
|
|||
m_HigestAddr = m_OurRange.HighestAddr();
|
||||
m_UseV6 = not m_OurRange.IsV4();
|
||||
|
||||
m_ifname = networkConfig.m_ifname;
|
||||
if (m_ifname.empty())
|
||||
{
|
||||
const auto maybe = m_Router->Net().FindFreeTun();
|
||||
if (not maybe.has_value())
|
||||
throw std::runtime_error("cannot find free interface name");
|
||||
m_ifname = *maybe;
|
||||
}
|
||||
m_ifname = networkConfig.ifname(m_Router->Net());
|
||||
|
||||
LogInfo(Name(), " set ifname to ", m_ifname);
|
||||
if (auto* quic = GetQUICTunnel())
|
||||
{
|
||||
|
@ -758,6 +768,7 @@ namespace llarp
|
|||
return llarp::SockAddr{ifaddr, huint16_t{port}};
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
huint128_t
|
||||
|
@ -809,7 +820,7 @@ namespace llarp
|
|||
m_SNodeKeys.emplace(pk.as_array());
|
||||
}
|
||||
m_ActiveExits.emplace(
|
||||
pk, std::make_unique<exit::Endpoint>(pk, handler, !wantInternet, ip, this));
|
||||
pk, std::make_unique<exit::Endpoint>(*m_Router, pk, handler, !wantInternet, ip));
|
||||
|
||||
m_Paths[path] = pk;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp/exit/endpoint.hpp>
|
||||
#include "tun.hpp"
|
||||
#include <llarp/exit/session.hpp>
|
||||
#include <llarp/dns/server.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
|
@ -64,13 +64,22 @@ namespace llarp
|
|||
|
||||
bool
|
||||
SendToOrQueue(
|
||||
service::ConvoTag tag, const llarp_buffer_t& payload, service::ProtocolType t) override;
|
||||
service::ConvoTag tag, std::vector<byte_t> payload, service::ProtocolType t) override;
|
||||
|
||||
std::string_view
|
||||
endpoint_name() const override
|
||||
{
|
||||
return Name();
|
||||
}
|
||||
|
||||
void
|
||||
Tick(llarp_time_t now);
|
||||
|
||||
void
|
||||
Configure(const NetworkConfig& networkConfig, const DnsConfig& dnsConfig);
|
||||
bool
|
||||
Configure(const NetworkConfig& networkConfig, const DnsConfig& dnsConfig) override;
|
||||
|
||||
bool
|
||||
LookupRC(RouterID remote, RouterLookupHandler handler) override;
|
||||
|
||||
std::string
|
||||
Name() const;
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
|
||||
namespace llarp::handlers
|
||||
{
|
||||
struct NullEndpoint final : public llarp::service::Endpoint,
|
||||
public std::enable_shared_from_this<NullEndpoint>
|
||||
struct NullEndpoint final : public llarp::service::Endpoint
|
||||
|
||||
{
|
||||
NullEndpoint(AbstractRouter* r, llarp::service::Context* parent)
|
||||
: llarp::service::Endpoint{r, parent}
|
||||
NullEndpoint(AbstractRouter& r)
|
||||
: llarp::service::Endpoint{r}
|
||||
, m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) {
|
||||
var::visit(
|
||||
[&pkt](auto&& from) {
|
||||
|
@ -22,17 +22,14 @@ namespace llarp::handlers
|
|||
from);
|
||||
}}}
|
||||
{
|
||||
r->loop()->add_ticker([this] { Pump(Now()); });
|
||||
r.loop()->add_ticker([this] { Pump(Now()); });
|
||||
}
|
||||
|
||||
virtual bool
|
||||
bool
|
||||
HandleInboundPacket(
|
||||
const service::ConvoTag tag,
|
||||
const llarp_buffer_t& buf,
|
||||
service::ProtocolType t,
|
||||
uint64_t) override
|
||||
const service::ConvoTag tag, std::vector<byte_t> buf, service::ProtocolType t, uint64_t)
|
||||
{
|
||||
LogTrace("Inbound ", t, " packet (", buf.sz, "B) on convo ", tag);
|
||||
LogTrace("Inbound ", t, " packet (", buf.size(), "B) on convo ", tag);
|
||||
if (t == service::ProtocolType::Control)
|
||||
{
|
||||
return true;
|
||||
|
@ -41,8 +38,8 @@ namespace llarp::handlers
|
|||
{
|
||||
if (auto from = GetEndpointWithConvoTag(tag))
|
||||
{
|
||||
net::IPPacket pkt{};
|
||||
if (not pkt.Load(buf))
|
||||
net::IPPacket pkt{std::move(buf)};
|
||||
if (pkt.empty())
|
||||
{
|
||||
LogWarn("invalid ip packet from remote T=", tag);
|
||||
return false;
|
||||
|
@ -65,35 +62,23 @@ namespace llarp::handlers
|
|||
LogWarn("incoming quic packet but this endpoint is not quic capable; dropping");
|
||||
return false;
|
||||
}
|
||||
if (buf.sz < 4)
|
||||
if (buf.size() < 4)
|
||||
{
|
||||
LogWarn("invalid incoming quic packet, dropping");
|
||||
return false;
|
||||
}
|
||||
quic->receive_packet(tag, buf);
|
||||
quic->receive_packet(tag, std::move(buf));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string
|
||||
GetIfName() const override
|
||||
GetIfName() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
path::PathSet_ptr
|
||||
GetSelf() override
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
std::weak_ptr<path::PathSet>
|
||||
GetWeak() override
|
||||
{
|
||||
return weak_from_this();
|
||||
}
|
||||
|
||||
bool
|
||||
SupportsV6() const override
|
||||
SupportsV6() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -101,18 +86,6 @@ namespace llarp::handlers
|
|||
void
|
||||
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
|
||||
|
||||
huint128_t
|
||||
ObtainIPForAddr(std::variant<service::Address, RouterID>) override
|
||||
{
|
||||
return {0};
|
||||
}
|
||||
|
||||
std::optional<std::variant<service::Address, RouterID>>
|
||||
ObtainAddrForIP(huint128_t) const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
vpn::EgresPacketRouter*
|
||||
EgresPacketRouter() override
|
||||
{
|
||||
|
|
|
@ -8,7 +8,8 @@
|
|||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include <llarp/dns/dns.hpp>
|
||||
#include <llarp/dns/server.hpp>
|
||||
#include <llarp/dns/bits.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
|
@ -25,6 +26,7 @@
|
|||
#include <llarp/rpc/endpoint_rpc.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <llarp/util/logging/buffer.hpp>
|
||||
#include <llarp/util/underlying.hpp>
|
||||
#include <llarp/dns/srv_data.hpp>
|
||||
#include <llarp/constants/net.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
@ -117,17 +119,17 @@ namespace llarp
|
|||
{
|
||||
std::optional<SockAddr> m_QueryBind;
|
||||
net::ipaddr_t m_OurIP;
|
||||
TunEndpoint* const m_Endpoint;
|
||||
TunEndpoint& m_Endpoint;
|
||||
|
||||
public:
|
||||
std::shared_ptr<dns::PacketSource_Base> PacketSource;
|
||||
|
||||
virtual ~TunDNS() = default;
|
||||
|
||||
explicit TunDNS(TunEndpoint* ep, const llarp::DnsConfig& conf)
|
||||
: dns::Server{ep->Router()->loop(), conf, 0}
|
||||
TunDNS(TunEndpoint& ep, const llarp::DnsConfig& conf)
|
||||
: dns::Server{ep.Router()->loop(), conf}
|
||||
, m_QueryBind{conf.m_QueryBind}
|
||||
, m_OurIP{ToNet(ep->GetIfAddr())}
|
||||
, m_OurIP{ToNet(ep.GetIfAddr())}
|
||||
, m_Endpoint{ep}
|
||||
{}
|
||||
|
||||
|
@ -135,8 +137,8 @@ namespace llarp
|
|||
MakePacketSourceOn(const SockAddr&, const llarp::DnsConfig& conf) override
|
||||
{
|
||||
auto ptr = std::make_shared<DnsInterceptor>(
|
||||
[ep = m_Endpoint](auto pkt) {
|
||||
ep->HandleWriteIPPacket(pkt.ConstBuffer(), pkt.srcv6(), pkt.dstv6(), 0);
|
||||
[this](auto pkt) {
|
||||
m_Endpoint.HandleWriteIPPacket(pkt.ConstBuffer(), pkt.srcv6(), pkt.dstv6(), 0);
|
||||
},
|
||||
m_OurIP,
|
||||
conf);
|
||||
|
@ -145,8 +147,7 @@ namespace llarp
|
|||
}
|
||||
};
|
||||
|
||||
TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent)
|
||||
: service::Endpoint{r, parent}
|
||||
TunEndpoint::TunEndpoint(AbstractRouter& r) : service::Endpoint{r}
|
||||
{
|
||||
m_PacketRouter = std::make_shared<vpn::PacketRouter>(
|
||||
[this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
|
||||
|
@ -158,7 +159,7 @@ namespace llarp
|
|||
const auto& info = GetVPNInterface()->Info();
|
||||
if (m_DnsConfig.m_raw_dns)
|
||||
{
|
||||
auto dns = std::make_shared<TunDNS>(this, m_DnsConfig);
|
||||
auto dns = std::make_shared<TunDNS>(*this, m_DnsConfig);
|
||||
m_DNS = dns;
|
||||
|
||||
m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) {
|
||||
|
@ -173,10 +174,10 @@ namespace llarp
|
|||
});
|
||||
}
|
||||
else
|
||||
m_DNS = std::make_shared<dns::Server>(Loop(), m_DnsConfig, info.index);
|
||||
m_DNS = std::make_shared<dns::Server>(Loop(), m_DnsConfig);
|
||||
|
||||
m_DNS->AddResolver(weak_from_this());
|
||||
m_DNS->Start();
|
||||
m_DNS->AddResolver(std::enable_shared_from_this<TunEndpoint>::weak_from_this());
|
||||
m_DNS->Start(info.index);
|
||||
|
||||
if (m_DnsConfig.m_raw_dns)
|
||||
{
|
||||
|
@ -307,7 +308,7 @@ namespace llarp
|
|||
conf.m_AuthWhitelist,
|
||||
conf.m_AuthStaticTokens,
|
||||
Router()->lmq(),
|
||||
shared_from_this());
|
||||
std::enable_shared_from_this<TunEndpoint>::shared_from_this());
|
||||
auth->Start();
|
||||
m_AuthPolicy = std::move(auth);
|
||||
}
|
||||
|
@ -331,25 +332,8 @@ namespace llarp
|
|||
return false;
|
||||
}
|
||||
|
||||
m_IfName = conf.m_ifname;
|
||||
if (m_IfName.empty())
|
||||
{
|
||||
const auto maybe = m_router->Net().FindFreeTun();
|
||||
if (not maybe.has_value())
|
||||
throw std::runtime_error("cannot find free interface name");
|
||||
m_IfName = *maybe;
|
||||
}
|
||||
|
||||
m_OurRange = conf.m_ifaddr;
|
||||
if (!m_OurRange.addr.h)
|
||||
{
|
||||
const auto maybe = m_router->Net().FindFreeRange();
|
||||
if (not maybe.has_value())
|
||||
{
|
||||
throw std::runtime_error("cannot find free address range");
|
||||
}
|
||||
m_OurRange = *maybe;
|
||||
}
|
||||
m_IfName = conf.ifname(m_router->Net());
|
||||
m_OurRange = conf.ifaddr(m_router->Net());
|
||||
|
||||
m_OurIP = m_OurRange.addr;
|
||||
m_UseV6 = false;
|
||||
|
@ -504,7 +488,7 @@ namespace llarp
|
|||
msg.authorities.resize(0);
|
||||
msg.additional.resize(0);
|
||||
msg.answers.resize(0);
|
||||
msg.hdr_fields &= ~dns::flags_RCODENameError;
|
||||
msg.hdr.rcode(~(dns::bits::rcode_name_error | dns::bits::rcode_servfail));
|
||||
return msg;
|
||||
}
|
||||
|
||||
|
@ -590,25 +574,19 @@ namespace llarp
|
|||
const auto& answer = msg.answers[0];
|
||||
if (answer.HasCNameForTLD(".snode"))
|
||||
{
|
||||
llarp_buffer_t buf(answer.rData);
|
||||
auto qname = dns::DecodeName(&buf, true);
|
||||
if (not qname)
|
||||
return false;
|
||||
auto name = answer.rr_data.as_dns_name();
|
||||
RouterID addr;
|
||||
if (not addr.FromString(*qname))
|
||||
if (not addr.FromString(name))
|
||||
return false;
|
||||
auto replyMsg = std::make_shared<dns::Message>(clear_dns_message(msg));
|
||||
return ReplyToSNodeDNSWhenReady(addr, std::move(replyMsg), false);
|
||||
}
|
||||
else if (answer.HasCNameForTLD(".loki"))
|
||||
{
|
||||
llarp_buffer_t buf(answer.rData);
|
||||
auto qname = dns::DecodeName(&buf, true);
|
||||
if (not qname)
|
||||
return false;
|
||||
auto name = answer.rr_data.as_dns_name();
|
||||
|
||||
service::Address addr;
|
||||
if (not addr.FromString(*qname))
|
||||
if (not addr.FromString(name))
|
||||
return false;
|
||||
|
||||
auto replyMsg = std::make_shared<dns::Message>(clear_dns_message(msg));
|
||||
|
@ -620,30 +598,28 @@ namespace llarp
|
|||
llarp::LogWarn("bad number of dns questions: ", msg.questions.size());
|
||||
return false;
|
||||
}
|
||||
std::string qname = msg.questions[0].Name();
|
||||
const auto nameparts = split(qname, ".");
|
||||
std::string lnsName;
|
||||
if (nameparts.size() >= 2 and ends_with(qname, ".loki"))
|
||||
{
|
||||
lnsName = nameparts[nameparts.size() - 2];
|
||||
lnsName += ".loki"sv;
|
||||
}
|
||||
if (msg.questions[0].qtype == dns::qTypeTXT)
|
||||
|
||||
auto qname = msg.questions[0].qname();
|
||||
qname = qname.substr(0, qname.size() - 2);
|
||||
std::string lnsName = qname;
|
||||
if (msg.questions[0].qtype == to_underlying(dns::RRType::TXT))
|
||||
{
|
||||
RouterID snode;
|
||||
if (snode.FromString(qname))
|
||||
{
|
||||
m_router->LookupRouter(snode, [reply, msg = std::move(msg)](const auto& found) mutable {
|
||||
m_router->LookupRouter(snode, [reply, _msg = std::move(msg), qname](const auto& found) {
|
||||
dns::Message msg{_msg};
|
||||
if (found.empty())
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string recs;
|
||||
for (const auto& rc : found)
|
||||
recs += rc.ToTXTRecord();
|
||||
msg.AddTXTReply(std::move(recs));
|
||||
|
||||
msg.AddTXTReply(std::string_view{recs}, 1);
|
||||
}
|
||||
reply(msg);
|
||||
});
|
||||
|
@ -660,30 +636,32 @@ namespace llarp
|
|||
m_ExitMap.ForEachEntry([&s](const auto& range, const auto& exit) {
|
||||
fmt::format_to(std::back_inserter(s), "{}={}; ", range, exit);
|
||||
});
|
||||
msg.AddTXTReply(std::move(s));
|
||||
|
||||
msg.AddTXTReply(std::string_view{s}, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
}
|
||||
else if (subdomain == "netid")
|
||||
{
|
||||
msg.AddTXTReply(fmt::format("netid={};", m_router->rc().netID));
|
||||
std::string s = fmt::format("netid={};", m_router->rc().netID);
|
||||
msg.AddTXTReply(std::string_view{s}, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
|
||||
reply(msg);
|
||||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypeMX)
|
||||
else if (msg.questions[0].qtype == to_underlying(dns::RRType::MX))
|
||||
{
|
||||
// mx record
|
||||
service::Address addr;
|
||||
|
@ -695,23 +673,23 @@ namespace llarp
|
|||
else if (service::NameIsValid(lnsName))
|
||||
{
|
||||
LookupNameAsync(lnsName, [msg, lnsName, reply](auto maybe) mutable {
|
||||
if (maybe.has_value())
|
||||
if (maybe)
|
||||
{
|
||||
var::visit([&](auto&& value) { msg.AddMXReply(value.ToString(), 1); }, *maybe);
|
||||
var::visit([&msg](auto&& value) { msg.AddMXReply(value.ToString(), 1); }, *maybe);
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
reply(msg);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
reply(msg);
|
||||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypeCNAME)
|
||||
else if (msg.questions[0].qtype == to_underlying(dns::RRType::CNAME))
|
||||
{
|
||||
if (is_random_snode(msg))
|
||||
{
|
||||
|
@ -721,7 +699,7 @@ namespace llarp
|
|||
msg.AddCNAMEReply(random.ToString(), 1);
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
else if (msg.questions[0].IsLocalhost() and msg.questions[0].HasSubdomains())
|
||||
{
|
||||
|
@ -733,34 +711,28 @@ namespace llarp
|
|||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
}
|
||||
else if (is_localhost_loki(msg))
|
||||
{
|
||||
size_t counter = 0;
|
||||
context->ForEachService(
|
||||
[&](const std::string&, const std::shared_ptr<service::Endpoint>& service) -> bool {
|
||||
const service::Address addr = service->GetIdentity().pub.Addr();
|
||||
msg.AddCNAMEReply(addr.ToString(), 1);
|
||||
++counter;
|
||||
return true;
|
||||
});
|
||||
if (counter == 0)
|
||||
msg.AddNXReply();
|
||||
const service::Address addr = GetIdentity().pub.Addr();
|
||||
msg.AddCNAMEReply(addr.ToString(), 1);
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
reply(msg);
|
||||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypeA || msg.questions[0].qtype == dns::qTypeAAAA)
|
||||
else if (
|
||||
msg.questions[0].qtype == to_underlying(dns::RRType::A)
|
||||
or msg.questions[0].qtype == to_underlying(dns::RRType::AAAA))
|
||||
{
|
||||
const bool isV6 = msg.questions[0].qtype == dns::qTypeAAAA;
|
||||
const bool isV4 = msg.questions[0].qtype == dns::qTypeA;
|
||||
const bool isV6 = msg.questions[0].qtype == to_underlying(dns::RRType::AAAA);
|
||||
const bool isV4 = msg.questions[0].qtype == to_underlying(dns::RRType::A);
|
||||
llarp::service::Address addr;
|
||||
if (isV6 && !SupportsV6())
|
||||
{ // empty reply but not a NXDOMAIN so that client can retry IPv4
|
||||
msg.AddNSReply("localhost.loki.");
|
||||
msg.AddNSReply("localhost.loki.", 1);
|
||||
}
|
||||
// on MacOS this is a typeA query
|
||||
else if (is_random_snode(msg))
|
||||
|
@ -772,7 +744,7 @@ namespace llarp
|
|||
return ReplyToSNodeDNSWhenReady(random, std::make_shared<dns::Message>(msg), isV6);
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
else if (is_localhost_loki(msg))
|
||||
{
|
||||
|
@ -784,48 +756,35 @@ namespace llarp
|
|||
{
|
||||
if (HasExit())
|
||||
{
|
||||
m_ExitMap.ForEachEntry(
|
||||
[&msg](const auto&, const auto& exit) { msg.AddCNAMEReply(exit.ToString()); });
|
||||
msg.AddINReply(ip, isV6);
|
||||
m_ExitMap.ForEachEntry([&msg](const auto&, const auto& exit) {
|
||||
msg.AddCNAMEReply(exit.ToString(), 1);
|
||||
});
|
||||
msg.rr_add_ipaddr(net::maybe_truncate_ip(ToNet(ip)));
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddCNAMEReply(m_Identity.pub.Name(), 1);
|
||||
msg.AddINReply(ip, isV6);
|
||||
msg.rr_add_ipaddr(net::maybe_truncate_ip(ToNet(ip)));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
}
|
||||
}
|
||||
else if (addr.FromString(qname, ".loki"))
|
||||
{
|
||||
if (isV4 && SupportsV6())
|
||||
{
|
||||
msg.hdr_fields |= dns::flags_QR | dns::flags_AA | dns::flags_RA;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReplyToLokiDNSWhenReady(addr, std::make_shared<dns::Message>(msg), isV6);
|
||||
}
|
||||
return ReplyToLokiDNSWhenReady(addr, std::make_shared<dns::Message>(msg), isV6);
|
||||
}
|
||||
else if (addr.FromString(qname, ".snode"))
|
||||
{
|
||||
if (isV4 && SupportsV6())
|
||||
{
|
||||
msg.hdr_fields |= dns::flags_QR | dns::flags_AA | dns::flags_RA;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ReplyToSNodeDNSWhenReady(
|
||||
addr.as_array(), std::make_shared<dns::Message>(msg), isV6);
|
||||
}
|
||||
return ReplyToSNodeDNSWhenReady(
|
||||
addr.as_array(), std::make_shared<dns::Message>(msg), isV6);
|
||||
}
|
||||
else if (service::NameIsValid(lnsName))
|
||||
{
|
||||
|
@ -837,11 +796,10 @@ namespace llarp
|
|||
isV6,
|
||||
reply,
|
||||
ReplyToDNSWhenReady](auto maybe) {
|
||||
if (not maybe.has_value())
|
||||
if (not maybe)
|
||||
{
|
||||
LogWarn(name, " lns name ", lnsName, " not resolved");
|
||||
msg->AddNXReply();
|
||||
reply(*msg);
|
||||
reply(msg->nx());
|
||||
return;
|
||||
}
|
||||
ReplyToDNSWhenReady(*maybe, msg, isV6);
|
||||
|
@ -849,31 +807,30 @@ namespace llarp
|
|||
return true;
|
||||
}
|
||||
else
|
||||
msg.AddNXReply();
|
||||
msg.nx();
|
||||
|
||||
reply(msg);
|
||||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypePTR)
|
||||
else if (msg.questions[0].qtype == to_underlying(dns::RRType::PTR))
|
||||
{
|
||||
// reverse dns
|
||||
if (auto ip = dns::DecodePTR(msg.questions[0].qname))
|
||||
if (auto ip = dns::DecodePTR(msg.questions[0].qname()))
|
||||
{
|
||||
if (auto maybe = ObtainAddrForIP(*ip))
|
||||
{
|
||||
var::visit([&msg](auto&& result) { msg.AddAReply(result.ToString()); }, *maybe);
|
||||
msg.AddINReply(var::visit([](auto&& addr) { return addr.ToString(); }, *maybe));
|
||||
reply(msg);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
msg.AddNXReply();
|
||||
reply(msg);
|
||||
reply(msg.nx());
|
||||
return true;
|
||||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypeSRV)
|
||||
else if (msg.questions[0].qtype == to_underlying(dns::RRType::SRV))
|
||||
{
|
||||
auto srv_for = msg.questions[0].Subdomains();
|
||||
auto name = msg.questions[0].qname;
|
||||
auto name = msg.questions[0].qname();
|
||||
if (is_localhost_loki(msg))
|
||||
{
|
||||
msg.AddSRVReply(introSet().GetMatchingSRVRecords(srv_for));
|
||||
|
@ -886,20 +843,19 @@ namespace llarp
|
|||
[reply, msg = std::make_shared<dns::Message>(std::move(msg))](auto records) {
|
||||
if (records.empty())
|
||||
{
|
||||
msg->AddNXReply();
|
||||
reply(msg->nx());
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->AddSRVReply(records);
|
||||
msg->AddSRVReply(records, 1);
|
||||
reply(*msg);
|
||||
}
|
||||
reply(*msg);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
msg.AddNXReply();
|
||||
reply(msg);
|
||||
reply(msg.nx());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -932,7 +888,7 @@ namespace llarp
|
|||
// hook any ranges we own
|
||||
if (msg.questions[0].qtype == llarp::dns::qTypePTR)
|
||||
{
|
||||
if (auto ip = dns::DecodePTR(msg.questions[0].qname))
|
||||
if (auto ip = dns::DecodePTR(msg.questions[0].qname()))
|
||||
return m_OurRange.Contains(*ip);
|
||||
return false;
|
||||
}
|
||||
|
@ -1257,7 +1213,7 @@ namespace llarp
|
|||
// this succeds for inbound convos, probably.
|
||||
if (auto maybe = GetBestConvoTagFor(to))
|
||||
{
|
||||
if (SendToOrQueue(*maybe, pkt.ConstBuffer(), type))
|
||||
if (SendToOrQueue(*maybe, pkt.copy(), type))
|
||||
{
|
||||
MarkIPActive(dst);
|
||||
Router()->TriggerPump();
|
||||
|
@ -1277,7 +1233,7 @@ namespace llarp
|
|||
},
|
||||
to);
|
||||
}
|
||||
if (SendToOrQueue(*maybe, pkt.ConstBuffer(), type))
|
||||
if (SendToOrQueue(*maybe, pkt.copy(), type))
|
||||
{
|
||||
MarkIPActive(dst);
|
||||
Router()->TriggerPump();
|
||||
|
|
|
@ -22,9 +22,9 @@ namespace llarp
|
|||
{
|
||||
struct TunEndpoint : public service::Endpoint,
|
||||
public dns::Resolver_Base,
|
||||
public std::enable_shared_from_this<TunEndpoint>
|
||||
private std::enable_shared_from_this<TunEndpoint>
|
||||
{
|
||||
TunEndpoint(AbstractRouter* r, llarp::service::Context* parent);
|
||||
explicit TunEndpoint(AbstractRouter& r);
|
||||
~TunEndpoint() override;
|
||||
|
||||
vpn::NetworkInterface*
|
||||
|
@ -52,18 +52,6 @@ namespace llarp
|
|||
const SockAddr& to,
|
||||
const SockAddr& from) override;
|
||||
|
||||
path::PathSet_ptr
|
||||
GetSelf() override
|
||||
{
|
||||
return shared_from_this();
|
||||
}
|
||||
|
||||
std::weak_ptr<path::PathSet>
|
||||
GetWeak() override
|
||||
{
|
||||
return weak_from_this();
|
||||
}
|
||||
|
||||
void
|
||||
Thaw() override;
|
||||
|
||||
|
@ -72,13 +60,13 @@ namespace llarp
|
|||
ReconfigureDNS(std::vector<SockAddr> servers);
|
||||
|
||||
bool
|
||||
Configure(const NetworkConfig& conf, const DnsConfig& dnsConf) override;
|
||||
Configure(const NetworkConfig& conf, const DnsConfig& dnsConf);
|
||||
|
||||
void
|
||||
SendPacketToRemote(const llarp_buffer_t&, service::ProtocolType) override{};
|
||||
|
||||
std::string
|
||||
GetIfName() const override;
|
||||
GetIfName() const;
|
||||
|
||||
void
|
||||
Tick(llarp_time_t now) override;
|
||||
|
@ -90,7 +78,7 @@ namespace llarp
|
|||
NotifyParams() const override;
|
||||
|
||||
bool
|
||||
SupportsV6() const override;
|
||||
SupportsV6() const;
|
||||
|
||||
bool
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const;
|
||||
|
@ -131,13 +119,12 @@ namespace llarp
|
|||
bool
|
||||
SetupNetworking() override;
|
||||
|
||||
/// overrides Endpoint
|
||||
bool
|
||||
HandleInboundPacket(
|
||||
const service::ConvoTag tag,
|
||||
const llarp_buffer_t& pkt,
|
||||
service::ProtocolType t,
|
||||
uint64_t seqno) override;
|
||||
uint64_t seqno);
|
||||
|
||||
/// handle inbound traffic
|
||||
bool
|
||||
|
@ -188,7 +175,7 @@ namespace llarp
|
|||
|
||||
/// get a key for ip address
|
||||
std::optional<std::variant<service::Address, RouterID>>
|
||||
ObtainAddrForIP(huint128_t ip) const override;
|
||||
ObtainAddrForIP(huint128_t ip) const;
|
||||
|
||||
bool
|
||||
HasAddress(const AlignedBuffer<32>& addr) const
|
||||
|
@ -198,7 +185,7 @@ namespace llarp
|
|||
|
||||
/// get ip address for key unconditionally
|
||||
huint128_t
|
||||
ObtainIPForAddr(std::variant<service::Address, RouterID> addr) override;
|
||||
ObtainIPForAddr(std::variant<service::Address, RouterID> addr);
|
||||
|
||||
void
|
||||
ResetInternalState() override;
|
||||
|
@ -272,12 +259,11 @@ namespace llarp
|
|||
{
|
||||
if (ctx)
|
||||
{
|
||||
huint128_t ip = ObtainIPForAddr(addr);
|
||||
query->answers.clear();
|
||||
query->AddINReply(ip, sendIPv6);
|
||||
huint128_t host_ip = ObtainIPForAddr(addr);
|
||||
query->rr_add_ipaddr(net::maybe_truncate_ip(ToNet(host_ip)), 1);
|
||||
}
|
||||
else
|
||||
query->AddNXReply();
|
||||
query->nx();
|
||||
reply(*query);
|
||||
}
|
||||
|
||||
|
|
60
llarp/layers/flow/flow_addr.cpp
Normal file
60
llarp/layers/flow/flow_addr.cpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include "flow_addr.hpp"
|
||||
#include <oxenc/base32z.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <llarp/util/str.hpp>
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
FlowAddr::Kind
|
||||
FlowAddr::get_kind(std::string_view addr_str)
|
||||
{
|
||||
if (ends_with(addr_str, ".loki"))
|
||||
return Kind::snapp;
|
||||
if (ends_with(addr_str, ".snode"))
|
||||
return Kind::snode;
|
||||
throw std::invalid_argument{"bad flow address: '{}'"_format(addr_str)};
|
||||
}
|
||||
namespace
|
||||
{
|
||||
llarp::AlignedBuffer<32>::Data
|
||||
decode_addr(std::string_view str)
|
||||
{
|
||||
llarp::AlignedBuffer<32>::Data data{};
|
||||
if (auto sz = oxenc::to_base32z_size(str.size()); sz != data.size())
|
||||
throw std::invalid_argument{"data decode size {} != {}"_format(sz, data.size())};
|
||||
oxenc::from_base32z(str.begin(), str.end(), data.begin());
|
||||
return data;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
FlowAddr::FlowAddr(std::string str)
|
||||
: llarp::AlignedBuffer<32>{decode_addr(split(str, ".", true)[0])}, _kind{get_kind(str)}
|
||||
{}
|
||||
|
||||
FlowAddr::FlowAddr(EndpointBase::AddressVariant_t addr)
|
||||
: FlowAddr{var::visit([](auto&& a) { return a.ToString(); }, addr)}
|
||||
{}
|
||||
|
||||
std::string
|
||||
FlowAddr::ToString() const
|
||||
{
|
||||
return oxenc::to_base32z(begin(), end())
|
||||
+ (kind() == Kind::snapp ? ".loki"
|
||||
: kind() == Kind::snode ? ".snode"
|
||||
: "");
|
||||
}
|
||||
|
||||
bool
|
||||
FlowAddr::operator==(const FlowAddr& other) const
|
||||
{
|
||||
using parent_t = AlignedBuffer<SIZE>;
|
||||
return kind() == other.kind() and parent_t::operator==(static_cast<const parent_t&>(other));
|
||||
}
|
||||
|
||||
FlowAddr::Kind
|
||||
FlowAddr::kind() const
|
||||
{
|
||||
return _kind;
|
||||
}
|
||||
} // namespace llarp::layers::flow
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
|
||||
#include <fmt/core.h>
|
||||
#include <llarp/util/aligned.hpp>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
|
25
llarp/layers/flow/flow_auth.cpp
Normal file
25
llarp/layers/flow/flow_auth.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "flow_auth.hpp"
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
std::string_view
|
||||
ToString(FlowAuthPhase phase)
|
||||
{
|
||||
switch (phase)
|
||||
{
|
||||
case FlowAuthPhase::auth_more:
|
||||
return "auth_more";
|
||||
case FlowAuthPhase::auth_nack:
|
||||
return "auth_nack";
|
||||
case FlowAuthPhase::auth_req_not_sent:
|
||||
return "auth_req_not_sent";
|
||||
case FlowAuthPhase::auth_req_sent:
|
||||
return "auth_req_sent";
|
||||
case FlowAuthPhase::auth_ok:
|
||||
return "auth_ok";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::flow
|
24
llarp/layers/flow/flow_data_kind.cpp
Normal file
24
llarp/layers/flow/flow_data_kind.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "flow_data_kind.hpp"
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
|
||||
std::string_view
|
||||
ToString(FlowDataKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case FlowDataKind::auth:
|
||||
return "auth";
|
||||
case FlowDataKind::direct_ip_unicast:
|
||||
return "direct_ip_unicast";
|
||||
case FlowDataKind::exit_ip_unicast:
|
||||
return "exit_ip_unicast";
|
||||
case FlowDataKind::stream_unicast:
|
||||
return "stream_unicast";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::flow
|
43
llarp/layers/flow/flow_establish.cpp
Normal file
43
llarp/layers/flow/flow_establish.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
#include "flow_establish.hpp"
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
|
||||
FlowEstablish::FlowEstablish(
|
||||
std::function<void(std::optional<FlowInfo>, std::string)> completiton_handler,
|
||||
std::function<void(FlowAuthPhase, std::string)> phase_handler)
|
||||
: _completion_handler{std::move(completiton_handler)}
|
||||
, _phase_handler{std::move(phase_handler)}
|
||||
{}
|
||||
|
||||
void
|
||||
FlowEstablish::fail(std::string name)
|
||||
{
|
||||
_result = std::nullopt;
|
||||
enter_phase(FlowAuthPhase::auth_nack, std::move(name));
|
||||
}
|
||||
|
||||
void
|
||||
FlowEstablish::ready(FlowInfo flow_info)
|
||||
{
|
||||
_result = flow_info;
|
||||
enter_phase(FlowAuthPhase::auth_ok, "");
|
||||
_result = std::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
FlowEstablish::enter_phase(FlowAuthPhase phase, std::string info)
|
||||
{
|
||||
bool completed = phase == FlowAuthPhase::auth_ok or phase == FlowAuthPhase::auth_nack;
|
||||
if (completed)
|
||||
{
|
||||
if (_completion_handler)
|
||||
_completion_handler(_result, std::move(info));
|
||||
_completion_handler = nullptr;
|
||||
}
|
||||
|
||||
if (_phase_handler)
|
||||
_phase_handler(phase, info);
|
||||
}
|
||||
} // namespace llarp::layers::flow
|
106
llarp/layers/flow/flow_identity.cpp
Normal file
106
llarp/layers/flow/flow_identity.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
#include "flow_identity.hpp"
|
||||
|
||||
#include <oxenc/bt_serialize.h>
|
||||
#include <chrono>
|
||||
#include <llarp/crypto/crypto.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <variant>
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
FlowIdentityPrivateKeys
|
||||
FlowIdentityPrivateKeys::keygen()
|
||||
{
|
||||
FlowIdentityPrivateKeys keys{};
|
||||
|
||||
auto crypto = CryptoManager::instance();
|
||||
|
||||
crypto->identity_keygen(keys.identity);
|
||||
crypto->encryption_keygen(keys.encryption);
|
||||
crypto->pqe_keygen(keys.sntrup);
|
||||
if (not crypto->derive_subkey_private(keys.derivedKey, keys.identity, 1))
|
||||
throw std::runtime_error("failed to derive subkey");
|
||||
return keys;
|
||||
}
|
||||
|
||||
const FlowAddr&
|
||||
FlowIdentityPrivateKeys::public_addr() const
|
||||
{
|
||||
if (_root_pubkey.IsZero())
|
||||
_root_pubkey = FlowAddr{service::Address{identity.toPublic()}};
|
||||
return _root_pubkey;
|
||||
}
|
||||
const dht::Key_t&
|
||||
FlowIdentityPrivateKeys::keyspace_location() const
|
||||
{
|
||||
if (_derived_pubkey.IsZero())
|
||||
derivedKey.toPublic(reinterpret_cast<PubKey&>(_derived_pubkey));
|
||||
return _derived_pubkey;
|
||||
}
|
||||
|
||||
FlowIdentity::FlowIdentity(
|
||||
FlowLayer& parent,
|
||||
FlowAddr remote_addr,
|
||||
FlowTag flow_tag_,
|
||||
const FlowIdentityPrivateKeys& privkeys)
|
||||
: _parent{parent}
|
||||
, _local_privkeys{privkeys}
|
||||
, _state{}
|
||||
, flow_info{privkeys.public_addr(), std::move(remote_addr), std::move(flow_tag_)}
|
||||
{}
|
||||
|
||||
bool
|
||||
FlowIdentity::operator==(const FlowIdentity& other) const
|
||||
{
|
||||
return flow_info == other.flow_info;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
EndpointBase::AddressVariant_t
|
||||
to_addr_variant(const FlowAddr& addr)
|
||||
{
|
||||
switch (addr.kind())
|
||||
{
|
||||
case FlowAddr::Kind::snode:
|
||||
return RouterID{addr.as_array()};
|
||||
case FlowAddr::Kind::snapp:
|
||||
return service::Address{addr.as_array()};
|
||||
default:
|
||||
throw std::invalid_argument{
|
||||
"flow addr cannot be converted to EndpointBase::AddressVariant_t"};
|
||||
}
|
||||
}
|
||||
|
||||
struct deprecated_path_ensure_handler
|
||||
{
|
||||
FlowEstablish handshaker;
|
||||
FlowInfo flow_info;
|
||||
|
||||
void
|
||||
operator()(std::optional<service::ConvoTag> maybe_tag)
|
||||
{
|
||||
if (not maybe_tag)
|
||||
{
|
||||
handshaker.fail("cannot establish flow");
|
||||
return;
|
||||
}
|
||||
|
||||
handshaker.ready(flow_info);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void
|
||||
FlowIdentity::async_ensure_flow(FlowEstablish handshaker)
|
||||
{
|
||||
handshaker.fail("not wired up");
|
||||
}
|
||||
|
||||
void
|
||||
FlowIdentity::send_to_remote(std::vector<byte_t>, FlowDataKind)
|
||||
{}
|
||||
|
||||
} // namespace llarp::layers::flow
|
16
llarp/layers/flow/flow_info.cpp
Normal file
16
llarp/layers/flow/flow_info.cpp
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "flow_info.hpp"
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
bool
|
||||
FlowInfo::operator==(const FlowInfo& other) const
|
||||
{
|
||||
return src == other.src and dst == other.dst and tag == other.tag;
|
||||
}
|
||||
|
||||
std::string
|
||||
FlowInfo::ToString() const
|
||||
{
|
||||
return fmt::format("[flow src={} dst={} tag={} mtu={}]", src, dst, tag, int{mtu});
|
||||
}
|
||||
} // namespace llarp::layers::flow
|
|
@ -15,13 +15,6 @@ namespace llarp::layers::flow
|
|||
/// source and destination addresses we associate with this flow.
|
||||
FlowAddr src, dst;
|
||||
|
||||
/// flow address of the pivot we use to get from src to dst.
|
||||
/// for .loki traffic this is a .snode address, which we aligh our paths to.
|
||||
/// for .snode traffic this is a .snode address, which we align our paths to.
|
||||
/// for .exit traffic this is a .loki address, which we will fetch the exit descriptor it is
|
||||
/// associated with.
|
||||
FlowAddr pivot;
|
||||
|
||||
/// cleartext identifier used on outer framing of the traffic. lets us tell what is from who.
|
||||
FlowTag tag;
|
||||
|
||||
|
|
213
llarp/layers/flow/flow_layer.cpp
Normal file
213
llarp/layers/flow/flow_layer.cpp
Normal file
|
@ -0,0 +1,213 @@
|
|||
#include "flow_layer.hpp"
|
||||
#include <oxenc/bt_producer.h>
|
||||
#include <oxenc/bt_serialize.h>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/service/endpoint.hpp>
|
||||
#include <llarp/path/path_context.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
|
||||
static auto logcat = log::Cat("flow-layer");
|
||||
|
||||
void
|
||||
FlowLayer::maybe_store_or_load_privkeys()
|
||||
{
|
||||
if (auto maybe_keyfile = _conf.m_keyfile)
|
||||
{
|
||||
// put down existing private keys if it's not on disk
|
||||
const auto& keyfile = *maybe_keyfile;
|
||||
if (not fs::exists(keyfile))
|
||||
{
|
||||
if (auto err = util::EnsurePrivateFile(keyfile))
|
||||
throw std::runtime_error{"failed to create file '{}': {}"_format(keyfile, err.message())};
|
||||
std::string_view keys{
|
||||
reinterpret_cast<const char*>(_privkeys.identity.data()), _privkeys.identity.size()};
|
||||
util::dump_file(keyfile, oxenc::bt_serialize(keys));
|
||||
return;
|
||||
}
|
||||
// otherwise we load the identity keys from disk
|
||||
// todo: migrate old format?
|
||||
auto data = oxenc::bt_deserialize<std::string>(util::slurp_file(keyfile));
|
||||
if (data.size() != _privkeys.identity.size())
|
||||
throw std::runtime_error{
|
||||
"privkey has bad size: {} != {}"_format(data.size(), _privkeys.identity.size())};
|
||||
|
||||
std::copy(data.begin(), data.end(), _privkeys.identity.begin());
|
||||
}
|
||||
}
|
||||
|
||||
/// handler of feeding flow traffic from flow layer to platform layer.
|
||||
struct platform_layer_waker
|
||||
{
|
||||
FlowLayer& flow_layer;
|
||||
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
// propagate flow traffic up to platform layer.
|
||||
if (auto traffic = flow_layer.poll_flow_traffic(); not traffic.empty())
|
||||
flow_layer.router.get_layers()->platform->flow_traffic(std::move(traffic));
|
||||
// continue feeding any downstream traffic into the flow layer.
|
||||
flow_layer.router.pathContext().PumpDownstream();
|
||||
}
|
||||
};
|
||||
|
||||
/// handler of sending flow layer stuff out to the void.
|
||||
/// called after we queued sending all the things in the flow layer in the current event loop
|
||||
/// cycle.
|
||||
struct flow_layer_waker
|
||||
{
|
||||
path::PathContext& paths;
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
// take the things we need to send out on paths and send them out.
|
||||
paths.PumpUpstream();
|
||||
}
|
||||
};
|
||||
|
||||
FlowLayer::FlowLayer(AbstractRouter& r, NetworkConfig conf)
|
||||
: _conf{std::move(conf)}
|
||||
, _privkeys{FlowIdentityPrivateKeys::keygen()}
|
||||
, _name_cache{}
|
||||
, _deprecated_endpoint{std::make_shared<service::Endpoint>(r)}
|
||||
, _wakeup_send{r.loop()->make_waker(flow_layer_waker{r.pathContext()})}
|
||||
, _wakeup_recv{r.loop()->make_waker(platform_layer_waker{*this})}
|
||||
, name_resolver{_name_cache, *this}
|
||||
, router{r}
|
||||
{}
|
||||
|
||||
const FlowAddr&
|
||||
FlowLayer::local_addr(const std::optional<FlowTag>& maybe_tag) const
|
||||
{
|
||||
if (maybe_tag)
|
||||
{
|
||||
const auto& flow_tag = *maybe_tag;
|
||||
for (const auto& local_flow : _local_flows)
|
||||
{
|
||||
if (local_flow->flow_info.tag == flow_tag)
|
||||
return local_flow->flow_info.src;
|
||||
}
|
||||
throw std::invalid_argument{"no such local address for flow tag: {}"_format(flow_tag)};
|
||||
}
|
||||
return _privkeys.public_addr();
|
||||
}
|
||||
|
||||
void
|
||||
FlowLayer::start()
|
||||
{
|
||||
log::info(logcat, "flow layer starting");
|
||||
const auto& _dns_conf = router.GetConfig()->dns;
|
||||
if (not _deprecated_endpoint->Configure(_conf, _dns_conf))
|
||||
throw std::runtime_error{"deprecated endpoint failed to configure"};
|
||||
|
||||
if (not _deprecated_endpoint->Start())
|
||||
throw std::runtime_error{"deprecated endpoint did not start"};
|
||||
|
||||
router.loop()->call_every(
|
||||
100ms, _deprecated_endpoint, [ep = _deprecated_endpoint]() { ep->Tick(ep->Now()); });
|
||||
log::info(logcat, "flow layer up");
|
||||
}
|
||||
|
||||
void
|
||||
FlowLayer::stop()
|
||||
{
|
||||
log::info(logcat, "flow layer stopping");
|
||||
if (not _deprecated_endpoint->Stop())
|
||||
throw std::runtime_error{"deprecated endpoint did not stop"};
|
||||
}
|
||||
|
||||
void
|
||||
FlowLayer::offer_flow_traffic(FlowTraffic&& traff)
|
||||
{
|
||||
_recv.push_back(traff);
|
||||
_wakeup_recv->Trigger();
|
||||
}
|
||||
|
||||
bool
|
||||
FlowLayer::has_flow(const FlowInfo& flow_info) const
|
||||
{
|
||||
for (const auto& _flow : _local_flows)
|
||||
{
|
||||
if (_flow->flow_info == flow_info)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
FlowLayer::remove_flow(const FlowInfo& flow_info)
|
||||
{
|
||||
for (auto itr = _local_flows.begin(); itr != _local_flows.end();)
|
||||
{
|
||||
if ((*itr)->flow_info == flow_info)
|
||||
itr = _local_flows.erase(itr);
|
||||
else
|
||||
++itr;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<FlowTraffic>
|
||||
FlowLayer::poll_flow_traffic()
|
||||
{
|
||||
std::vector<FlowTraffic> ret{_recv.capacity()};
|
||||
std::swap(ret, _recv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
FlowStats
|
||||
FlowLayer::current_stats() const
|
||||
{
|
||||
// todo: implement
|
||||
return FlowStats{};
|
||||
}
|
||||
|
||||
FlowTag
|
||||
FlowLayer::unique_flow_tag() const
|
||||
{
|
||||
FlowTag flow_tag{};
|
||||
do
|
||||
{
|
||||
flow_tag.Randomize();
|
||||
} while (has_flow_tag(flow_tag));
|
||||
return flow_tag;
|
||||
}
|
||||
|
||||
bool
|
||||
FlowLayer::has_flow_tag(const FlowTag& flow_tag) const
|
||||
{
|
||||
for (const auto& local_flow : _local_flows)
|
||||
{
|
||||
if (local_flow->flow_info.tag == flow_tag)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<service::Endpoint>
|
||||
FlowLayer::local_deprecated_loki_endpoint() const
|
||||
{
|
||||
return _deprecated_endpoint;
|
||||
}
|
||||
|
||||
std::shared_ptr<FlowIdentity>
|
||||
FlowLayer::flow_to(const FlowAddr& addr)
|
||||
{
|
||||
for (auto& local_flow : _local_flows)
|
||||
{
|
||||
if (local_flow->flow_info.dst == addr)
|
||||
return local_flow;
|
||||
}
|
||||
return _local_flows.emplace_back(
|
||||
std::make_shared<FlowIdentity>(*this, addr, unique_flow_tag(), _privkeys));
|
||||
}
|
||||
} // namespace llarp::layers::flow
|
12
llarp/layers/flow/flow_state.cpp
Normal file
12
llarp/layers/flow/flow_state.cpp
Normal file
|
@ -0,0 +1,12 @@
|
|||
#include "flow_state.hpp"
|
||||
|
||||
#include <llarp/service/outbound_context.hpp>
|
||||
#include <memory>
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
struct FlowState_Pimpl
|
||||
{
|
||||
std::shared_ptr<service::OutboundContext> obctx;
|
||||
};
|
||||
} // namespace llarp::layers::flow
|
37
llarp/layers/flow/flow_stats.cpp
Normal file
37
llarp/layers/flow/flow_stats.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "flow_stats.hpp"
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
|
||||
bool
|
||||
FlowStats::has_good_flow_to(const FlowAddr& remote) const
|
||||
{
|
||||
for (const auto& [flow_info, stats] : all_flows)
|
||||
{
|
||||
if (flow_info.dst == remote and stats.state == layers::flow::FlowInfoState::good)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_set<FlowInfo>
|
||||
FlowStats::good_flows() const
|
||||
{
|
||||
// todo: implement me
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<FlowAddr>
|
||||
FlowStats::local_addrs() const
|
||||
{
|
||||
// todo: implement me
|
||||
return {};
|
||||
}
|
||||
|
||||
std::unordered_map<FlowAddr, std::string>
|
||||
FlowStats::auth_map() const
|
||||
{
|
||||
// todo: implement me
|
||||
return {};
|
||||
}
|
||||
} // namespace llarp::layers::flow
|
20
llarp/layers/flow/flow_tag.cpp
Normal file
20
llarp/layers/flow/flow_tag.cpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include "flow_tag.hpp"
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
|
||||
FlowTag
|
||||
FlowTag::random()
|
||||
{
|
||||
FlowTag tag;
|
||||
tag.Randomize();
|
||||
return tag;
|
||||
}
|
||||
|
||||
std::string
|
||||
FlowTag::ToString() const
|
||||
{
|
||||
return fmt::format("[flowtag '{}']", ToHex());
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::flow
|
23
llarp/layers/flow/name_cache.cpp
Normal file
23
llarp/layers/flow/name_cache.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include "name_cache.hpp"
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
NameCache::NameCache(std::chrono::seconds ttl) : _names{ttl}
|
||||
{}
|
||||
|
||||
std::optional<FlowAddr>
|
||||
NameCache::get(std::string name)
|
||||
{
|
||||
auto now = uptime();
|
||||
_names.Decay(now);
|
||||
return _names.Get(std::move(name));
|
||||
}
|
||||
|
||||
void
|
||||
NameCache::put(std::string name, FlowAddr addr)
|
||||
{
|
||||
auto now = uptime();
|
||||
_names.Decay(now);
|
||||
_names.Put(name, addr, now);
|
||||
}
|
||||
} // namespace llarp::layers::flow
|
31
llarp/layers/flow/name_resolver.cpp
Normal file
31
llarp/layers/flow/name_resolver.cpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#include "name_resolver.hpp"
|
||||
#include "flow_layer.hpp"
|
||||
#include <llarp/service/endpoint.hpp>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
|
||||
NameResolver::NameResolver(NameCache& name_cache, FlowLayer& parent)
|
||||
: _name_cache{name_cache}, _parent{parent}
|
||||
{}
|
||||
|
||||
void
|
||||
NameResolver::resolve_flow_addr_async(
|
||||
std::string name, std::function<void(std::optional<FlowAddr>)> result_handler)
|
||||
{
|
||||
_parent.local_deprecated_loki_endpoint()->LookupNameAsync(
|
||||
name, [result_handler = std::move(result_handler)](auto maybe_addr) {
|
||||
if (not maybe_addr)
|
||||
{
|
||||
result_handler(std::nullopt);
|
||||
return;
|
||||
}
|
||||
result_handler(FlowAddr{*maybe_addr});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::flow
|
46
llarp/layers/layers.cpp
Normal file
46
llarp/layers/layers.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include "layers.hpp"
|
||||
#include <memory>
|
||||
#include <llarp/config/config.hpp>
|
||||
|
||||
namespace llarp::layers
|
||||
{
|
||||
|
||||
std::unique_ptr<Layers>
|
||||
make_layers(AbstractRouter& router, const Config& conf)
|
||||
{
|
||||
auto layers = std::make_unique<Layers>();
|
||||
// TODO: placeholders
|
||||
layers->onion = std::make_unique<onion::OnionLayer>(router);
|
||||
layers->route = std::make_unique<route::RouteLayer>(router);
|
||||
|
||||
layers->flow = std::make_unique<flow::FlowLayer>(router, conf.network);
|
||||
layers->platform =
|
||||
std::make_unique<platform::PlatformLayer>(router, *layers->flow, conf.network);
|
||||
// TODO: add each layer as they are wired up
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
std::unique_ptr<const Layers>
|
||||
Layers::freeze()
|
||||
{
|
||||
return std::unique_ptr<const Layers>(
|
||||
new const Layers{std::move(platform), std::move(flow), std::move(route), std::move(onion)});
|
||||
}
|
||||
|
||||
void
|
||||
Layers::start_all() const
|
||||
{
|
||||
platform->start();
|
||||
flow->start();
|
||||
onion->start();
|
||||
}
|
||||
|
||||
void
|
||||
Layers::stop_all() const
|
||||
{
|
||||
platform->stop();
|
||||
flow->stop();
|
||||
onion->stop();
|
||||
}
|
||||
} // namespace llarp::layers
|
25
llarp/layers/onion/onion_layer.cpp
Normal file
25
llarp/layers/onion/onion_layer.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
#include "onion_layer.hpp"
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
|
||||
namespace llarp::layers::onion
|
||||
{
|
||||
OnionStats
|
||||
OnionLayer::current_stats() const
|
||||
{
|
||||
// todo: implement
|
||||
return OnionStats{};
|
||||
}
|
||||
|
||||
void
|
||||
OnionLayer::start()
|
||||
{}
|
||||
|
||||
void
|
||||
OnionLayer::stop()
|
||||
{}
|
||||
|
||||
OnionLayer::OnionLayer(AbstractRouter& router) : _router{router}
|
||||
{}
|
||||
} // namespace llarp::layers::onion
|
19
llarp/layers/onion/onion_stats.cpp
Normal file
19
llarp/layers/onion/onion_stats.cpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include "onion_stats.hpp"
|
||||
#include <unordered_set>
|
||||
|
||||
namespace llarp::layers::onion
|
||||
{
|
||||
bool
|
||||
OnionStats::ready() const
|
||||
{
|
||||
// todo: implement me
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unordered_set<OnionPathInfo>
|
||||
OnionStats::built_paths() const
|
||||
{
|
||||
// todo: implement me
|
||||
return {};
|
||||
}
|
||||
} // namespace llarp::layers::onion
|
260
llarp/layers/platform/addr_mapper.cpp
Normal file
260
llarp/layers/platform/addr_mapper.cpp
Normal file
|
@ -0,0 +1,260 @@
|
|||
#include "addr_mapper.hpp"
|
||||
#include <fmt/core.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <llarp/layers/flow/flow_layer.hpp>
|
||||
#include "llarp/layers/flow/flow_addr.hpp"
|
||||
#include "llarp/layers/flow/flow_info.hpp"
|
||||
#include "llarp/layers/platform/platform_addr.hpp"
|
||||
#include "llarp/layers/platform/platform_layer.hpp"
|
||||
#include "llarp/net/ip_range.hpp"
|
||||
#include "llarp/net/net_int.hpp"
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
|
||||
AddressMapping&
|
||||
AddressMappingEntry::access()
|
||||
{
|
||||
_last_used_at = decltype(_last_used_at)::clock::now();
|
||||
return _entry;
|
||||
}
|
||||
|
||||
const AddressMapping&
|
||||
AddressMappingEntry::view() const
|
||||
{
|
||||
return _entry;
|
||||
}
|
||||
|
||||
bool
|
||||
AddressMappingEntry::has_addr(const PlatformAddr& src, const PlatformAddr& dst) const
|
||||
{
|
||||
return _entry.src == src and _entry.dst == dst;
|
||||
}
|
||||
|
||||
std::optional<AddressMapping>
|
||||
AddrMapper::get_addr_for_flow(const flow::FlowInfo& flow_info)
|
||||
{
|
||||
auto itr =
|
||||
find_if([&flow_info](const auto& mapping) { return mapping.flow_info == flow_info; });
|
||||
if (itr == std::end(_addrs))
|
||||
return std::nullopt;
|
||||
return itr->access();
|
||||
}
|
||||
|
||||
bool
|
||||
AddressMapping::owns_range(const IPRange& range) const
|
||||
{
|
||||
return std::find(owned_ranges.begin(), owned_ranges.end(), range) != std::end(owned_ranges);
|
||||
}
|
||||
|
||||
std::string
|
||||
AddressMapping::ToString() const
|
||||
{
|
||||
std::optional<std::string> flow_info_str;
|
||||
if (flow_info)
|
||||
flow_info_str = flow_info->ToString();
|
||||
|
||||
return fmt::format(
|
||||
"[AddressMapping flow={} src={} dst={} owned_ranges=({})]",
|
||||
flow_info_str.value_or("none"),
|
||||
src,
|
||||
dst,
|
||||
fmt::join(owned_ranges, ", "));
|
||||
}
|
||||
|
||||
AddrMapper::AddrMapper(const IPRange& range) : _our_range{range}
|
||||
{}
|
||||
|
||||
AddressMapping&
|
||||
AddrMapper::allocate_mapping(std::optional<PlatformAddr> src)
|
||||
{
|
||||
auto dst = next_addr();
|
||||
auto& mapping = _addrs.emplace_back().access();
|
||||
mapping.dst = dst;
|
||||
mapping.src = src.value_or(PlatformAddr{_our_range.addr});
|
||||
return mapping;
|
||||
}
|
||||
|
||||
PlatformAddr
|
||||
AddrMapper::next_addr()
|
||||
{
|
||||
const PlatformAddr highest{_our_range.HighestAddr()};
|
||||
const PlatformAddr lowest{huint128_t{1} + _our_range.addr};
|
||||
|
||||
// first try using highest mapped value.
|
||||
PlatformAddr next{lowest};
|
||||
for (const auto& addr : _addrs)
|
||||
next = std::max(next, addr.view().dst);
|
||||
|
||||
// we already allocated the highest one before.
|
||||
if (next == highest)
|
||||
{
|
||||
// try finding the lowest one.
|
||||
for (const auto& addr : _addrs)
|
||||
next = std::min(next, addr.view().dst);
|
||||
|
||||
// we are full.
|
||||
if (next == lowest)
|
||||
throw std::runtime_error{"addr mapper full"};
|
||||
// use 1 below the lowest one we have allocated now.
|
||||
next = PlatformAddr{net::ToHost(next.ip) - huint128_t{1}};
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
bool
|
||||
AddrMapper::is_full() const
|
||||
{
|
||||
const PlatformAddr highest{_our_range.HighestAddr()};
|
||||
const PlatformAddr lowest{huint128_t{1} + _our_range.addr};
|
||||
|
||||
PlatformAddr lo{highest}, hi{lowest};
|
||||
for (const auto& addr : _addrs)
|
||||
{
|
||||
lo = std::min(addr.view().dst, lo);
|
||||
hi = std::max(addr.view().dst, hi);
|
||||
}
|
||||
|
||||
return highest == hi and lowest == lo;
|
||||
}
|
||||
|
||||
std::optional<AddressMapping>
|
||||
AddrMapper::mapping_for(const PlatformAddr& src, const PlatformAddr& dst)
|
||||
{
|
||||
for (auto& ent : _addrs)
|
||||
{
|
||||
if (ent.has_addr(src, dst))
|
||||
return ent.access();
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::vector<AddressMapping>
|
||||
AddrMapper::all_exits() const
|
||||
{
|
||||
std::vector<AddressMapping> exits;
|
||||
view_all_entries([&exits](const auto& ent) {
|
||||
if (not ent.owned_ranges.empty())
|
||||
exits.emplace_back(ent);
|
||||
return true;
|
||||
});
|
||||
return exits;
|
||||
}
|
||||
|
||||
net::IPRangeMap<flow::FlowAddr>
|
||||
AddrMapper::exit_map() const
|
||||
{
|
||||
net::IPRangeMap<flow::FlowAddr> map{};
|
||||
view_all_entries([&map](const auto& ent) {
|
||||
for (const auto& range : ent.owned_ranges)
|
||||
{
|
||||
if (ent.flow_info)
|
||||
map.Insert(range, ent.flow_info->dst);
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
return map;
|
||||
}
|
||||
|
||||
void
|
||||
AddrMapper::remove_lru()
|
||||
{
|
||||
std::chrono::steady_clock::time_point lru_time{std::chrono::steady_clock::time_point::max()};
|
||||
auto lru_itr = _addrs.end();
|
||||
for (auto itr = _addrs.begin(); itr != _addrs.end(); ++itr)
|
||||
{
|
||||
if (lru_time >= itr->last_used_at())
|
||||
continue;
|
||||
lru_itr = itr;
|
||||
lru_time = lru_itr->last_used_at();
|
||||
}
|
||||
if (lru_itr != _addrs.end())
|
||||
_addrs.erase(lru_itr);
|
||||
}
|
||||
|
||||
void
|
||||
AddrMapper::put(AddressMapping&& ent)
|
||||
{
|
||||
auto itr = find_if([&ent](const auto& other) {
|
||||
if (ent.flow_info and other.flow_info)
|
||||
return *ent.flow_info == *other.flow_info;
|
||||
return ent.dst == other.dst and ent.src == other.src;
|
||||
});
|
||||
|
||||
// construct it if we dont have it.
|
||||
if (itr == std::end(_addrs))
|
||||
{
|
||||
if (is_full())
|
||||
remove_lru();
|
||||
itr = _addrs.emplace(std::end(_addrs));
|
||||
}
|
||||
// access the entry for assignment to mark as just used now.
|
||||
itr->access() = ent;
|
||||
}
|
||||
|
||||
void
|
||||
AddrMapper::prune(flow::FlowLayer& flow_layer)
|
||||
{
|
||||
// todo: put constexpr elsewhere.
|
||||
constexpr auto max_idle_threshold = 15min;
|
||||
|
||||
remove_if_entry([&flow_layer, max_idle_threshold](const auto& ent) {
|
||||
// remove stale entries or ones that we dont have a flow on.
|
||||
const auto& maybe_flow_info = ent.view().flow_info;
|
||||
if ((not maybe_flow_info) or not flow_layer.has_flow(*maybe_flow_info))
|
||||
return ent.idle_for() > max_idle_threshold;
|
||||
|
||||
// remove stale entries we have a flow for and remove the flow for them too.
|
||||
if (ent.idle_for() > max_idle_threshold)
|
||||
flow_layer.remove_flow(*maybe_flow_info);
|
||||
|
||||
return not flow_layer.has_flow(*maybe_flow_info);
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<AddressMapping>
|
||||
AddrMapper::mappings_to(const flow::FlowAddr& remote) const
|
||||
{
|
||||
std::vector<AddressMapping> mappings;
|
||||
view_all_entries([&mappings, remote](const auto& entry) {
|
||||
if (not entry.flow_info)
|
||||
return true;
|
||||
if (entry.flow_info->dst == remote)
|
||||
mappings.emplace_back(entry);
|
||||
return true;
|
||||
});
|
||||
return mappings;
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::platform
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
nlohmann::json
|
||||
to_json(const layers::platform::AddressMapping& mapping)
|
||||
{
|
||||
auto ranges = nlohmann::json::array();
|
||||
for (const auto& range : mapping.owned_ranges)
|
||||
ranges.push_back(range.ToString());
|
||||
|
||||
auto object = nlohmann::json::object(
|
||||
{{"ranges", ranges},
|
||||
{"src_ip",
|
||||
var::visit([](auto&& addr) { return addr.ToString(); }, mapping.src.as_ipaddr())},
|
||||
{"dst_ip",
|
||||
var::visit([](auto&& addr) { return addr.ToString(); }, mapping.dst.as_ipaddr())}});
|
||||
if (mapping.flow_info)
|
||||
{
|
||||
object["remote_addr"] = mapping.flow_info->dst.ToString();
|
||||
object["local_addr"] = mapping.flow_info->src.ToString();
|
||||
}
|
||||
return object;
|
||||
}
|
||||
} // namespace llarp
|
|
@ -1,15 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include "llarp/layers/flow/flow_addr.hpp"
|
||||
#include "platform_addr.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <llarp/layers/flow/flow_info.hpp>
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
|
182
llarp/layers/platform/dns_bridge.cpp
Normal file
182
llarp/layers/platform/dns_bridge.cpp
Normal file
|
@ -0,0 +1,182 @@
|
|||
#include "dns_bridge.hpp"
|
||||
#include "platform_layer.hpp"
|
||||
#include "addr_mapper.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <llarp/dns/question.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <llarp/layers/flow/flow_layer.hpp>
|
||||
#include <llarp/dns/name.hpp>
|
||||
#include <llarp/util/logging.hpp>
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
DNSZone::DNSZone() : _flow_addr{}, _addr_mapper{nullptr}
|
||||
{}
|
||||
|
||||
DNSZone::DNSZone(AddrMapper& addr_mapper, flow::FlowAddr addr)
|
||||
: _flow_addr{std::move(addr)}, _addr_mapper{&addr_mapper}
|
||||
{}
|
||||
|
||||
void
|
||||
DNSZone::add_srv_record(dns::SRVTuple tup)
|
||||
{
|
||||
_srv.emplace_back(std::move(tup));
|
||||
}
|
||||
|
||||
std::vector<dns::RData>
|
||||
DNSZone::cname_records() const
|
||||
{
|
||||
std::vector<dns::RData> datas;
|
||||
for (const auto& name : _ons_names)
|
||||
datas.emplace_back(dns::encode_dns_name(name));
|
||||
return datas;
|
||||
}
|
||||
|
||||
std::vector<dns::RData>
|
||||
DNSZone::srv_records() const
|
||||
{
|
||||
std::vector<dns::RData> datas;
|
||||
for (const auto& srv : _srv)
|
||||
datas.emplace_back(srv.encode_dns(zone_name()));
|
||||
return datas;
|
||||
}
|
||||
|
||||
std::string
|
||||
DNSZone::zone_name() const
|
||||
{
|
||||
return _flow_addr.ToString();
|
||||
}
|
||||
|
||||
std::vector<dns::ResourceRecord>
|
||||
DNSZone::synth_rr_for(dns::RRType rr_type) const
|
||||
{
|
||||
std::vector<dns::RData> recs;
|
||||
for (const auto& mapping : addr_mapper().mappings_to(_flow_addr))
|
||||
{
|
||||
if (auto maybe_v4 = mapping.dst.as_ipv4addr(); maybe_v4 and rr_type == dns::RRType::A)
|
||||
recs.emplace_back(*maybe_v4);
|
||||
|
||||
else if (rr_type == dns::RRType::AAAA)
|
||||
recs.emplace_back(mapping.dst.ip);
|
||||
else if (rr_type == dns::RRType::SRV and not _srv.empty())
|
||||
{
|
||||
for (auto srv : srv_records())
|
||||
recs.insert(recs.end(), std::move(srv));
|
||||
}
|
||||
else if (rr_type == dns::RRType::CNAME and not _ons_names.empty())
|
||||
{
|
||||
for (auto cname : cname_records())
|
||||
recs.insert(recs.end(), std::move(cname));
|
||||
}
|
||||
else if (rr_type == dns::RRType::MX)
|
||||
recs.emplace_back(uint16_t{10}, dns::split_dns_name(zone_name()));
|
||||
}
|
||||
std::vector<dns::ResourceRecord> ret;
|
||||
for (auto& rdata : recs)
|
||||
ret.emplace_back(zone_name(), rr_type, std::move(rdata), ttl(rr_type));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
DNSZone::ttl(dns::RRType) const
|
||||
{
|
||||
// todo: this is a placeholder value.
|
||||
return 1;
|
||||
}
|
||||
|
||||
DNSQueryHandler::DNSQueryHandler(PlatformLayer& plat) : _plat{plat}
|
||||
{}
|
||||
|
||||
void
|
||||
DNSQueryHandler::async_obtain_dns_zone(
|
||||
const dns::Question& question, std::function<void(std::optional<DNSZone>)> result_handler)
|
||||
{
|
||||
if (question.IsLocalhost())
|
||||
{
|
||||
result_handler(_plat.local_dns_zone());
|
||||
return;
|
||||
}
|
||||
// todo: get auth code from config.
|
||||
std::string auth = "";
|
||||
_plat.map_remote(
|
||||
question.Domain(),
|
||||
auth,
|
||||
{},
|
||||
std::nullopt,
|
||||
[result_handler = std::move(result_handler), this](auto maybe_flow_info, auto) {
|
||||
if (not maybe_flow_info)
|
||||
{
|
||||
result_handler(std::nullopt);
|
||||
return;
|
||||
}
|
||||
const auto& addr = maybe_flow_info->dst;
|
||||
auto [itr, inserted] = _zones.try_emplace(addr, _plat.addr_mapper, addr);
|
||||
if (inserted)
|
||||
log::debug(logcat, "allocated new address for {}", addr);
|
||||
result_handler(itr->second);
|
||||
});
|
||||
}
|
||||
|
||||
int
|
||||
DNSQueryHandler::Rank() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string_view
|
||||
DNSQueryHandler::ResolverName() const
|
||||
{
|
||||
return "lokinet";
|
||||
}
|
||||
|
||||
bool
|
||||
DNSQueryHandler::MaybeHookDNS(
|
||||
std::shared_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from)
|
||||
{
|
||||
if (query.questions.empty())
|
||||
return false;
|
||||
|
||||
// make sure this query is meant for us.
|
||||
const auto& question = query.questions.at(0);
|
||||
if (not dns::is_lokinet_tld(question.tld()))
|
||||
return false;
|
||||
// make sure we understand the rr type requested.
|
||||
auto maybe_qtype = dns::get_rr_type(question.qtype);
|
||||
if (not maybe_qtype)
|
||||
{
|
||||
dns::Message reply{query};
|
||||
// no such rr type know by us. we dont serv fail so queries dont leak.
|
||||
source->SendTo(from, to, reply.nx().ToBuffer());
|
||||
return true;
|
||||
}
|
||||
|
||||
async_obtain_dns_zone(
|
||||
question,
|
||||
[source, qtype = *maybe_qtype, query = dns::Message{query}, from, to](auto maybe_zone) {
|
||||
dns::Message msg{query};
|
||||
if (not maybe_zone)
|
||||
{
|
||||
source->SendTo(from, to, msg.nx().ToBuffer());
|
||||
return;
|
||||
}
|
||||
for (auto found_rr : maybe_zone->synth_rr_for(qtype))
|
||||
msg.answers.emplace_back(std::move(found_rr));
|
||||
source->SendTo(from, to, msg.ToBuffer());
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::platform
|
|
@ -8,11 +8,8 @@
|
|||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <llarp/layers/flow/flow_addr.hpp>
|
||||
#include "llarp/dns/question.hpp"
|
||||
#include "llarp/dns/rr.hpp"
|
||||
#include "llarp/layers/flow/flow_layer.hpp"
|
||||
#include "llarp/util/decaying_hashtable.hpp"
|
||||
#include "llarp/util/str.hpp"
|
||||
#include <llarp/dns/question.hpp>
|
||||
#include <llarp/dns/rr.hpp>
|
||||
|
||||
namespace llarp::layers::flow
|
||||
{
|
||||
|
@ -34,7 +31,7 @@ namespace llarp::layers::platform
|
|||
/// any known cached ons names for this zone.
|
||||
std::vector<std::string> _ons_names;
|
||||
std::vector<dns::SRVData> _srv;
|
||||
AddrMapper& _addr_mapper;
|
||||
AddrMapper* const _addr_mapper;
|
||||
|
||||
/// synth srv records rdata.
|
||||
std::vector<dns::RData>
|
||||
|
@ -51,8 +48,15 @@ namespace llarp::layers::platform
|
|||
std::string
|
||||
zone_name() const;
|
||||
|
||||
constexpr AddrMapper&
|
||||
addr_mapper() const
|
||||
{
|
||||
return *_addr_mapper;
|
||||
}
|
||||
|
||||
public:
|
||||
DNSZone(AddrMapper&, flow::FlowAddr addr);
|
||||
DNSZone();
|
||||
DNSZone(AddrMapper& mapper, flow::FlowAddr addr);
|
||||
|
||||
/// add a new srv recrod to this dns zone.
|
||||
void
|
||||
|
@ -68,7 +72,8 @@ namespace llarp::layers::platform
|
|||
{
|
||||
PlatformLayer& _plat;
|
||||
|
||||
std::unordered_map<flow::FlowAddr, std::unique_ptr<DNSZone>> _zones;
|
||||
/// all remote zones.
|
||||
std::unordered_map<flow::FlowAddr, DNSZone> _zones;
|
||||
|
||||
/// async resolve dns zone given a dns question.
|
||||
void
|
||||
|
|
15
llarp/layers/platform/ethertype.cpp
Normal file
15
llarp/layers/platform/ethertype.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
#include "ethertype.hpp"
|
||||
#include "llarp/util/time.hpp"
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
std::string
|
||||
ToString(EtherType_t kind)
|
||||
{
|
||||
const std::unordered_map<EtherType_t, std::string> loopkup{
|
||||
{EtherType_t::ip_unicast, "ip_unicast"},
|
||||
{EtherType_t::plainquic, "plainquic"},
|
||||
{EtherType_t::proto_auth, "auth"}};
|
||||
return loopkup.at(kind);
|
||||
}
|
||||
} // namespace llarp::layers::platform
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "llarp/util/formattable.hpp"
|
||||
#include <llarp/util/formattable.hpp>
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
/// the kind of traffic we are tunneling
|
||||
|
|
25
llarp/layers/platform/name_cache.cpp
Normal file
25
llarp/layers/platform/name_cache.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
#include "name_cache.hpp"
|
||||
#include "llarp/layers/flow/flow_addr.hpp"
|
||||
#include "llarp/util/time.hpp"
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
NameCache::NameCache(std::chrono::seconds ttl) : _names{ttl}
|
||||
{}
|
||||
|
||||
std::optional<flow::FlowAddr>
|
||||
NameCache::get(std::string name)
|
||||
{
|
||||
auto now = uptime();
|
||||
_names.Decay(now);
|
||||
return _names.Get(std::move(name));
|
||||
}
|
||||
|
||||
void
|
||||
NameCache::put(std::string name, flow::FlowAddr addr)
|
||||
{
|
||||
auto now = uptime();
|
||||
_names.Decay(now);
|
||||
_names.Put(name, addr, now);
|
||||
}
|
||||
} // namespace llarp::layers::platform
|
50
llarp/layers/platform/name_resolver.cpp
Normal file
50
llarp/layers/platform/name_resolver.cpp
Normal file
|
@ -0,0 +1,50 @@
|
|||
#include "name_resolver.hpp"
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "name_cache.hpp"
|
||||
|
||||
#include "deprecated_pimpl.hpp"
|
||||
|
||||
#include <llarp/layers/flow/deprecated_flow_layer_pimpl.hpp>
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
|
||||
NameResolver::NameResolver(NameCache& name_cache, flow::FlowLayer& flow_layer)
|
||||
: _name_cache{name_cache}, _flow_layer{flow_layer}
|
||||
{}
|
||||
|
||||
namespace
|
||||
{
|
||||
/// converts an optional address variant to an optional flow addr in a callback.
|
||||
struct name_resolver_lookup_handler
|
||||
{
|
||||
std::function<void(std::optional<flow::FlowAddr>)> result_handler;
|
||||
void
|
||||
operator()(std::optional<EndpointBase::AddressVariant_t> result) const
|
||||
{
|
||||
if (result)
|
||||
|
||||
result_handler(flow::FlowAddr{*result});
|
||||
else
|
||||
result_handler(std::nullopt);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void
|
||||
NameResolver::resolve_flow_addr_async(
|
||||
std::string name, std::function<void(std::optional<flow::FlowAddr>)> result_handler)
|
||||
{
|
||||
// todo: use flow layer
|
||||
if (const auto& endpoint_ptr = _flow_layer.pimpl->endpoint)
|
||||
{
|
||||
endpoint_ptr->LookupNameAsync(
|
||||
std::move(name), name_resolver_lookup_handler{std::move(result_handler)});
|
||||
}
|
||||
else
|
||||
result_handler(std::nullopt);
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::platform
|
23
llarp/layers/platform/os_traffic.cpp
Normal file
23
llarp/layers/platform/os_traffic.cpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#include "os_traffic.hpp"
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
OSTraffic::OSTraffic(
|
||||
EtherType_t ethertype, PlatformAddr src_, PlatformAddr dst_, std::vector<uint8_t> data_)
|
||||
: kind{ethertype}, src{std::move(src_)}, dst{std::move(dst_)}, data{std::move(data_)}
|
||||
{}
|
||||
|
||||
void
|
||||
OSTraffic::recompute_checksums()
|
||||
{
|
||||
// todo: implement me
|
||||
}
|
||||
|
||||
std::string
|
||||
OSTraffic::ToString() const
|
||||
{
|
||||
return fmt::format("[OSTraffic kind={} src={} dst={} {} bytes]", kind, src, dst, data.size());
|
||||
}
|
||||
} // namespace llarp::layers::platform
|
61
llarp/layers/platform/platform_addr.cpp
Normal file
61
llarp/layers/platform/platform_addr.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
#include "platform_addr.hpp"
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include "llarp/layers/platform/platform_layer.hpp"
|
||||
#include "llarp/net/ip.hpp"
|
||||
#include "llarp/net/ip_range.hpp"
|
||||
#include "llarp/net/net_int.hpp"
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
|
||||
PlatformAddr::PlatformAddr(huint128_t addr) : PlatformAddr{ToNet(addr)}
|
||||
{}
|
||||
|
||||
PlatformAddr::PlatformAddr(net::ipaddr_t addr) : ip{net::maybe_expand_ip(addr)}
|
||||
{}
|
||||
|
||||
PlatformAddr::PlatformAddr(const std::string& str) : PlatformAddr{net::ipaddr_from_string(str)}
|
||||
{}
|
||||
|
||||
net::ipaddr_t
|
||||
PlatformAddr::as_ipaddr() const
|
||||
{
|
||||
return net::maybe_truncate_ip(ip);
|
||||
}
|
||||
|
||||
std::optional<net::ipv4addr_t>
|
||||
PlatformAddr::as_ipv4addr() const
|
||||
{
|
||||
auto ip = as_ipaddr();
|
||||
if (auto* ptr = std::get_if<net::ipv4addr_t>(&ip))
|
||||
return *ptr;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool
|
||||
PlatformAddr::operator==(const PlatformAddr& other) const
|
||||
{
|
||||
return ip == other.ip;
|
||||
}
|
||||
bool
|
||||
PlatformAddr::operator!=(const PlatformAddr& other) const
|
||||
{
|
||||
return not(*this == other);
|
||||
}
|
||||
|
||||
bool
|
||||
PlatformAddr::operator<(const PlatformAddr& other) const
|
||||
{
|
||||
return ToHost(ip) < ToHost(other.ip);
|
||||
}
|
||||
|
||||
std::string
|
||||
PlatformAddr::ToString() const
|
||||
{
|
||||
return fmt::format(
|
||||
"[PlatformAddr '{}' flowlabel={:x}']",
|
||||
var::visit([](auto&& addr) { return addr.ToString(); }, as_ipaddr()),
|
||||
ToHost(flowlabel).h);
|
||||
}
|
||||
} // namespace llarp::layers::platform
|
487
llarp/layers/platform/platform_layer.cpp
Normal file
487
llarp/layers/platform/platform_layer.cpp
Normal file
|
@ -0,0 +1,487 @@
|
|||
#include "platform_layer.hpp"
|
||||
#include "dns_bridge.hpp"
|
||||
#include "llarp/layers/flow/flow_addr.hpp"
|
||||
#include "llarp/layers/platform/addr_mapper.hpp"
|
||||
#include "llarp/layers/platform/platform_addr.hpp"
|
||||
#include "llarp/net/ip_packet.hpp"
|
||||
#include "llarp/net/ip_range.hpp"
|
||||
#include "oxen/log.hpp"
|
||||
|
||||
#include <llarp/config/config.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/layers/flow/flow_layer.hpp>
|
||||
#include <llarp/dns/server.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
#include <llarp/link/i_link_manager.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include <llarp/router/route_poker.hpp>
|
||||
#include <memory>
|
||||
#include <nlohmann/detail/string_concat.hpp>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{
|
||||
static auto logcat = log::Cat("platform-layer");
|
||||
|
||||
void
|
||||
OSTraffic_IO_Base::got_ip_packet(net::IPPacket pkt)
|
||||
{
|
||||
if (not _running.load())
|
||||
return;
|
||||
auto& os_pkt = _recv_queue.emplace_back();
|
||||
os_pkt.kind = EtherType_t::ip_unicast;
|
||||
os_pkt.src.ip = net::ipv6addr_t::from_host(pkt.srcv6());
|
||||
os_pkt.dst.ip = net::ipv6addr_t::from_host(pkt.dstv6());
|
||||
os_pkt.data = pkt.steal();
|
||||
log::trace(logcat, "got_ip_packet: {}", os_pkt);
|
||||
}
|
||||
|
||||
OSTraffic_IO_Base::OSTraffic_IO_Base(const EventLoop_ptr& loop) : _running{false}, _loop{loop}
|
||||
{
|
||||
_running.store(true);
|
||||
}
|
||||
|
||||
std::vector<OSTraffic>
|
||||
OSTraffic_IO_Base::read_platform_traffic()
|
||||
{
|
||||
std::vector<OSTraffic> ret = std::move(_recv_queue);
|
||||
_recv_queue = decltype(_recv_queue){};
|
||||
_recv_queue.reserve(ret.capacity());
|
||||
log::trace(logcat, "read_platform_traffic: {} packets", ret.size());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
OSTraffic_IO_Base::write_platform_traffic(std::vector<OSTraffic>&& traff)
|
||||
{
|
||||
if (not _running.load())
|
||||
return;
|
||||
if (not _netif)
|
||||
return;
|
||||
log::trace(logcat, "write_platform_traffic: {} packets", traff.size());
|
||||
for (auto& os_pkt : traff)
|
||||
{
|
||||
// todo: figure out plainquic.
|
||||
if (os_pkt.kind != EtherType_t::ip_unicast)
|
||||
continue;
|
||||
|
||||
net::IPPacket pkt{std::move(os_pkt.data)};
|
||||
// drop any malformed packets.
|
||||
if (pkt.empty())
|
||||
continue;
|
||||
|
||||
_netif->WritePacket(std::move(pkt));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OSTraffic_IO_Base::attach(
|
||||
std::shared_ptr<vpn::NetworkInterface> netif, std::shared_ptr<EventLoopWakeup> wakeup_recv)
|
||||
{
|
||||
log::debug(logcat, "attach OSTraffic_IO_Base to {}", netif->Info().ifname);
|
||||
_netif = std::move(netif);
|
||||
_our_addr = PlatformAddr{_netif->Info().addrs[0].range.addr};
|
||||
|
||||
// set up event loop reader.
|
||||
// this idempotently wakes up those who want ip packets from us.
|
||||
auto loop = _loop.lock();
|
||||
if (not loop)
|
||||
throw std::runtime_error{"event loop gone"};
|
||||
loop->add_network_interface(_netif, [this, wakeup = std::move(wakeup_recv)](auto pkt) {
|
||||
if (not _running)
|
||||
return;
|
||||
got_ip_packet(std::move(pkt));
|
||||
wakeup->Trigger();
|
||||
});
|
||||
_netif->Start();
|
||||
log::debug(logcat, "network interface '{}' started up", _netif->Info().ifname);
|
||||
}
|
||||
|
||||
void
|
||||
OSTraffic_IO_Base::detach()
|
||||
{
|
||||
if (not _running.exchange(false))
|
||||
return;
|
||||
log::trace(logcat, "detach from event loop");
|
||||
if (_netif)
|
||||
_netif->Stop();
|
||||
_netif = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
PlatformLayer::on_io()
|
||||
{
|
||||
log::trace(logcat, "on_io()");
|
||||
// read all packets from last io cycle.
|
||||
auto pkts = _io->read_platform_traffic();
|
||||
// if we read nothing we are done.
|
||||
if (pkts.empty())
|
||||
return;
|
||||
os_traffic(std::move(pkts));
|
||||
}
|
||||
|
||||
static std::vector<byte_t>
|
||||
strip_ip_packet(std::vector<byte_t> data, bool strip_src, bool strip_dst)
|
||||
{
|
||||
net::IPPacket pkt{std::move(data)};
|
||||
// malformed?
|
||||
if (pkt.empty())
|
||||
return std::vector<byte_t>{};
|
||||
|
||||
if (strip_dst and strip_src)
|
||||
{
|
||||
pkt.ZeroAddresses();
|
||||
}
|
||||
else if (strip_src)
|
||||
{
|
||||
pkt.ZeroSourceAddress();
|
||||
}
|
||||
|
||||
return pkt.steal();
|
||||
}
|
||||
|
||||
static EtherType_t
|
||||
to_ether_type(flow::FlowDataKind kind)
|
||||
{
|
||||
const std::unordered_map<flow::FlowDataKind, EtherType_t> kind_to_ethertype = {
|
||||
{flow::FlowDataKind::auth, EtherType_t::proto_auth},
|
||||
{flow::FlowDataKind::stream_unicast, EtherType_t::plainquic},
|
||||
{flow::FlowDataKind::direct_ip_unicast, EtherType_t::ip_unicast},
|
||||
{flow::FlowDataKind::exit_ip_unicast, EtherType_t::ip_unicast},
|
||||
};
|
||||
|
||||
return kind_to_ethertype.at(kind);
|
||||
}
|
||||
|
||||
void
|
||||
PlatformLayer::flow_traffic(std::vector<flow::FlowTraffic>&& traff)
|
||||
{
|
||||
std::vector<OSTraffic> os_pkts;
|
||||
// for each flow layer traffic ...
|
||||
for (auto& pkt : traff)
|
||||
{
|
||||
// figure out what it is tracked on.
|
||||
auto maybe_addr = addr_mapper.get_addr_for_flow(pkt.flow_info);
|
||||
if (not maybe_addr)
|
||||
{
|
||||
// this is traffic for something we are not tracking.
|
||||
log::warning(
|
||||
logcat,
|
||||
"unexpected flow traffic on {}: {} bytes of {}",
|
||||
pkt.flow_info,
|
||||
pkt.datum.size(),
|
||||
pkt.kind);
|
||||
continue;
|
||||
}
|
||||
// translate flow traffic to platform traffic.
|
||||
log::trace(logcat, "got {} bytes of {} on {}", pkt.datum.size(), pkt.kind, *maybe_addr);
|
||||
os_pkts.emplace_back(OSTraffic{
|
||||
to_ether_type(pkt.kind), maybe_addr->dst, maybe_addr->src, std::move(pkt.datum)});
|
||||
}
|
||||
// write platform traffic.
|
||||
if (not os_pkts.empty())
|
||||
_io->write_platform_traffic(std::move(os_pkts));
|
||||
}
|
||||
|
||||
void
|
||||
PlatformLayer::os_traffic(std::vector<OSTraffic>&& traff)
|
||||
{
|
||||
std::vector<OSTraffic> icmp_reject;
|
||||
// for each ip packet...
|
||||
for (auto& os_pkt : traff)
|
||||
{
|
||||
if (os_pkt.kind != EtherType_t::ip_unicast)
|
||||
continue;
|
||||
if (os_pkt.data.empty())
|
||||
{
|
||||
// there is nothing here.. ?
|
||||
log::warning(logcat, "got empty packet? src={} dst={}", os_pkt.src, os_pkt.dst);
|
||||
continue;
|
||||
}
|
||||
// find an existing flow.
|
||||
auto maybe_mapping = addr_mapper.mapping_for(os_pkt.src, os_pkt.dst);
|
||||
if ((not maybe_mapping) or (not maybe_mapping->flow_info))
|
||||
{
|
||||
// we have no flows to that place so we will send an icmp reject back from our address to
|
||||
// whoever sent it.
|
||||
log::trace(logcat, "no mapping for src={} dst={}", os_pkt.src, os_pkt.dst);
|
||||
// dest host unreachable
|
||||
auto icmp = net::IPPacket::make_icmp(
|
||||
std::move(os_pkt.data), _io->our_platform_addr().as_ipaddr(), 3, 1);
|
||||
|
||||
icmp_reject.emplace_back(
|
||||
os_pkt.kind, _io->our_platform_addr(), os_pkt.src, std::move(icmp));
|
||||
continue;
|
||||
}
|
||||
const auto& flow_info = *maybe_mapping->flow_info;
|
||||
|
||||
if (os_pkt.data.size() > flow_info.mtu)
|
||||
{
|
||||
log::trace(logcat, "packet too big: {} > {}", os_pkt.data.size(), flow_info.mtu);
|
||||
// traffic too big. write icmp reject for fragmentation.
|
||||
OSTraffic reply;
|
||||
reply.kind = os_pkt.kind;
|
||||
reply.src = _io->our_platform_addr();
|
||||
reply.dst = os_pkt.src;
|
||||
reply.data = net::IPPacket::make_icmp(
|
||||
std::move(os_pkt.data), reply.src.as_ipaddr(), 3, 4); // ip fragmentation.
|
||||
icmp_reject.push_back(std::move(reply));
|
||||
}
|
||||
|
||||
flow::FlowDataKind data_kind = flow::FlowDataKind::direct_ip_unicast;
|
||||
bool is_exit = not maybe_mapping->owned_ranges.empty();
|
||||
if (is_exit)
|
||||
data_kind = flow::FlowDataKind::exit_ip_unicast;
|
||||
|
||||
// get a flow layer source to send with, ...
|
||||
auto local_source = _flow_layer.flow_to(flow_info.dst);
|
||||
// ... ensure it is sanitized and well formed ...
|
||||
auto pkt = strip_ip_packet(std::move(os_pkt.data), true, not is_exit);
|
||||
if (pkt.empty())
|
||||
{
|
||||
log::debug(logcat, "did not strip ip packet on {}", local_source->flow_info);
|
||||
continue;
|
||||
}
|
||||
|
||||
log::trace(logcat, "send {} bytes to {}", pkt.size(), local_source->flow_info);
|
||||
// ... and send the data along.
|
||||
local_source->send_to_remote(std::move(pkt), data_kind);
|
||||
}
|
||||
// write any reject packets we need to.
|
||||
if (not icmp_reject.empty())
|
||||
_io->write_platform_traffic(std::move(icmp_reject));
|
||||
}
|
||||
|
||||
/// this calls the io handlers after an io cycle consumes events.
|
||||
struct platform_io_wakeup_handler
|
||||
{
|
||||
PlatformLayer& driver;
|
||||
void
|
||||
operator()()
|
||||
{
|
||||
driver.on_io();
|
||||
}
|
||||
};
|
||||
|
||||
PlatformLayer::PlatformLayer(
|
||||
AbstractRouter& router, flow::FlowLayer& flow_layer, const NetworkConfig& netconf)
|
||||
: _router{router}
|
||||
, _flow_layer{flow_layer}
|
||||
, _os_recv{router.loop()->make_waker(platform_io_wakeup_handler{*this})}
|
||||
, _netconf{netconf}
|
||||
, _dns_query_handler{std::make_shared<DNSQueryHandler>(*this)}
|
||||
, addr_mapper{_netconf.ifaddr(router.Net())}
|
||||
{}
|
||||
|
||||
void
|
||||
PlatformLayer::start()
|
||||
{
|
||||
_io = make_io();
|
||||
|
||||
// attach dns resolver.
|
||||
if (const auto& dns = _router.get_dns())
|
||||
dns->AddResolver(std::weak_ptr<dns::Resolver_Base>{_dns_query_handler});
|
||||
|
||||
auto* vpn = _router.GetVPNPlatform();
|
||||
if (not vpn)
|
||||
return;
|
||||
|
||||
// create vpn interface and attach to platform layer.
|
||||
const auto& _net = _router.Net();
|
||||
auto netif = vpn->CreateInterface(vpn::InterfaceInfo{_netconf, _net}, &_router);
|
||||
|
||||
if (not netif)
|
||||
throw std::runtime_error{"did not make vpn interface"};
|
||||
_netif = netif;
|
||||
_io->attach(netif, _os_recv);
|
||||
|
||||
// attach vpn interface to dns and start it.
|
||||
if (const auto& dns = _router.get_dns())
|
||||
dns->Start(netif->Info().index);
|
||||
}
|
||||
|
||||
void
|
||||
PlatformLayer::stop()
|
||||
{
|
||||
log::info(logcat, "stop");
|
||||
// stop all io operations on our end and detach from event loop.
|
||||
_io->detach();
|
||||
}
|
||||
|
||||
/// handles when we have established a flow with a remote.
|
||||
struct got_flow_handler
|
||||
{
|
||||
AddrMapper& mapper;
|
||||
AddressMapping entry;
|
||||
std::string name;
|
||||
std::function<void(std::optional<flow::FlowInfo>, std::string)> result_handler;
|
||||
void
|
||||
fail(std::string error_reason) const
|
||||
{
|
||||
result_handler(std::nullopt, "could not obtain flow to '{}': {}"_format(name, error_reason));
|
||||
}
|
||||
|
||||
void
|
||||
success(const flow::FlowInfo& flow_info)
|
||||
{
|
||||
entry.flow_info = flow_info;
|
||||
mapper.put(std::move(entry));
|
||||
|
||||
result_handler(flow_info, "");
|
||||
}
|
||||
|
||||
void
|
||||
operator()(std::optional<flow::FlowInfo> maybe_flow_info, std::string error)
|
||||
{
|
||||
if (maybe_flow_info)
|
||||
success(*maybe_flow_info);
|
||||
else
|
||||
fail(std::move(error));
|
||||
}
|
||||
};
|
||||
|
||||
/// called to handle when name resolution completes with either failure or success.
|
||||
struct resolved_name_handler
|
||||
{
|
||||
flow::FlowLayer& flow_layer;
|
||||
std::string name;
|
||||
flow::FlowEstablish flow_establisher;
|
||||
bool hydrate_cache{false};
|
||||
|
||||
void
|
||||
operator()(std::optional<flow::FlowAddr> maybe_addr)
|
||||
{
|
||||
if (not maybe_addr)
|
||||
{
|
||||
flow_establisher.fail("failed to resolve name");
|
||||
return;
|
||||
}
|
||||
// get a flow tag for this remote.
|
||||
flow_layer.flow_to(*maybe_addr)->async_ensure_flow(std::move(flow_establisher));
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
PlatformLayer::map_remote(
|
||||
std::string name,
|
||||
std::string auth,
|
||||
std::vector<IPRange> dst_ranges,
|
||||
std::optional<PlatformAddr> src,
|
||||
std::function<void(std::optional<flow::FlowInfo>, std::string)> result_handler)
|
||||
{
|
||||
if (addr_mapper.is_full())
|
||||
{
|
||||
result_handler(std::nullopt, "address map full");
|
||||
return;
|
||||
}
|
||||
// reserve a mapping.
|
||||
AddressMapping mapping = addr_mapper.allocate_mapping(src.value_or(_io->our_platform_addr()));
|
||||
mapping.owned_ranges = std::move(dst_ranges);
|
||||
|
||||
// call this when we got a flow to the guy
|
||||
// it will wire up the mapping.
|
||||
got_flow_handler got_flow{addr_mapper, std::move(mapping), name, std::move(result_handler)};
|
||||
|
||||
// do any extra handshaking with a flow handshaker, then call the got flow handler
|
||||
// todo: this really belongs in flow layer.
|
||||
flow::FlowEstablish flow_establisher{std::move(got_flow), nullptr};
|
||||
flow_establisher.authcode = std::move(auth);
|
||||
|
||||
resolved_name_handler resolved_name{_flow_layer, name, std::move(flow_establisher)};
|
||||
|
||||
// fire off the lookup.
|
||||
_flow_layer.name_resolver.resolve_flow_addr_async(std::move(name), std::move(resolved_name));
|
||||
}
|
||||
|
||||
std::shared_ptr<vpn::NetworkInterface>
|
||||
PlatformLayer::vpn_interface() const
|
||||
{
|
||||
return _netif.lock();
|
||||
}
|
||||
|
||||
vpn::IRouteManager*
|
||||
PlatformLayer::route_manager() const
|
||||
{
|
||||
if (auto* vpn_plat = _router.GetVPNPlatform())
|
||||
return &vpn_plat->RouteManager();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<OSTraffic_IO_Base>
|
||||
PlatformLayer::make_io() const
|
||||
{
|
||||
return std::make_unique<OSTraffic_IO_Base>(_router.loop());
|
||||
}
|
||||
|
||||
/// handles when we map an exit to an ip range after we have gotten a flow with it.
|
||||
struct map_exit_handler
|
||||
{
|
||||
AbstractRouter& router;
|
||||
std::function<void(bool, std::string)> result_handler;
|
||||
void
|
||||
operator()(std::optional<flow::FlowInfo> found, std::string msg) const
|
||||
{
|
||||
if (found)
|
||||
{
|
||||
// found the result, put firewall up.
|
||||
if (const auto& poker = router.routePoker())
|
||||
poker->Up();
|
||||
result_handler(true, std::move(msg));
|
||||
return;
|
||||
}
|
||||
|
||||
result_handler(false, std::move(msg));
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
PlatformLayer::map_exit(
|
||||
std::string name,
|
||||
std::string auth,
|
||||
std::vector<IPRange> ranges,
|
||||
std::function<void(bool, std::string)> result_handler)
|
||||
{
|
||||
map_remote(
|
||||
std::move(name),
|
||||
std::move(auth),
|
||||
std::move(ranges),
|
||||
_io->our_platform_addr(),
|
||||
map_exit_handler{_router, std::move(result_handler)});
|
||||
}
|
||||
|
||||
void
|
||||
PlatformLayer::unmap_all_exits_on_range(IPRange range)
|
||||
{
|
||||
addr_mapper.remove_if_mapping(
|
||||
[range](const auto& mapping) -> bool { return mapping.owns_range(range); });
|
||||
}
|
||||
|
||||
void
|
||||
PlatformLayer::unmap_exit(flow::FlowAddr addr, std::optional<IPRange> range)
|
||||
{
|
||||
addr_mapper.remove_if_mapping([addr, range](const auto& mapping) -> bool {
|
||||
return mapping.flow_info and mapping.flow_info->dst == addr
|
||||
and (range ? mapping.owns_range(*range) : true);
|
||||
});
|
||||
}
|
||||
|
||||
PlatformStats
|
||||
PlatformLayer::current_stats() const
|
||||
{
|
||||
return PlatformStats{};
|
||||
}
|
||||
|
||||
DNSZone&
|
||||
PlatformLayer::local_dns_zone()
|
||||
{
|
||||
if (not _local_dns_zone)
|
||||
_local_dns_zone = std::make_shared<DNSZone>(addr_mapper, _flow_layer.local_addr());
|
||||
return *_local_dns_zone;
|
||||
}
|
||||
|
||||
} // namespace llarp::layers::platform
|
4
llarp/layers/platform/platform_stats.cpp
Normal file
4
llarp/layers/platform/platform_stats.cpp
Normal file
|
@ -0,0 +1,4 @@
|
|||
#include "platform_stats.hpp"
|
||||
|
||||
namespace llarp::layers::platform
|
||||
{}
|
63
llarp/layers/route/route_layer.cpp
Normal file
63
llarp/layers/route/route_layer.cpp
Normal file
|
@ -0,0 +1,63 @@
|
|||
#include "route_layer.hpp"
|
||||
|
||||
namespace llarp::layers::route
|
||||
{
|
||||
RouteLayer::RouteLayer(AbstractRouter&)
|
||||
{}
|
||||
|
||||
Destination*
|
||||
RouteLayer::create_destination(const path::TransitHopInfo&, PubKey, RouterID)
|
||||
{
|
||||
// todo: implement me.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
RouteLayer::remove_destination_on(const path::TransitHopInfo&)
|
||||
{
|
||||
// todo: implement me.
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
RouteLayer::remove_destinations_from(const PubKey&)
|
||||
{
|
||||
// todo: implement me.
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t
|
||||
RouteLayer::remove_destinations_to(const PubKey&, const RouterID&)
|
||||
{
|
||||
// todo: implement me.
|
||||
return 0;
|
||||
}
|
||||
|
||||
Destination*
|
||||
RouteLayer::destination_to(const PathID_t&)
|
||||
{
|
||||
// todo: implement me.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Destination*
|
||||
RouteLayer::destination_from(const PathID_t&)
|
||||
{
|
||||
// todo: implement me.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::vector<Destination*>
|
||||
RouteLayer::all_destinations_from(const PubKey&)
|
||||
{
|
||||
// todo: implement me.
|
||||
return {};
|
||||
}
|
||||
|
||||
Destination*
|
||||
RouteLayer::destination_on(const path::TransitHopInfo&) const
|
||||
{
|
||||
// todo: implement me.
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace llarp::layers::route
|
|
@ -142,13 +142,8 @@ namespace llarp
|
|||
pkt.resize(buf.sz);
|
||||
std::copy_n(buf.base, buf.sz, pkt.data());
|
||||
RecvFrom(from, std::move(pkt));
|
||||
});
|
||||
|
||||
if (m_udp->listen(m_ourAddr))
|
||||
return;
|
||||
|
||||
throw std::runtime_error{
|
||||
fmt::format("failed to listen {} udp socket on {}", Name(), m_ourAddr)};
|
||||
},
|
||||
m_ourAddr);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
#include <llarp/util/logging/buffer.hpp>
|
||||
#include <llarp/util/logging/callback_sink.hpp>
|
||||
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
|
||||
#include <llarp/net/net_int.hpp>
|
||||
|
||||
#include <oxenc/base32z.h>
|
||||
|
||||
#include <mutex>
|
||||
|
@ -80,9 +84,9 @@ namespace
|
|||
|
||||
struct UDPHandler
|
||||
{
|
||||
using AddressVariant_t = llarp::vpn::AddressVariant_t;
|
||||
using AddressVariant_t = llarp::EndpointBase::AddressVariant_t;
|
||||
int m_SocketID;
|
||||
llarp::nuint16_t m_LocalPort;
|
||||
llarp::net::port_t m_LocalPort;
|
||||
lokinet_udp_flow_filter m_Filter;
|
||||
lokinet_udp_flow_recv_func m_Recv;
|
||||
lokinet_udp_flow_timeout_func m_Timeout;
|
||||
|
@ -303,11 +307,12 @@ struct lokinet_context
|
|||
}
|
||||
|
||||
[[nodiscard]] auto
|
||||
endpoint(std::string name = "default") const
|
||||
endpoint() const
|
||||
{
|
||||
return impl->router->hiddenServiceContext().GetEndpointByName(name);
|
||||
return impl->router->get_layers()->flow->local_deprecated_loki_endpoint();
|
||||
}
|
||||
|
||||
/// maps socket id to a bool which says if it is for a socket where we are the recipiant or not.
|
||||
std::unordered_map<int, bool> streams;
|
||||
std::unordered_map<int, std::shared_ptr<UDPHandler>> udp_sockets;
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@
|
|||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/mem.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#ifndef _WIN32
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
@ -561,6 +564,9 @@ namespace llarp::net
|
|||
std::optional<IPPacket>
|
||||
IPPacket::MakeICMPUnreachable() const
|
||||
{
|
||||
if (empty())
|
||||
return std::nullopt;
|
||||
|
||||
if (IsV4())
|
||||
{
|
||||
constexpr auto icmp_Header_size = 8;
|
||||
|
@ -603,7 +609,7 @@ namespace llarp::net
|
|||
*checksum = ipchksum(icmp_begin, icmp_size);
|
||||
return pkt;
|
||||
}
|
||||
return std::nullopt;
|
||||
return IPPacket{};
|
||||
}
|
||||
|
||||
std::optional<std::pair<const char*, size_t>>
|
||||
|
@ -700,4 +706,14 @@ namespace llarp::net
|
|||
// TODO: ipv6
|
||||
return net::IPPacket{size_t{}};
|
||||
}
|
||||
|
||||
std::vector<byte_t>
|
||||
IPPacket::make_icmp(std::vector<byte_t>&& data, net::ipaddr_t our_ip, int type, int code)
|
||||
{
|
||||
(void)our_ip, (void)type, (void)code;
|
||||
IPPacket pkt{std::vector<byte_t>{data}};
|
||||
if (auto maybe = pkt.MakeICMPUnreachable())
|
||||
return maybe->steal();
|
||||
return std::vector<byte_t>{};
|
||||
}
|
||||
} // namespace llarp::net
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <memory>
|
||||
#include <llarp/service/protocol_type.hpp>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp::net
|
||||
{
|
||||
|
@ -189,6 +190,24 @@ namespace llarp::net
|
|||
return buf;
|
||||
}
|
||||
|
||||
/// make a copy of the underlying vector
|
||||
inline std::vector<byte_t>
|
||||
copy() const
|
||||
{
|
||||
return _buf;
|
||||
}
|
||||
|
||||
/// return address family this ip packet belongs to.
|
||||
inline auto
|
||||
family() const
|
||||
{
|
||||
if (IsV4())
|
||||
return AF_INET;
|
||||
if (IsV6())
|
||||
return AF_INET6;
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
|
||||
inline byte_t*
|
||||
data()
|
||||
{
|
||||
|
@ -372,6 +391,14 @@ namespace llarp::net
|
|||
MakeICMPUnreachable() const;
|
||||
|
||||
std::function<void(net::IPPacket)> reply;
|
||||
|
||||
/// recompute all ip packet checksums so that they match match the data.
|
||||
void
|
||||
recompute_checksums();
|
||||
|
||||
/// make an icmp packet from an existing ip packet, replacing the source address with our ip.
|
||||
static std::vector<byte_t>
|
||||
make_icmp(std::vector<byte_t>&& pkt, net::ipaddr_t our_ip, int type, int code);
|
||||
};
|
||||
|
||||
/// generate ip checksum
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "ip.hpp"
|
||||
#include "llarp/net/net_int.hpp"
|
||||
#include "net_bits.hpp"
|
||||
#include <llarp/util/bits.hpp>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
|
@ -10,6 +11,7 @@
|
|||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -44,11 +46,9 @@ namespace llarp
|
|||
}
|
||||
|
||||
static inline IPRange
|
||||
FromIPv4(net::ipv4addr_t addr, net::ipv4addr_t netmask)
|
||||
FromIPv4(net::ipv4addr_t addr, byte_t mask)
|
||||
{
|
||||
return IPRange{
|
||||
net::ExpandV4(llarp::net::ToHost(addr)),
|
||||
netmask_ipv6_bits(bits::count_bits(netmask) + 96)};
|
||||
return IPRange{net::ExpandV4(llarp::net::ToHost(addr)), netmask_ipv6_bits(96 + mask)};
|
||||
}
|
||||
|
||||
/// return true if this iprange is in the IPv4 mapping range for containing ipv4 addresses
|
||||
|
@ -80,12 +80,19 @@ namespace llarp
|
|||
|
||||
/// return true if our range and other intersect
|
||||
constexpr bool
|
||||
operator*(const IPRange& other) const
|
||||
intersects_with(const IPRange& other) const
|
||||
{
|
||||
return Contains(other) or other.Contains(*this);
|
||||
}
|
||||
|
||||
/// return true if the other range is inside our range
|
||||
/// calls intersects_with()
|
||||
constexpr bool
|
||||
operator*(const IPRange& other) const
|
||||
{
|
||||
return intersects_with(other);
|
||||
}
|
||||
|
||||
/// return true if the other range is inside ( subset of ) our range
|
||||
constexpr bool
|
||||
Contains(const IPRange& other) const
|
||||
{
|
||||
|
@ -111,7 +118,18 @@ namespace llarp
|
|||
inline bool
|
||||
Contains(const net::ipaddr_t& ip) const
|
||||
{
|
||||
return var::visit([this](auto&& ip) { return Contains(llarp::net::ToHost(ip)); }, ip);
|
||||
Addr_t _ip{};
|
||||
if (auto* ptr = std::get_if<net::ipv4addr_t>(&ip))
|
||||
{
|
||||
if (not IsV4())
|
||||
return false;
|
||||
_ip = net::ExpandV4(ToHost(*ptr));
|
||||
}
|
||||
if (auto* ptr = std::get_if<net::ipv6addr_t>(&ip))
|
||||
{
|
||||
_ip = ToHost(*ptr);
|
||||
}
|
||||
return (addr & netmask_bits) == (_ip & netmask_bits);
|
||||
}
|
||||
|
||||
/// get the highest address on this range
|
||||
|
|
|
@ -1,34 +1,37 @@
|
|||
#pragma once
|
||||
#include <cstddef>
|
||||
#include "net_int.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
/// get a netmask with the higest numset bits set
|
||||
constexpr huint128_t
|
||||
_netmask_ipv6_bits(uint32_t numset)
|
||||
{
|
||||
return (128 - numset) ? (huint128_t{1} << numset) | _netmask_ipv6_bits(numset + 1)
|
||||
: huint128_t{0};
|
||||
}
|
||||
|
||||
constexpr huint128_t
|
||||
netmask_ipv6_bits(uint32_t numset)
|
||||
{
|
||||
return _netmask_ipv6_bits(128 - numset);
|
||||
}
|
||||
|
||||
/// get a netmask with the higest numset bits set
|
||||
constexpr uint32_t
|
||||
_netmask_ipv4_bits(uint32_t numset)
|
||||
{
|
||||
return (32 - numset) ? (1 << numset) | _netmask_ipv4_bits(numset + 1) : 0;
|
||||
}
|
||||
|
||||
/// get a netmask given some /N range
|
||||
constexpr huint32_t
|
||||
netmask_ipv4_bits(uint32_t num)
|
||||
template <typename UInt_t>
|
||||
constexpr huint_t<UInt_t>
|
||||
netmask_bits(const int numset)
|
||||
{
|
||||
return huint32_t{_netmask_ipv4_bits(32 - num)};
|
||||
constexpr int max_bits = sizeof(UInt_t) * 8;
|
||||
if (numset > max_bits)
|
||||
throw std::invalid_argument{
|
||||
fmt::format("invalid netmask size requested: {} > {}", numset, max_bits)};
|
||||
huint_t<UInt_t> bits{};
|
||||
for (int idx = numset; idx > 0; --idx)
|
||||
{
|
||||
bits.h |= (UInt_t{1} << (max_bits - idx));
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
constexpr huint128_t
|
||||
netmask_ipv6_bits(int numset)
|
||||
{
|
||||
return netmask_bits<uint128_t>(numset);
|
||||
}
|
||||
|
||||
constexpr huint32_t
|
||||
netmask_ipv4_bits(int numset)
|
||||
{
|
||||
return netmask_bits<uint32_t>(numset);
|
||||
}
|
||||
|
||||
constexpr huint32_t
|
||||
|
@ -40,14 +43,13 @@ namespace llarp
|
|||
// IPv4 mapped address live at ::ffff:0:0/96
|
||||
constexpr std::array<uint8_t, 12> ipv4_map_prefix{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff};
|
||||
|
||||
namespace net
|
||||
constexpr auto
|
||||
ipaddr_netmask_bits(int cidr, int af)
|
||||
{
|
||||
inline auto
|
||||
ipaddr_netmask_bits(uint32_t bits, int af)
|
||||
{
|
||||
if (af == AF_INET6)
|
||||
return netmask_ipv6_bits(bits);
|
||||
return ExpandV4(netmask_ipv4_bits(bits));
|
||||
};
|
||||
} // namespace net
|
||||
if (af == AF_INET)
|
||||
return net::ExpandV4(netmask_bits<uint32_t>(cidr));
|
||||
else
|
||||
return netmask_bits<uint128_t>(cidr);
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
#include "net_int.hpp"
|
||||
#include "ip.hpp"
|
||||
#include "llarp/net/ip_range.hpp"
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
#include <oxenc/endian.h>
|
||||
#include <oxen/log/format.hpp>
|
||||
|
||||
using namespace oxen::log::literals;
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace net
|
||||
{
|
||||
|
||||
huint16_t
|
||||
ToHost(port_t x)
|
||||
{
|
||||
|
@ -44,6 +51,34 @@ namespace llarp
|
|||
{
|
||||
return ipv6addr_t{hton128(x.h)};
|
||||
}
|
||||
|
||||
ipv6addr_t
|
||||
maybe_expand_ip(ipaddr_t ip)
|
||||
{
|
||||
if (auto* ptr = std::get_if<ipv6addr_t>(&ip))
|
||||
return *ptr;
|
||||
return ToNet(ExpandV4(ToHost(std::get<ipv4addr_t>(ip))));
|
||||
}
|
||||
|
||||
ipaddr_t
|
||||
maybe_truncate_ip(ipv6addr_t ip)
|
||||
{
|
||||
if (IPRange::V4MappedRange().Contains(ip))
|
||||
return ToNet(TruncateV6(ToHost(ip)));
|
||||
return ip;
|
||||
}
|
||||
|
||||
ipaddr_t
|
||||
ipaddr_from_string(const std::string& str)
|
||||
{
|
||||
ipv6addr_t v6{};
|
||||
if (v6.FromString(str))
|
||||
return v6;
|
||||
ipv4addr_t v4{};
|
||||
if (v4.FromString(str))
|
||||
return v4;
|
||||
throw std::invalid_argument{"'{}' is not an valid ip address"_format(str)};
|
||||
}
|
||||
} // namespace net
|
||||
|
||||
template <>
|
||||
|
|
|
@ -105,6 +105,12 @@ namespace llarp
|
|||
return h == x.h;
|
||||
}
|
||||
|
||||
constexpr bool
|
||||
operator<=(huint_t x) const
|
||||
{
|
||||
return h <= x.h;
|
||||
}
|
||||
|
||||
using V6Container = std::vector<uint8_t>;
|
||||
[[deprecated]] void
|
||||
ToV6(V6Container& c);
|
||||
|
@ -241,6 +247,18 @@ namespace llarp
|
|||
ipv4addr_t ToNet(huint32_t);
|
||||
ipv6addr_t ToNet(huint128_t);
|
||||
|
||||
/// construct an ip variant from a string.
|
||||
ipaddr_t
|
||||
ipaddr_from_string(const std::string& str);
|
||||
|
||||
/// takes in an ipaddr_t and if it's a ipv4 address it converts it to a mapped ipv6 address.
|
||||
ipv6addr_t
|
||||
maybe_expand_ip(ipaddr_t ip);
|
||||
|
||||
/// takes in an ipv6 address and truncates it to v4 if it is in the ipv4 mapped range.
|
||||
ipaddr_t
|
||||
maybe_truncate_ip(ipv6addr_t ip);
|
||||
|
||||
} // namespace net
|
||||
|
||||
template <>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#include "llarp/util/bits.hpp"
|
||||
#include "net.hpp"
|
||||
#include "net_if.hpp"
|
||||
#include <stdexcept>
|
||||
|
@ -86,7 +87,7 @@ namespace llarp::net
|
|||
{
|
||||
ipv4addr_t addr{reinterpret_cast<sockaddr_in*>(i->ifa_addr)->sin_addr.s_addr};
|
||||
ipv4addr_t mask{reinterpret_cast<sockaddr_in*>(i->ifa_netmask)->sin_addr.s_addr};
|
||||
currentRanges.emplace_back(IPRange::FromIPv4(addr, mask));
|
||||
currentRanges.emplace_back(IPRange::FromIPv4(addr, bits::count_bits(mask)));
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -85,13 +85,19 @@ namespace llarp
|
|||
RouterID
|
||||
Path::Endpoint() const
|
||||
{
|
||||
return hops[hops.size() - 1].rc.pubkey;
|
||||
return EndpointHop().rc.pubkey;
|
||||
}
|
||||
|
||||
PubKey
|
||||
const PubKey&
|
||||
Path::EndpointPubKey() const
|
||||
{
|
||||
return hops[hops.size() - 1].rc.pubkey;
|
||||
return EndpointHop().rc.pubkey;
|
||||
}
|
||||
|
||||
const PathHopConfig&
|
||||
Path::EndpointHop() const
|
||||
{
|
||||
return hops[hops.size() - 1];
|
||||
}
|
||||
|
||||
PathID_t
|
||||
|
|
|
@ -353,9 +353,12 @@ namespace llarp
|
|||
RouterID
|
||||
Endpoint() const;
|
||||
|
||||
PubKey
|
||||
const PubKey&
|
||||
EndpointPubKey() const;
|
||||
|
||||
const PathHopConfig&
|
||||
EndpointHop() const;
|
||||
|
||||
bool
|
||||
IsEndpoint(const RouterID& router, const PathID_t& path) const;
|
||||
|
||||
|
|
|
@ -1,14 +1,23 @@
|
|||
#include "path_context.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <llarp/messages/relay_commit.hpp>
|
||||
#include "llarp/path/pathset.hpp"
|
||||
#include "llarp/util/compare_ptr.hpp"
|
||||
#include "llarp/util/thread/annotations.hpp"
|
||||
#include "oxen/log.hpp"
|
||||
#include "path.hpp"
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/router/i_outbound_message_handler.hpp>
|
||||
#include <type_traits>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace path
|
||||
{
|
||||
static auto logcat = log::Cat("path-context");
|
||||
|
||||
static constexpr auto DefaultPathBuildLimit = 500ms;
|
||||
|
||||
PathContext::PathContext(AbstractRouter* router)
|
||||
|
@ -415,5 +424,12 @@ namespace llarp
|
|||
void
|
||||
PathContext::RemovePathSet(PathSet_ptr)
|
||||
{}
|
||||
|
||||
void
|
||||
PathContext::periodic_tick()
|
||||
{
|
||||
const auto now = m_Router->Now();
|
||||
ExpirePaths(now);
|
||||
}
|
||||
} // namespace path
|
||||
} // namespace llarp
|
||||
|
|
|
@ -33,8 +33,7 @@ namespace llarp
|
|||
struct PathContext
|
||||
{
|
||||
explicit PathContext(AbstractRouter* router);
|
||||
|
||||
/// called from router tick function
|
||||
/// apply path expiration.
|
||||
void
|
||||
ExpirePaths(llarp_time_t now);
|
||||
|
||||
|
@ -177,6 +176,10 @@ namespace llarp
|
|||
uint64_t
|
||||
CurrentOwnedPaths(path::PathStatus status = path::PathStatus::ePathEstablished);
|
||||
|
||||
/// handle a periodic tick on all pathsets.
|
||||
void
|
||||
periodic_tick();
|
||||
|
||||
private:
|
||||
AbstractRouter* m_Router;
|
||||
SyncTransitMap_t m_TransitPaths;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include "pathbuilder.hpp"
|
||||
#include "oxen/log.hpp"
|
||||
#include "path_context.hpp"
|
||||
|
||||
#include <llarp/crypto/crypto.hpp>
|
||||
|
@ -239,13 +240,13 @@ namespace llarp
|
|||
std::optional<RouterContact> found = std::nullopt;
|
||||
m_router->ForEachPeer(
|
||||
[&](const ILinkSession* s, bool isOutbound) {
|
||||
if (s && s->IsEstablished() && isOutbound && not found.has_value())
|
||||
if (s && s->IsEstablished() && isOutbound && not found)
|
||||
{
|
||||
const RouterContact rc = s->GetRemoteRC();
|
||||
#ifndef TESTNET
|
||||
if (m_router->IsBootstrapNode(rc.pubkey))
|
||||
|
||||
if (not m_router->rcLookupHandler().PathIsAllowed(rc.pubkey))
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (exclude.count(rc.pubkey))
|
||||
return;
|
||||
|
||||
|
@ -349,9 +350,19 @@ namespace llarp
|
|||
std::vector<RouterContact> hops;
|
||||
{
|
||||
const auto maybe = SelectFirstHop(exclude);
|
||||
if (not maybe.has_value())
|
||||
if (not maybe)
|
||||
{
|
||||
log::warning(log_path, "{} has no first hop candidate", Name());
|
||||
if (m_router->NumberOfConnectedRouters())
|
||||
log::warning(
|
||||
log_path,
|
||||
"cannot build path for '{}' becuase we do not have enough edge connections to the "
|
||||
"network.",
|
||||
Name());
|
||||
else
|
||||
log::warning(
|
||||
log_path,
|
||||
"cannot build path for '{}' because we are not collected to the network",
|
||||
Name());
|
||||
return std::nullopt;
|
||||
}
|
||||
hops.emplace_back(*maybe);
|
||||
|
|
|
@ -1,18 +1,19 @@
|
|||
#include "path.hpp"
|
||||
|
||||
#include "transit_hop.hpp"
|
||||
#include "path_context.hpp"
|
||||
|
||||
#include <llarp/dht/context.hpp>
|
||||
#include <llarp/exit/context.hpp>
|
||||
#include <llarp/exit/exit_messages.hpp>
|
||||
#include <llarp/link/i_link_manager.hpp>
|
||||
#include <llarp/messages/discard.hpp>
|
||||
#include <llarp/messages/relay_commit.hpp>
|
||||
#include <llarp/messages/relay_status.hpp>
|
||||
#include "path_context.hpp"
|
||||
#include "transit_hop.hpp"
|
||||
#include <llarp/routing/transfer_traffic_message.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/routing/path_latency_message.hpp>
|
||||
#include <llarp/routing/path_transfer_message.hpp>
|
||||
#include <llarp/routing/handler.hpp>
|
||||
|
||||
#include <llarp/util/buffer.hpp>
|
||||
|
||||
#include <oxenc/endian.h>
|
||||
|
@ -21,6 +22,9 @@ namespace llarp
|
|||
{
|
||||
namespace path
|
||||
{
|
||||
|
||||
static auto logcat = log::Cat("transit-hop");
|
||||
|
||||
std::string
|
||||
TransitHopInfo::ToString() const
|
||||
{
|
||||
|
@ -284,52 +288,65 @@ namespace llarp
|
|||
TransitHop::HandleObtainExitMessage(
|
||||
const llarp::routing::ObtainExitMessage& msg, AbstractRouter* r)
|
||||
{
|
||||
if (msg.Verify() && r->exitContext().ObtainNewExit(msg.I, info.rxID, msg.E != 0))
|
||||
if (not msg.Verify())
|
||||
{
|
||||
llarp::routing::GrantExitMessage grant;
|
||||
grant.S = NextSeqNo();
|
||||
grant.T = msg.T;
|
||||
if (!grant.Sign(r->identity()))
|
||||
// invalid sig, tell them their request was discarded.
|
||||
return SendRoutingMessage(routing::DataDiscardMessage{info.rxID, msg.S}, r);
|
||||
}
|
||||
// this message is always asking for our local snode.
|
||||
// future protocol versions are expected to permit non local routing destinations.
|
||||
RouterID to{r->pubkey()};
|
||||
auto destination = r->get_layers()->route->create_destination(info, msg.source_identity, to);
|
||||
if (not destination)
|
||||
{
|
||||
// we did not give the requester an allocation.
|
||||
// send them a reject.
|
||||
llarp::routing::RejectExitMessage reject;
|
||||
reject.S = NextSeqNo();
|
||||
reject.txid = msg.txid;
|
||||
// todo: exponential backoff
|
||||
reject.backoff = 2000;
|
||||
if (not reject.Sign(r->identity()))
|
||||
{
|
||||
llarp::LogError("Failed to sign grant exit message");
|
||||
log::info(
|
||||
logcat,
|
||||
"snode destionation for {} to {} was rejected by us"_format(msg.source_identity, to));
|
||||
return false;
|
||||
}
|
||||
return SendRoutingMessage(grant, r);
|
||||
}
|
||||
// TODO: exponential backoff
|
||||
// TODO: rejected policies
|
||||
llarp::routing::RejectExitMessage reject;
|
||||
reject.S = NextSeqNo();
|
||||
reject.T = msg.T;
|
||||
if (!reject.Sign(r->identity()))
|
||||
llarp::routing::GrantExitMessage grant;
|
||||
grant.S = NextSeqNo();
|
||||
grant.txid = msg.txid;
|
||||
if (not grant.Sign(r->identity()))
|
||||
{
|
||||
llarp::LogError("Failed to sign reject exit message");
|
||||
return false;
|
||||
// failed to sign, tell them it was discarded after removing routing destination.
|
||||
r->get_layers()->route->remove_destination_on(info);
|
||||
return SendRoutingMessage(routing::DataDiscardMessage{info.rxID, msg.S}, r);
|
||||
}
|
||||
return SendRoutingMessage(reject, r);
|
||||
return SendRoutingMessage(grant, r);
|
||||
}
|
||||
|
||||
bool
|
||||
TransitHop::HandleCloseExitMessage(
|
||||
const llarp::routing::CloseExitMessage& msg, AbstractRouter* r)
|
||||
{
|
||||
const llarp::routing::DataDiscardMessage discard(info.rxID, msg.S);
|
||||
auto ep = r->exitContext().FindEndpointForPath(info.rxID);
|
||||
if (ep && msg.Verify(ep->PubKey()))
|
||||
{
|
||||
llarp::routing::CloseExitMessage reply;
|
||||
reply.Y = msg.Y;
|
||||
reply.S = NextSeqNo();
|
||||
if (reply.Sign(r->identity()))
|
||||
{
|
||||
if (SendRoutingMessage(reply, r))
|
||||
{
|
||||
ep->Close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SendRoutingMessage(discard, r);
|
||||
const llarp::routing::DataDiscardMessage discard{info.rxID, msg.S};
|
||||
|
||||
auto maybe_dest = r->get_layers()->route->destination_on(info);
|
||||
if (not maybe_dest)
|
||||
return SendRoutingMessage(discard, r);
|
||||
|
||||
if (not msg.Verify(maybe_dest->src))
|
||||
return SendRoutingMessage(discard, r);
|
||||
|
||||
llarp::routing::CloseExitMessage reply;
|
||||
reply.Y = msg.Y;
|
||||
reply.S = NextSeqNo();
|
||||
|
||||
r->get_layers()->route->remove_destination_on(info);
|
||||
|
||||
reply.Sign(r->identity());
|
||||
return SendRoutingMessage(reply, r);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -346,23 +363,26 @@ namespace llarp
|
|||
TransitHop::HandleUpdateExitMessage(
|
||||
const llarp::routing::UpdateExitMessage& msg, AbstractRouter* r)
|
||||
{
|
||||
auto ep = r->exitContext().FindEndpointForPath(msg.P);
|
||||
if (ep)
|
||||
{
|
||||
if (!msg.Verify(ep->PubKey()))
|
||||
return false;
|
||||
auto maybe_previous_dest = r->get_layers()->route->destination_from(msg.path_id);
|
||||
// make sure we have the last destination.
|
||||
if (not maybe_previous_dest)
|
||||
return SendRoutingMessage(routing::DataDiscardMessage{info.rxID, msg.S}, r);
|
||||
// verify that the update message was signed by the previous mapping's identity.
|
||||
if (not msg.Verify(maybe_previous_dest->src))
|
||||
return SendRoutingMessage(routing::DataDiscardMessage{info.rxID, msg.S}, r);
|
||||
|
||||
if (ep->UpdateLocalPath(info.rxID))
|
||||
{
|
||||
llarp::routing::UpdateExitVerifyMessage reply;
|
||||
reply.T = msg.T;
|
||||
reply.S = NextSeqNo();
|
||||
return SendRoutingMessage(reply, r);
|
||||
}
|
||||
}
|
||||
// on fail tell message was discarded
|
||||
llarp::routing::DataDiscardMessage discard(info.rxID, msg.S);
|
||||
return SendRoutingMessage(discard, r);
|
||||
auto next_dest = r->get_layers()->route->create_destination(
|
||||
info, maybe_previous_dest->src, maybe_previous_dest->dst);
|
||||
// allocate new routing destination.
|
||||
if (not next_dest)
|
||||
return SendRoutingMessage(routing::DataDiscardMessage{info.rxID, msg.S}, r);
|
||||
|
||||
// remove old routing destination.
|
||||
// r->get_layers()->route->remove_destination_on(maybe_previous_dest->onion_info);
|
||||
llarp::routing::UpdateExitVerifyMessage reply;
|
||||
reply.T = msg.T;
|
||||
reply.S = NextSeqNo();
|
||||
return SendRoutingMessage(reply, r);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -389,27 +409,23 @@ namespace llarp
|
|||
TransitHop::HandleTransferTrafficMessage(
|
||||
const llarp::routing::TransferTrafficMessage& msg, AbstractRouter* r)
|
||||
{
|
||||
auto endpoint = r->exitContext().FindEndpointForPath(info.rxID);
|
||||
if (endpoint)
|
||||
{
|
||||
bool sent = true;
|
||||
for (const auto& pkt : msg.X)
|
||||
{
|
||||
// check short packet buffer
|
||||
if (pkt.size() <= 8)
|
||||
continue;
|
||||
auto counter = oxenc::load_big_to_host<uint64_t>(pkt.data());
|
||||
llarp_buffer_t buf{pkt.data() + 8, pkt.size() - 8};
|
||||
sent =
|
||||
endpoint->QueueOutboundTraffic(info.rxID, buf.copy(), counter, msg.protocol) and sent;
|
||||
}
|
||||
return sent;
|
||||
}
|
||||
auto maybe_dest = r->get_layers()->route->destination_on(info);
|
||||
// no destination on this path.
|
||||
if (not maybe_dest)
|
||||
return SendRoutingMessage(llarp::routing::DataDiscardMessage{info.rxID, msg.S}, r);
|
||||
// destination not applicable for this use case.
|
||||
if (not maybe_dest->flow_info)
|
||||
return SendRoutingMessage(llarp::routing::DataDiscardMessage{info.rxID, msg.S}, r);
|
||||
|
||||
llarp::LogError("No exit endpoint on ", info);
|
||||
// discarded
|
||||
llarp::routing::DataDiscardMessage discard(info.rxID, msg.S);
|
||||
return SendRoutingMessage(discard, r);
|
||||
const auto& flow_info = *maybe_dest->flow_info;
|
||||
|
||||
// propagate all traffic up to flow layer.
|
||||
for (auto& traffic : msg.to_flow_traffic())
|
||||
{
|
||||
traffic.flow_info = flow_info;
|
||||
r->get_layers()->flow->offer_flow_traffic(std::move(traffic));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include "i_outbound_message_handler.hpp"
|
||||
|
||||
#include <llarp/layers/layers.hpp>
|
||||
|
||||
#include <llarp/config/config.hpp>
|
||||
#include <llarp/config/key_manager.hpp>
|
||||
#include <memory>
|
||||
#include <llarp/util/types.hpp>
|
||||
#include <llarp/util/status.hpp>
|
||||
#include "i_outbound_message_handler.hpp"
|
||||
#include <vector>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <functional>
|
||||
#include <llarp/router_contact.hpp>
|
||||
#include <llarp/tooling/router_event.hpp>
|
||||
#include <llarp/peerstats/peer_db.hpp>
|
||||
#include <llarp/consensus/reachability_testing.hpp>
|
||||
|
||||
#include <optional>
|
||||
|
||||
#ifdef LOKINET_HIVE
|
||||
#include <llarp/tooling/router_event.hpp>
|
||||
#endif
|
||||
|
@ -45,19 +48,27 @@ namespace llarp
|
|||
struct I_RCLookupHandler;
|
||||
struct RoutePoker;
|
||||
|
||||
class EndpointBase;
|
||||
|
||||
namespace dns
|
||||
{
|
||||
class I_SystemSettings;
|
||||
}
|
||||
class Server;
|
||||
} // namespace dns
|
||||
|
||||
namespace net
|
||||
{
|
||||
class Platform;
|
||||
}
|
||||
|
||||
namespace exit
|
||||
namespace layers
|
||||
{
|
||||
struct Context;
|
||||
struct Layers;
|
||||
}
|
||||
|
||||
namespace layers::platform
|
||||
{
|
||||
class PlatformDriver;
|
||||
}
|
||||
|
||||
namespace rpc
|
||||
|
@ -70,16 +81,16 @@ namespace llarp
|
|||
struct PathContext;
|
||||
}
|
||||
|
||||
namespace quic
|
||||
{
|
||||
class TunnelManager;
|
||||
}
|
||||
|
||||
namespace routing
|
||||
{
|
||||
struct IMessageHandler;
|
||||
}
|
||||
|
||||
namespace service
|
||||
{
|
||||
struct Context;
|
||||
}
|
||||
|
||||
namespace thread
|
||||
{
|
||||
class ThreadPool;
|
||||
|
@ -100,6 +111,19 @@ namespace llarp
|
|||
|
||||
virtual ~AbstractRouter() = default;
|
||||
|
||||
/// overrideable creation of protocol layers
|
||||
virtual std::unique_ptr<const layers::Layers>
|
||||
create_layers() = 0;
|
||||
|
||||
virtual const std::unique_ptr<const layers::Layers>&
|
||||
get_layers() const = 0;
|
||||
|
||||
virtual const std::shared_ptr<dns::Server>&
|
||||
get_dns() const = 0;
|
||||
|
||||
virtual const std::shared_ptr<quic::TunnelManager>&
|
||||
quic_tunnel() const = 0;
|
||||
|
||||
virtual bool
|
||||
HandleRecvLinkMessageBuffer(ILinkSession* from, const llarp_buffer_t& msg) = 0;
|
||||
|
||||
|
@ -136,9 +160,6 @@ namespace llarp
|
|||
virtual void
|
||||
ModifyOurRC(std::function<std::optional<RouterContact>(RouterContact)> modify) = 0;
|
||||
|
||||
virtual exit::Context&
|
||||
exitContext() = 0;
|
||||
|
||||
virtual const std::shared_ptr<KeyManager>&
|
||||
keyManager() const = 0;
|
||||
|
||||
|
@ -161,16 +182,7 @@ namespace llarp
|
|||
virtual void QueueDiskIO(std::function<void(void)>) = 0;
|
||||
|
||||
virtual std::shared_ptr<Config>
|
||||
GetConfig() const
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
virtual service::Context&
|
||||
hiddenServiceContext() = 0;
|
||||
|
||||
virtual const service::Context&
|
||||
hiddenServiceContext() const = 0;
|
||||
GetConfig() const = 0;
|
||||
|
||||
virtual IOutboundMessageHandler&
|
||||
outboundMessageHandler() = 0;
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace llarp
|
|||
QueueMessage(const RouterID& remote, const ILinkMessage& msg, SendStatusHandler callback) = 0;
|
||||
|
||||
virtual void
|
||||
Pump() = 0;
|
||||
IdempotentPump() = 0;
|
||||
|
||||
virtual void
|
||||
RemovePath(const PathID_t& pathid) = 0;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -79,19 +80,25 @@ namespace llarp
|
|||
}
|
||||
|
||||
void
|
||||
OutboundMessageHandler::Pump()
|
||||
OutboundMessageHandler::Pump(bool also_pump_router)
|
||||
{
|
||||
m_Killer.TryAccess([this]() {
|
||||
m_Killer.TryAccess([this, also_pump_router]() {
|
||||
recentlyRemovedPaths.Decay();
|
||||
ProcessOutboundQueue();
|
||||
// TODO: this probably shouldn't be pumping, as it defeats the purpose
|
||||
// of having a limit on sends per tick, but chaning it is potentially bad
|
||||
// and requires testing so it should be changed later.
|
||||
if (/*bool more = */ SendRoundRobin())
|
||||
if (bool more = SendRoundRobin(); more and also_pump_router)
|
||||
_router->TriggerPump();
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
OutboundMessageHandler::IdempotentPump()
|
||||
{
|
||||
_wakeup->Trigger();
|
||||
}
|
||||
|
||||
void
|
||||
OutboundMessageHandler::RemovePath(const PathID_t& pathid)
|
||||
{
|
||||
|
@ -131,6 +138,7 @@ namespace llarp
|
|||
OutboundMessageHandler::Init(AbstractRouter* router)
|
||||
{
|
||||
_router = router;
|
||||
_wakeup = router->loop()->make_waker([this]() { Pump(false); });
|
||||
outboundMessageQueues.emplace(zeroID, MessageQueue());
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <llarp/router_id.hpp>
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
|
@ -57,9 +58,15 @@ namespace llarp
|
|||
*
|
||||
* Sends all routing messages that have been queued, indicated by pathid 0 when queued.
|
||||
* Sends messages from path queues until all are empty or a set cap has been reached.
|
||||
*
|
||||
* if also_pump_router is true, this call will call Router::TriggerPump() if we queued things.
|
||||
*/
|
||||
void
|
||||
Pump() override;
|
||||
Pump(bool also_pump_router = true);
|
||||
|
||||
/// idempotently call Pump()
|
||||
void
|
||||
IdempotentPump() override;
|
||||
|
||||
/* Called from outside this class to inform it that a path has died / expired
|
||||
* and its queue should be discarded.
|
||||
|
@ -202,6 +209,8 @@ namespace llarp
|
|||
static const PathID_t zeroID;
|
||||
|
||||
MessageQueueStats m_queueStats;
|
||||
|
||||
std::shared_ptr<EventLoopWakeup> _wakeup;
|
||||
};
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#include <llarp/link/i_link_manager.hpp>
|
||||
#include <llarp/link/server.hpp>
|
||||
#include <llarp/crypto/crypto.hpp>
|
||||
#include <llarp/service/context.hpp>
|
||||
#include <llarp/router_contact.hpp>
|
||||
#include <llarp/util/types.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
|
@ -39,6 +38,9 @@ namespace llarp
|
|||
beigelist.insert(new_beige.begin(), new_beige.end());
|
||||
}
|
||||
|
||||
RCLookupHandler::RCLookupHandler(AbstractRouter& router) : _router{router}
|
||||
{}
|
||||
|
||||
void
|
||||
RCLookupHandler::SetRouterWhitelist(
|
||||
const std::vector<RouterID>& whitelist,
|
||||
|
@ -69,9 +71,9 @@ namespace llarp
|
|||
RouterContact remoteRC;
|
||||
if (not forceLookup)
|
||||
{
|
||||
if (const auto maybe = _nodedb->Get(router); maybe.has_value())
|
||||
if (auto maybe = _router.nodedb()->Get(router))
|
||||
{
|
||||
remoteRC = *maybe;
|
||||
remoteRC = std::move(*maybe);
|
||||
if (callback)
|
||||
{
|
||||
callback(router, &remoteRC, RCRequestResult::Success);
|
||||
|
@ -97,24 +99,14 @@ namespace llarp
|
|||
if (shouldDoLookup)
|
||||
{
|
||||
auto fn = [this, router](const auto& res) { HandleDHTLookupResult(router, res); };
|
||||
auto lookup = [this](RouterID target, RouterLookupHandler handler) -> bool {
|
||||
if (_router.IsServiceNode())
|
||||
return _router.dht()->impl->LookupRouter(target, std::move(handler));
|
||||
// todo: client lookup via path.
|
||||
return false;
|
||||
};
|
||||
|
||||
// if we are a client try using the hidden service endpoints
|
||||
if (!isServiceNode)
|
||||
{
|
||||
bool sent = false;
|
||||
LogInfo("Lookup ", router, " anonymously");
|
||||
_hiddenServiceContext->ForEachService(
|
||||
[&](const std::string&, const std::shared_ptr<service::Endpoint>& ep) -> bool {
|
||||
const bool success = ep->LookupRouterAnon(router, fn);
|
||||
sent = sent || success;
|
||||
return !success;
|
||||
});
|
||||
if (sent)
|
||||
return;
|
||||
LogWarn("cannot lookup ", router, " anonymously");
|
||||
}
|
||||
|
||||
if (!_dht->impl->LookupRouter(router, fn))
|
||||
if (not lookup(router, std::move(fn)))
|
||||
{
|
||||
FinalizeRequest(router, nullptr, RCRequestResult::RouterNotFound);
|
||||
}
|
||||
|
@ -160,11 +152,11 @@ namespace llarp
|
|||
bool
|
||||
RCLookupHandler::PathIsAllowed(const RouterID& remote) const
|
||||
{
|
||||
if (_strictConnectPubkeys.size() && _strictConnectPubkeys.count(remote) == 0
|
||||
&& !RemoteInBootstrap(remote))
|
||||
{
|
||||
if (_strictConnectPubkeys.count(remote) == 1 and not isServiceNode)
|
||||
return true;
|
||||
// clients shall not build paths over bootstrap node.
|
||||
if (RemoteInBootstrap(remote) and not isServiceNode)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not useWhitelist)
|
||||
return true;
|
||||
|
@ -177,11 +169,10 @@ namespace llarp
|
|||
bool
|
||||
RCLookupHandler::SessionIsAllowed(const RouterID& remote) const
|
||||
{
|
||||
if (_strictConnectPubkeys.size() && _strictConnectPubkeys.count(remote) == 0
|
||||
&& !RemoteInBootstrap(remote))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_strictConnectPubkeys.count(remote) == 1 and not isServiceNode)
|
||||
return true;
|
||||
if (RemoteInBootstrap(remote))
|
||||
return true;
|
||||
|
||||
if (not useWhitelist)
|
||||
return true;
|
||||
|
@ -196,11 +187,11 @@ namespace llarp
|
|||
{
|
||||
if (not SessionIsAllowed(rc.pubkey))
|
||||
{
|
||||
_dht->impl->DelRCNodeAsync(dht::Key_t{rc.pubkey});
|
||||
_router.dht()->impl->DelRCNodeAsync(dht::Key_t{rc.pubkey});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not rc.Verify(_dht->impl->Now()))
|
||||
if (not rc.Verify(_router.dht()->impl->Now()))
|
||||
{
|
||||
LogWarn("RC for ", RouterID(rc.pubkey), " is invalid");
|
||||
return false;
|
||||
|
@ -210,8 +201,8 @@ namespace llarp
|
|||
if (rc.IsPublicRouter())
|
||||
{
|
||||
LogDebug("Adding or updating RC for ", RouterID(rc.pubkey), " to nodedb and dht.");
|
||||
_loop->call([rc, n = _nodedb] { n->PutIfNewer(rc); });
|
||||
_dht->impl->PutRCNodeAsync(rc);
|
||||
_router.loop()->call([rc, n = _router.nodedb()] { n->PutIfNewer(rc); });
|
||||
_router.dht()->impl->PutRCNodeAsync(rc);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -248,13 +239,12 @@ namespace llarp
|
|||
if (!SessionIsAllowed(newrc.pubkey))
|
||||
return false;
|
||||
|
||||
auto func = [this, newrc] { CheckRC(newrc); };
|
||||
_work(func);
|
||||
_router.QueueWork([this, newrc] { CheckRC(newrc); });
|
||||
|
||||
// update dht if required
|
||||
if (_dht->impl->Nodes()->HasNode(dht::Key_t{newrc.pubkey}))
|
||||
if (_router.dht()->impl->Nodes()->HasNode(dht::Key_t{newrc.pubkey}))
|
||||
{
|
||||
_dht->impl->Nodes()->PutNode(newrc);
|
||||
_router.dht()->impl->Nodes()->PutNode(newrc);
|
||||
}
|
||||
|
||||
// TODO: check for other places that need updating the RC
|
||||
|
@ -267,8 +257,8 @@ namespace llarp
|
|||
// try looking up stale routers
|
||||
std::unordered_set<RouterID> routersToLookUp;
|
||||
|
||||
_nodedb->VisitInsertedBefore(
|
||||
[&](const RouterContact& rc) {
|
||||
_router.nodedb()->VisitInsertedBefore(
|
||||
[this, &routersToLookUp](const RouterContact& rc) {
|
||||
if (HavePendingLookup(rc.pubkey))
|
||||
return;
|
||||
routersToLookUp.insert(rc.pubkey);
|
||||
|
@ -280,13 +270,14 @@ namespace llarp
|
|||
GetRC(router, nullptr, true);
|
||||
}
|
||||
|
||||
_nodedb->RemoveStaleRCs(_bootstrapRouterIDList, now - RouterContact::StaleInsertionAge);
|
||||
_router.nodedb()->RemoveStaleRCs(
|
||||
_bootstrapRouterIDList, now - RouterContact::StaleInsertionAge);
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::ExploreNetwork()
|
||||
{
|
||||
const size_t known = _nodedb->NumLoaded();
|
||||
const size_t known = _router.nodedb()->NumLoaded();
|
||||
if (_bootstrapRCList.empty() && known == 0)
|
||||
{
|
||||
LogError("we have no bootstrap nodes specified");
|
||||
|
@ -296,7 +287,7 @@ namespace llarp
|
|||
for (const auto& rc : _bootstrapRCList)
|
||||
{
|
||||
LogInfo("Doing explore via bootstrap node: ", RouterID(rc.pubkey));
|
||||
_dht->impl->ExploreNetworkVia(dht::Key_t{rc.pubkey});
|
||||
_router.dht()->impl->ExploreNetworkVia(dht::Key_t{rc.pubkey});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -315,7 +306,7 @@ namespace llarp
|
|||
util::Lock l(_mutex);
|
||||
for (const auto& r : whitelistRouters)
|
||||
{
|
||||
if (now > _routerLookupTimes[r] + RerequestInterval and not _nodedb->Has(r))
|
||||
if (now > _routerLookupTimes[r] + RerequestInterval and not _router.nodedb()->Has(r))
|
||||
{
|
||||
lookupRouters.emplace_back(r);
|
||||
}
|
||||
|
@ -333,43 +324,32 @@ namespace llarp
|
|||
return;
|
||||
}
|
||||
// service nodes gossip, not explore
|
||||
if (_dht->impl->GetRouter()->IsServiceNode())
|
||||
if (_router.dht()->impl->GetRouter()->IsServiceNode())
|
||||
return;
|
||||
|
||||
// explore via every connected peer
|
||||
_linkManager->ForEachPeer([&](ILinkSession* s) {
|
||||
_router.linkManager().ForEachPeer([this](ILinkSession* s) {
|
||||
if (!s->IsEstablished())
|
||||
return;
|
||||
const RouterContact rc = s->GetRemoteRC();
|
||||
if (rc.IsPublicRouter() && (_bootstrapRCList.find(rc) == _bootstrapRCList.end()))
|
||||
{
|
||||
LogDebug("Doing explore via public node: ", RouterID(rc.pubkey));
|
||||
_dht->impl->ExploreNetworkVia(dht::Key_t{rc.pubkey});
|
||||
_router.dht()->impl->ExploreNetworkVia(dht::Key_t{rc.pubkey});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
RCLookupHandler::Init(
|
||||
llarp_dht_context* dht,
|
||||
std::shared_ptr<NodeDB> nodedb,
|
||||
EventLoop_ptr loop,
|
||||
WorkerFunc_t dowork,
|
||||
ILinkManager* linkManager,
|
||||
service::Context* hiddenServiceContext,
|
||||
const std::unordered_set<RouterID>& strictConnectPubkeys,
|
||||
const std::set<RouterContact>& bootstrapRCList,
|
||||
bool useWhitelist_arg,
|
||||
bool isServiceNode_arg)
|
||||
{
|
||||
_dht = dht;
|
||||
_nodedb = std::move(nodedb);
|
||||
_loop = std::move(loop);
|
||||
_work = std::move(dowork);
|
||||
_hiddenServiceContext = hiddenServiceContext;
|
||||
_strictConnectPubkeys = strictConnectPubkeys;
|
||||
_bootstrapRCList = bootstrapRCList;
|
||||
_linkManager = linkManager;
|
||||
|
||||
useWhitelist = useWhitelist_arg;
|
||||
isServiceNode = isServiceNode_arg;
|
||||
|
||||
|
|
|
@ -17,12 +17,7 @@ namespace llarp
|
|||
class NodeDB;
|
||||
class EventLoop;
|
||||
|
||||
namespace service
|
||||
{
|
||||
struct Context;
|
||||
|
||||
} // namespace service
|
||||
|
||||
struct AbstractRouter;
|
||||
struct ILinkManager;
|
||||
|
||||
struct RCLookupHandler final : public I_RCLookupHandler
|
||||
|
@ -32,6 +27,8 @@ namespace llarp
|
|||
using WorkerFunc_t = std::function<void(Work_t)>;
|
||||
using CallbacksQueue = std::list<RCRequestCallback>;
|
||||
|
||||
RCLookupHandler(AbstractRouter&);
|
||||
|
||||
~RCLookupHandler() override = default;
|
||||
|
||||
void
|
||||
|
@ -94,12 +91,6 @@ namespace llarp
|
|||
|
||||
void
|
||||
Init(
|
||||
llarp_dht_context* dht,
|
||||
std::shared_ptr<NodeDB> nodedb,
|
||||
std::shared_ptr<EventLoop> loop,
|
||||
WorkerFunc_t dowork,
|
||||
ILinkManager* linkManager,
|
||||
service::Context* hiddenServiceContext,
|
||||
const std::unordered_set<RouterID>& strictConnectPubkeys,
|
||||
const std::set<RouterContact>& bootstrapRCList,
|
||||
bool useWhitelist_arg,
|
||||
|
@ -128,12 +119,7 @@ namespace llarp
|
|||
|
||||
mutable util::Mutex _mutex; // protects pendingCallbacks, whitelistRouters
|
||||
|
||||
llarp_dht_context* _dht = nullptr;
|
||||
std::shared_ptr<NodeDB> _nodedb;
|
||||
std::shared_ptr<EventLoop> _loop;
|
||||
WorkerFunc_t _work = nullptr;
|
||||
service::Context* _hiddenServiceContext = nullptr;
|
||||
ILinkManager* _linkManager = nullptr;
|
||||
AbstractRouter& _router;
|
||||
|
||||
/// explicit whitelist of routers we will connect to directly (not for
|
||||
/// service nodes)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "route_poker.hpp"
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/net/sock_addr.hpp>
|
||||
#include <llarp/service/context.hpp>
|
||||
#include <llarp/dns/platform.hpp>
|
||||
#include <llarp/dns/server.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace llarp
|
||||
|
@ -129,26 +130,22 @@ namespace llarp
|
|||
void
|
||||
RoutePoker::Update()
|
||||
{
|
||||
if (not m_Router)
|
||||
throw std::runtime_error{"Attempting to use RoutePoker before calling Init"};
|
||||
|
||||
// ensure we have an endpoint
|
||||
auto ep = m_Router->hiddenServiceContext().GetDefault();
|
||||
if (ep == nullptr)
|
||||
return;
|
||||
// ensure we have a vpn platform
|
||||
auto* platform = m_Router->GetVPNPlatform();
|
||||
if (platform == nullptr)
|
||||
return;
|
||||
// ensure we have a vpn interface
|
||||
auto* vpn = ep->GetVPNInterface();
|
||||
if (vpn == nullptr)
|
||||
// we dont need to do anything we are not enabled on runtime by config.
|
||||
if (not IsEnabled())
|
||||
return;
|
||||
|
||||
auto& route = platform->RouteManager();
|
||||
auto* route_man = m_Router->get_layers()->platform->route_manager();
|
||||
auto vpn = m_Router->get_layers()->platform->vpn_interface();
|
||||
if (not(vpn and route_man))
|
||||
{
|
||||
// no vpn interface with route manager so we cannot poke routes yet.
|
||||
return;
|
||||
}
|
||||
|
||||
// get current gateways, assume sorted by lowest metric first
|
||||
auto gateways = route.GetGatewaysNotOnInterface(*vpn);
|
||||
|
||||
auto gateways = route_man->GetGatewaysNotOnInterface(*vpn);
|
||||
|
||||
std::optional<net::ipv4addr_t> next_gw;
|
||||
for (auto& gateway : gateways)
|
||||
{
|
||||
|
@ -188,10 +185,10 @@ namespace llarp
|
|||
void
|
||||
RoutePoker::SetDNSMode(bool exit_mode_on) const
|
||||
{
|
||||
auto ep = m_Router->hiddenServiceContext().GetDefault();
|
||||
if (not ep)
|
||||
if (m_Router->IsServiceNode())
|
||||
return;
|
||||
if (auto dns_server = ep->DNS())
|
||||
|
||||
if (const auto& dns_server = m_Router->get_dns())
|
||||
dns_server->SetDNSMode(exit_mode_on);
|
||||
}
|
||||
|
||||
|
@ -210,24 +207,22 @@ namespace llarp
|
|||
{
|
||||
log::warning(logcat, "RokerPoker came up, but we don't know of a gateway!");
|
||||
}
|
||||
else
|
||||
else if (auto vpn = m_Router->get_layers()->platform->vpn_interface())
|
||||
{
|
||||
log::info(logcat, "RoutePoker coming up; poking routes");
|
||||
if (auto* route_man = m_Router->get_layers()->platform->route_manager())
|
||||
{
|
||||
log::info(logcat, "RoutePoker coming up; poking routes");
|
||||
// black hole all routes if enabled
|
||||
if (m_Router->GetConfig()->network.m_BlackholeRoutes)
|
||||
route_man->AddBlackhole();
|
||||
|
||||
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
|
||||
|
||||
// black hole all routes if enabled
|
||||
if (m_Router->GetConfig()->network.m_BlackholeRoutes)
|
||||
route.AddBlackhole();
|
||||
|
||||
// explicit route pokes for first hops
|
||||
m_Router->ForEachPeer(
|
||||
[this](auto session, auto) { AddRoute(session->GetRemoteEndpoint().getIPv4()); },
|
||||
false);
|
||||
// add default route
|
||||
const auto ep = m_Router->hiddenServiceContext().GetDefault();
|
||||
if (auto* vpn = ep->GetVPNInterface())
|
||||
route.AddDefaultRouteViaInterface(*vpn);
|
||||
// explicit route pokes for first hops
|
||||
m_Router->ForEachPeer(
|
||||
[this](auto session, auto) { AddRoute(session->GetRemoteEndpoint().getIPv4()); },
|
||||
false);
|
||||
// add default route
|
||||
route_man->AddDefaultRouteViaInterface(*vpn);
|
||||
}
|
||||
log::info(logcat, "route poker up");
|
||||
}
|
||||
}
|
||||
|
@ -243,16 +238,14 @@ namespace llarp
|
|||
[this](auto session, auto) { DelRoute(session->GetRemoteEndpoint().getIPv4()); }, false);
|
||||
|
||||
// remove default route
|
||||
|
||||
if (IsEnabled() and m_up)
|
||||
if (const auto& vpn = m_Router->get_layers()->platform->vpn_interface();
|
||||
vpn and IsEnabled() and m_up)
|
||||
{
|
||||
vpn::IRouteManager& route = m_Router->GetVPNPlatform()->RouteManager();
|
||||
const auto ep = m_Router->hiddenServiceContext().GetDefault();
|
||||
if (auto* vpn = ep->GetVPNInterface())
|
||||
route.DelDefaultRouteViaInterface(*vpn);
|
||||
|
||||
// delete route blackhole
|
||||
route.DelBlackhole();
|
||||
if (auto route_man = m_Router->get_layers()->platform->route_manager())
|
||||
{
|
||||
route_man->DelBlackhole();
|
||||
route_man->DelDefaultRouteViaInterface(*vpn);
|
||||
}
|
||||
log::info(logcat, "route poker down");
|
||||
}
|
||||
if (m_up)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue