diff --git a/.drone.jsonnet b/.drone.jsonnet index 58ca7ae6e..3e4d9da65 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -1,7 +1,10 @@ -local default_deps_base='libsystemd-dev python3-dev libcurl4-openssl-dev libuv1-dev'; +local default_deps_base='libsystemd-dev python3-dev libcurl4-openssl-dev libuv1-dev libunbound-dev nettle-dev libssl-dev libevent-dev'; local default_deps_nocxx='libsodium-dev ' + default_deps_base; // libsodium-dev needs to be >= 1.0.18 local default_deps='g++ ' + default_deps_nocxx; // g++ sometimes needs replacement +local default_windows_deps='mingw-w64-binutils mingw-w64-gcc mingw-w64-crt mingw-w64-headers mingw-w64-winpthreads perl openssh bash'; // deps for windows cross compile + + local submodules = { name: 'submodules', image: 'drone/git', @@ -50,51 +53,15 @@ local debian_pipeline(name, image, ], }; -// 32-bit windows build on alpine: -local alpine_wow64_pipeline(name, image, - arch='amd64', - deps='mingw-w64-binutils mingw-w64-gcc mingw-w64-crt mingw-w64-headers mingw-w64-winpthreads', - build_type='Release', - lto=false, - werror=false, - cmake_extra='', - extra_cmds=[], - allow_fail=false) = { - kind: 'pipeline', - type: 'docker', - name: name, - platform: { arch: arch }, - trigger: { branch: { exclude: ['debian/*', 'ubuntu/*'] } }, - steps: [ - submodules, - { - name: 'build', - image: image, - [if allow_fail then "failure"]: "ignore", - environment: { SSH_KEY: { from_secret: "SSH_KEY" } }, - commands: [ - 'apk add cmake git ninja pkgconf ccache patch make ' + deps, - 'git clone https://github.com/despair86/libuv.git win32-setup/libuv', - 'mkdir build', - 'cd build', - 'cmake .. -G Ninja -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector -DLIBUV_ROOT=$PWD/../win32-setup/libuv -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_TOOLCHAIN_FILE=../contrib/cross/mingw32.cmake -DCMAKE_BUILD_TYPE='+build_type+' ' + - (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + - (if lto then '' else '-DWITH_LTO=OFF ') + - cmake_extra, - 'ninja -v', - ] + extra_cmds, - } - ], -}; - -// 64-bit windows build on alpine: -local alpine_win32_pipeline(name, image, +// windows cross compile on alpine linux +local windows_cross_pipeline(name, image, arch='amd64', - deps='mingw-w64-binutils mingw-w64-gcc mingw-w64-crt mingw-w64-headers mingw-w64-winpthreads', + deps=default_windows_deps, build_type='Release', lto=false, werror=false, cmake_extra='', + toolchain='mingw32', extra_cmds=[], allow_fail=false) = { kind: 'pipeline', @@ -114,9 +81,10 @@ local alpine_win32_pipeline(name, image, 'git clone https://github.com/despair86/libuv.git win32-setup/libuv', 'mkdir build', 'cd build', - 'cmake .. -G Ninja -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector -DLIBUV_ROOT=$PWD/../win32-setup/libuv -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_TOOLCHAIN_FILE=../contrib/cross/mingw64.cmake -DCMAKE_BUILD_TYPE='+build_type+' ' + + 'cmake .. -G Ninja -DCMAKE_CROSSCOMPILE=ON -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector -DLIBUV_ROOT=$PWD/../win32-setup/libuv -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_TOOLCHAIN_FILE=../contrib/cross/'+toolchain+'.cmake -DCMAKE_BUILD_TYPE='+build_type+' ' + (if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') + (if lto then '' else '-DWITH_LTO=OFF ') + + "-DBUILD_STATIC_DEPS=ON -DDOWNLOAD_SODIUM=ON -DBUILD_PACKAGE=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=ON -DNATIVE_BUILD=OFF -DSTATIC_LINK=ON" + cmake_extra, 'ninja -v', ] + extra_cmds, @@ -230,12 +198,18 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra debian_pipeline("Debian buster (armhf)", "arm32v7/debian:buster", arch="arm64", cmake_extra='-DDOWNLOAD_SODIUM=ON'), // Windows builds (WOW64 and native) - alpine_win32_pipeline("win32 on alpine (amd64)", "alpine:edge", cmake_extra="-DDOWNLOAD_SODIUM=ON -DBUILD_PACKAGE=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=ON -DNATIVE_BUILD=OFF -DSTATIC_LINK=ON", lto=false), - alpine_wow64_pipeline("win32 on alpine (i386)", "i386/alpine:edge", cmake_extra="-DDOWNLOAD_SODIUM=ON -DBUILD_PACKAGE=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_TESTING=ON -DNATIVE_BUILD=OFF -DSTATIC_LINK=ON", lto=false), + windows_cross_pipeline("win32 on alpine (amd64)", "alpine:edge", + toolchain='mingw64', extra_cmds=[ + '../contrib/ci/drone-static-upload.sh' + ]), + windows_cross_pipeline("win32 on alpine (i386)", "i386/alpine:edge", + toolchain='mingw32', extra_cmds=[ + '../contrib/ci/drone-static-upload.sh' + ]), // Static build (on bionic) which gets uploaded to builds.lokinet.dev: debian_pipeline("Static (bionic amd64)", "ubuntu:bionic", deps='g++-8 python3-dev', lto=true, - cmake_extra='-DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ' + + cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ' + '-DDOWNLOAD_SODIUM=ON -DDOWNLOAD_CURL=ON -DDOWNLOAD_UV=ON -DWITH_SYSTEMD=OFF', extra_cmds=[ '../contrib/ci/drone-check-static-libs.sh', @@ -255,7 +229,7 @@ local mac_builder(name, build_type='Release', werror=true, cmake_extra='', extra // Macos builds: mac_builder('macOS (Release)'), mac_builder('macOS (Debug)', build_type='Debug'), - mac_builder('macOS (Static)', cmake_extra='-DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DDOWNLOAD_SODIUM=FORCE -DDOWNLOAD_CURL=FORCE -DDOWNLOAD_UV=FORCE', + mac_builder('macOS (Static)', cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DDOWNLOAD_SODIUM=FORCE -DDOWNLOAD_CURL=FORCE -DDOWNLOAD_UV=FORCE', extra_cmds=[ '../contrib/ci/drone-check-static-libs.sh', '../contrib/ci/drone-static-upload.sh' diff --git a/CMakeLists.txt b/CMakeLists.txt index 0ff8ae7ec..c917eff0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,9 @@ if(RELEASE_MOTTO) endif() +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") + + # Core options option(USE_AVX2 "enable avx2 code" OFF) option(USE_NETNS "enable networking namespace support. Linux only" OFF) @@ -94,8 +97,6 @@ endif() include(cmake/solaris.cmake) include(cmake/win32.cmake) -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") - # No in-source building include(MacroEnsureOutOfSourceBuild) macro_ensure_out_of_source_build("${PROJECT_NAME} requires an out-of-source build. Create a build directory and run 'cmake ${CMAKE_SOURCE_DIR} [options]'.") @@ -225,21 +226,25 @@ endif() string(REGEX REPLACE "^fatal.*$" nogit GIT_VERSION_REAL "${GIT_VERSION}") +find_package(PkgConfig REQUIRED) -find_package(PkgConfig QUIET) -if(PKG_CONFIG_FOUND) - pkg_check_modules(SD libsystemd) - # Default WITH_SYSTEMD to true if we found it - option(WITH_SYSTEMD "enable systemd integration for sd_notify" ${SD_FOUND}) +if (NOT BUILD_STATIC_DEPS) + pkg_check_modules(UNBOUND libunbound REQUIRED IMPORTED_TARGET) + add_library(libunbound INTERFACE) + target_link_libraries(libunbound INTERFACE PkgConfig::UNBOUND) +endif() - if(WITH_SYSTEMD) - if(NOT SD_FOUND) - message(FATAL_ERROR "libsystemd not found") - endif() - add_definitions(-DWITH_SYSTEMD) - include_directories(${SD_INCLUDE_DIRS}) - set(SD_LIBS ${SD_LDFLAGS}) - endif() +pkg_check_modules(SD libsystemd) +# Default WITH_SYSTEMD to true if we found it +option(WITH_SYSTEMD "enable systemd integration for sd_notify" ${SD_FOUND}) + +if(WITH_SYSTEMD) + if(NOT SD_FOUND) + message(FATAL_ERROR "libsystemd not found") + endif() + add_definitions(-DWITH_SYSTEMD) + include_directories(${SD_INCLUDE_DIRS}) + set(SD_LIBS ${SD_LDFLAGS}) endif() option(SUBMODULE_CHECK "Enables checking that vendored library submodules are up to date" ON) diff --git a/StaticBuild.cmake b/cmake/StaticBuild.cmake similarity index 93% rename from StaticBuild.cmake rename to cmake/StaticBuild.cmake index 1c24bff27..36cac7f71 100644 --- a/StaticBuild.cmake +++ b/cmake/StaticBuild.cmake @@ -139,9 +139,9 @@ endfunction() set(openssl_system_env "") if(CMAKE_CROSSCOMPILING) if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32) - set(openssl_system_env SYSTEM=MINGW64 RC=${CMAKE_RC_COMPILER}) + set(openssl_system_env SYSTEM=MINGW64 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib) elseif(ARCH_TRIPLET STREQUAL i686-w64-mingw32) - set(openssl_system_env SYSTEM=MINGW64 RC=${CMAKE_RC_COMPILER}) + set(openssl_system_env SYSTEM=MINGW32 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib) endif() endif() build_external(openssl @@ -156,6 +156,10 @@ build_external(openssl ) add_static_target(OpenSSL::SSL openssl_external libssl.a) add_static_target(OpenSSL::Crypto openssl_external libcrypto.a) +if(WIN32) + target_link_libraries(OpenSSL::Crypto INTERFACE "ws2_32;crypt32;iphlpapi") +endif() + set(OPENSSL_INCLUDE_DIR ${DEPS_DESTDIR}/include) set(OPENSSL_VERSION 1.1.1) @@ -178,8 +182,10 @@ build_external(unbound "CC=${deps_cc}" "CFLAGS=-O2 ${flto}" ) add_static_target(libunbound unbound_external libunbound.a) -if(WIN32) - set_target_properties(libunbound PROPERTIES INTERFACE_LINK_LIBRARIES "ws2_32;crypt32;iphlpapi") +if(NOT WIN32) + set_target_properties(libunbound PROPERTIES INTERFACE_LINK_LIBRARIES "OpenSSL::SSL;OpenSSL::Crypto") +else() + set_target_properties(libunbound PROPERTIES INTERFACE_LINK_LIBRARIES "OpenSSL::SSL;OpenSSL::Crypto;ws2_32;crypt32;iphlpapi") endif() @@ -189,8 +195,8 @@ add_static_target(sodium sodium_external libsodium.a) if(ZMQ_VERSION VERSION_LESS 4.3.3 AND CMAKE_CROSSCOMPILING AND ARCH_TRIPLET MATCHES mingw) - set(zmq_patch PATCH_COMMAND patch -p1 -i ${PROJECT_SOURCE_DIR}/utils/build_scripts/libzmq-pr3601-mingw-build-fix.patch - COMMAND patch -p1 -i ${PROJECT_SOURCE_DIR}/utils/build_scripts/libzmq-pr3613-fix-funcptr-call.patch) + set(zmq_patch PATCH_COMMAND patch -p1 -i ${PROJECT_SOURCE_DIR}/contrib/cross/patches/libzmq-pr3601-mingw-build-fix.patch + COMMAND patch -p1 -i ${PROJECT_SOURCE_DIR}/contrib/cross/patches/libzmq-pr3613-fix-funcptr-call.patch) endif() build_external(zmq DEPENDS sodium_external diff --git a/contrib/ci/drone-static-upload.sh b/contrib/ci/drone-static-upload.sh index c639e0506..fd609da21 100755 --- a/contrib/ci/drone-static-upload.sh +++ b/contrib/ci/drone-static-upload.sh @@ -29,8 +29,11 @@ else fi mkdir -v "$base" -mv -v daemon/lokinet "$base" -cp -av ../lokinet-bootstrap "$base" +# copy lokinet-bootstrap.ps1 and lokinet.exe if we are a windows build +test -e daemon/lokinet.exe && cp -av daemon/lokinet.exe ../lokinet-bootstrap.ps1 "$base" +# copy lokinet-bootstrap shell script and built binary if we aren't a windows build +test -e daemon/lokinet && cp -av daemon/lokinet ../lokinet-bootstrap "$base" +# tar dat shiz up yo tar cJvf "${base}.tar.xz" "$base" upload_to="builds.lokinet.dev/${DRONE_REPO// /_}/${DRONE_BRANCH// /_}" diff --git a/contrib/cross/mingw_core.cmake b/contrib/cross/mingw_core.cmake index 4308542da..d4702c74e 100644 --- a/contrib/cross/mingw_core.cmake +++ b/contrib/cross/mingw_core.cmake @@ -23,3 +23,5 @@ else() endif() set(CMAKE_RC_COMPILER ${TOOLCHAIN_PREFIX}-windres) +set(CMAKE_CROSSCOMPILE ON) +set(ARCH_TRIPLET ${CROSS_TARGET}) diff --git a/contrib/cross/patches/libzmq-pr3601-mingw-build-fix.patch b/contrib/cross/patches/libzmq-pr3601-mingw-build-fix.patch new file mode 100644 index 000000000..0693ed92d --- /dev/null +++ b/contrib/cross/patches/libzmq-pr3601-mingw-build-fix.patch @@ -0,0 +1,69 @@ +diff --git a/src/thread.cpp b/src/thread.cpp +index b14d70757..3675899be 100644 +--- a/src/thread.cpp ++++ b/src/thread.cpp +@@ -32,6 +32,10 @@ + #include "thread.hpp" + #include "err.hpp" + ++#ifdef ZMQ_HAVE_WINDOWS ++#include ++#endif ++ + bool zmq::thread_t::get_started () const + { + return _started; +@@ -113,10 +117,22 @@ struct thread_info_t + #pragma pack(pop) + } + ++typedef struct _MY_EXCEPTION_REGISTRATION_RECORD ++{ ++ struct _MY_EXCEPTION_REGISTRATION_RECORD *Next; ++ void *Handler; ++} MY_EXCEPTION_REGISTRATION_RECORD; ++ ++static EXCEPTION_DISPOSITION NTAPI continue_execution (EXCEPTION_RECORD *rec, ++ void *frame, CONTEXT *ctx, void *disp) ++{ ++ return ExceptionContinueExecution; ++} ++ + void zmq::thread_t:: + applyThreadName () // to be called in secondary thread context + { +- if (!_name[0]) ++ if (!_name[0] || !IsDebuggerPresent()) + return; + + thread_info_t thread_info; +@@ -125,17 +141,19 @@ void zmq::thread_t:: + thread_info._thread_id = -1; + thread_info._flags = 0; + +-#pragma warning(push) +-#pragma warning(disable : 6320 6322) +- __try { +- DWORD MS_VC_EXCEPTION = 0x406D1388; ++ NT_TIB *tib = ((NT_TIB*)NtCurrentTeb()); ++ ++ MY_EXCEPTION_REGISTRATION_RECORD rec; ++ rec.Next = (MY_EXCEPTION_REGISTRATION_RECORD *)tib->ExceptionList; ++ rec.Handler = continue_execution; ++ ++ // push our handler, raise, and finally pop our handler ++ tib->ExceptionList = (_EXCEPTION_REGISTRATION_RECORD *)&rec; ++ DWORD MS_VC_EXCEPTION = 0x406D1388; + RaiseException (MS_VC_EXCEPTION, 0, +- sizeof (thread_info) / sizeof (ULONG_PTR), +- (ULONG_PTR *) &thread_info); +- } +- __except (EXCEPTION_CONTINUE_EXECUTION) { +- } +-#pragma warning(pop) ++ sizeof (thread_info) / sizeof (ULONG_PTR), ++ (ULONG_PTR *) &thread_info); ++ tib->ExceptionList = (_EXCEPTION_REGISTRATION_RECORD *)(((MY_EXCEPTION_REGISTRATION_RECORD *)tib->ExceptionList)->Next); + } + + #elif defined ZMQ_HAVE_VXWORKS diff --git a/contrib/cross/patches/libzmq-pr3613-fix-funcptr-call.patch b/contrib/cross/patches/libzmq-pr3613-fix-funcptr-call.patch new file mode 100644 index 000000000..a17252823 --- /dev/null +++ b/contrib/cross/patches/libzmq-pr3613-fix-funcptr-call.patch @@ -0,0 +1,43 @@ +diff --git a/RELICENSE/tomzbench.md b/RELICENSE/tomzbench.md +new file mode 100644 +index 000000000..1cbcc4fdb +--- /dev/null ++++ b/RELICENSE/tomzbench.md +@@ -0,0 +1,14 @@ ++# Permission to Relicense under MPLv2 ++ ++This is a statement by Thomas Chiantia ++that grants permission to relicense its copyrights in the libzmq C++ ++library (ZeroMQ) under the Mozilla Public License v2 (MPLv2). ++ ++A portion of the commits made by the Github handle "tomzbench", with ++commit author "Thomas", are copyright of ++Thomas Chiantia. ++This document hereby grants the libzmq project team to relicense libzmq, ++including all past, present and future contributions of the author listed above. ++ ++Thomas Chiantia ++2019/08/10 +diff --git a/src/thread.cpp b/src/thread.cpp +index 2cad2adaa..6f07e9cee 100644 +--- a/src/thread.cpp ++++ b/src/thread.cpp +@@ -117,11 +117,14 @@ struct thread_info_t + #pragma pack(pop) + } + +-typedef struct _MY_EXCEPTION_REGISTRATION_RECORD ++struct MY_EXCEPTION_REGISTRATION_RECORD + { +- struct _MY_EXCEPTION_REGISTRATION_RECORD *Next; +- void *Handler; +-} MY_EXCEPTION_REGISTRATION_RECORD; ++ typedef EXCEPTION_DISPOSITION (NTAPI *HandlerFunctionType) ( ++ EXCEPTION_RECORD *, void *, CONTEXT *, void *); ++ ++ MY_EXCEPTION_REGISTRATION_RECORD *Next; ++ HandlerFunctionType Handler; ++}; + + static EXCEPTION_DISPOSITION NTAPI continue_execution (EXCEPTION_RECORD *rec, + void *frame, diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index dc62d83ff..b28426d2d 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -70,11 +70,11 @@ endif() option(DOWNLOAD_SODIUM "Allow libsodium to be downloaded and built locally if not found on the system" OFF) # Allow -DDOWNLOAD_SODIUM=FORCE to download without even checking for a local libsodium -if(NOT DOWNLOAD_SODIUM STREQUAL "FORCE") +if((NOT BUILD_STATIC_DEPS) AND (NOT DOWNLOAD_SODIUM STREQUAL "FORCE")) find_package(Sodium 1.0.18) endif() -if(sodium_FOUND) +if(sodium_FOUND OR BUILD_STATIC_DEPS) target_link_libraries(lokinet-cryptography PUBLIC sodium) elseif(DOWNLOAD_SODIUM) message(STATUS "Sodium >= 1.0.18 not found, but DOWNLOAD_SODIUM specified, so downloading it") diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 41270b359..becf08cf9 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -101,6 +101,7 @@ add_library(liblokinet dns/rr.cpp dns/serialize.cpp dns/server.cpp + dns/unbound_resolver.cpp consensus/table.cpp @@ -222,6 +223,8 @@ endif() target_link_libraries(liblokinet PUBLIC cxxopts abyss lokinet-platform lokinet-util lokinet-cryptography) +target_link_libraries(liblokinet PRIVATE libunbound) + if(BUILD_SHARED_LIBS) install(TARGETS lokinet-util lokinet-platform liblokinet LIBRARY DESTINATION lib) if(WIN32) diff --git a/llarp/dns/message.hpp b/llarp/dns/message.hpp index b4b0379fb..a0de70189 100644 --- a/llarp/dns/message.hpp +++ b/llarp/dns/message.hpp @@ -48,9 +48,6 @@ namespace llarp Message(Message&& other); Message(const Message& other); - void - UpdateHeader(); - void AddNXReply(RR_TTL_t ttl = 1); diff --git a/llarp/dns/server.cpp b/llarp/dns/server.cpp index e0eeb2e3d..b2c7ca58d 100644 --- a/llarp/dns/server.cpp +++ b/llarp/dns/server.cpp @@ -37,8 +37,14 @@ namespace llarp bool Proxy::Start(const IpAddress& addr, const std::vector& resolvers) { - m_Resolvers.clear(); - m_Resolvers = resolvers; + if (resolvers.size()) + { + if (not SetupUnboundResolver(resolvers)) + { + return false; + } + } + const IpAddress any("0.0.0.0", 0); auto self = shared_from_this(); LogicCall(m_ClientLogic, [=]() { @@ -89,29 +95,85 @@ namespace llarp return *itr; } + bool + Proxy::SetupUnboundResolver(const std::vector& resolvers) + { + auto failFunc = [self = weak_from_this()](SockAddr to, Message msg) { + auto this_ptr = self.lock(); + if (this_ptr) + { + this_ptr->SendServerMessageTo(to, std::move(msg)); + } + }; + + auto replyFunc = [self = weak_from_this()](SockAddr to, std::vector buf) { + auto this_ptr = self.lock(); + if (this_ptr) + { + this_ptr->HandleUpstreamResponse(to, std::move(buf)); + } + }; + + m_UnboundResolver = std::make_shared( + m_ServerLoop, std::move(replyFunc), std::move(failFunc)); + if (not m_UnboundResolver->Init()) + { + llarp::LogError("Failed to initialize upstream DNS resolver."); + m_UnboundResolver = nullptr; + return false; + } + for (const auto& resolver : resolvers) + { + if (not m_UnboundResolver->AddUpstreamResolver(resolver.toString())) + { + llarp::LogError("Failed to add upstream DNS server: ", resolver.toString()); + m_UnboundResolver = nullptr; + return false; + } + } + + return true; + } + void Proxy::HandleTick(llarp_udp_io*) { } + void + Proxy::SendServerMessageBufferTo(const SockAddr& to, const llarp_buffer_t& buf) + { + llarp_ev_udp_sendto(&m_Server, to, buf); + } + void Proxy::SendServerMessageTo(const SockAddr& to, Message msg) { auto self = shared_from_this(); - LogicCall(m_ServerLogic, [to, msg, self]() { + LogicCall(m_ServerLogic, [to, msg = std::move(msg), self]() { std::array tmp = {{0}}; llarp_buffer_t buf(tmp); if (msg.Encode(&buf)) { buf.sz = buf.cur - buf.base; buf.cur = buf.base; - llarp_ev_udp_sendto(&self->m_Server, to, buf); + self->SendServerMessageBufferTo(to, buf); } else llarp::LogWarn("failed to encode dns message when sending"); }); } + void + Proxy::HandleUpstreamResponse(SockAddr to, std::vector buf) + { + auto self = shared_from_this(); + LogicCall(m_ServerLogic, [to, buffer = std::move(buf), self]() { + llarp_buffer_t buf(buffer); + self->SendServerMessageBufferTo(to, buf); + }); + } + void Proxy::SendClientMessageTo(const SockAddr& to, Message msg) { @@ -186,7 +248,6 @@ namespace llarp } TX tx = {hdr.id, from}; - auto itr = m_Forwarded.find(tx); Message msg(hdr); if (!msg.Decode(&pkt)) { @@ -222,7 +283,7 @@ namespace llarp llarp::LogWarn("failed to handle hooked dns"); } } - else if (m_Resolvers.size() == 0) + else if (not m_UnboundResolver) { // no upstream resolvers // let's serv fail it @@ -230,26 +291,9 @@ namespace llarp SendServerMessageTo(from, std::move(msg)); } - else if (itr == m_Forwarded.end()) - { - // new forwarded query - tx.from = PickRandomResolver(); - m_Forwarded[tx] = from; - LogicCall(m_ClientLogic, [=] { - // do query - const llarp_buffer_t tmpbuf(buf); - llarp_ev_udp_sendto(&self->m_Client, tx.from.createSockAddr(), tmpbuf); - }); - } else { - // send the query again because it's probably FEC from the requester - const auto resolver = itr->first.from; - LogicCall(m_ClientLogic, [=] { - // send it - const llarp_buffer_t tmpbuf(buf); - llarp_ev_udp_sendto(&self->m_Client, resolver.createSockAddr(), tmpbuf); - }); + m_UnboundResolver->Lookup(from, std::move(msg)); } } diff --git a/llarp/dns/server.hpp b/llarp/dns/server.hpp index 40cb4cca6..5ecaee124 100644 --- a/llarp/dns/server.hpp +++ b/llarp/dns/server.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include @@ -55,9 +56,6 @@ namespace llarp static void HandleTick(llarp_udp_io*); - void - Tick(llarp_time_t now); - void HandlePktClient(const SockAddr& from, Buffer_t buf); @@ -67,12 +65,24 @@ namespace llarp void SendClientMessageTo(const SockAddr& to, Message msg); + void + SendServerMessageBufferTo(const SockAddr& to, const llarp_buffer_t& buf); + void SendServerMessageTo(const SockAddr& to, Message msg); + void + HandleUpstreamResponse(SockAddr to, std::vector buf); + + void + HandleUpstreamFailure(const SockAddr& to, Message msg); + IpAddress PickRandomResolver() const; + bool + SetupUnboundResolver(const std::vector& resolvers); + private: llarp_udp_io m_Server; llarp_udp_io m_Client; @@ -82,6 +92,7 @@ namespace llarp Logic_ptr m_ClientLogic; IQueryHandler* m_QueryHandler; std::vector m_Resolvers; + std::shared_ptr m_UnboundResolver; struct TX { diff --git a/llarp/dns/unbound_resolver.cpp b/llarp/dns/unbound_resolver.cpp new file mode 100644 index 000000000..e8192c990 --- /dev/null +++ b/llarp/dns/unbound_resolver.cpp @@ -0,0 +1,150 @@ +#include + +#include +#include + +namespace llarp::dns +{ + struct PendingUnboundLookup + { + std::weak_ptr resolver; + Message msg; + SockAddr source; + }; + + void + UnboundResolver::Reset() + { + started = false; + if (unboundContext) + { + DeregisterPollFD(); + ub_ctx_delete(unboundContext); + } + unboundContext = nullptr; + } + + void + UnboundResolver::DeregisterPollFD() + { + eventLoop->deregister_poll_fd_readable(ub_fd(unboundContext)); + } + + void + UnboundResolver::RegisterPollFD() + { + eventLoop->register_poll_fd_readable( + ub_fd(unboundContext), [=]() { ub_process(unboundContext); }); + } + + UnboundResolver::UnboundResolver( + llarp_ev_loop_ptr eventLoop, ReplyFunction replyFunc, FailFunction failFunc) + : unboundContext(nullptr) + , started(false) + , eventLoop(eventLoop) + , replyFunc(replyFunc) + , failFunc(failFunc) + { + } + + // static callback + void + UnboundResolver::Callback(void* data, int err, ub_result* result) + { + std::unique_ptr lookup{static_cast(data)}; + + auto this_ptr = lookup->resolver.lock(); + if (not this_ptr) + return; // resolver is gone, so we don't reply. + + if (err != 0) + { + Message& msg = lookup->msg; + msg.AddServFail(); + this_ptr->failFunc(lookup->source, msg); + ub_resolve_free(result); + return; + } + + llarp_buffer_t buf; + buf.base = buf.cur = static_cast(result->answer_packet); + buf.sz = result->answer_len; + + MessageHeader hdr; + hdr.Decode(&buf); + hdr.id = lookup->msg.hdr_id; + + buf.cur = buf.base; + hdr.Encode(&buf); + + std::vector buf_copy(buf.sz); + std::copy_n(buf.base, buf.sz, buf_copy.begin()); + + this_ptr->replyFunc(lookup->source, std::move(buf_copy)); + + ub_resolve_free(result); + } + + bool + UnboundResolver::Init() + { + if (started) + { + Reset(); + } + + unboundContext = ub_ctx_create(); + + if (not unboundContext) + { + return false; + } + + RegisterPollFD(); + + return true; + } + + bool + UnboundResolver::AddUpstreamResolver(const std::string& upstreamResolverIP) + { + if (ub_ctx_set_fwd(unboundContext, upstreamResolverIP.c_str()) != 0) + { + Reset(); + return false; + } + return true; + } + + void + UnboundResolver::Lookup(const SockAddr& source, Message msg) + { + if (not unboundContext) + { + msg.AddServFail(); + failFunc(source, std::move(msg)); + return; + } + + started = true; + + const auto& q = msg.questions[0]; + auto* lookup = new PendingUnboundLookup{weak_from_this(), msg, source}; + int err = ub_resolve_async( + unboundContext, + q.Name().c_str(), + q.qtype, + q.qclass, + (void*)lookup, + &UnboundResolver::Callback, + nullptr); + + if (err != 0) + { + msg.AddServFail(); + failFunc(source, std::move(msg)); + return; + } + } + +} // namespace llarp::dns diff --git a/llarp/dns/unbound_resolver.hpp b/llarp/dns/unbound_resolver.hpp new file mode 100644 index 000000000..24c7594e6 --- /dev/null +++ b/llarp/dns/unbound_resolver.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include + +#include + +namespace llarp::dns +{ + using ReplyFunction = std::function buf)>; + using FailFunction = std::function; + + class UnboundResolver : public std::enable_shared_from_this + { + private: + ub_ctx* unboundContext; + + bool started; + + llarp_ev_loop_ptr eventLoop; + ReplyFunction replyFunc; + FailFunction failFunc; + + void + Reset(); + + void + DeregisterPollFD(); + void + RegisterPollFD(); + + public: + UnboundResolver(llarp_ev_loop_ptr eventLoop, ReplyFunction replyFunc, FailFunction failFunc); + + static void + Callback(void* data, int err, ub_result* result); + + // upstream resolver IP can be IPv4 or IPv6 + bool + Init(); + + bool + AddUpstreamResolver(const std::string& upstreamResolverIP); + + void + Lookup(const SockAddr& source, Message msg); + }; + +} // namespace llarp::dns diff --git a/llarp/ev/ev.hpp b/llarp/ev/ev.hpp index ee9789e51..defd231e5 100644 --- a/llarp/ev/ev.hpp +++ b/llarp/ev/ev.hpp @@ -810,6 +810,12 @@ struct llarp_ev_loop virtual void call_soon(std::function f) = 0; + + virtual void + register_poll_fd_readable(int fd, std::function callback) = 0; + + virtual void + deregister_poll_fd_readable(int fd) = 0; }; #endif diff --git a/llarp/ev/ev_libuv.cpp b/llarp/ev/ev_libuv.cpp index 0334b87cd..78fa8f3e2 100644 --- a/llarp/ev/ev_libuv.cpp +++ b/llarp/ev/ev_libuv.cpp @@ -957,7 +957,7 @@ namespace libuv [](uv_handle_t* h, void*) { if (uv_is_closing(h)) return; - if (h->data && uv_is_active(h) && h->type != UV_TIMER) + if (h->data && uv_is_active(h) && h->type != UV_TIMER && h->type != UV_POLL) { static_cast(h->data)->Close(); } @@ -1056,4 +1056,51 @@ namespace libuv uv_async_send(&m_WakeUp); } + void + OnUVPollFDReadable(uv_poll_t* handle, int status, [[maybe_unused]] int events) + { + if (status < 0) + return; // probably fd was closed + + auto func = static_cast(handle->data); + + (*func)(); + } + + void + Loop::register_poll_fd_readable(int fd, Callback callback) + { + if (m_Polls.count(fd)) + { + llarp::LogError( + "Attempting to create event loop poll on fd ", + fd, + ", but an event loop poll for that fd already exists."); + return; + } + + // new a copy as the one passed in here will go out of scope + auto function_ptr = new Callback(callback); + + auto& new_poll = m_Polls[fd]; + + uv_poll_init(&m_Impl, &new_poll, fd); + new_poll.data = (void*)function_ptr; + uv_poll_start(&new_poll, UV_READABLE, &OnUVPollFDReadable); + } + + void + Loop::deregister_poll_fd_readable(int fd) + { + auto itr = m_Polls.find(fd); + + if (itr != m_Polls.end()) + { + uv_poll_stop(&(itr->second)); + auto func = static_cast(itr->second.data); + delete func; + m_Polls.erase(itr); + } + } + } // namespace libuv diff --git a/llarp/ev/ev_libuv.hpp b/llarp/ev/ev_libuv.hpp index 3eac53481..6d4cd5bb0 100644 --- a/llarp/ev/ev_libuv.hpp +++ b/llarp/ev/ev_libuv.hpp @@ -125,6 +125,12 @@ namespace libuv void call_soon(std::function f) override; + void + register_poll_fd_readable(int fd, Callback callback) override; + + void + deregister_poll_fd_readable(int fd) override; + void FlushLogic(); @@ -144,6 +150,8 @@ namespace libuv std::map m_pendingCalls; + std::unordered_map m_Polls; + llarp::thread::Queue m_timerQueue; llarp::thread::Queue m_timerCancelQueue; };