mirror of https://github.com/oxen-io/lokinet
Merge pull request #1969 from majestrate/wintun-windivert-2022-08-02
use wintun and windivert for windows platform bits
This commit is contained in:
commit
8b321612da
|
@ -76,6 +76,7 @@ local debian_pipeline(name,
|
|||
'mkdir build',
|
||||
'cd build',
|
||||
'cmake .. -DWITH_SETCAP=OFF -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always -DCMAKE_BUILD_TYPE=' + build_type + ' ' +
|
||||
(if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') +
|
||||
(if werror then '-DWARNINGS_AS_ERRORS=ON ' else '') +
|
||||
'-DWITH_LTO=' + (if lto then 'ON ' else 'OFF ') +
|
||||
'-DWITH_TESTS=' + (if tests then 'ON ' else 'OFF ') +
|
||||
|
@ -121,7 +122,7 @@ local windows_cross_pipeline(name,
|
|||
lto=false,
|
||||
werror=false,
|
||||
cmake_extra='',
|
||||
toolchain='32',
|
||||
gui_zip_url='',
|
||||
extra_cmds=[],
|
||||
jobs=6,
|
||||
allow_fail=false) = {
|
||||
|
@ -137,16 +138,16 @@ local windows_cross_pipeline(name,
|
|||
image: image,
|
||||
pull: 'always',
|
||||
[if allow_fail then 'failure']: 'ignore',
|
||||
environment: { SSH_KEY: { from_secret: 'SSH_KEY' }, WINDOWS_BUILD_NAME: toolchain + 'bit' },
|
||||
environment: { SSH_KEY: { from_secret: 'SSH_KEY' }, WINDOWS_BUILD_NAME: '64bit' },
|
||||
commands: [
|
||||
'echo "Building on ${DRONE_STAGE_MACHINE}"',
|
||||
'echo "man-db man-db/auto-update boolean false" | debconf-set-selections',
|
||||
apt_get_quiet + ' update',
|
||||
apt_get_quiet + ' install -y eatmydata',
|
||||
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool',
|
||||
'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y p7zip-full build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip automake libtool',
|
||||
'update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix',
|
||||
'update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix',
|
||||
'VERBOSE=1 JOBS=' + jobs + ' ./contrib/windows.sh ' + ci_mirror_opts,
|
||||
'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh ' + (if std.length(gui_zip_url) > 0 then '-DBUILD_GUI=OFF -DGUI_ZIP_URL=' + gui_zip_url else '') + ' -DSTRIP_SYMBOLS=ON ' + ci_mirror_opts,
|
||||
] + extra_cmds,
|
||||
},
|
||||
],
|
||||
|
@ -277,7 +278,13 @@ local mac_builder(name,
|
|||
// basic system headers. WTF apple:
|
||||
'export SDKROOT="$(xcrun --sdk macosx --show-sdk-path)"',
|
||||
'ulimit -n 1024', // because macos sets ulimit to 256 for some reason yeah idk
|
||||
'./contrib/mac.sh ' + ci_mirror_opts + ' ' + codesign,
|
||||
'./contrib/mac-configure.sh ' + ci_mirror_opts + (if build_type == 'Debug' then ' -DWARN_DEPRECATED=OFF ' else '') + codesign,
|
||||
'cd build-mac',
|
||||
// We can't use the 'package' target here because making a .dmg requires an active logged in
|
||||
// macos gui to invoke Finder to invoke the partitioning tool to create a partitioned (!)
|
||||
// disk image. Most likely the GUI is required because if you lose sight of how pretty the
|
||||
// surface of macOS is you might see how ugly the insides are.
|
||||
'ninja -j' + jobs + ' assemble_gui',
|
||||
] + extra_cmds,
|
||||
},
|
||||
],
|
||||
|
@ -349,17 +356,17 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
|
|||
debian_pipeline('Debian stable (armhf)', docker_base + 'debian-stable/arm32v7', arch='arm64', jobs=4),
|
||||
|
||||
// cross compile targets
|
||||
linux_cross_pipeline('Cross Compile (mips)', cross_targets=['mips-linux-gnu', 'mipsel-linux-gnu']),
|
||||
linux_cross_pipeline('Cross Compile (arm/arm64)', cross_targets=['arm-linux-gnueabihf', 'aarch64-linux-gnu']),
|
||||
linux_cross_pipeline('Cross Compile (ppc64le)', cross_targets=['powerpc64le-linux-gnu']),
|
||||
// Not currently building successfully:
|
||||
//linux_cross_pipeline('Cross Compile (mips)', cross_targets=['mips-linux-gnu', 'mipsel-linux-gnu']),
|
||||
|
||||
// android apk builder
|
||||
apk_builder('android apk', docker_base + 'flutter', extra_cmds=['UPLOAD_OS=android ./contrib/ci/drone-static-upload.sh']),
|
||||
|
||||
// Windows builds (x64)
|
||||
windows_cross_pipeline('Windows (amd64)',
|
||||
docker_base + 'debian-win32-cross',
|
||||
toolchain='64',
|
||||
docker_base + 'nodejs-lts',
|
||||
extra_cmds=[
|
||||
'./contrib/ci/drone-static-upload.sh',
|
||||
]),
|
||||
|
@ -371,7 +378,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
|
|||
lto=true,
|
||||
tests=false,
|
||||
oxen_repo=true,
|
||||
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' +
|
||||
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + ci_mirror_opts +
|
||||
'-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8 ' +
|
||||
'-DCMAKE_CXX_FLAGS="-march=x86-64 -mtune=haswell" ' +
|
||||
'-DCMAKE_C_FLAGS="-march=x86-64 -mtune=haswell" ' +
|
||||
|
@ -385,7 +392,7 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
|
|||
docker_base + 'debian-buster/arm32v7',
|
||||
arch='arm64',
|
||||
deps=['g++', 'python3-dev', 'automake', 'libtool'],
|
||||
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' +
|
||||
cmake_extra='-DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON ' + ci_mirror_opts +
|
||||
'-DCMAKE_CXX_FLAGS="-march=armv7-a+fp -Wno-psabi" -DCMAKE_C_FLAGS="-march=armv7-a+fp" ' +
|
||||
'-DNATIVE_BUILD=OFF -DWITH_SYSTEMD=OFF -DWITH_BOOTSTRAP=OFF',
|
||||
extra_cmds=[
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.10) # bionic's cmake version
|
||||
cmake_minimum_required(VERSION 3.13...3.24) # 3.13 is buster's version
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
|
@ -61,6 +61,8 @@ option(WITH_HIVE "build simulation stubs" OFF)
|
|||
option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF)
|
||||
option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP})
|
||||
option(WITH_PEERSTATS "build with experimental peerstats db support" OFF)
|
||||
option(STRIP_SYMBOLS "strip off all debug symbols into an external archive for all executables built" OFF)
|
||||
|
||||
|
||||
include(cmake/enable_lto.cmake)
|
||||
|
||||
|
@ -81,6 +83,14 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||
set(CMAKE_BUILD_TYPE RelWithDebInfo)
|
||||
endif()
|
||||
|
||||
set(debug OFF)
|
||||
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
|
||||
set(debug ON)
|
||||
add_definitions(-DLOKINET_DEBUG)
|
||||
endif()
|
||||
|
||||
option(WARN_DEPRECATED "show deprecation warnings" ${debug})
|
||||
|
||||
if(BUILD_STATIC_DEPS AND STATIC_LINK)
|
||||
message(STATUS "we are building static deps so we won't build shared libs")
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "")
|
||||
|
@ -171,12 +181,19 @@ if(NOT TARGET sodium)
|
|||
export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake)
|
||||
endif()
|
||||
|
||||
if(NOT APPLE)
|
||||
add_compile_options(-Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations -Werror=vla)
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
add_compile_options(-Wno-unknown-warning-option)
|
||||
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()
|
||||
if(WARN_DEPRECATED)
|
||||
list(APPEND warning_flags -Wdeprecated-declarations)
|
||||
else()
|
||||
list(APPEND warning_flags -Wno-deprecated-declarations)
|
||||
endif()
|
||||
|
||||
# If we blindly add these directly as compile_options then they get passed to swiftc on Apple and
|
||||
# break, so we use a generate expression to set them only for C++/C/ObjC
|
||||
add_compile_options("$<$<OR:$<COMPILE_LANGUAGE:CXX>,$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:OBJC>>:${warning_flags}>")
|
||||
|
||||
if(XSAN)
|
||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fsanitize=${XSAN} -fno-omit-frame-pointer -fno-sanitize-recover")
|
||||
|
@ -186,11 +203,6 @@ if(XSAN)
|
|||
message(STATUS "Doing a ${XSAN} sanitizer build")
|
||||
endif()
|
||||
|
||||
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
|
||||
add_definitions(-DLOKINET_DEBUG)
|
||||
endif()
|
||||
|
||||
|
||||
include(cmake/coverage.cmake)
|
||||
|
||||
# these vars are set by the cmake toolchain spec
|
||||
|
@ -317,7 +329,10 @@ if(NOT TARGET uninstall)
|
|||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
|
||||
endif()
|
||||
|
||||
|
||||
if(BUILD_PACKAGE AND NOT APPLE)
|
||||
include(cmake/installer.cmake)
|
||||
include(cmake/installer.cmake)
|
||||
endif()
|
||||
|
||||
if(TARGET package)
|
||||
add_dependencies(package assemble_gui)
|
||||
endif()
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
|
||||
set(LOCAL_MIRROR "" CACHE STRING "local mirror path/URL for lib downloads")
|
||||
|
||||
set(OPENSSL_VERSION 1.1.1o CACHE STRING "openssl version")
|
||||
set(OPENSSL_VERSION 3.0.5 CACHE STRING "openssl version")
|
||||
set(OPENSSL_MIRROR ${LOCAL_MIRROR} https://www.openssl.org/source CACHE STRING "openssl download mirror(s)")
|
||||
set(OPENSSL_SOURCE openssl-${OPENSSL_VERSION}.tar.gz)
|
||||
set(OPENSSL_HASH SHA256=9384a2b0570dd80358841464677115df785edb941c71211f75076d72fe6b438f
|
||||
set(OPENSSL_HASH SHA256=aa7d8d9bef71ad6525c55ba11e5f4397889ce49c2c9349dcea6d3e4f0b024a7a
|
||||
CACHE STRING "openssl source hash")
|
||||
|
||||
set(EXPAT_VERSION 2.4.8 CACHE STRING "expat version")
|
||||
|
@ -19,10 +19,10 @@ set(EXPAT_SOURCE expat-${EXPAT_VERSION}.tar.xz)
|
|||
set(EXPAT_HASH SHA256=f79b8f904b749e3e0d20afeadecf8249c55b2e32d4ebb089ae378df479dcaf25
|
||||
CACHE STRING "expat source hash")
|
||||
|
||||
set(UNBOUND_VERSION 1.15.0 CACHE STRING "unbound version")
|
||||
set(UNBOUND_VERSION 1.16.2 CACHE STRING "unbound version")
|
||||
set(UNBOUND_MIRROR ${LOCAL_MIRROR} https://nlnetlabs.nl/downloads/unbound CACHE STRING "unbound download mirror(s)")
|
||||
set(UNBOUND_SOURCE unbound-${UNBOUND_VERSION}.tar.gz)
|
||||
set(UNBOUND_HASH SHA256=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f
|
||||
set(UNBOUND_HASH SHA512=0ea65ea63265be677441bd2a28df12098ec5e86c3372240c2874f9bd13752b8b818da81ae6076cf02cbeba3d36e397698a4c2b50570be1a6a8e47f57a0251572
|
||||
CACHE STRING "unbound source hash")
|
||||
|
||||
set(SQLITE3_VERSION 3380500 CACHE STRING "sqlite3 version")
|
||||
|
@ -61,7 +61,7 @@ set(ZLIB_MIRROR ${LOCAL_MIRROR} https://zlib.net
|
|||
set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.gz)
|
||||
set(ZLIB_HASH SHA256=91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9
|
||||
CACHE STRING "zlib source hash")
|
||||
|
||||
|
||||
set(CURL_VERSION 7.83.1 CACHE STRING "curl version")
|
||||
set(CURL_MIRROR ${LOCAL_MIRROR} https://curl.haxx.se/download https://curl.askapache.com
|
||||
CACHE STRING "curl mirror(s)")
|
||||
|
@ -125,21 +125,21 @@ if(ANDROID)
|
|||
set(android_toolchain_prefix x86_64)
|
||||
set(android_toolchain_suffix linux-android)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES x86)
|
||||
set(android_machine i686)
|
||||
set(android_machine x86)
|
||||
set(cross_host "--host=i686-linux-android")
|
||||
set(android_compiler_prefix i686)
|
||||
set(android_compiler_suffix linux-android23)
|
||||
set(android_toolchain_prefix i686)
|
||||
set(android_toolchain_suffix linux-android)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES armeabi-v7a)
|
||||
set(android_machine armv7)
|
||||
set(android_machine arm)
|
||||
set(cross_host "--host=armv7a-linux-androideabi")
|
||||
set(android_compiler_prefix armv7a)
|
||||
set(android_compiler_suffix linux-androideabi23)
|
||||
set(android_toolchain_prefix arm)
|
||||
set(android_toolchain_suffix linux-androideabi)
|
||||
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES arm64-v8a)
|
||||
set(android_machine aarch64)
|
||||
set(android_machine arm64)
|
||||
set(cross_host "--host=aarch64-linux-android")
|
||||
set(android_compiler_prefix aarch64)
|
||||
set(android_compiler_suffix linux-android23)
|
||||
|
@ -223,7 +223,7 @@ build_external(libuv
|
|||
add_static_target(libuv libuv_external libuv.a)
|
||||
target_link_libraries(libuv INTERFACE ${CMAKE_DL_LIBS})
|
||||
|
||||
|
||||
|
||||
build_external(zlib
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS} -fPIC" ${cross_extra} ./configure --prefix=${DEPS_DESTDIR} --static
|
||||
BUILD_BYPRODUCTS
|
||||
|
@ -234,43 +234,51 @@ add_static_target(zlib zlib_external libz.a)
|
|||
|
||||
|
||||
set(openssl_system_env "")
|
||||
set(openssl_arch "")
|
||||
set(openssl_configure_command ./config)
|
||||
set(openssl_flags "CFLAGS=${deps_CFLAGS}")
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32)
|
||||
set(openssl_system_env SYSTEM=MINGW64 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
|
||||
set(openssl_arch mingw64)
|
||||
set(openssl_system_env RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
|
||||
elseif(ARCH_TRIPLET STREQUAL i686-w64-mingw32)
|
||||
set(openssl_system_env SYSTEM=MINGW32 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
|
||||
set(openssl_arch mingw)
|
||||
set(openssl_system_env RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
|
||||
elseif(ANDROID)
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=${android_machine} LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar})
|
||||
set(openssl_arch android-${android_machine})
|
||||
set(openssl_system_env LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar} ANDROID_NDK_ROOT=${CMAKE_ANDROID_NDK} "PATH=${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin:$ENV{PATH}")
|
||||
list(APPEND openssl_flags "CPPFLAGS=-D__ANDROID_API__=${ANDROID_API}")
|
||||
set(openssl_extra_opts no-asm)
|
||||
elseif(ARCH_TRIPLET STREQUAL mips64-linux-gnuabi64)
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=mips64)
|
||||
set(openssl_configure_command ./Configure linux64-mips64)
|
||||
set(openssl_arch linux-mips64)
|
||||
elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu)
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=mips)
|
||||
set(openssl_arch linux-mips32)
|
||||
elseif(ARCH_TRIPLET STREQUAL mipsel-linux-gnu)
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=mipsel)
|
||||
set(openssl_arch linux-mips)
|
||||
elseif(ARCH_TRIPLET STREQUAL aarch64-linux-gnu)
|
||||
# cross compile arm64
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=aarch64)
|
||||
set(openssl_arch linux-aarch64)
|
||||
elseif(ARCH_TRIPLET MATCHES arm-linux)
|
||||
# cross compile armhf
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=armv4)
|
||||
set(openssl_arch linux-armv4)
|
||||
elseif(ARCH_TRIPLET MATCHES powerpc64le)
|
||||
# cross compile ppc64le
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=ppc64le)
|
||||
set(openssl_arch linux-ppc64le)
|
||||
endif()
|
||||
elseif(CMAKE_C_FLAGS MATCHES "-march=armv7")
|
||||
# Help openssl figure out that we're building from armv7 even if on armv8 hardware:
|
||||
set(openssl_system_env SYSTEM=Linux MACHINE=armv7)
|
||||
set(openssl_arch linux-armv4)
|
||||
endif()
|
||||
|
||||
|
||||
build_external(openssl
|
||||
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CC=${deps_cc} ${openssl_system_env} ${openssl_configure_command}
|
||||
--prefix=${DEPS_DESTDIR} ${openssl_extra_opts} no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost
|
||||
no-heartbeats no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl2 no-ssl3
|
||||
no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic "CFLAGS=${deps_CFLAGS}"
|
||||
--prefix=${DEPS_DESTDIR} --libdir=lib ${openssl_extra_opts}
|
||||
no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost
|
||||
no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl3
|
||||
no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic ${openssl_flags}
|
||||
${openssl_arch}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} -E env ${openssl_system_env} ${_make}
|
||||
INSTALL_COMMAND ${_make} install_sw
|
||||
BUILD_BYPRODUCTS
|
||||
${DEPS_DESTDIR}/lib/libssl.a ${DEPS_DESTDIR}/lib/libcrypto.a
|
||||
|
@ -284,7 +292,6 @@ endif()
|
|||
|
||||
set(OPENSSL_INCLUDE_DIR ${DEPS_DESTDIR}/include)
|
||||
set(OPENSSL_CRYPTO_LIBRARY ${DEPS_DESTDIR}/lib/libcrypto.a ${DEPS_DESTDIR}/lib/libssl.a)
|
||||
set(OPENSSL_VERSION 1.1.1)
|
||||
set(OPENSSL_ROOT_DIR ${DEPS_DESTDIR})
|
||||
|
||||
build_external(expat
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
include(CheckIPOSupported)
|
||||
option(WITH_LTO "enable lto on compile time" ON)
|
||||
if(WITH_LTO)
|
||||
if(WIN32)
|
||||
message(FATAL_ERROR "LTO not supported on win32 targets, please set -DWITH_LTO=OFF")
|
||||
endif()
|
||||
check_ipo_supported(RESULT IPO_ENABLED OUTPUT ipo_error)
|
||||
if(IPO_ENABLED)
|
||||
message(STATUS "LTO enabled")
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
set(default_build_gui OFF)
|
||||
set(default_gui_target pack)
|
||||
if(APPLE)
|
||||
|
@ -9,24 +8,30 @@ elseif(WIN32)
|
|||
set(default_gui_target win32)
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
option(GUI_EXE "path to an externally built lokinet gui.exe" OFF)
|
||||
endif()
|
||||
|
||||
option(BUILD_GUI "build electron gui from 'gui' submodule source" ${default_build_gui})
|
||||
set(GUI_YARN_TARGET "${default_gui_target}" CACHE STRING "yarn target for building the GUI")
|
||||
set(GUI_YARN_EXTRA_OPTS "" CACHE STRING "extra options to pass into the yarn build command")
|
||||
|
||||
|
||||
if (BUILD_GUI)
|
||||
message(STATUS "Building lokinet-gui")
|
||||
|
||||
find_program(YARN NAMES yarn yarnpkg REQUIRED)
|
||||
message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}")
|
||||
set(wine_env)
|
||||
if(WIN32)
|
||||
set(wine_env WINEDEBUG=-all "WINEPREFIX=${PROJECT_BINARY_DIR}/wineprefix")
|
||||
# allow manually specifying yarn with -DYARN=
|
||||
if(NOT YARN)
|
||||
find_program(YARN NAMES yarnpkg yarn REQUIRED)
|
||||
endif()
|
||||
message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}")
|
||||
|
||||
add_custom_target(lokinet-gui
|
||||
COMMAND ${YARN} install --frozen-lockfile &&
|
||||
${wine_env} ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
|
||||
|
||||
if(NOT WIN32)
|
||||
add_custom_target(lokinet-gui
|
||||
COMMAND ${YARN} install --frozen-lockfile &&
|
||||
${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
add_custom_target(assemble_gui ALL
|
||||
|
@ -38,27 +43,42 @@ if (BUILD_GUI)
|
|||
COMMAND cp "${lokinet_app}/Contents/Resources/icon.icns" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns"
|
||||
COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/"
|
||||
COMMAND /usr/libexec/PlistBuddy
|
||||
-c "Delete :CFBundleDisplayName"
|
||||
-c "Add :LSHasLocalizedDisplayName bool true"
|
||||
-c "Add :CFBundleDevelopmentRegion string en"
|
||||
-c "Set :CFBundleShortVersionString ${lokinet_VERSION}"
|
||||
-c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}"
|
||||
"${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist"
|
||||
-c "Delete :CFBundleDisplayName"
|
||||
-c "Add :LSHasLocalizedDisplayName bool true"
|
||||
-c "Add :CFBundleDevelopmentRegion string en"
|
||||
-c "Set :CFBundleShortVersionString ${lokinet_VERSION}"
|
||||
-c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}"
|
||||
"${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist"
|
||||
)
|
||||
|
||||
elseif(WIN32)
|
||||
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui")
|
||||
add_custom_target(copy_gui ALL
|
||||
DEPENDS lokinet lokinet-gui
|
||||
# FIXME: we really shouldn't be building inside the source directory but this is npm...
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
option(GUI_ZIP_FILE "custom lokinet gui for windows from zip file" OFF)
|
||||
if(GUI_ZIP_FILE)
|
||||
message(STATUS "using custom lokinet gui from ${GUI_ZIP_FILE}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${GUI_ZIP_FILE}
|
||||
WORKING_DIRECTORY ${PROJECT_BINARY_DIR})
|
||||
add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true")
|
||||
elseif(GUI_EXE)
|
||||
message(STATUS "using custom lokinet gui executable: ${GUI_EXE}")
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GUI_EXE}" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe")
|
||||
add_custom_target("${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe" COMMAND "true")
|
||||
else()
|
||||
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
|
||||
COMMAND ${YARN} install --frozen-lockfile &&
|
||||
USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all WINEPREFIX="${PROJECT_BINARY_DIR}/wineprefix" ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe"
|
||||
"${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
|
||||
)
|
||||
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
|
||||
endif()
|
||||
add_custom_target(assemble_gui ALL COMMAND "true" DEPENDS "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe")
|
||||
else()
|
||||
message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform")
|
||||
endif()
|
||||
|
||||
else()
|
||||
message(STATUS "Not building lokinet-gui")
|
||||
message(STATUS "not building gui")
|
||||
endif()
|
||||
|
||||
if(NOT TARGET assemble_gui)
|
||||
add_custom_target(assemble_gui COMMAND "true")
|
||||
endif()
|
||||
|
|
|
@ -5,11 +5,42 @@ set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
|
|||
|
||||
if(WIN32)
|
||||
include(cmake/win32_installer_deps.cmake)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-exit.ini DESTINATION share/conf.d COMPONENT exit_configs)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-keyfile.ini DESTINATION share/conf.d COMPONENT keyfile_configs)
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-debug-log.ini DESTINATION share/conf.d COMPONENT debug_configs)
|
||||
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
|
||||
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified" "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs")
|
||||
list(APPEND CPACK_COMPONENTS_ALL "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs")
|
||||
elseif(APPLE)
|
||||
set(CPACK_GENERATOR DragNDrop;ZIP)
|
||||
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
|
||||
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
|
||||
endif()
|
||||
|
||||
|
||||
# This must always be last!
|
||||
include(CPack)
|
||||
|
||||
if(WIN32)
|
||||
cpack_add_component(lokinet
|
||||
DISPLAY_NAME "lokinet"
|
||||
DESCRIPTION "core required lokinet files"
|
||||
REQUIRED)
|
||||
|
||||
cpack_add_component(gui
|
||||
DISPLAY_NAME "lokinet gui"
|
||||
DESCRIPTION "electron based control panel for lokinet")
|
||||
|
||||
cpack_add_component(exit_configs
|
||||
DISPLAY_NAME "auto-enable exit"
|
||||
DESCRIPTION "automatically enable usage of exit.loki as an exit node\n"
|
||||
DISABLED)
|
||||
|
||||
cpack_add_component(keyfile_configs
|
||||
DISPLAY_NAME "persist address"
|
||||
DESCRIPTION "persist .loki address across restarts of lokinet\nnot recommended when enabling exit nodes"
|
||||
DISABLED)
|
||||
|
||||
cpack_add_component(debug_configs
|
||||
DISPLAY_NAME "debug logging"
|
||||
DESCRIPTION "enable debug spew log level by default"
|
||||
DISABLED)
|
||||
endif()
|
||||
|
|
|
@ -1,32 +1,52 @@
|
|||
if(NOT WIN32)
|
||||
return()
|
||||
endif()
|
||||
if (NOT STATIC_LINK)
|
||||
message(FATAL_ERROR "windows requires static builds (thanks balmer)")
|
||||
endif()
|
||||
|
||||
enable_language(RC)
|
||||
|
||||
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
|
||||
option(WITH_WINDOWS_32 "build 32 bit windows" OFF)
|
||||
|
||||
if(NOT MSVC_VERSION)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-bad-function-cast>)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-cast-function-type>)
|
||||
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fpermissive>)
|
||||
# unlike unix where you get a *single* compiler ID string in .comment
|
||||
# GNU ld sees fit to merge *all* the .ident sections in object files
|
||||
# to .r[o]data section one after the other!
|
||||
add_compile_options(-fno-ident -Wa,-mbig-obj)
|
||||
link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
|
||||
# the minimum windows version, set to 6 rn because supporting older windows is hell
|
||||
set(_winver 0x0600)
|
||||
add_definitions(-DWINVER=${_winver} -D_WIN32_WINNT=${_winver})
|
||||
endif()
|
||||
# unlike unix where you get a *single* compiler ID string in .comment
|
||||
# GNU ld sees fit to merge *all* the .ident sections in object files
|
||||
# to .r[o]data section one after the other!
|
||||
add_compile_options(-fno-ident -Wa,-mbig-obj)
|
||||
# the minimum windows version, set to 6 rn because supporting older windows is hell
|
||||
set(_winver 0x0600)
|
||||
add_definitions(-D_WIN32_WINNT=${_winver})
|
||||
|
||||
if(EMBEDDED_CFG)
|
||||
link_libatomic()
|
||||
endif()
|
||||
|
||||
add_definitions(-DWIN32_LEAN_AND_MEAN -DWIN32)
|
||||
set(WINTUN_VERSION 0.14.1 CACHE STRING "wintun version")
|
||||
set(WINTUN_MIRROR https://www.wintun.net/builds
|
||||
CACHE STRING "wintun mirror(s)")
|
||||
set(WINTUN_SOURCE wintun-${WINTUN_VERSION}.zip)
|
||||
set(WINTUN_HASH SHA256=07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51
|
||||
CACHE STRING "wintun source hash")
|
||||
|
||||
if (NOT STATIC_LINK AND NOT MSVC)
|
||||
message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll")
|
||||
message("for release builds, turn on STATIC_LINK in cmake options")
|
||||
endif()
|
||||
set(WINDIVERT_VERSION 2.2.0-A CACHE STRING "windivert version")
|
||||
set(WINDIVERT_MIRROR https://reqrypt.org/download
|
||||
CACHE STRING "windivert mirror(s)")
|
||||
set(WINDIVERT_SOURCE WinDivert-${WINDIVERT_VERSION}.zip)
|
||||
set(WINDIVERT_HASH SHA256=2a7630aac0914746fbc565ac862fa096e3e54233883ac52d17c83107496b7a7f
|
||||
CACHE STRING "windivert source hash")
|
||||
|
||||
set(WINTUN_URL ${WINTUN_MIRROR}/${WINTUN_SOURCE}
|
||||
CACHE STRING "wintun download url")
|
||||
set(WINDIVERT_URL ${WINDIVERT_MIRROR}/${WINDIVERT_SOURCE}
|
||||
CACHE STRING "windivert download url")
|
||||
|
||||
message(STATUS "Downloading wintun from ${WINTUN_URL}")
|
||||
file(DOWNLOAD ${WINTUN_URL} ${CMAKE_BINARY_DIR}/wintun.zip EXPECTED_HASH ${WINTUN_HASH})
|
||||
message(STATUS "Downloading windivert from ${WINDIVERT_URL}")
|
||||
file(DOWNLOAD ${WINDIVERT_URL} ${CMAKE_BINARY_DIR}/windivert.zip EXPECTED_HASH ${WINDIVERT_HASH})
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/wintun.zip
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/windivert.zip
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
|
|
@ -1,11 +1,3 @@
|
|||
set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe")
|
||||
set(TUNTAP_EXE "${CMAKE_BINARY_DIR}/tuntap-install.exe")
|
||||
set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed")
|
||||
|
||||
file(DOWNLOAD
|
||||
${TUNTAP_URL}
|
||||
${TUNTAP_EXE})
|
||||
|
||||
if(NOT BUILD_GUI)
|
||||
if(NOT GUI_ZIP_URL)
|
||||
set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/lokinet-gui/dev/lokinet-windows-x64-20220331T180338Z-569f90ad8.zip")
|
||||
|
@ -25,9 +17,19 @@ if(NOT BUILD_GUI)
|
|||
message(FATAL_ERROR "Downloaded gui archive from ${GUI_ZIP_URL} does not contain gui/lokinet-gui.exe!")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui)
|
||||
install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin COMPONENT tuntap)
|
||||
|
||||
if(WITH_WINDOWS_32)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/x86/wintun.dll DESTINATION bin COMPONENT lokinet)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.sys DESTINATION lib COMPONENT lokinet)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.dll DESTINATION bin COMPONENT lokinet)
|
||||
else()
|
||||
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/amd64/wintun.dll DESTINATION bin COMPONENT lokinet)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert64.sys DESTINATION lib COMPONENT lokinet)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert.dll DESTINATION bin COMPONENT lokinet)
|
||||
endif()
|
||||
|
||||
set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed")
|
||||
install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet RENAME bootstrap.signed)
|
||||
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet")
|
||||
|
@ -35,7 +37,6 @@ set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico")
|
|||
set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin")
|
||||
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
|
||||
|
||||
|
||||
function(read_nsis_file filename outvar)
|
||||
file(STRINGS "${filename}" _outvar)
|
||||
list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\")
|
||||
|
@ -51,9 +52,8 @@ read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_delete_icons.nsis" _extra_
|
|||
|
||||
set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}")
|
||||
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}")
|
||||
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
|
||||
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
|
||||
set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}")
|
||||
set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}")
|
||||
|
||||
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
|
||||
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
|
||||
set(CPACK_NSIS_COMPRESSOR "/SOLID lzma")
|
||||
|
|
|
@ -26,6 +26,7 @@ for abi in $build_abis; do
|
|||
-DANDROID_ABI=$abi \
|
||||
-DANDROID_ARM_MODE=arm \
|
||||
-DANDROID_PLATFORM=android-23 \
|
||||
-DANDROID_API=23 \
|
||||
-DANDROID_STL=c++_static \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
|
||||
-DBUILD_STATIC_DEPS=ON \
|
||||
|
@ -34,6 +35,7 @@ for abi in $build_abis; do
|
|||
-DBUILD_TESTING=OFF \
|
||||
-DBUILD_LIBLOKINET=OFF \
|
||||
-DWITH_TESTS=OFF \
|
||||
-DWITH_BOOTSTRAP=OFF \
|
||||
-DNATIVE_BUILD=OFF \
|
||||
-DSTATIC_LINK=ON \
|
||||
-DWITH_SYSTEMD=OFF \
|
||||
|
@ -45,7 +47,7 @@ for abi in $build_abis; do
|
|||
-DSUBMODULE_CHECK=OFF \
|
||||
-DWITH_LTO=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
$@
|
||||
"$@"
|
||||
cd -
|
||||
done
|
||||
rm -f $build/Makefile
|
||||
|
|
|
@ -34,8 +34,11 @@ else
|
|||
fi
|
||||
|
||||
mkdir -v "$base"
|
||||
if [ -e build-windows ]; then
|
||||
cp -av build-windows/lokinet-*.exe "$base"
|
||||
if [ -e build/win32 ]; then
|
||||
# save debug symbols
|
||||
cp -av build/win32/daemon/debug-symbols.tar.xz "$base-debug-symbols.tar.xz"
|
||||
# save installer
|
||||
cp -av build/win32/*.exe "$base"
|
||||
# zipit up yo
|
||||
archive="$base.zip"
|
||||
zip -r "$archive" "$base"
|
||||
|
@ -48,8 +51,9 @@ elif [ -e build-docs ]; then
|
|||
cp -av build-docs/docs/mkdocs.yml build-docs/docs/markdown "$base"
|
||||
tar cJvf "$archive" "$base"
|
||||
elif [ -e build-mac ]; then
|
||||
archive="$base.dmg"
|
||||
mv build-mac/Lokinet*.dmg "$archive"
|
||||
archive="$base.tar.xz"
|
||||
mv build-mac/Lokinet*/ "$base"
|
||||
tar cJvf "$archive" "$base"
|
||||
else
|
||||
cp -av daemon/lokinet daemon/lokinet-vpn "$base"
|
||||
cp -av ../contrib/bootstrap/mainnet.signed "$base/bootstrap.signed"
|
||||
|
@ -64,6 +68,7 @@ upload_to="oxen.rocks/${DRONE_REPO// /_}/${DRONE_BRANCH// /_}"
|
|||
# -mkdir a/, -mkdir a/b/, -mkdir a/b/c/, ... commands. The leading `-` allows the command to fail
|
||||
# without error.
|
||||
upload_dirs=(${upload_to//\// })
|
||||
put_debug=
|
||||
mkdirs=
|
||||
dir_tmp=""
|
||||
for p in "${upload_dirs[@]}"; do
|
||||
|
@ -71,10 +76,13 @@ for p in "${upload_dirs[@]}"; do
|
|||
mkdirs="$mkdirs
|
||||
-mkdir $dir_tmp"
|
||||
done
|
||||
|
||||
if [ -e "$base-debug-symbols.tar.xz" ] ; then
|
||||
put_debug="put $base-debug-symbols.tar.xz $upload_to"
|
||||
fi
|
||||
sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks <<SFTP
|
||||
$mkdirs
|
||||
put $archive $upload_to
|
||||
$put_debug
|
||||
SFTP
|
||||
|
||||
set +o xtrace
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
[logging]
|
||||
level=debug
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# "suggested" default exit node config
|
||||
#
|
||||
[network]
|
||||
exit-node=exit.loki
|
|
@ -0,0 +1,5 @@
|
|||
#
|
||||
# persist .loki address in a private key file in the data dir
|
||||
#
|
||||
[network]
|
||||
keyfile=lokinet-addr.privkey
|
|
@ -21,23 +21,23 @@ fi
|
|||
|
||||
cd "$(dirname $0)/../"
|
||||
if [ "$1" = "verify" ] ; then
|
||||
if [ $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|mm?)$' | grep -v '\#') | grep '</replacement>' | wc -l) -ne 0 ] ; then
|
||||
if [ $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|m(m)?)$' | grep -v '#') | grep '</replacement>' | wc -l) -ne 0 ] ; then
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
$binary -i $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|mm)$' | grep -v '\#') &> /dev/null
|
||||
$binary -i $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|m(m)?)$' | grep -v '#') &> /dev/null
|
||||
fi
|
||||
|
||||
swift_format=$(command -v swiftformat 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
if [ "$1" = "verify" ] ; then
|
||||
for f in $(find daemon | grep -E '\.swift$' | grep -v '\#') ; do
|
||||
for f in $(find daemon | grep -E '\.swift$' | grep -v '#') ; do
|
||||
if [ $($swift_format --quiet --dryrun < "$f" | diff "$f" - | wc -l) -ne 0 ] ; then
|
||||
exit 3
|
||||
fi
|
||||
done
|
||||
else
|
||||
$swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '\#')
|
||||
$swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '#')
|
||||
fi
|
||||
|
||||
fi
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
set -x
|
||||
|
||||
if ! [ -f LICENSE ] || ! [ -d llarp ]; then
|
||||
echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-mac
|
||||
cd build-mac
|
||||
cmake \
|
||||
-G Ninja \
|
||||
-DBUILD_STATIC_DEPS=ON \
|
||||
-DBUILD_LIBLOKINET=OFF \
|
||||
-DWITH_TESTS=OFF \
|
||||
-DWITH_BOOTSTRAP=OFF \
|
||||
-DNATIVE_BUILD=OFF \
|
||||
-DWITH_LTO=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DMACOS_SYSTEM_EXTENSION=ON \
|
||||
-DCODESIGN=ON \
|
||||
-DBUILD_PACKAGE=ON \
|
||||
"$@" \
|
||||
..
|
||||
|
||||
echo "cmake build configured in build-mac"
|
|
@ -11,27 +11,14 @@ set -e
|
|||
set -x
|
||||
|
||||
if ! [ -f LICENSE ] || ! [ -d llarp ]; then
|
||||
echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory"
|
||||
echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p build-mac
|
||||
cd build-mac
|
||||
cmake \
|
||||
-G Ninja \
|
||||
-DBUILD_STATIC_DEPS=ON \
|
||||
-DBUILD_LIBLOKINET=OFF \
|
||||
-DWITH_TESTS=OFF \
|
||||
-DWITH_BOOTSTRAP=OFF \
|
||||
-DNATIVE_BUILD=OFF \
|
||||
-DWITH_LTO=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DMACOS_SYSTEM_EXTENSION=ON \
|
||||
-DCODESIGN=ON \
|
||||
-DBUILD_PACKAGE=ON \
|
||||
"$@" \
|
||||
..
|
||||
ninja -j1 package
|
||||
./contrib/mac-configure.sh "$@"
|
||||
|
||||
cd build-mac
|
||||
ninja -j${JOBS:-1} package
|
||||
cd ..
|
||||
|
||||
echo -e "Build complete, your app is here:\n"
|
||||
|
|
|
@ -2,12 +2,25 @@
|
|||
set -e
|
||||
set -x
|
||||
|
||||
root=$(readlink -f "$1")
|
||||
shift
|
||||
mkdir -p "$1"
|
||||
build=$(readlink -f "$1")
|
||||
shift
|
||||
cd "$build"
|
||||
# Usage: windows-configure.sh [rootdir [builddir]] -DWHATEVER=BLAH ...
|
||||
|
||||
if [ $# -ge 1 ] && [[ "$1" != -* ]]; then
|
||||
root="$1"
|
||||
shift
|
||||
else
|
||||
root="$(dirname $0)"/..
|
||||
fi
|
||||
root="$(readlink -f "$root")"
|
||||
|
||||
if [ $# -ge 1 ] && [[ "$1" != -* ]]; then
|
||||
build="$(readlink -f "$1")"
|
||||
shift
|
||||
else
|
||||
build="$root/build/win32"
|
||||
echo "Setting up build in $build"
|
||||
fi
|
||||
|
||||
mkdir -p "$build"
|
||||
cmake \
|
||||
-S "$root" -B "$build" \
|
||||
-G 'Unix Makefiles' \
|
||||
|
@ -21,6 +34,7 @@ cmake \
|
|||
-DBUILD_TESTING=OFF \
|
||||
-DBUILD_LIBLOKINET=OFF \
|
||||
-DWITH_TESTS=OFF \
|
||||
-DWITH_BOOTSTRAP=OFF \
|
||||
-DNATIVE_BUILD=OFF \
|
||||
-DSTATIC_LINK=ON \
|
||||
-DWITH_SYSTEMD=OFF \
|
||||
|
@ -30,4 +44,4 @@ cmake \
|
|||
-DFORCE_SPDLOG_SUBMODULE=ON \
|
||||
-DFORCE_NLOHMANN_SUBMODULE=ON \
|
||||
-DWITH_LTO=OFF \
|
||||
$@
|
||||
"$@"
|
||||
|
|
|
@ -6,9 +6,8 @@
|
|||
|
||||
set -e
|
||||
set +x
|
||||
|
||||
|
||||
root="$(readlink -f $(dirname $0)/../)"
|
||||
cd "$root"
|
||||
./contrib/windows-configure.sh . build-windows "$@"
|
||||
make package -j${JOBS:-$(nproc)} -C build-windows
|
||||
mkdir -p $root/build/win32
|
||||
$root/contrib/windows-configure.sh $root $root/build/win32 "$@"
|
||||
make package -j${JOBS:-$(nproc)} -C $root/build/win32
|
||||
|
||||
|
|
|
@ -46,15 +46,26 @@ if(WITH_BOOTSTRAP)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
# cmake interface library for bunch of cmake hacks to fix final link order
|
||||
add_library(hax_and_shims_for_cmake INTERFACE)
|
||||
if(WIN32)
|
||||
target_link_libraries(hax_and_shims_for_cmake INTERFACE uvw oxenmq::oxenmq -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
|
||||
endif()
|
||||
|
||||
foreach(exe ${exetargets})
|
||||
if(WIN32 AND NOT MSVC_VERSION)
|
||||
if(WIN32)
|
||||
target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc)
|
||||
target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00)
|
||||
target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi)
|
||||
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
target_link_directories(${exe} PRIVATE /usr/local/lib)
|
||||
endif()
|
||||
target_link_libraries(${exe} PUBLIC liblokinet)
|
||||
target_link_libraries(${exe} PUBLIC lokinet-amalgum hax_and_shims_for_cmake)
|
||||
if(STRIP_SYMBOLS)
|
||||
add_custom_command(TARGET ${exe}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_OBJCOPY} ARGS --only-keep-debug $<TARGET_FILE:${exe}> $<TARGET_FILE:${exe}>.debug
|
||||
COMMAND ${CMAKE_STRIP} ARGS --strip-all $<TARGET_FILE:${exe}>)
|
||||
endif()
|
||||
target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}")
|
||||
if(should_install)
|
||||
if(APPLE)
|
||||
|
@ -71,3 +82,9 @@ endforeach()
|
|||
if(SETCAP)
|
||||
install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)")
|
||||
endif()
|
||||
|
||||
if(STRIP_SYMBOLS)
|
||||
add_custom_target(symbols ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E tar cJf ${CMAKE_CURRENT_BINARY_DIR}/debug-symbols.tar.xz $<TARGET_FILE:lokinet>.debug
|
||||
DEPENDS lokinet)
|
||||
endif()
|
||||
|
|
|
@ -101,7 +101,7 @@ install_win32_daemon()
|
|||
// Create the service
|
||||
schService = CreateService(
|
||||
schSCManager, // SCM database
|
||||
"lokinet", // name of service
|
||||
strdup("lokinet"), // name of service
|
||||
"Lokinet for Windows", // service name to display
|
||||
SERVICE_ALL_ACCESS, // desired access
|
||||
SERVICE_WIN32_OWN_PROCESS, // service type
|
||||
|
@ -134,10 +134,10 @@ insert_description()
|
|||
SC_HANDLE schSCManager;
|
||||
SC_HANDLE schService;
|
||||
SERVICE_DESCRIPTION sd;
|
||||
LPTSTR szDesc =
|
||||
LPTSTR szDesc = strdup(
|
||||
"LokiNET is a free, open source, private, "
|
||||
"decentralized, \"market based sybil resistant\" "
|
||||
"and IP based onion routing network";
|
||||
"and IP based onion routing network");
|
||||
// Get a handle to the SCM database.
|
||||
schSCManager = OpenSCManager(
|
||||
NULL, // local computer
|
||||
|
@ -270,7 +270,7 @@ run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions o
|
|||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
llarp::LogError("failed to start up lokinet: {}", ex.what());
|
||||
llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what()));
|
||||
exit_code.set_value(1);
|
||||
return;
|
||||
}
|
||||
|
@ -329,8 +329,9 @@ class WindowsServiceStopped
|
|||
LONG
|
||||
GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
|
||||
{
|
||||
const DWORD flags = MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData
|
||||
| MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo;
|
||||
const auto flags = (MINIDUMP_TYPE)(
|
||||
MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData
|
||||
| MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "C:\\ProgramData\\lokinet\\crash-" << llarp::time_now_ms().count() << ".dmp";
|
||||
|
@ -367,7 +368,7 @@ main(int argc, char* argv[])
|
|||
return lokinet_main(argc, argv);
|
||||
#else
|
||||
SERVICE_TABLE_ENTRY DispatchTable[] = {
|
||||
{"lokinet", (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
|
||||
{strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
|
||||
if (lstrcmpi(argv[1], "--win32-daemon") == 0)
|
||||
{
|
||||
start_as_daemon = true;
|
||||
|
@ -680,7 +681,7 @@ win32_daemon_entry(DWORD argc, LPTSTR* argv)
|
|||
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
|
||||
// SCM clobbers startup args, regenerate them here
|
||||
argc = 2;
|
||||
argv[1] = "c:/programdata/lokinet/lokinet.ini";
|
||||
argv[1] = strdup("c:\\programdata\\lokinet\\lokinet.ini");
|
||||
argv[2] = nullptr;
|
||||
lokinet_main(argc, argv);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ this codebase is a bit large. this is a high level map of the current code struc
|
|||
* `/llarp/constants`: contains all compile time constants
|
||||
* `/llarp/crypto`: cryptography interface and implementation, includes various secure helpers
|
||||
* `/llarp/dht`: dht message structs, parsing, validation and handlers of dht related parts of the protocol
|
||||
* `/llarp/dns`: dns subsytem, dns udp wire parsers, resolver, server, rewriter/intercepter, the works
|
||||
* `/llarp/dns`: dns subsytem, dns udp wire parsers, resolver, server, rewriter/interceptor, the works
|
||||
* `/llarp/ev`: event loop interfaces and implementations
|
||||
* `/llarp/exit`: `.snode` endpoint "backend"
|
||||
* `/llarp/handlers`: packet endpoint "frontends"
|
||||
|
|
|
@ -2,4 +2,4 @@ add_library(lokinet-android
|
|||
SHARED
|
||||
lokinet_config.cpp
|
||||
lokinet_daemon.cpp)
|
||||
target_link_libraries(lokinet-android liblokinet)
|
||||
target_link_libraries(lokinet-android lokinet-amalgum)
|
||||
|
|
|
@ -84,9 +84,8 @@ extern "C"
|
|||
JNIEXPORT jint JNICALL
|
||||
Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self)
|
||||
{
|
||||
auto ptr = GetImpl<llarp::Context>(env, self);
|
||||
if (const auto& router = ptr->router; ptr and ptr->router)
|
||||
return router->OutboundUDPSocket();
|
||||
if (auto ptr = GetImpl<llarp::Context>(env, self); ptr and ptr->router)
|
||||
return ptr->router->OutboundUDPSocket();
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ add_library(lokinet-util
|
|||
util/fs.cpp
|
||||
util/json.cpp
|
||||
util/logging/buffer.cpp
|
||||
util/lokinet_init.c
|
||||
util/easter_eggs.cpp
|
||||
util/mem.cpp
|
||||
util/str.cpp
|
||||
util/thread/queue_manager.cpp
|
||||
|
@ -32,12 +32,11 @@ add_library(lokinet-platform
|
|||
STATIC
|
||||
# for networking
|
||||
ev/ev.cpp
|
||||
ev/ev_libuv.cpp
|
||||
ev/libuv.cpp
|
||||
net/ip.cpp
|
||||
net/ip_address.cpp
|
||||
net/ip_packet.cpp
|
||||
net/ip_range.cpp
|
||||
net/net.cpp
|
||||
net/net_int.cpp
|
||||
net/sock_addr.cpp
|
||||
vpn/packet_router.cpp
|
||||
|
@ -53,41 +52,72 @@ if (ANDROID)
|
|||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
|
||||
if(NON_PC_TARGET)
|
||||
add_import_library(rt)
|
||||
target_link_libraries(lokinet-platform PUBLIC rt)
|
||||
endif()
|
||||
target_sources(lokinet-platform PRIVATE linux/dbus.cpp)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
target_sources(lokinet-platform PRIVATE
|
||||
win32/win32_inet.c
|
||||
win32/win32_intrnl.c)
|
||||
|
||||
target_link_libraries(lokinet-platform PUBLIC iphlpapi)
|
||||
net/win32.cpp
|
||||
vpn/win32.cpp
|
||||
win32/exec.cpp)
|
||||
add_library(lokinet-win32 STATIC
|
||||
win32/dll.cpp
|
||||
win32/exception.cpp)
|
||||
add_library(lokinet-wintun STATIC
|
||||
win32/wintun.cpp)
|
||||
add_library(lokinet-windivert STATIC
|
||||
win32/windivert.cpp)
|
||||
|
||||
# wintun and windivert are privated linked by lokinet-platform
|
||||
# this is so their details do not leak out to deps of lokinet-platform
|
||||
# wintun and windivert still need things from lokinet-platform
|
||||
target_compile_options(lokinet-wintun PUBLIC -I${CMAKE_BINARY_DIR}/wintun/include/)
|
||||
target_compile_options(lokinet-windivert PUBLIC -I${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/include/)
|
||||
target_include_directories(lokinet-windivert PUBLIC ${PROJECT_SOURCE_DIR})
|
||||
target_link_libraries(lokinet-wintun PUBLIC lokinet-platform lokinet-util lokinet-config)
|
||||
target_link_libraries(lokinet-win32 PUBLIC lokinet-util)
|
||||
target_link_libraries(lokinet-windivert PUBLIC oxen-logging)
|
||||
target_link_libraries(lokinet-windivert PRIVATE lokinet-win32)
|
||||
target_link_libraries(lokinet-platform PRIVATE lokinet-win32 lokinet-wintun lokinet-windivert)
|
||||
else()
|
||||
target_sources(lokinet-platform PRIVATE
|
||||
net/posix.cpp)
|
||||
endif()
|
||||
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
target_include_directories(lokinet-platform SYSTEM PUBLIC /usr/local/include)
|
||||
endif()
|
||||
|
||||
add_library(liblokinet
|
||||
add_library(lokinet-dns
|
||||
STATIC
|
||||
config/config.cpp
|
||||
config/definition.cpp
|
||||
config/ini.cpp
|
||||
config/key_manager.cpp
|
||||
|
||||
dns/message.cpp
|
||||
dns/name.cpp
|
||||
dns/platform.cpp
|
||||
dns/question.cpp
|
||||
dns/rr.cpp
|
||||
dns/serialize.cpp
|
||||
dns/server.cpp
|
||||
dns/srv_data.cpp
|
||||
dns/unbound_resolver.cpp
|
||||
dns/srv_data.cpp)
|
||||
|
||||
consensus/table.cpp
|
||||
if(WITH_SYSTEMD)
|
||||
target_sources(lokinet-dns PRIVATE dns/nm_platform.cpp dns/sd_platform.cpp)
|
||||
endif()
|
||||
|
||||
target_link_libraries(lokinet-dns PUBLIC lokinet-platform uvw)
|
||||
target_link_libraries(lokinet-dns PRIVATE libunbound lokinet-config)
|
||||
|
||||
add_library(lokinet-config
|
||||
STATIC
|
||||
config/config.cpp
|
||||
config/definition.cpp
|
||||
config/ini.cpp
|
||||
config/key_manager.cpp)
|
||||
|
||||
target_link_libraries(lokinet-config PUBLIC lokinet-dns lokinet-platform oxenmq::oxenmq)
|
||||
|
||||
add_library(lokinet-amalgum
|
||||
STATIC
|
||||
consensus/reachability_testing.cpp
|
||||
|
||||
bootstrap.cpp
|
||||
|
@ -116,7 +146,7 @@ add_library(liblokinet
|
|||
dht/taglookup.cpp
|
||||
|
||||
endpoint_base.cpp
|
||||
|
||||
|
||||
exit/context.cpp
|
||||
exit/endpoint.cpp
|
||||
exit/exit_messages.cpp
|
||||
|
@ -151,7 +181,7 @@ add_library(liblokinet
|
|||
peerstats/types.cpp
|
||||
pow.cpp
|
||||
profiling.cpp
|
||||
|
||||
|
||||
quic/address.cpp
|
||||
quic/client.cpp
|
||||
quic/connection.cpp
|
||||
|
@ -171,7 +201,7 @@ add_library(liblokinet
|
|||
router/rc_gossiper.cpp
|
||||
router/router.cpp
|
||||
router/route_poker.cpp
|
||||
router/systemd_resolved.cpp
|
||||
|
||||
routing/dht_message.cpp
|
||||
routing/message_parser.cpp
|
||||
routing/path_confirm_message.cpp
|
||||
|
@ -205,42 +235,42 @@ add_library(liblokinet
|
|||
service/tag.cpp
|
||||
)
|
||||
|
||||
|
||||
if(WITH_PEERSTATS_BACKEND)
|
||||
target_compile_definitions(liblokinet PRIVATE -DLOKINET_PEERSTATS_BACKEND)
|
||||
target_link_libraries(liblokinet PUBLIC sqlite_orm)
|
||||
endif()
|
||||
|
||||
set_target_properties(liblokinet PROPERTIES OUTPUT_NAME lokinet)
|
||||
|
||||
enable_lto(lokinet-util lokinet-platform liblokinet)
|
||||
|
||||
if(TRACY_ROOT)
|
||||
target_sources(liblokinet PRIVATE ${TRACY_ROOT}/TracyClient.cpp)
|
||||
target_compile_definitions(lokinet-amalgum PRIVATE -DLOKINET_PEERSTATS_BACKEND)
|
||||
target_link_libraries(lokinet-amalgum PUBLIC sqlite_orm)
|
||||
endif()
|
||||
|
||||
if(WITH_HIVE)
|
||||
target_sources(liblokinet PRIVATE
|
||||
target_sources(lokinet-amalgum PRIVATE
|
||||
tooling/router_hive.cpp
|
||||
tooling/hive_router.cpp
|
||||
tooling/hive_context.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
target_link_libraries(liblokinet PUBLIC
|
||||
# TODO: make libunbound hidden behind a feature flag like sqlite for embedded lokinet
|
||||
target_link_libraries(lokinet-amalgum PRIVATE libunbound)
|
||||
|
||||
target_link_libraries(lokinet-amalgum PUBLIC
|
||||
cxxopts
|
||||
oxenc::oxenc
|
||||
lokinet-platform
|
||||
lokinet-config
|
||||
lokinet-dns
|
||||
lokinet-util
|
||||
lokinet-cryptography
|
||||
ngtcp2_static
|
||||
oxenmq::oxenmq)
|
||||
target_link_libraries(liblokinet PRIVATE libunbound)
|
||||
|
||||
enable_lto(lokinet-util lokinet-platform lokinet-dns lokinet-config lokinet-amalgum)
|
||||
|
||||
pkg_check_modules(CRYPT libcrypt IMPORTED_TARGET)
|
||||
if(CRYPT_FOUND AND NOT CMAKE_CROSSCOMPILING)
|
||||
add_definitions(-DHAVE_CRYPT)
|
||||
add_library(libcrypt INTERFACE)
|
||||
target_link_libraries(libcrypt INTERFACE PkgConfig::CRYPT)
|
||||
target_link_libraries(liblokinet PRIVATE libcrypt)
|
||||
target_link_libraries(lokinet-amalgum PRIVATE libcrypt)
|
||||
message(STATUS "using libcrypt ${CRYPT_VERSION}")
|
||||
endif()
|
||||
|
||||
|
@ -248,7 +278,7 @@ endif()
|
|||
if(BUILD_LIBLOKINET)
|
||||
include(GNUInstallDirs)
|
||||
add_library(lokinet-shared SHARED lokinet_shared.cpp)
|
||||
target_link_libraries(lokinet-shared PUBLIC liblokinet)
|
||||
target_link_libraries(lokinet-shared PUBLIC lokinet-amalgum)
|
||||
if(WIN32)
|
||||
set(CMAKE_SHARED_LIBRARY_PREFIX_CXX "")
|
||||
endif()
|
||||
|
|
|
@ -18,12 +18,9 @@ target_sources(lokinet-platform PRIVATE vpn_platform.cpp vpn_interface.cpp route
|
|||
add_executable(lokinet-extension MACOSX_BUNDLE
|
||||
PacketTunnelProvider.m
|
||||
DNSTrampoline.m
|
||||
)
|
||||
)
|
||||
|
||||
enable_lto(lokinet-extension)
|
||||
target_link_libraries(lokinet-extension PRIVATE
|
||||
liblokinet
|
||||
${COREFOUNDATION}
|
||||
${NETEXT})
|
||||
|
||||
# -fobjc-arc enables automatic reference counting for objective-C code
|
||||
# -e _NSExtensionMain because the appex has that instead of a `main` function entry point, of course.
|
||||
|
@ -43,6 +40,11 @@ else()
|
|||
set(product_type com.apple.product-type.app-extension)
|
||||
endif()
|
||||
|
||||
target_link_libraries(lokinet-extension PRIVATE
|
||||
lokinet-amalgum
|
||||
${COREFOUNDATION}
|
||||
${NETEXT})
|
||||
|
||||
set_target_properties(lokinet-extension PROPERTIES
|
||||
BUNDLE TRUE
|
||||
BUNDLE_EXTENSION ${bundle_ext}
|
||||
|
|
|
@ -3,23 +3,32 @@
|
|||
|
||||
NSString* error_domain = @"org.lokinet";
|
||||
|
||||
|
||||
// Receiving an incoming packet, presumably from libunbound. NB: this is called from the libuv
|
||||
// event loop.
|
||||
static void on_request(uv_udp_t* socket, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) {
|
||||
if (nread < 0) {
|
||||
static void
|
||||
on_request(
|
||||
uv_udp_t* socket,
|
||||
ssize_t nread,
|
||||
const uv_buf_t* buf,
|
||||
const struct sockaddr* addr,
|
||||
unsigned flags)
|
||||
{
|
||||
(void)flags;
|
||||
if (nread < 0)
|
||||
{
|
||||
NSLog(@"Read error: %s", uv_strerror(nread));
|
||||
free(buf->base);
|
||||
return;
|
||||
}
|
||||
|
||||
if (nread == 0 || !addr) {
|
||||
if (nread == 0 || !addr)
|
||||
{
|
||||
if (buf)
|
||||
free(buf->base);
|
||||
return;
|
||||
}
|
||||
|
||||
LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*) socket->data;
|
||||
LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)socket->data;
|
||||
|
||||
// We configure libunbound to use just one single port so we'll just send replies to the last port
|
||||
// to talk to us. (And we're only listening on localhost in the first place).
|
||||
|
@ -31,61 +40,70 @@ static void on_request(uv_udp_t* socket, ssize_t nread, const uv_buf_t* buf, con
|
|||
[t flushWrites];
|
||||
}
|
||||
|
||||
static void on_sent(uv_udp_send_t* req, int status) {
|
||||
NSArray<NSData*>* datagrams = (__bridge_transfer NSArray<NSData*>*) req->data;
|
||||
static void
|
||||
on_sent(uv_udp_send_t* req, int status)
|
||||
{
|
||||
(void)status;
|
||||
NSArray<NSData*>* datagrams = (__bridge_transfer NSArray<NSData*>*)req->data;
|
||||
(void)datagrams;
|
||||
free(req);
|
||||
}
|
||||
|
||||
// NB: called from the libuv event loop (so we don't have to worry about the above and this one
|
||||
// running at once from different threads).
|
||||
static void write_flusher(uv_async_t* async) {
|
||||
LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*) async->data;
|
||||
static void
|
||||
write_flusher(uv_async_t* async)
|
||||
{
|
||||
LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)async->data;
|
||||
if (t->pending_writes.count == 0)
|
||||
return;
|
||||
|
||||
NSArray<NSData*>* data = [NSArray<NSData*> arrayWithArray:t->pending_writes];
|
||||
[t->pending_writes removeAllObjects];
|
||||
__weak LLARPDNSTrampoline* weakSelf = t;
|
||||
[t->upstream writeMultipleDatagrams:data completionHandler: ^(NSError* error)
|
||||
{
|
||||
if (error)
|
||||
NSLog(@"Failed to send request to upstream DNS: %@", error);
|
||||
|
||||
// Trigger another flush in case anything built up while Apple was doing its things. Just
|
||||
// call it unconditionally (rather than checking the queue) because this handler is probably
|
||||
// running in some other thread.
|
||||
[weakSelf flushWrites];
|
||||
}
|
||||
];
|
||||
[t->upstream writeMultipleDatagrams:data
|
||||
completionHandler:^(NSError* error) {
|
||||
if (error)
|
||||
NSLog(@"Failed to send request to upstream DNS: %@", error);
|
||||
|
||||
// Trigger another flush in case anything built up while Apple was doing its
|
||||
// things. Just call it unconditionally (rather than checking the queue)
|
||||
// because this handler is probably running in some other thread.
|
||||
[weakSelf flushWrites];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) {
|
||||
static void
|
||||
alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf)
|
||||
{
|
||||
(void)handle;
|
||||
buf->base = malloc(suggested_size);
|
||||
buf->len = suggested_size;
|
||||
}
|
||||
|
||||
@implementation LLARPDNSTrampoline
|
||||
|
||||
- (void)startWithUpstreamDns:(NWUDPSession*) dns
|
||||
listenIp:(NSString*) listenIp
|
||||
listenPort:(uint16_t) listenPort
|
||||
uvLoop:(uv_loop_t*) loop
|
||||
- (void)startWithUpstreamDns:(NWUDPSession*)dns
|
||||
listenIp:(NSString*)listenIp
|
||||
listenPort:(uint16_t)listenPort
|
||||
uvLoop:(uv_loop_t*)loop
|
||||
completionHandler:(void (^)(NSError* error))completionHandler
|
||||
{
|
||||
NSLog(@"Setting up trampoline");
|
||||
pending_writes = [[NSMutableArray<NSData*> alloc] init];
|
||||
write_trigger.data = (__bridge void*) self;
|
||||
write_trigger.data = (__bridge void*)self;
|
||||
uv_async_init(loop, &write_trigger, write_flusher);
|
||||
|
||||
request_socket.data = (__bridge void*) self;
|
||||
request_socket.data = (__bridge void*)self;
|
||||
uv_udp_init(loop, &request_socket);
|
||||
struct sockaddr_in recv_addr;
|
||||
uv_ip4_addr(listenIp.UTF8String, listenPort, &recv_addr);
|
||||
int ret = uv_udp_bind(&request_socket, (const struct sockaddr*) &recv_addr, UV_UDP_REUSEADDR);
|
||||
if (ret < 0) {
|
||||
NSString* errstr = [NSString stringWithFormat:@"Failed to start DNS trampoline: %s", uv_strerror(ret)];
|
||||
NSError *err = [NSError errorWithDomain:error_domain code:ret userInfo:@{@"Error": errstr}];
|
||||
int ret = uv_udp_bind(&request_socket, (const struct sockaddr*)&recv_addr, UV_UDP_REUSEADDR);
|
||||
if (ret < 0)
|
||||
{
|
||||
NSString* errstr =
|
||||
[NSString stringWithFormat:@"Failed to start DNS trampoline: %s", uv_strerror(ret)];
|
||||
NSError* err = [NSError errorWithDomain:error_domain code:ret userInfo:@{@"Error": errstr}];
|
||||
NSLog(@"%@", err);
|
||||
return completionHandler(err);
|
||||
}
|
||||
|
@ -95,30 +113,40 @@ static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* b
|
|||
|
||||
upstream = dns;
|
||||
__weak LLARPDNSTrampoline* weakSelf = self;
|
||||
[upstream setReadHandler: ^(NSArray<NSData*>* datagrams, NSError* error) {
|
||||
// Reading a reply back from the UDP socket used to talk to upstream
|
||||
if (error) {
|
||||
NSLog(@"Reader handler failed: %@", error);
|
||||
return;
|
||||
}
|
||||
LLARPDNSTrampoline* strongSelf = weakSelf;
|
||||
if (!strongSelf || datagrams.count == 0)
|
||||
return;
|
||||
[upstream
|
||||
setReadHandler:^(NSArray<NSData*>* datagrams, NSError* error) {
|
||||
// Reading a reply back from the UDP socket used to talk to upstream
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"Reader handler failed: %@", error);
|
||||
return;
|
||||
}
|
||||
LLARPDNSTrampoline* strongSelf = weakSelf;
|
||||
if (!strongSelf || datagrams.count == 0)
|
||||
return;
|
||||
|
||||
uv_buf_t* buffers = malloc(datagrams.count * sizeof(uv_buf_t));
|
||||
size_t buf_count = 0;
|
||||
for (NSData* packet in datagrams) {
|
||||
buffers[buf_count].base = (void*) packet.bytes;
|
||||
buffers[buf_count].len = packet.length;
|
||||
buf_count++;
|
||||
}
|
||||
uv_udp_send_t* uvsend = malloc(sizeof(uv_udp_send_t));
|
||||
uvsend->data = (__bridge_retained void*) datagrams;
|
||||
int ret = uv_udp_send(uvsend, &strongSelf->request_socket, buffers, buf_count, &strongSelf->reply_addr, on_sent);
|
||||
free(buffers);
|
||||
if (ret < 0)
|
||||
NSLog(@"Error returning DNS responses to unbound: %s", uv_strerror(ret));
|
||||
} maxDatagrams:NSUIntegerMax];
|
||||
uv_buf_t* buffers = malloc(datagrams.count * sizeof(uv_buf_t));
|
||||
size_t buf_count = 0;
|
||||
for (NSData* packet in datagrams)
|
||||
{
|
||||
buffers[buf_count].base = (void*)packet.bytes;
|
||||
buffers[buf_count].len = packet.length;
|
||||
buf_count++;
|
||||
}
|
||||
uv_udp_send_t* uvsend = malloc(sizeof(uv_udp_send_t));
|
||||
uvsend->data = (__bridge_retained void*)datagrams;
|
||||
int ret = uv_udp_send(
|
||||
uvsend,
|
||||
&strongSelf->request_socket,
|
||||
buffers,
|
||||
buf_count,
|
||||
&strongSelf->reply_addr,
|
||||
on_sent);
|
||||
free(buffers);
|
||||
if (ret < 0)
|
||||
NSLog(@"Error returning DNS responses to unbound: %s", uv_strerror(ret));
|
||||
}
|
||||
maxDatagrams:NSUIntegerMax];
|
||||
|
||||
completionHandler(nil);
|
||||
}
|
||||
|
@ -128,11 +156,11 @@ static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* b
|
|||
uv_async_send(&write_trigger);
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
- (void)dealloc
|
||||
{
|
||||
NSLog(@"Stopping DNS trampoline");
|
||||
uv_close((uv_handle_t*) &request_socket, NULL);
|
||||
uv_close((uv_handle_t*) &write_trigger, NULL);
|
||||
uv_close((uv_handle_t*)&request_socket, NULL);
|
||||
uv_close((uv_handle_t*)&write_trigger, NULL);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -9,9 +9,12 @@
|
|||
{
|
||||
void* lokinet;
|
||||
llarp_incoming_packet packet_buf[LLARP_APPLE_PACKET_BUF_SIZE];
|
||||
@public NEPacketTunnelNetworkSettings* settings;
|
||||
@public NEIPv4Route* tun_route4;
|
||||
@public NEIPv6Route* tun_route6;
|
||||
@public
|
||||
NEPacketTunnelNetworkSettings* settings;
|
||||
@public
|
||||
NEIPv4Route* tun_route4;
|
||||
@public
|
||||
NEIPv6Route* tun_route6;
|
||||
LLARPDNSTrampoline* dns_tramp;
|
||||
}
|
||||
|
||||
|
@ -30,107 +33,133 @@
|
|||
|
||||
@end
|
||||
|
||||
static void nslogger(const char* msg) { NSLog(@"%s", msg); }
|
||||
static void
|
||||
nslogger(const char* msg)
|
||||
{
|
||||
NSLog(@"%s", msg);
|
||||
}
|
||||
|
||||
static void packet_writer(int af, const void* data, size_t size, void* ctx) {
|
||||
static void
|
||||
packet_writer(int af, const void* data, size_t size, void* ctx)
|
||||
{
|
||||
if (ctx == nil || data == nil)
|
||||
return;
|
||||
|
||||
NSData* buf = [NSData dataWithBytesNoCopy:(void*)data length:size freeWhenDone:NO];
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
[t.packetFlow writePackets:@[buf]
|
||||
withProtocols:@[[NSNumber numberWithInt:af]]];
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
[t.packetFlow writePackets:@[buf] withProtocols:@[[NSNumber numberWithInt:af]]];
|
||||
}
|
||||
|
||||
static void start_packet_reader(void* ctx) {
|
||||
static void
|
||||
start_packet_reader(void* ctx)
|
||||
{
|
||||
if (ctx == nil)
|
||||
return;
|
||||
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
[t readPackets];
|
||||
}
|
||||
|
||||
static void add_ipv4_route(const char* addr, const char* netmask, void* ctx) {
|
||||
static void
|
||||
add_ipv4_route(const char* addr, const char* netmask, void* ctx)
|
||||
{
|
||||
NSLog(@"Adding IPv4 route %s:%s to packet tunnel", addr, netmask);
|
||||
NEIPv4Route* route = [[NEIPv4Route alloc]
|
||||
initWithDestinationAddress: [NSString stringWithUTF8String:addr]
|
||||
subnetMask: [NSString stringWithUTF8String:netmask]];
|
||||
NEIPv4Route* route =
|
||||
[[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr]
|
||||
subnetMask:[NSString stringWithUTF8String:netmask]];
|
||||
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
for (NEIPv4Route* r in t->settings.IPv4Settings.includedRoutes)
|
||||
if ([r.destinationAddress isEqualToString:route.destinationAddress] &&
|
||||
[r.destinationSubnetMask isEqualToString:route.destinationSubnetMask])
|
||||
return; // Already in the settings, nothing to add.
|
||||
[r.destinationSubnetMask isEqualToString:route.destinationSubnetMask])
|
||||
return; // Already in the settings, nothing to add.
|
||||
|
||||
t->settings.IPv4Settings.includedRoutes =
|
||||
[t->settings.IPv4Settings.includedRoutes arrayByAddingObject:route];
|
||||
[t->settings.IPv4Settings.includedRoutes arrayByAddingObject:route];
|
||||
|
||||
[t updateNetworkSettings];
|
||||
}
|
||||
|
||||
static void del_ipv4_route(const char* addr, const char* netmask, void* ctx) {
|
||||
static void
|
||||
del_ipv4_route(const char* addr, const char* netmask, void* ctx)
|
||||
{
|
||||
NSLog(@"Removing IPv4 route %s:%s to packet tunnel", addr, netmask);
|
||||
NEIPv4Route* route = [[NEIPv4Route alloc]
|
||||
initWithDestinationAddress: [NSString stringWithUTF8String:addr]
|
||||
subnetMask: [NSString stringWithUTF8String:netmask]];
|
||||
NEIPv4Route* route =
|
||||
[[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr]
|
||||
subnetMask:[NSString stringWithUTF8String:netmask]];
|
||||
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
NSMutableArray<NEIPv4Route*>* routes = [NSMutableArray arrayWithArray:t->settings.IPv4Settings.includedRoutes];
|
||||
for (int i = 0; i < routes.count; i++) {
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
NSMutableArray<NEIPv4Route*>* routes =
|
||||
[NSMutableArray arrayWithArray:t->settings.IPv4Settings.includedRoutes];
|
||||
for (size_t i = 0; i < routes.count; i++)
|
||||
{
|
||||
if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] &&
|
||||
[routes[i].destinationSubnetMask isEqualToString:route.destinationSubnetMask]) {
|
||||
[routes[i].destinationSubnetMask isEqualToString:route.destinationSubnetMask])
|
||||
{
|
||||
[routes removeObjectAtIndex:i];
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (routes.count != t->settings.IPv4Settings.includedRoutes.count) {
|
||||
if (routes.count != t->settings.IPv4Settings.includedRoutes.count)
|
||||
{
|
||||
t->settings.IPv4Settings.includedRoutes = routes;
|
||||
[t updateNetworkSettings];
|
||||
}
|
||||
}
|
||||
|
||||
static void add_ipv6_route(const char* addr, int prefix, void* ctx) {
|
||||
NEIPv6Route* route = [[NEIPv6Route alloc]
|
||||
initWithDestinationAddress: [NSString stringWithUTF8String:addr]
|
||||
networkPrefixLength: [NSNumber numberWithInt:prefix]];
|
||||
static void
|
||||
add_ipv6_route(const char* addr, int prefix, void* ctx)
|
||||
{
|
||||
NEIPv6Route* route =
|
||||
[[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr]
|
||||
networkPrefixLength:[NSNumber numberWithInt:prefix]];
|
||||
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
for (NEIPv6Route* r in t->settings.IPv6Settings.includedRoutes)
|
||||
if ([r.destinationAddress isEqualToString:route.destinationAddress] &&
|
||||
[r.destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength])
|
||||
return; // Already in the settings, nothing to add.
|
||||
[r.destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength])
|
||||
return; // Already in the settings, nothing to add.
|
||||
|
||||
t->settings.IPv6Settings.includedRoutes =
|
||||
[t->settings.IPv6Settings.includedRoutes arrayByAddingObject:route];
|
||||
[t->settings.IPv6Settings.includedRoutes arrayByAddingObject:route];
|
||||
|
||||
[t updateNetworkSettings];
|
||||
}
|
||||
|
||||
static void del_ipv6_route(const char* addr, int prefix, void* ctx) {
|
||||
NEIPv6Route* route = [[NEIPv6Route alloc]
|
||||
initWithDestinationAddress: [NSString stringWithUTF8String:addr]
|
||||
networkPrefixLength: [NSNumber numberWithInt:prefix]];
|
||||
static void
|
||||
del_ipv6_route(const char* addr, int prefix, void* ctx)
|
||||
{
|
||||
NEIPv6Route* route =
|
||||
[[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr]
|
||||
networkPrefixLength:[NSNumber numberWithInt:prefix]];
|
||||
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
NSMutableArray<NEIPv6Route*>* routes = [NSMutableArray arrayWithArray:t->settings.IPv6Settings.includedRoutes];
|
||||
for (int i = 0; i < routes.count; i++) {
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
NSMutableArray<NEIPv6Route*>* routes =
|
||||
[NSMutableArray arrayWithArray:t->settings.IPv6Settings.includedRoutes];
|
||||
for (size_t i = 0; i < routes.count; i++)
|
||||
{
|
||||
if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] &&
|
||||
[routes[i].destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) {
|
||||
[routes[i].destinationNetworkPrefixLength
|
||||
isEqualToNumber:route.destinationNetworkPrefixLength])
|
||||
{
|
||||
[routes removeObjectAtIndex:i];
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
if (routes.count != t->settings.IPv6Settings.includedRoutes.count) {
|
||||
if (routes.count != t->settings.IPv6Settings.includedRoutes.count)
|
||||
{
|
||||
t->settings.IPv6Settings.includedRoutes = routes;
|
||||
[t updateNetworkSettings];
|
||||
}
|
||||
}
|
||||
|
||||
static void add_default_route(void* ctx) {
|
||||
static void
|
||||
add_default_route(void* ctx)
|
||||
{
|
||||
NSLog(@"Making the tunnel the default route");
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
|
||||
t->settings.IPv4Settings.includedRoutes = @[NEIPv4Route.defaultRoute];
|
||||
t->settings.IPv6Settings.includedRoutes = @[NEIPv6Route.defaultRoute];
|
||||
|
@ -138,9 +167,11 @@ static void add_default_route(void* ctx) {
|
|||
[t updateNetworkSettings];
|
||||
}
|
||||
|
||||
static void del_default_route(void* ctx) {
|
||||
static void
|
||||
del_default_route(void* ctx)
|
||||
{
|
||||
NSLog(@"Removing default route from tunnel");
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*) ctx;
|
||||
LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx;
|
||||
|
||||
t->settings.IPv4Settings.includedRoutes = @[t->tun_route4];
|
||||
t->settings.IPv6Settings.includedRoutes = @[t->tun_route6];
|
||||
|
@ -152,12 +183,13 @@ static void del_default_route(void* ctx) {
|
|||
|
||||
- (void)readPackets
|
||||
{
|
||||
[self.packetFlow readPacketObjectsWithCompletionHandler: ^(NSArray<NEPacket*>* packets) {
|
||||
[self.packetFlow readPacketObjectsWithCompletionHandler:^(NSArray<NEPacket*>* packets) {
|
||||
if (lokinet == nil)
|
||||
return;
|
||||
|
||||
size_t size = 0;
|
||||
for (NEPacket* p in packets) {
|
||||
for (NEPacket* p in packets)
|
||||
{
|
||||
packet_buf[size].bytes = p.data.bytes;
|
||||
packet_buf[size].size = p.data.length;
|
||||
size++;
|
||||
|
@ -186,19 +218,21 @@ static void del_default_route(void* ctx) {
|
|||
.ns_logger = nslogger,
|
||||
.packet_writer = packet_writer,
|
||||
.start_reading = start_packet_reader,
|
||||
.route_callbacks = {
|
||||
.add_ipv4_route = add_ipv4_route,
|
||||
.del_ipv4_route = del_ipv4_route,
|
||||
.add_ipv6_route = add_ipv6_route,
|
||||
.del_ipv6_route = del_ipv6_route,
|
||||
.add_default_route = add_default_route,
|
||||
.del_default_route = del_default_route
|
||||
},
|
||||
.route_callbacks =
|
||||
{.add_ipv4_route = add_ipv4_route,
|
||||
.del_ipv4_route = del_ipv4_route,
|
||||
.add_ipv6_route = add_ipv6_route,
|
||||
.del_ipv6_route = del_ipv6_route,
|
||||
.add_default_route = add_default_route,
|
||||
.del_default_route = del_default_route},
|
||||
};
|
||||
|
||||
lokinet = llarp_apple_init(&conf);
|
||||
if (!lokinet) {
|
||||
NSError *init_failure = [NSError errorWithDomain:error_domain code:500 userInfo:@{@"Error": @"Failed to initialize lokinet"}];
|
||||
if (!lokinet)
|
||||
{
|
||||
NSError* init_failure = [NSError errorWithDomain:error_domain
|
||||
code:500
|
||||
userInfo:@{@"Error": @"Failed to initialize lokinet"}];
|
||||
NSLog(@"%@", [init_failure localizedDescription]);
|
||||
return completionHandler(init_failure);
|
||||
}
|
||||
|
@ -237,11 +271,12 @@ static void del_default_route(void* ctx) {
|
|||
|
||||
NWHostEndpoint* upstreamdns_ep;
|
||||
if (strlen(conf.upstream_dns))
|
||||
upstreamdns_ep = [NWHostEndpoint endpointWithHostname:[NSString stringWithUTF8String:conf.upstream_dns] port:@(conf.upstream_dns_port).stringValue];
|
||||
upstreamdns_ep =
|
||||
[NWHostEndpoint endpointWithHostname:[NSString stringWithUTF8String:conf.upstream_dns]
|
||||
port:@(conf.upstream_dns_port).stringValue];
|
||||
|
||||
NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[ip]
|
||||
subnetMasks:@[mask]];
|
||||
tun_route4 = [[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask: mask];
|
||||
NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[ip] subnetMasks:@[mask]];
|
||||
tun_route4 = [[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask:mask];
|
||||
ipv4.includedRoutes = @[tun_route4];
|
||||
settings.IPv4Settings = ipv4;
|
||||
|
||||
|
@ -249,50 +284,62 @@ static void del_default_route(void* ctx) {
|
|||
NSNumber* ip6_prefix = [NSNumber numberWithUnsignedInt:conf.tunnel_ipv6_prefix];
|
||||
NEIPv6Settings* ipv6 = [[NEIPv6Settings alloc] initWithAddresses:@[ip6]
|
||||
networkPrefixLengths:@[ip6_prefix]];
|
||||
tun_route6 = [[NEIPv6Route alloc] initWithDestinationAddress:ip6
|
||||
networkPrefixLength:ip6_prefix];
|
||||
tun_route6 = [[NEIPv6Route alloc] initWithDestinationAddress:ip6 networkPrefixLength:ip6_prefix];
|
||||
ipv6.includedRoutes = @[tun_route6];
|
||||
settings.IPv6Settings = ipv6;
|
||||
|
||||
__weak LLARPPacketTunnel* weakSelf = self;
|
||||
[self setTunnelNetworkSettings:settings completionHandler:^(NSError* err) {
|
||||
if (err) {
|
||||
NSLog(@"Failed to configure lokinet tunnel: %@", err);
|
||||
return completionHandler(err);
|
||||
}
|
||||
LLARPPacketTunnel* strongSelf = weakSelf;
|
||||
if (!strongSelf)
|
||||
return completionHandler(nil);
|
||||
[self setTunnelNetworkSettings:settings
|
||||
completionHandler:^(NSError* err) {
|
||||
if (err)
|
||||
{
|
||||
NSLog(@"Failed to configure lokinet tunnel: %@", err);
|
||||
return completionHandler(err);
|
||||
}
|
||||
LLARPPacketTunnel* strongSelf = weakSelf;
|
||||
if (!strongSelf)
|
||||
return completionHandler(nil);
|
||||
|
||||
int start_ret = llarp_apple_start(strongSelf->lokinet, (__bridge void*) strongSelf);
|
||||
if (start_ret != 0) {
|
||||
NSError *start_failure = [NSError errorWithDomain:error_domain code:start_ret userInfo:@{@"Error": @"Failed to start lokinet"}];
|
||||
NSLog(@"%@", start_failure);
|
||||
lokinet = nil;
|
||||
return completionHandler(start_failure);
|
||||
}
|
||||
int start_ret = llarp_apple_start(strongSelf->lokinet, (__bridge void*)strongSelf);
|
||||
if (start_ret != 0)
|
||||
{
|
||||
NSError* start_failure =
|
||||
[NSError errorWithDomain:error_domain
|
||||
code:start_ret
|
||||
userInfo:@{@"Error": @"Failed to start lokinet"}];
|
||||
NSLog(@"%@", start_failure);
|
||||
lokinet = nil;
|
||||
return completionHandler(start_failure);
|
||||
}
|
||||
|
||||
NSString* dns_tramp_ip = @"127.0.0.1";
|
||||
NSLog(@"Starting DNS exit mode trampoline to %@ on %@:%d", upstreamdns_ep, dns_tramp_ip, dns_trampoline_port);
|
||||
NWUDPSession* upstreamdns = [strongSelf createUDPSessionThroughTunnelToEndpoint:upstreamdns_ep fromEndpoint:nil];
|
||||
strongSelf->dns_tramp = [LLARPDNSTrampoline alloc];
|
||||
[strongSelf->dns_tramp
|
||||
startWithUpstreamDns:upstreamdns
|
||||
listenIp:dns_tramp_ip
|
||||
listenPort:dns_trampoline_port
|
||||
uvLoop:llarp_apple_get_uv_loop(strongSelf->lokinet)
|
||||
completionHandler:^(NSError* error) {
|
||||
if (error)
|
||||
NSLog(@"Error starting dns trampoline: %@", error);
|
||||
return completionHandler(error);
|
||||
}];
|
||||
}];
|
||||
NSString* dns_tramp_ip = @"127.0.0.1";
|
||||
NSLog(
|
||||
@"Starting DNS exit mode trampoline to %@ on %@:%d",
|
||||
upstreamdns_ep,
|
||||
dns_tramp_ip,
|
||||
dns_trampoline_port);
|
||||
NWUDPSession* upstreamdns =
|
||||
[strongSelf createUDPSessionThroughTunnelToEndpoint:upstreamdns_ep
|
||||
fromEndpoint:nil];
|
||||
strongSelf->dns_tramp = [LLARPDNSTrampoline alloc];
|
||||
[strongSelf->dns_tramp
|
||||
startWithUpstreamDns:upstreamdns
|
||||
listenIp:dns_tramp_ip
|
||||
listenPort:dns_trampoline_port
|
||||
uvLoop:llarp_apple_get_uv_loop(strongSelf->lokinet)
|
||||
completionHandler:^(NSError* error) {
|
||||
if (error)
|
||||
NSLog(@"Error starting dns trampoline: %@", error);
|
||||
return completionHandler(error);
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)stopTunnelWithReason:(NEProviderStopReason)reason
|
||||
completionHandler:(void (^)(void))completionHandler
|
||||
{
|
||||
if (lokinet) {
|
||||
if (lokinet)
|
||||
{
|
||||
llarp_apple_shutdown(lokinet);
|
||||
lokinet = nil;
|
||||
}
|
||||
|
@ -319,29 +366,35 @@ static void del_default_route(void* ctx) {
|
|||
//
|
||||
// Thanks for the accurate documentation, Apple.
|
||||
//
|
||||
[self setTunnelNetworkSettings:nil completionHandler:^(NSError* err) {
|
||||
if (err)
|
||||
NSLog(@"Failed to clear lokinet tunnel settings: %@", err);
|
||||
LLARPPacketTunnel* strongSelf = weakSelf;
|
||||
if (strongSelf) {
|
||||
[weakSelf setTunnelNetworkSettings:strongSelf->settings completionHandler:^(NSError* err) {
|
||||
LLARPPacketTunnel* strongSelf = weakSelf;
|
||||
if (strongSelf)
|
||||
strongSelf.reasserting = NO;
|
||||
if (err)
|
||||
NSLog(@"Failed to reconfigure lokinet tunnel settings: %@", err);
|
||||
}];
|
||||
}
|
||||
}];
|
||||
[self setTunnelNetworkSettings:nil
|
||||
completionHandler:^(NSError* err) {
|
||||
if (err)
|
||||
NSLog(@"Failed to clear lokinet tunnel settings: %@", err);
|
||||
LLARPPacketTunnel* strongSelf = weakSelf;
|
||||
if (strongSelf)
|
||||
{
|
||||
[weakSelf
|
||||
setTunnelNetworkSettings:strongSelf->settings
|
||||
completionHandler:^(NSError* err) {
|
||||
LLARPPacketTunnel* strongSelf = weakSelf;
|
||||
if (strongSelf)
|
||||
strongSelf.reasserting = NO;
|
||||
if (err)
|
||||
NSLog(@"Failed to reconfigure lokinet tunnel settings: %@", err);
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#ifdef MACOS_SYSTEM_EXTENSION
|
||||
|
||||
int main() {
|
||||
[NEProvider startSystemExtensionMode];
|
||||
dispatch_main();
|
||||
int
|
||||
main()
|
||||
{
|
||||
[NEProvider startSystemExtensionMode];
|
||||
dispatch_main();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -90,7 +90,7 @@ llarp_apple_init(llarp_apple_config* appleconf)
|
|||
#ifdef MACOS_SYSTEM_EXTENSION
|
||||
std::strncpy(
|
||||
appleconf->dns_bind_ip,
|
||||
config->dns.m_bind.hostString().c_str(),
|
||||
config->dns.m_bind.front().hostString().c_str(),
|
||||
sizeof(appleconf->dns_bind_ip));
|
||||
#endif
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace llarp::apple
|
|||
}
|
||||
|
||||
std::shared_ptr<llarp::handlers::TunEndpoint> tun;
|
||||
router->hiddenServiceContext().ForEachService([&tun](const auto& name, const auto ep) {
|
||||
router->hiddenServiceContext().ForEachService([&tun](const auto& /*name*/, const auto ep) {
|
||||
tun = std::dynamic_pointer_cast<llarp::handlers::TunEndpoint>(ep);
|
||||
return !tun;
|
||||
});
|
||||
|
@ -31,21 +31,23 @@ namespace llarp::apple
|
|||
}
|
||||
|
||||
if (enable)
|
||||
saved_upstream_dns =
|
||||
tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, huint16_t{dns_trampoline_port}}});
|
||||
tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, {dns_trampoline_port}}});
|
||||
else
|
||||
tun->ReconfigureDNS(std::move(saved_upstream_dns));
|
||||
tun->ReconfigureDNS(router->GetConfig()->dns.m_upstreamDNS);
|
||||
|
||||
trampoline_active = enable;
|
||||
}
|
||||
|
||||
void RouteManager::AddDefaultRouteViaInterface(std::string)
|
||||
void
|
||||
RouteManager::AddDefaultRouteViaInterface(vpn::NetworkInterface&)
|
||||
{
|
||||
check_trampoline(true);
|
||||
if (callback_context and route_callbacks.add_default_route)
|
||||
route_callbacks.add_default_route(callback_context);
|
||||
}
|
||||
|
||||
void RouteManager::DelDefaultRouteViaInterface(std::string)
|
||||
void
|
||||
RouteManager::DelDefaultRouteViaInterface(vpn::NetworkInterface&)
|
||||
{
|
||||
check_trampoline(false);
|
||||
if (callback_context and route_callbacks.del_default_route)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include "context_wrapper.h"
|
||||
|
||||
namespace llarp::apple
|
||||
|
@ -10,24 +10,22 @@ namespace llarp::apple
|
|||
{
|
||||
public:
|
||||
RouteManager(llarp::Context& ctx, llarp_route_callbacks rcs, void* callback_context)
|
||||
: context{ctx}, route_callbacks{std::move(rcs)}, callback_context{callback_context}
|
||||
: context{ctx}, callback_context{callback_context}, route_callbacks{std::move(rcs)}
|
||||
{}
|
||||
|
||||
/// These are called for poking route holes, but we don't have to do that at all on macos
|
||||
/// because the appex isn't subject to its own rules.
|
||||
void
|
||||
AddRoute(IPVariant_t ip, IPVariant_t gateway) override
|
||||
void AddRoute(net::ipaddr_t /*ip*/, net::ipaddr_t /*gateway*/) override
|
||||
{}
|
||||
|
||||
void DelRoute(net::ipaddr_t /*ip*/, net::ipaddr_t /*gateway*/) override
|
||||
{}
|
||||
|
||||
void
|
||||
DelRoute(IPVariant_t ip, IPVariant_t gateway) override
|
||||
{}
|
||||
AddDefaultRouteViaInterface(vpn::NetworkInterface& vpn) override;
|
||||
|
||||
void
|
||||
AddDefaultRouteViaInterface(std::string ifname) override;
|
||||
|
||||
void
|
||||
DelDefaultRouteViaInterface(std::string ifname) override;
|
||||
DelDefaultRouteViaInterface(vpn::NetworkInterface& vpn) override;
|
||||
|
||||
void
|
||||
AddRouteViaInterface(vpn::NetworkInterface& vpn, IPRange range) override;
|
||||
|
@ -35,20 +33,19 @@ namespace llarp::apple
|
|||
void
|
||||
DelRouteViaInterface(vpn::NetworkInterface& vpn, IPRange range) override;
|
||||
|
||||
virtual std::vector<IPVariant_t>
|
||||
GetGatewaysNotOnInterface(std::string ifname) override
|
||||
std::vector<net::ipaddr_t>
|
||||
GetGatewaysNotOnInterface(vpn::NetworkInterface& /*vpn*/) override
|
||||
{
|
||||
// We can't get this on mac from our sandbox, but we don't actually need it because we
|
||||
// ignore the gateway for AddRoute/DelRoute anyway, so just return a zero IP.
|
||||
std::vector<IPVariant_t> ret;
|
||||
ret.push_back(huint32_t{0});
|
||||
std::vector<net::ipaddr_t> ret;
|
||||
ret.emplace_back(net::ipv4addr_t{});
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
llarp::Context& context;
|
||||
bool trampoline_active = false;
|
||||
std::vector<llarp::SockAddr> saved_upstream_dns;
|
||||
void
|
||||
check_trampoline(bool enable);
|
||||
|
||||
|
|
|
@ -10,7 +10,8 @@ namespace llarp::apple
|
|||
packet_write_callback packet_writer,
|
||||
on_readable_callback on_readable,
|
||||
AbstractRouter* router)
|
||||
: m_PacketWriter{std::move(packet_writer)}
|
||||
: vpn::NetworkInterface{{}}
|
||||
, m_PacketWriter{std::move(packet_writer)}
|
||||
, m_OnReadable{std::move(on_readable)}
|
||||
, _router{router}
|
||||
{
|
||||
|
@ -39,12 +40,6 @@ namespace llarp::apple
|
|||
return -1;
|
||||
}
|
||||
|
||||
std::string
|
||||
VPNInterface::IfName() const
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
net::IPPacket
|
||||
VPNInterface::ReadNextPacket()
|
||||
{
|
||||
|
@ -58,7 +53,7 @@ namespace llarp::apple
|
|||
VPNInterface::WritePacket(net::IPPacket pkt)
|
||||
{
|
||||
int af_family = pkt.IsV6() ? AF_INET6 : AF_INET;
|
||||
return m_PacketWriter(af_family, pkt.buf, pkt.sz);
|
||||
return m_PacketWriter(af_family, pkt.data(), pkt.size());
|
||||
}
|
||||
|
||||
} // namespace llarp::apple
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp.hpp>
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include <llarp/util/thread/queue.hpp>
|
||||
#include <memory>
|
||||
|
||||
|
@ -29,9 +29,6 @@ namespace llarp::apple
|
|||
int
|
||||
PollFD() const override;
|
||||
|
||||
std::string
|
||||
IfName() const override;
|
||||
|
||||
net::IPPacket
|
||||
ReadNextPacket() override;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
#include "vpn_interface.hpp"
|
||||
#include "route_manager.hpp"
|
||||
|
||||
|
|
|
@ -140,14 +140,14 @@ namespace llarp
|
|||
"this setting specifies the public IP at which this router is reachable. When",
|
||||
"provided the public-port option must also be specified.",
|
||||
},
|
||||
[this](std::string arg) {
|
||||
[this, net = params.Net_ptr()](std::string arg) {
|
||||
if (arg.empty())
|
||||
return;
|
||||
nuint32_t addr{};
|
||||
if (not addr.FromString(arg))
|
||||
throw std::invalid_argument{fmt::format("{} is not a valid IPv4 address", arg)};
|
||||
|
||||
if (IsIPv4Bogon(addr))
|
||||
if (net->IsBogonIP(addr))
|
||||
throw std::invalid_argument{
|
||||
fmt::format("{} is not a publicly routable ip address", addr)};
|
||||
|
||||
|
@ -648,6 +648,7 @@ namespace llarp
|
|||
throw std::invalid_argument{
|
||||
fmt::format("[network]:ip6-range invalid value: '{}'", arg)};
|
||||
});
|
||||
|
||||
// TODO: could be useful for snodes in the future, but currently only implemented for clients:
|
||||
conf.defineOption<std::string>(
|
||||
"network",
|
||||
|
@ -775,18 +776,26 @@ namespace llarp
|
|||
// Most non-linux platforms have loopback as 127.0.0.1/32, but linux uses 127.0.0.1/8 so that we
|
||||
// can bind to other 127.* IPs to avoid conflicting with something else that may be listening on
|
||||
// 127.0.0.1:53.
|
||||
constexpr Default DefaultDNSBind{platform::is_linux ? "127.3.2.1:53" : "127.0.0.1:53"};
|
||||
constexpr std::array DefaultDNSBind{
|
||||
#ifdef __linux__
|
||||
#ifdef WITH_SYSTEMD
|
||||
// when we have systemd support add a random high port on loopback as well
|
||||
// see https://github.com/oxen-io/lokinet/issues/1887#issuecomment-1091897282
|
||||
Default{"127.0.0.1:0"},
|
||||
#endif
|
||||
Default{"127.3.2.1:53"},
|
||||
#else
|
||||
Default{"127.0.0.1:53"},
|
||||
#endif
|
||||
};
|
||||
|
||||
// Default, but if we get any upstream (including upstream=, i.e. empty string) we clear it
|
||||
constexpr Default DefaultUpstreamDNS{"9.9.9.10"};
|
||||
constexpr Default DefaultUpstreamDNS{"9.9.9.10:53"};
|
||||
m_upstreamDNS.emplace_back(DefaultUpstreamDNS.val);
|
||||
if (!m_upstreamDNS.back().getPort())
|
||||
m_upstreamDNS.back().setPort(53);
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
"dns",
|
||||
"upstream",
|
||||
DefaultUpstreamDNS,
|
||||
MultiValue,
|
||||
Comment{
|
||||
"Upstream resolver(s) to use as fallback for non-loki addresses.",
|
||||
|
@ -798,25 +807,52 @@ namespace llarp
|
|||
m_upstreamDNS.clear();
|
||||
first = false;
|
||||
}
|
||||
if (!arg.empty())
|
||||
if (not arg.empty())
|
||||
{
|
||||
auto& entry = m_upstreamDNS.emplace_back(std::move(arg));
|
||||
if (!entry.getPort())
|
||||
if (not entry.getPort())
|
||||
entry.setPort(53);
|
||||
}
|
||||
});
|
||||
|
||||
conf.defineOption<bool>(
|
||||
"dns",
|
||||
"l3-intercept",
|
||||
Default{
|
||||
platform::is_windows or platform::is_android
|
||||
or (platform::is_macos and not platform::is_apple_sysex)},
|
||||
Comment{"Intercept all dns traffic (udp/53) going into our lokinet network interface "
|
||||
"instead of binding a local udp socket"},
|
||||
AssignmentAcceptor(m_raw_dns));
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
"dns",
|
||||
"query-bind",
|
||||
#if defined(_WIN32)
|
||||
Default{"0.0.0.0:0"},
|
||||
#else
|
||||
Hidden,
|
||||
#endif
|
||||
Comment{
|
||||
"Address to bind to for sending upstream DNS requests.",
|
||||
},
|
||||
[this](std::string arg) { m_QueryBind = SockAddr{arg}; });
|
||||
|
||||
conf.defineOption<std::string>(
|
||||
"dns",
|
||||
"bind",
|
||||
DefaultDNSBind,
|
||||
MultiValue,
|
||||
Comment{
|
||||
"Address to bind to for handling DNS requests.",
|
||||
},
|
||||
[=](std::string arg) {
|
||||
m_bind = SockAddr{std::move(arg)};
|
||||
if (!m_bind.getPort())
|
||||
m_bind.setPort(53);
|
||||
SockAddr addr{arg};
|
||||
// set dns port if no explicit port specified
|
||||
// explicit :0 allowed
|
||||
if (not addr.getPort() and not ends_with(arg, ":0"))
|
||||
addr.setPort(53);
|
||||
m_bind.emplace_back(addr);
|
||||
});
|
||||
|
||||
conf.defineOption<fs::path>(
|
||||
|
@ -843,6 +879,11 @@ namespace llarp
|
|||
"(This is not used directly by lokinet itself, but by the lokinet init scripts",
|
||||
"on systems which use resolveconf)",
|
||||
});
|
||||
|
||||
// forwad the rest to libunbound
|
||||
conf.addUndeclaredHandler("dns", [this](auto, std::string_view key, std::string_view val) {
|
||||
m_ExtraOpts.emplace(key, val);
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -1321,15 +1362,25 @@ namespace llarp
|
|||
}
|
||||
|
||||
void
|
||||
Config::LoadOverrides()
|
||||
Config::LoadOverrides(ConfigDefinition& conf) const
|
||||
{
|
||||
ConfigParser parser;
|
||||
const auto overridesDir = GetOverridesDir(m_DataDir);
|
||||
if (fs::exists(overridesDir))
|
||||
{
|
||||
util::IterDir(overridesDir, [&](const fs::path& overrideFile) {
|
||||
if (overrideFile.extension() == ".ini")
|
||||
{
|
||||
m_Parser.LoadFile(overrideFile);
|
||||
ConfigParser parser;
|
||||
if (not parser.LoadFile(overrideFile))
|
||||
throw std::runtime_error{"cannot load '" + overrideFile.u8string() + "'"};
|
||||
|
||||
parser.IterAll([&](std::string_view section, const SectionValues_t& values) {
|
||||
for (const auto& pair : values)
|
||||
{
|
||||
conf.addConfigValue(section, pair.first, pair.second);
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
@ -1343,7 +1394,7 @@ namespace llarp
|
|||
}
|
||||
|
||||
bool
|
||||
Config::LoadString(std::string_view ini, bool isRelay)
|
||||
Config::LoadConfigData(std::string_view ini, std::optional<fs::path> filename, bool isRelay)
|
||||
{
|
||||
auto params = MakeGenParams();
|
||||
params->isRelay = isRelay;
|
||||
|
@ -1351,7 +1402,18 @@ namespace llarp
|
|||
ConfigDefinition conf{isRelay};
|
||||
initializeConfig(conf, *params);
|
||||
|
||||
for (const auto& item : m_Additional)
|
||||
{
|
||||
conf.addConfigValue(item[0], item[1], item[2]);
|
||||
}
|
||||
|
||||
m_Parser.Clear();
|
||||
|
||||
if (filename)
|
||||
m_Parser.Filename(*filename);
|
||||
else
|
||||
m_Parser.Filename(fs::path{});
|
||||
|
||||
if (not m_Parser.LoadFromStr(ini))
|
||||
return false;
|
||||
|
||||
|
@ -1362,6 +1424,8 @@ namespace llarp
|
|||
}
|
||||
});
|
||||
|
||||
LoadOverrides(conf);
|
||||
|
||||
conf.process();
|
||||
|
||||
return true;
|
||||
|
@ -1370,37 +1434,24 @@ namespace llarp
|
|||
bool
|
||||
Config::Load(std::optional<fs::path> fname, bool isRelay)
|
||||
{
|
||||
if (not fname.has_value())
|
||||
return LoadDefault(isRelay);
|
||||
try
|
||||
std::vector<char> ini{};
|
||||
if (fname)
|
||||
{
|
||||
auto params = MakeGenParams();
|
||||
params->isRelay = isRelay;
|
||||
params->defaultDataDir = m_DataDir;
|
||||
|
||||
ConfigDefinition conf{isRelay};
|
||||
initializeConfig(conf, *params);
|
||||
m_Parser.Clear();
|
||||
if (!m_Parser.LoadFile(*fname))
|
||||
{
|
||||
if (not fs::exists(*fname))
|
||||
return false;
|
||||
}
|
||||
LoadOverrides();
|
||||
fs::ifstream inf{*fname, std::ios::in | std::ios::binary};
|
||||
auto sz = inf.seekg(0, std::ios::end).tellg();
|
||||
inf.seekg(0, std::ios::beg);
|
||||
ini.resize(sz);
|
||||
inf.read(ini.data(), ini.size());
|
||||
}
|
||||
return LoadConfigData(std::string_view{ini.data(), ini.size()}, fname, isRelay);
|
||||
}
|
||||
|
||||
m_Parser.IterAll([&](std::string_view section, const SectionValues_t& values) {
|
||||
for (const auto& pair : values)
|
||||
{
|
||||
conf.addConfigValue(section, pair.first, pair.second);
|
||||
}
|
||||
});
|
||||
conf.process();
|
||||
return true;
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
LogError("Error trying to init and parse config from file: ", e.what());
|
||||
return false;
|
||||
}
|
||||
bool
|
||||
Config::LoadString(std::string_view ini, bool isRelay)
|
||||
{
|
||||
return LoadConfigData(ini, std::nullopt, isRelay);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -155,9 +155,13 @@ namespace llarp
|
|||
|
||||
struct DnsConfig
|
||||
{
|
||||
SockAddr m_bind;
|
||||
bool m_raw_dns;
|
||||
std::vector<SockAddr> m_bind;
|
||||
std::vector<SockAddr> m_upstreamDNS;
|
||||
std::vector<fs::path> m_hostfiles;
|
||||
std::optional<SockAddr> m_QueryBind;
|
||||
|
||||
std::unordered_multimap<std::string, std::string> m_ExtraOpts;
|
||||
|
||||
void
|
||||
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);
|
||||
|
@ -294,8 +298,12 @@ namespace llarp
|
|||
bool
|
||||
LoadDefault(bool isRelay);
|
||||
|
||||
bool
|
||||
LoadConfigData(
|
||||
std::string_view ini, std::optional<fs::path> fname = std::nullopt, bool isRelay = false);
|
||||
|
||||
void
|
||||
LoadOverrides();
|
||||
LoadOverrides(ConfigDefinition& conf) const;
|
||||
|
||||
std::vector<std::array<std::string, 3>> m_Additional;
|
||||
ConfigParser m_Parser;
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
#include <llarp/util/logging.hpp>
|
||||
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <cassert>
|
||||
|
||||
|
@ -176,12 +175,14 @@ namespace llarp
|
|||
std::string
|
||||
ConfigDefinition::generateINIConfig(bool useValues)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
std::string ini;
|
||||
auto ini_append = std::back_inserter(ini);
|
||||
|
||||
int sectionsVisited = 0;
|
||||
|
||||
visitSections([&](const std::string& section, const DefinitionMap&) {
|
||||
std::ostringstream sect_out;
|
||||
std::string sect_str;
|
||||
auto sect_append = std::back_inserter(sect_str);
|
||||
|
||||
visitDefinitions(section, [&](const std::string& name, const OptionDefinition_ptr& def) {
|
||||
bool has_comment = false;
|
||||
|
@ -190,44 +191,43 @@ namespace llarp
|
|||
// (i.e. those handled by UndeclaredValueHandler's)
|
||||
for (const std::string& comment : m_definitionComments[section][name])
|
||||
{
|
||||
sect_out << "\n# " << comment;
|
||||
fmt::format_to(sect_append, "\n# {}", comment);
|
||||
has_comment = true;
|
||||
}
|
||||
|
||||
if (useValues and def->getNumberFound() > 0)
|
||||
{
|
||||
sect_out << "\n" << name << "=" << def->valueAsString(false) << "\n";
|
||||
for (const auto& val : def->valuesAsString())
|
||||
fmt::format_to(sect_append, "\n{}={}\n", name, val);
|
||||
}
|
||||
else if (not(def->hidden and not has_comment))
|
||||
{
|
||||
sect_out << "\n";
|
||||
if (not def->required)
|
||||
sect_out << "#";
|
||||
sect_out << name << "=" << def->defaultValueAsString() << "\n";
|
||||
for (const auto& val : def->defaultValuesAsString())
|
||||
fmt::format_to(sect_append, "\n{}{}={}\n", def->required ? "" : "#", name, val);
|
||||
}
|
||||
});
|
||||
|
||||
auto sect_str = sect_out.str();
|
||||
if (sect_str.empty())
|
||||
return; // Skip sections with no options
|
||||
|
||||
if (sectionsVisited > 0)
|
||||
oss << "\n\n";
|
||||
ini += "\n\n";
|
||||
|
||||
oss << "[" << section << "]\n";
|
||||
fmt::format_to(ini_append, "[{}]\n", section);
|
||||
|
||||
// TODO: this will create empty objects as a side effect of map's operator[]
|
||||
// TODO: this also won't handle sections which have no definition
|
||||
for (const std::string& comment : m_sectionComments[section])
|
||||
{
|
||||
oss << "# " << comment << "\n";
|
||||
fmt::format_to(ini_append, "# {}\n", comment);
|
||||
}
|
||||
oss << "\n" << sect_str;
|
||||
ini += "\n";
|
||||
ini += sect_str;
|
||||
|
||||
sectionsVisited++;
|
||||
});
|
||||
|
||||
return oss.str();
|
||||
return ini;
|
||||
}
|
||||
|
||||
const OptionDefinition_ptr&
|
||||
|
|
|
@ -97,12 +97,19 @@ namespace llarp
|
|||
template <typename U>
|
||||
constexpr bool is_default<U&> = is_default<remove_cvref_t<U>>;
|
||||
|
||||
template <typename T>
|
||||
constexpr bool is_default_array = false;
|
||||
template <typename T, size_t N>
|
||||
constexpr bool is_default_array<std::array<Default<T>, N>> = true;
|
||||
template <typename U>
|
||||
constexpr bool is_default_array<U&> = is_default_array<remove_cvref_t<U>>;
|
||||
|
||||
template <typename T, typename Option>
|
||||
constexpr bool is_option =
|
||||
std::is_base_of_v<
|
||||
option_flag,
|
||||
remove_cvref_t<
|
||||
Option>> or std::is_same_v<Comment, Option> or is_default<Option> or std::is_invocable_v<remove_cvref_t<Option>, T>;
|
||||
Option>> or std::is_same_v<Comment, Option> or is_default<Option> or is_default_array<Option> or std::is_invocable_v<remove_cvref_t<Option>, T>;
|
||||
} // namespace config
|
||||
|
||||
/// A base class for specifying config options and their constraints. The basic to/from string
|
||||
|
@ -128,8 +135,8 @@ namespace llarp
|
|||
/// Subclasses should provide their default value as a string
|
||||
///
|
||||
/// @return the option's default value represented as a string
|
||||
virtual std::string
|
||||
defaultValueAsString() = 0;
|
||||
virtual std::vector<std::string>
|
||||
defaultValuesAsString() = 0;
|
||||
|
||||
/// Subclasses should parse and store the provided input
|
||||
///
|
||||
|
@ -143,13 +150,11 @@ namespace llarp
|
|||
virtual size_t
|
||||
getNumberFound() const = 0;
|
||||
|
||||
/// Subclasess should write their parsed value as a string, optionally falling back to any
|
||||
/// specified default if `useDefault` is true.
|
||||
/// Subclasess should write their parsed values as strings.
|
||||
///
|
||||
/// @param useDefault should specify whether to fallback to default when possible
|
||||
/// @return the option's value as a string
|
||||
virtual std::string
|
||||
valueAsString(bool useDefault) = 0;
|
||||
/// @return the option's value(s) as strings
|
||||
virtual std::vector<std::string>
|
||||
valuesAsString() = 0;
|
||||
|
||||
/// Subclassess should call their acceptor, if present. See OptionDefinition for more details.
|
||||
///
|
||||
|
@ -170,9 +175,9 @@ namespace llarp
|
|||
std::vector<std::string> comments;
|
||||
};
|
||||
|
||||
/// The primary type-aware implementation of OptionDefinitionBase, this templated class allows
|
||||
/// for implementations which can use the std::ostringstream and std::istringstream for to/from
|
||||
/// string functionality.
|
||||
/// The primary type-aware implementation of OptionDefinitionBase, this templated class allows for
|
||||
/// implementations which can use fmt::format for conversion to string and std::istringstream for
|
||||
/// input from string.
|
||||
///
|
||||
/// Note that types (T) used as template parameters here must be used verbatim when calling
|
||||
/// ConfigDefinition::getConfigValue(). Similar types such as uint32_t and int32_t cannot be
|
||||
|
@ -184,7 +189,7 @@ namespace llarp
|
|||
///
|
||||
/// @param defaultValue_ is used in the following situations:
|
||||
/// 1) as the return value for getValue() if there is no parsed value and required==false
|
||||
/// 2) as the output in defaultValueAsString(), used to generate config files
|
||||
/// 2) as the output in defaultValuesAsString(), used to generate config files
|
||||
/// 3) as the output in valueAsString(), used to generate config files
|
||||
///
|
||||
/// @param opts - 0 or more of config::Required, config::Hidden, config::Default{...}, etc.
|
||||
|
@ -202,18 +207,28 @@ namespace llarp
|
|||
(extractComments(std::forward<Options>(opts)), ...);
|
||||
}
|
||||
|
||||
/// Extracts a default value from an config::Default<U>; ignores anything that isn't an
|
||||
/// config::Default<U>.
|
||||
/// Extracts a default value from an config::Default<U> or an array of defaults (for
|
||||
/// multi-valued options with multi-value default); ignores anything else.
|
||||
template <typename U>
|
||||
void
|
||||
extractDefault(U&& defaultValue_)
|
||||
{
|
||||
if constexpr (config::is_default<U>)
|
||||
if constexpr (config::is_default_array<U>)
|
||||
{
|
||||
if (!multiValued)
|
||||
throw std::logic_error{"Array config defaults require multiValue mode"};
|
||||
|
||||
defaultValues.clear();
|
||||
defaultValues.reserve(defaultValue_.size());
|
||||
for (const auto& def : defaultValue_)
|
||||
defaultValues.push_back(def.val);
|
||||
}
|
||||
else if constexpr (config::is_default<U>)
|
||||
{
|
||||
static_assert(
|
||||
std::is_convertible_v<decltype(std::forward<U>(defaultValue_).val), T>,
|
||||
"Cannot convert given llarp::config::Default to the required value type");
|
||||
defaultValue = std::forward<U>(defaultValue_).val;
|
||||
defaultValues = {std::forward<U>(defaultValue_).val};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -236,33 +251,22 @@ namespace llarp
|
|||
comments = std::forward<U>(comment).comments;
|
||||
}
|
||||
|
||||
/// Returns the first parsed value, if available. Otherwise, provides the default value if the
|
||||
/// option is not required. Otherwise, returns an empty optional.
|
||||
/// Returns the first parsed value, if available. Otherwise, provides the (first) default value
|
||||
/// if the option is not required. Otherwise, returns an empty optional.
|
||||
///
|
||||
/// @return an optional with the parsed value, the default value, or no value.
|
||||
/// @return an optional with the parsed value, the (first) default value, or no value.
|
||||
std::optional<T>
|
||||
getValue() const
|
||||
{
|
||||
if (parsedValues.empty())
|
||||
return required ? std::nullopt : defaultValue;
|
||||
{
|
||||
if (required || defaultValues.empty())
|
||||
return std::nullopt;
|
||||
return defaultValues.front();
|
||||
}
|
||||
return parsedValues.front();
|
||||
}
|
||||
|
||||
/// Returns the value at the given index.
|
||||
///
|
||||
/// @param index
|
||||
/// @return the value at the given index, if it exists
|
||||
/// @throws range_error exception if index >= size
|
||||
T
|
||||
getValueAt(size_t index) const
|
||||
{
|
||||
if (index >= parsedValues.size())
|
||||
throw std::range_error{
|
||||
fmt::format("no value at index {}, size: {}", index, parsedValues.size())};
|
||||
|
||||
return parsedValues[index];
|
||||
}
|
||||
|
||||
/// Returns the number of values found.
|
||||
///
|
||||
/// @return number of values found
|
||||
|
@ -272,18 +276,21 @@ namespace llarp
|
|||
return parsedValues.size();
|
||||
}
|
||||
|
||||
std::string
|
||||
defaultValueAsString() override
|
||||
std::vector<std::string>
|
||||
defaultValuesAsString() override
|
||||
{
|
||||
if (!defaultValue)
|
||||
return "";
|
||||
|
||||
if (defaultValues.empty())
|
||||
return {};
|
||||
if constexpr (std::is_same_v<fs::path, T>)
|
||||
return defaultValue->string();
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << *defaultValue;
|
||||
return oss.str();
|
||||
return {{defaultValues.front().u8string()}};
|
||||
else
|
||||
{
|
||||
std::vector<std::string> def_strs;
|
||||
def_strs.reserve(defaultValues.size());
|
||||
for (const auto& v : defaultValues)
|
||||
def_strs.push_back(fmt::format("{}", v));
|
||||
return def_strs;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -317,21 +324,21 @@ namespace llarp
|
|||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
valueAsString(bool useDefault) override
|
||||
std::vector<std::string>
|
||||
valuesAsString() override
|
||||
{
|
||||
std::ostringstream oss;
|
||||
if (parsedValues.size() > 0)
|
||||
oss << parsedValues[0];
|
||||
else if (useDefault and defaultValue)
|
||||
oss << *defaultValue;
|
||||
|
||||
return oss.str();
|
||||
if (parsedValues.empty())
|
||||
return {};
|
||||
std::vector<std::string> result;
|
||||
result.reserve(parsedValues.size());
|
||||
for (const auto& v : parsedValues)
|
||||
result.push_back(fmt::format("{}", v));
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Attempts to call the acceptor function, if present. This function may throw if the value is
|
||||
/// not acceptable. Additionally, tryAccept should not be called if the option is required and
|
||||
/// no value has been provided.
|
||||
/// Attempts to call the acceptor function, if present. This function may throw if the value
|
||||
/// is not acceptable. Additionally, tryAccept should not be called if the option is required
|
||||
/// and no value has been provided.
|
||||
///
|
||||
/// @throws if required and no value present or if the acceptor throws
|
||||
void
|
||||
|
@ -350,8 +357,9 @@ namespace llarp
|
|||
if (multiValued)
|
||||
{
|
||||
// add default value in multi value mode
|
||||
if (defaultValue and parsedValues.empty())
|
||||
acceptor(*defaultValue);
|
||||
if (parsedValues.empty() and not defaultValues.empty())
|
||||
for (const auto& v : defaultValues)
|
||||
acceptor(v);
|
||||
|
||||
for (auto value : parsedValues)
|
||||
{
|
||||
|
@ -367,7 +375,7 @@ namespace llarp
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<T> defaultValue;
|
||||
std::vector<T> defaultValues;
|
||||
std::vector<T> parsedValues;
|
||||
std::function<void(T)> acceptor;
|
||||
};
|
||||
|
@ -389,8 +397,8 @@ namespace llarp
|
|||
// map of section-name to map-of-definitions
|
||||
using SectionMap = std::unordered_map<std::string, DefinitionMap>;
|
||||
|
||||
/// A ConfigDefinition holds an ordered set of OptionDefinitions defining the allowable values and
|
||||
/// their constraints (specified through calls to defineOption()).
|
||||
/// A ConfigDefinition holds an ordered set of OptionDefinitions defining the allowable values
|
||||
/// and their constraints (specified through calls to defineOption()).
|
||||
///
|
||||
/// The layout and grouping of the config options are modelled after the INI file format; each
|
||||
/// option has a name and is grouped under a section. Duplicate option names are allowed only if
|
||||
|
@ -400,9 +408,9 @@ namespace llarp
|
|||
/// Configured values (e.g. those encountered when parsing a file) can be provided through calls
|
||||
/// to addConfigValue(). These take a std::string as a value, which is automatically parsed.
|
||||
///
|
||||
/// The ConfigDefinition can be used to print out a full config string (or file), including fields
|
||||
/// with defaults and optionally fields which have a specified value (values provided through
|
||||
/// calls to addConfigValue()).
|
||||
/// The ConfigDefinition can be used to print out a full config string (or file), including
|
||||
/// fields with defaults and optionally fields which have a specified value (values provided
|
||||
/// through calls to addConfigValue()).
|
||||
struct ConfigDefinition
|
||||
{
|
||||
explicit ConfigDefinition(bool relay) : relay{relay}
|
||||
|
@ -411,8 +419,8 @@ namespace llarp
|
|||
/// Specify the parameters and type of a configuration option. The parameters are members of
|
||||
/// OptionDefinitionBase; the type is inferred from OptionDefinition's template parameter T.
|
||||
///
|
||||
/// This function should be called for every option that this Configuration supports, and should
|
||||
/// be done before any other interactions involving that option.
|
||||
/// This function should be called for every option that this Configuration supports, and
|
||||
/// should be done before any other interactions involving that option.
|
||||
///
|
||||
/// @param def should be a unique_ptr to a valid subclass of OptionDefinitionBase
|
||||
/// @return `*this` for chaining calls
|
||||
|
@ -420,8 +428,8 @@ namespace llarp
|
|||
ConfigDefinition&
|
||||
defineOption(OptionDefinition_ptr def);
|
||||
|
||||
/// Convenience function which calls defineOption with a OptionDefinition of the specified type
|
||||
/// and with parameters passed through to OptionDefinition's constructor.
|
||||
/// Convenience function which calls defineOption with a OptionDefinition of the specified
|
||||
/// type and with parameters passed through to OptionDefinition's constructor.
|
||||
template <typename T, typename... Params>
|
||||
ConfigDefinition&
|
||||
defineOption(Params&&... args)
|
||||
|
@ -433,8 +441,9 @@ namespace llarp
|
|||
/// representing the type used by the option (e.g. the type provided when defineOption() was
|
||||
/// called).
|
||||
///
|
||||
/// If the specified option doesn't exist, an exception will be thrown. Otherwise, the option's
|
||||
/// parseValue() will be invoked, and should throw an exception if the string can't be parsed.
|
||||
/// If the specified option doesn't exist, an exception will be thrown. Otherwise, the
|
||||
/// option's parseValue() will be invoked, and should throw an exception if the string can't
|
||||
/// be parsed.
|
||||
///
|
||||
/// @param section is the section this value resides in
|
||||
/// @param name is the name of the value
|
||||
|
@ -493,9 +502,9 @@ namespace llarp
|
|||
void
|
||||
validateRequiredFields();
|
||||
|
||||
/// Accept all options. This will call the acceptor (if present) on each option. Note that this
|
||||
/// should only be called if all required fields are present (that is, validateRequiredFields()
|
||||
/// has been or could be called without throwing).
|
||||
/// Accept all options. This will call the acceptor (if present) on each option. Note that
|
||||
/// this should only be called if all required fields are present (that is,
|
||||
/// validateRequiredFields() has been or could be called without throwing).
|
||||
///
|
||||
/// @throws if any option's acceptor throws
|
||||
void
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace llarp
|
|||
{
|
||||
{
|
||||
std::ifstream f(fname, std::ios::in | std::ios::binary);
|
||||
if (!f.is_open())
|
||||
if (not f.is_open())
|
||||
return false;
|
||||
f.seekg(0, std::ios::end);
|
||||
m_Data.resize(f.tellg());
|
||||
|
|
|
@ -47,6 +47,12 @@ namespace llarp
|
|||
void
|
||||
Save();
|
||||
|
||||
inline void
|
||||
Filename(fs::path f)
|
||||
{
|
||||
m_FileName = f;
|
||||
};
|
||||
|
||||
private:
|
||||
bool
|
||||
Parse();
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
#include "table.hpp"
|
||||
#include <llarp/crypto/crypto.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace consensus
|
||||
{
|
||||
ShortHash
|
||||
Table::CalculateHash() const
|
||||
{
|
||||
ShortHash h;
|
||||
const llarp_buffer_t buf(begin()->data(), size());
|
||||
CryptoManager::instance()->shorthash(h, buf);
|
||||
return h;
|
||||
}
|
||||
} // namespace consensus
|
||||
} // namespace llarp
|
|
@ -1,17 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <llarp/crypto/types.hpp>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace consensus
|
||||
{
|
||||
/// consensus table
|
||||
struct Table : public std::vector<RouterID>
|
||||
{
|
||||
ShortHash
|
||||
CalculateHash() const;
|
||||
};
|
||||
} // namespace consensus
|
||||
} // namespace llarp
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include "platform.hpp"
|
||||
|
||||
#include <llarp/util/fs.hpp>
|
||||
|
||||
|
@ -21,20 +22,22 @@ namespace llarp
|
|||
inline fs::path
|
||||
GetDefaultDataDir()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return "C:/programdata/lokinet";
|
||||
#else
|
||||
fs::path datadir{"/var/lib/lokinet"};
|
||||
|
||||
if (auto uid = ::geteuid())
|
||||
if constexpr (not platform::is_windows)
|
||||
{
|
||||
if (auto* pw = getpwuid(uid))
|
||||
fs::path datadir{"/var/lib/lokinet"};
|
||||
#ifndef _WIN32
|
||||
if (auto uid = geteuid())
|
||||
{
|
||||
datadir = fs::path{pw->pw_dir} / ".lokinet";
|
||||
if (auto* pw = getpwuid(uid))
|
||||
{
|
||||
datadir = fs::path{pw->pw_dir} / ".lokinet";
|
||||
}
|
||||
}
|
||||
}
|
||||
return datadir;
|
||||
#endif
|
||||
return datadir;
|
||||
}
|
||||
else
|
||||
return "C:\\ProgramData\\Lokinet";
|
||||
}
|
||||
|
||||
inline fs::path
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
namespace llarp::constants
|
||||
{
|
||||
constexpr auto udp_header_bytes = 8;
|
||||
constexpr auto ip_header_min_bytes = 20;
|
||||
} // namespace llarp::constants
|
|
@ -12,6 +12,15 @@ namespace llarp::platform
|
|||
#endif
|
||||
;
|
||||
|
||||
/// building with systemd enabled ?
|
||||
inline constexpr bool with_systemd =
|
||||
#ifdef WITH_SYSTEMD
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
;
|
||||
|
||||
/// are we freebsd ?
|
||||
inline constexpr bool is_freebsd =
|
||||
#ifdef __FreeBSD__
|
||||
|
@ -39,6 +48,15 @@ namespace llarp::platform
|
|||
#endif
|
||||
;
|
||||
|
||||
/// are we building as an apple system extension
|
||||
inline constexpr bool is_apple_sysex =
|
||||
#ifdef MACOS_SYSTEM_EXTENSION
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
;
|
||||
|
||||
/// are we an android platform ?
|
||||
inline constexpr bool is_android =
|
||||
#ifdef ANDROID
|
||||
|
@ -57,9 +75,26 @@ namespace llarp::platform
|
|||
#endif
|
||||
;
|
||||
|
||||
/// are we running with pybind simulation mode enabled?
|
||||
inline constexpr bool is_simulation =
|
||||
#ifdef LOKINET_HIVE
|
||||
true
|
||||
#else
|
||||
false
|
||||
#endif
|
||||
;
|
||||
/// do we have systemd support ?
|
||||
// on cross compiles sometimes weird permutations of target and host make this value not correct,
|
||||
// this ensures it always is
|
||||
inline constexpr bool has_systemd = is_linux and with_systemd and not(is_android or is_windows);
|
||||
|
||||
/// are we using macos ?
|
||||
inline constexpr bool is_macos = is_apple and not is_iphone;
|
||||
|
||||
/// are we a mobile phone ?
|
||||
inline constexpr bool is_mobile = is_android or is_iphone;
|
||||
|
||||
/// does this platform support native ipv6 ?
|
||||
// TODO: make windows support ipv6
|
||||
inline constexpr bool supports_ipv6 = not is_windows;
|
||||
} // namespace llarp::platform
|
||||
|
|
|
@ -35,7 +35,7 @@ namespace llarp
|
|||
// first key
|
||||
if (firstKey)
|
||||
{
|
||||
if (!(*key == "A"))
|
||||
if (!(key->startswith("A")))
|
||||
return false;
|
||||
if (!bencode_read_string(buffer, &strbuf))
|
||||
return false;
|
||||
|
|
|
@ -26,11 +26,11 @@ namespace llarp::dht
|
|||
bool
|
||||
FindNameMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
|
||||
{
|
||||
if (key == "H")
|
||||
if (key.startswith("H"))
|
||||
{
|
||||
return NameHash.BDecode(val);
|
||||
}
|
||||
if (key == "T")
|
||||
if (key.startswith("T"))
|
||||
{
|
||||
return bencode_read_integer(val, &TxID);
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ namespace llarp
|
|||
{
|
||||
llarp_buffer_t strbuf;
|
||||
|
||||
if (key == "E")
|
||||
if (key.startswith("E"))
|
||||
{
|
||||
uint64_t result;
|
||||
if (!bencode_read_integer(val, &result))
|
||||
|
@ -115,7 +115,7 @@ namespace llarp
|
|||
return true;
|
||||
}
|
||||
|
||||
if (key == "I")
|
||||
if (key.startswith("I"))
|
||||
{
|
||||
uint64_t result;
|
||||
if (!bencode_read_integer(val, &result))
|
||||
|
@ -124,7 +124,7 @@ namespace llarp
|
|||
iterative = result != 0;
|
||||
return true;
|
||||
}
|
||||
if (key == "K")
|
||||
if (key.startswith("K"))
|
||||
{
|
||||
if (!bencode_read_string(val, &strbuf))
|
||||
return false;
|
||||
|
@ -134,11 +134,11 @@ namespace llarp
|
|||
std::copy(strbuf.base, strbuf.base + targetKey.SIZE, targetKey.begin());
|
||||
return true;
|
||||
}
|
||||
if (key == "T")
|
||||
if (key.startswith("T"))
|
||||
{
|
||||
return bencode_read_integer(val, &txid);
|
||||
}
|
||||
if (key == "V")
|
||||
if (key.startswith("V"))
|
||||
{
|
||||
return bencode_read_integer(val, &version);
|
||||
}
|
||||
|
|
|
@ -79,11 +79,11 @@ namespace llarp
|
|||
bool
|
||||
GotIntroMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
if (key == "I")
|
||||
if (key.startswith("I"))
|
||||
{
|
||||
return BEncodeReadList(found, buf);
|
||||
}
|
||||
if (key == "K")
|
||||
if (key.startswith("K"))
|
||||
{
|
||||
if (closer) // duplicate key?
|
||||
return false;
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace llarp::dht
|
|||
bool
|
||||
GotNameMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
|
||||
{
|
||||
if (key == "D")
|
||||
if (key.startswith("D"))
|
||||
{
|
||||
llarp_buffer_t str{};
|
||||
if (not bencode_read_string(val, &str))
|
||||
|
@ -38,11 +38,11 @@ namespace llarp::dht
|
|||
std::copy_n(str.cur, str.sz, result.ciphertext.data());
|
||||
return true;
|
||||
}
|
||||
if (key == "N")
|
||||
if (key.startswith("N"))
|
||||
{
|
||||
return result.nonce.BDecode(val);
|
||||
}
|
||||
if (key == "T")
|
||||
if (key.startswith("T"))
|
||||
{
|
||||
return bencode_read_integer(val, &TxID);
|
||||
}
|
||||
|
|
|
@ -53,22 +53,22 @@ namespace llarp
|
|||
bool
|
||||
GotRouterMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* val)
|
||||
{
|
||||
if (key == "K")
|
||||
if (key.startswith("K"))
|
||||
{
|
||||
if (closerTarget) // duplicate key?
|
||||
return false;
|
||||
closerTarget = std::make_unique<dht::Key_t>();
|
||||
return closerTarget->BDecode(val);
|
||||
}
|
||||
if (key == "N")
|
||||
if (key.startswith("N"))
|
||||
{
|
||||
return BEncodeReadList(nearKeys, val);
|
||||
}
|
||||
if (key == "R")
|
||||
if (key.startswith("R"))
|
||||
{
|
||||
return BEncodeReadList(foundRCs, val);
|
||||
}
|
||||
if (key == "T")
|
||||
if (key.startswith("T"))
|
||||
{
|
||||
return bencode_read_integer(val, &txid);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ namespace llarp
|
|||
{
|
||||
namespace dns
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
bool
|
||||
MessageHeader::Encode(llarp_buffer_t* buf) const
|
||||
{
|
||||
|
@ -116,16 +118,16 @@ namespace llarp
|
|||
{
|
||||
if (!qd.Decode(buf))
|
||||
{
|
||||
LogError("failed to decode question");
|
||||
log::error(logcat, "failed to decode question");
|
||||
return false;
|
||||
}
|
||||
LogDebug("dns question: ", qd);
|
||||
log::debug(logcat, "question: {}", qd);
|
||||
}
|
||||
for (auto& an : answers)
|
||||
{
|
||||
if (not an.Decode(buf))
|
||||
{
|
||||
LogDebug("failed to decode answer");
|
||||
log::debug(logcat, "failed to decode answer");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -414,5 +416,17 @@ namespace llarp
|
|||
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
|
||||
|
|
|
@ -103,6 +103,9 @@ namespace llarp
|
|||
std::vector<ResourceRecord> authorities;
|
||||
std::vector<ResourceRecord> additional;
|
||||
};
|
||||
|
||||
std::optional<Message>
|
||||
MaybeParseDNSMessage(llarp_buffer_t buf);
|
||||
} // namespace dns
|
||||
|
||||
template <>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#include "nm_platform.hpp"
|
||||
#ifdef WITH_SYSTEMD
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <net/if.h>
|
||||
}
|
||||
|
||||
#include <llarp/linux/dbus.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace llarp::dns::nm
|
||||
{
|
||||
void
|
||||
Platform::set_resolver(unsigned int, llarp::SockAddr, bool)
|
||||
{
|
||||
// todo: implement me eventually
|
||||
}
|
||||
} // namespace llarp::dns::nm
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include "platform.hpp"
|
||||
|
||||
#include <llarp/constants/platform.hpp>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace nm
|
||||
{
|
||||
// a dns platform that sets dns via network manager
|
||||
class Platform : public I_Platform
|
||||
{
|
||||
public:
|
||||
virtual ~Platform() = default;
|
||||
|
||||
void
|
||||
set_resolver(unsigned int index, llarp::SockAddr dns, bool global) override;
|
||||
};
|
||||
}; // namespace nm
|
||||
using NM_Platform_t = std::conditional_t<false, nm::Platform, Null_Platform>;
|
||||
} // namespace llarp::dns
|
|
@ -0,0 +1,32 @@
|
|||
#include "platform.hpp"
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
void
|
||||
Multi_Platform::add_impl(std::unique_ptr<I_Platform> impl)
|
||||
{
|
||||
m_Impls.emplace_back(std::move(impl));
|
||||
}
|
||||
|
||||
void
|
||||
Multi_Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool global)
|
||||
{
|
||||
if (m_Impls.empty())
|
||||
return;
|
||||
size_t fails{0};
|
||||
for (const auto& ptr : m_Impls)
|
||||
{
|
||||
try
|
||||
{
|
||||
ptr->set_resolver(index, dns, global);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
log::warning(log::Cat("dns"), "{}", ex.what());
|
||||
fails++;
|
||||
}
|
||||
}
|
||||
if (fails == m_Impls.size())
|
||||
throw std::runtime_error{"tried all ways to set resolver and failed"};
|
||||
}
|
||||
} // namespace llarp::dns
|
|
@ -0,0 +1,59 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <llarp/net/sock_addr.hpp>
|
||||
#include <llarp/util/logging.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
/// sets dns settings in a platform dependant way
|
||||
class I_Platform
|
||||
{
|
||||
public:
|
||||
virtual ~I_Platform() = default;
|
||||
|
||||
/// Attempts to set lokinet as the DNS server.
|
||||
/// throws if unsupported or fails.
|
||||
///
|
||||
///
|
||||
/// \param if_index -- the interface index to which we add the DNS servers, this can be gotten
|
||||
/// from the interface name e.g. lokitun0 (Typically tun_endpoint.GetIfName().) and then put
|
||||
/// through if_nametoindex().
|
||||
/// \param dns -- the listening address of the lokinet DNS server
|
||||
/// \param global -- whether to set up lokinet for all DNS queries (true) or just .loki & .snode
|
||||
/// addresses (false).
|
||||
virtual void
|
||||
set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) = 0;
|
||||
};
|
||||
|
||||
/// a dns platform does silently does nothing, successfully
|
||||
class Null_Platform : public I_Platform
|
||||
{
|
||||
public:
|
||||
~Null_Platform() override = default;
|
||||
void
|
||||
set_resolver(unsigned int, llarp::SockAddr, bool) override
|
||||
{}
|
||||
};
|
||||
|
||||
/// a collection of dns platforms that are tried in order when setting dns
|
||||
class Multi_Platform : public I_Platform
|
||||
{
|
||||
std::vector<std::unique_ptr<I_Platform>> m_Impls;
|
||||
|
||||
public:
|
||||
~Multi_Platform() override = default;
|
||||
/// add a platform to be owned
|
||||
void
|
||||
add_impl(std::unique_ptr<I_Platform> impl);
|
||||
|
||||
/// try all owned platforms to set the resolver, throws if none of them work
|
||||
void
|
||||
set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override;
|
||||
};
|
||||
} // namespace llarp::dns
|
|
@ -8,6 +8,8 @@ namespace llarp
|
|||
{
|
||||
namespace dns
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
Question::Question(Question&& other)
|
||||
: qname(std::move(other.qname))
|
||||
, qtype(std::move(other.qtype))
|
||||
|
@ -41,17 +43,17 @@ namespace llarp
|
|||
qname = *std::move(name);
|
||||
else
|
||||
{
|
||||
llarp::LogError("failed to decode name");
|
||||
log::error(logcat, "failed to decode name");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint16(qtype))
|
||||
{
|
||||
llarp::LogError("failed to decode type");
|
||||
log::error(logcat, "failed to decode type");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint16(qclass))
|
||||
{
|
||||
llarp::LogError("failed to decode class");
|
||||
log::error(logcat, "failed to decode class");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -8,6 +8,8 @@ namespace llarp
|
|||
{
|
||||
namespace dns
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
ResourceRecord::ResourceRecord(const ResourceRecord& other)
|
||||
: rr_name(other.rr_name)
|
||||
, rr_type(other.rr_type)
|
||||
|
@ -64,22 +66,22 @@ namespace llarp
|
|||
return false;
|
||||
if (!buf->read_uint16(rr_type))
|
||||
{
|
||||
llarp::LogDebug("failed to decode rr type");
|
||||
log::debug(logcat, "failed to decode rr type");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint16(rr_class))
|
||||
{
|
||||
llarp::LogDebug("failed to decode rr class");
|
||||
log::debug(logcat, "failed to decode rr class");
|
||||
return false;
|
||||
}
|
||||
if (!buf->read_uint32(ttl))
|
||||
{
|
||||
llarp::LogDebug("failed to decode ttl");
|
||||
log::debug(logcat, "failed to decode ttl");
|
||||
return false;
|
||||
}
|
||||
if (!DecodeRData(buf, rData))
|
||||
{
|
||||
llarp::LogDebug("failed to decode rr rdata ", *this);
|
||||
log::debug(logcat, "failed to decode rr rdata {}", *this);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
|
@ -0,0 +1,128 @@
|
|||
#ifdef WITH_SYSTEMD
|
||||
#include "sd_platform.hpp"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <net/if.h>
|
||||
}
|
||||
|
||||
#include <llarp/linux/dbus.hpp>
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace llarp::dns::sd
|
||||
{
|
||||
void
|
||||
Platform::set_resolver(unsigned int if_ndx, llarp::SockAddr dns, bool global)
|
||||
{
|
||||
linux::DBUS _dbus{
|
||||
"org.freedesktop.resolve1",
|
||||
"/org/freedesktop/resolve1",
|
||||
"org.freedesktop.resolve1.Manager"};
|
||||
// This passing address by bytes and using two separate calls for ipv4/ipv6 is gross, but
|
||||
// the alternative is to build up a bunch of crap with va_args, which is slightly more
|
||||
// gross.
|
||||
const bool isStandardDNSPort = dns.getPort() == 53;
|
||||
if (dns.isIPv6())
|
||||
{
|
||||
auto ipv6 = dns.getIPv6();
|
||||
static_assert(sizeof(ipv6) == 16);
|
||||
auto* a = reinterpret_cast<const uint8_t*>(&ipv6);
|
||||
if (isStandardDNSPort)
|
||||
{
|
||||
_dbus(
|
||||
"SetLinkDNS",
|
||||
"ia(iay)",
|
||||
(int32_t)if_ndx,
|
||||
(int)1, // number of "iayqs"s we are passing
|
||||
(int32_t)AF_INET6, // network address type
|
||||
(int)16, // network addr byte size
|
||||
// clang-format off
|
||||
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7],
|
||||
a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15] // yuck
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus(
|
||||
"SetLinkDNSEx",
|
||||
"ia(iayqs)",
|
||||
(int32_t)if_ndx,
|
||||
(int)1, // number of "iayqs"s we are passing
|
||||
(int32_t)AF_INET6, // network address type
|
||||
(int)16, // network addr byte size
|
||||
// clang-format off
|
||||
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7],
|
||||
a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], // yuck
|
||||
// clang-format on
|
||||
(uint16_t)dns.getPort(),
|
||||
nullptr // dns server name (for TLS SNI which we don't care about)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ipv4 = dns.getIPv4();
|
||||
static_assert(sizeof(ipv4) == 4);
|
||||
auto* a = reinterpret_cast<const uint8_t*>(&ipv4);
|
||||
if (isStandardDNSPort)
|
||||
{
|
||||
_dbus(
|
||||
"SetLinkDNS",
|
||||
"ia(iay)",
|
||||
(int32_t)if_ndx,
|
||||
(int)1, // number of "iayqs"s we are passing
|
||||
(int32_t)AF_INET, // network address type
|
||||
(int)4, // network addr byte size
|
||||
// clang-format off
|
||||
a[0], a[1], a[2], a[3] // yuck
|
||||
// clang-format on
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dbus(
|
||||
"SetLinkDNSEx",
|
||||
"ia(iayqs)",
|
||||
(int32_t)if_ndx,
|
||||
(int)1, // number of "iayqs"s we are passing
|
||||
(int32_t)AF_INET, // network address type
|
||||
(int)4, // network addr byte size
|
||||
// clang-format off
|
||||
a[0], a[1], a[2], a[3], // yuck
|
||||
// clang-format on
|
||||
(uint16_t)dns.getPort(),
|
||||
nullptr // dns server name (for TLS SNI which we don't care about)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (global)
|
||||
// Setting "." as a routing domain gives this DNS server higher priority in resolution
|
||||
// compared to dns servers that are set without a domain (e.g. the default for a
|
||||
// DHCP-configured DNS server)
|
||||
_dbus(
|
||||
"SetLinkDomains",
|
||||
"ia(sb)",
|
||||
(int32_t)if_ndx,
|
||||
(int)1, // array size
|
||||
"." // global DNS root
|
||||
);
|
||||
else
|
||||
// Only resolve .loki and .snode through lokinet (so you keep using your local DNS
|
||||
// server for everything else, which is nicer than forcing everything though lokinet's
|
||||
// upstream DNS).
|
||||
_dbus(
|
||||
"SetLinkDomains",
|
||||
"ia(sb)",
|
||||
(int32_t)if_ndx,
|
||||
(int)2, // array size
|
||||
"loki", // domain
|
||||
(int)1, // routing domain = true
|
||||
"snode", // domain
|
||||
(int)1 // routing domain = true
|
||||
);
|
||||
}
|
||||
} // namespace llarp::dns::sd
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include "platform.hpp"
|
||||
|
||||
#include <llarp/constants/platform.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace sd
|
||||
{
|
||||
/// a dns platform that sets dns via systemd resolved
|
||||
class Platform : public I_Platform
|
||||
{
|
||||
public:
|
||||
virtual ~Platform() = default;
|
||||
|
||||
void
|
||||
set_resolver(unsigned int if_index, llarp::SockAddr dns, bool global) override;
|
||||
};
|
||||
} // namespace sd
|
||||
using SD_Platform_t =
|
||||
std::conditional_t<llarp::platform::has_systemd, sd::Platform, Null_Platform>;
|
||||
} // namespace llarp::dns
|
|
@ -1,161 +1,690 @@
|
|||
#include "server.hpp"
|
||||
#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 <utility>
|
||||
#include <llarp/ev/udp_handle.hpp>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <unbound.h>
|
||||
#include <uvw.hpp>
|
||||
|
||||
#include "oxen/log.hpp"
|
||||
#include "sd_platform.hpp"
|
||||
#include "nm_platform.hpp"
|
||||
#include "win32_platform.hpp"
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
PacketHandler::PacketHandler(EventLoop_ptr loop, IQueryHandler* h)
|
||||
: m_QueryHandler{h}, m_Loop{std::move(loop)}
|
||||
{}
|
||||
|
||||
Proxy::Proxy(EventLoop_ptr loop, IQueryHandler* h)
|
||||
: PacketHandler{loop, h}, m_Loop(std::move(loop))
|
||||
{
|
||||
m_Server = m_Loop->make_udp(
|
||||
[this](UDPHandle&, SockAddr a, OwnedBuffer buf) { HandlePacket(a, a, buf); });
|
||||
}
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
void
|
||||
PacketHandler::Stop()
|
||||
QueryJob_Base::Cancel() const
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
m_UnboundResolver->Stop();
|
||||
Message reply{m_Query};
|
||||
reply.AddServFail();
|
||||
SendReply(reply.ToBuffer());
|
||||
}
|
||||
|
||||
bool
|
||||
Proxy::Start(SockAddr addr, std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles)
|
||||
/// 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>
|
||||
{
|
||||
if (not PacketHandler::Start(addr, std::move(resolvers), std::move(hostfiles)))
|
||||
return false;
|
||||
return m_Server->listen(addr);
|
||||
}
|
||||
Server& m_DNS;
|
||||
std::shared_ptr<llarp::UDPHandle> m_udp;
|
||||
SockAddr m_LocalAddr;
|
||||
|
||||
void
|
||||
PacketHandler::Restart()
|
||||
{
|
||||
if (m_UnboundResolver)
|
||||
public:
|
||||
explicit UDPReader(Server& dns, const EventLoop_ptr& loop, llarp::SockAddr bindaddr)
|
||||
: m_DNS{dns}
|
||||
{
|
||||
LogInfo("reset libunbound's internal stuff");
|
||||
m_UnboundResolver->Init();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::Start(SockAddr, std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles)
|
||||
{
|
||||
return SetupUnboundResolver(std::move(resolvers), std::move(hostfiles));
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::SetupUnboundResolver(
|
||||
std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles)
|
||||
{
|
||||
// if we have no resolvers don't set up unbound
|
||||
if (resolvers.empty())
|
||||
return true;
|
||||
|
||||
auto failFunc = [self = weak_from_this()](
|
||||
const SockAddr& to, const SockAddr& from, Message msg) {
|
||||
if (auto this_ptr = self.lock())
|
||||
this_ptr->SendServerMessageBufferTo(to, from, msg.ToBuffer());
|
||||
};
|
||||
|
||||
auto replyFunc = [self = weak_from_this()](auto&&... args) {
|
||||
if (auto this_ptr = self.lock())
|
||||
this_ptr->SendServerMessageBufferTo(std::forward<decltype(args)>(args)...);
|
||||
};
|
||||
|
||||
m_UnboundResolver =
|
||||
std::make_shared<UnboundResolver>(m_Loop, std::move(replyFunc), std::move(failFunc));
|
||||
m_Resolvers.clear();
|
||||
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))
|
||||
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())
|
||||
{
|
||||
llarp::LogError("Failed to add upstream DNS server: ", resolver);
|
||||
m_UnboundResolver = nullptr;
|
||||
m_LocalAddr = *maybe_addr;
|
||||
}
|
||||
else
|
||||
throw std::runtime_error{"cannot find which address our dns socket is bound on"};
|
||||
}
|
||||
|
||||
std::optional<SockAddr>
|
||||
BoundOn() const override
|
||||
{
|
||||
return m_udp->LocalAddr();
|
||||
}
|
||||
|
||||
bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr&) const override
|
||||
{
|
||||
return to != m_LocalAddr;
|
||||
}
|
||||
|
||||
void
|
||||
SendTo(const SockAddr& to, const SockAddr&, llarp::OwnedBuffer buf) const override
|
||||
{
|
||||
m_udp->send(to, std::move(buf));
|
||||
}
|
||||
|
||||
void
|
||||
Stop() override
|
||||
{
|
||||
m_udp->close();
|
||||
}
|
||||
};
|
||||
|
||||
namespace libunbound
|
||||
{
|
||||
class Resolver;
|
||||
|
||||
class Query : public QueryJob_Base
|
||||
{
|
||||
std::weak_ptr<Resolver> parent;
|
||||
std::shared_ptr<PacketSource_Base> src;
|
||||
SockAddr resolverAddr;
|
||||
SockAddr askerAddr;
|
||||
|
||||
public:
|
||||
explicit Query(
|
||||
std::weak_ptr<Resolver> parent_,
|
||||
Message query,
|
||||
std::shared_ptr<PacketSource_Base> pktsrc,
|
||||
SockAddr toaddr,
|
||||
SockAddr fromaddr)
|
||||
: QueryJob_Base{std::move(query)}
|
||||
, parent{parent_}
|
||||
, src{std::move(pktsrc)}
|
||||
, resolverAddr{std::move(toaddr)}
|
||||
, askerAddr{std::move(fromaddr)}
|
||||
{}
|
||||
|
||||
virtual void
|
||||
SendReply(llarp::OwnedBuffer replyBuf) const override;
|
||||
};
|
||||
|
||||
/// Resolver_Base that uses libunbound
|
||||
class Resolver final : public Resolver_Base, public std::enable_shared_from_this<Resolver>
|
||||
{
|
||||
ub_ctx* m_ctx = nullptr;
|
||||
std::weak_ptr<EventLoop> m_Loop;
|
||||
#ifdef _WIN32
|
||||
// windows is dumb so we do ub mainloop in a thread
|
||||
std::thread runner;
|
||||
std::atomic<bool> running;
|
||||
#else
|
||||
std::shared_ptr<uvw::PollHandle> m_Poller;
|
||||
#endif
|
||||
|
||||
std::optional<SockAddr> m_LocalAddr;
|
||||
|
||||
struct ub_result_deleter
|
||||
{
|
||||
void
|
||||
operator()(ub_result* ptr)
|
||||
{
|
||||
::ub_resolve_free(ptr);
|
||||
}
|
||||
};
|
||||
|
||||
const net::Platform*
|
||||
Net_ptr() const
|
||||
{
|
||||
return m_Loop.lock()->Net_ptr();
|
||||
}
|
||||
|
||||
static void
|
||||
Callback(void* data, int err, ub_result* _result)
|
||||
{
|
||||
// take ownership of ub_result
|
||||
std::unique_ptr<ub_result, ub_result_deleter> result{_result};
|
||||
// take ownership of our query
|
||||
std::unique_ptr<Query> query{static_cast<Query*>(data)};
|
||||
|
||||
if (err)
|
||||
{
|
||||
// some kind of error from upstream
|
||||
log::warning(logcat, "Upstream DNS failure: {}", ub_strerror(err));
|
||||
query->Cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
void
|
||||
AddUpstreamResolver(const SockAddr& dns)
|
||||
{
|
||||
std::string str = dns.hostString();
|
||||
|
||||
if (const auto port = dns.getPort(); port != 53)
|
||||
fmt::format_to(std::back_inserter(str), "@{}", port);
|
||||
|
||||
if (auto err = ub_ctx_set_fwd(m_ctx, str.c_str()))
|
||||
{
|
||||
throw std::runtime_error{
|
||||
fmt::format("cannot use {} as upstream dns: {}", str, ub_strerror(err))};
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ConfigureAppleTrampoline(const SockAddr& dns)
|
||||
{
|
||||
// On Apple, when we turn on exit mode, we tear down and then reestablish the unbound
|
||||
// resolver: in exit mode, we set use upstream to a localhost trampoline that redirects
|
||||
// packets through the tunnel. In non-exit mode, we directly use the upstream, so we look
|
||||
// here for a reconfiguration to use the trampoline port to check which state we're in.
|
||||
//
|
||||
// We have to do all this crap because we can't directly connect to upstream from here:
|
||||
// within the network extension, macOS ignores the tunnel we are managing and so, if we
|
||||
// didn't do this, all our DNS queries would leak out around the tunnel. Instead we have to
|
||||
// bounce things through the objective C trampoline code (which is what actually handles the
|
||||
// upstream querying) so that it can call into Apple's special snowflake API to set up a
|
||||
// socket that has the magic Apple snowflake sauce added on top so that it actually routes
|
||||
// through the tunnel instead of around it.
|
||||
//
|
||||
// But the trampoline *always* tries to send the packet through the tunnel, and that will
|
||||
// only work in exit mode.
|
||||
//
|
||||
// All of this macos behaviour is all carefully and explicitly documented by Apple with
|
||||
// plenty of examples and other exposition, of course, just like all of their wonderful new
|
||||
// APIs to reinvent standard unix interfaces with half-baked replacements.
|
||||
|
||||
if constexpr (platform::is_apple)
|
||||
{
|
||||
if (dns.hostString() == "127.0.0.1" and dns.getPort() == apple::dns_trampoline_port)
|
||||
{
|
||||
// macOS is stupid: the default (0.0.0.0) fails with "send failed: Can't assign
|
||||
// requested address" when unbound tries to connect to the localhost address using a
|
||||
// source address of 0.0.0.0. Yay apple.
|
||||
SetOpt("outgoing-interface:", "127.0.0.1");
|
||||
|
||||
// The trampoline expects just a single source port (and sends everything back to it).
|
||||
SetOpt("outgoing-range:", "1");
|
||||
SetOpt("outgoing-port-avoid:", "0-65535");
|
||||
SetOpt("outgoing-port-permit:", "{}", apple::dns_trampoline_source_port);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
m_Resolvers.emplace(resolver);
|
||||
}
|
||||
for (const auto& path : hostfiles)
|
||||
{
|
||||
m_UnboundResolver->AddHostsFile(path);
|
||||
}
|
||||
|
||||
return true;
|
||||
void
|
||||
ConfigureUpstream(const llarp::DnsConfig& conf)
|
||||
{
|
||||
bool is_apple_tramp = false;
|
||||
|
||||
// set up forward dns
|
||||
for (const auto& dns : conf.m_upstreamDNS)
|
||||
{
|
||||
AddUpstreamResolver(dns);
|
||||
is_apple_tramp = is_apple_tramp or ConfigureAppleTrampoline(dns);
|
||||
}
|
||||
|
||||
if (auto maybe_addr = conf.m_QueryBind; maybe_addr and not is_apple_tramp)
|
||||
{
|
||||
SockAddr addr{*maybe_addr};
|
||||
std::string host{addr.hostString()};
|
||||
|
||||
if (addr.getPort() == 0)
|
||||
{
|
||||
// unbound manages their own sockets because of COURSE it does. so we find an open port
|
||||
// on our system and use it so we KNOW what it is before giving it to unbound to
|
||||
// explicitly bind to JUST that port.
|
||||
|
||||
auto fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
#ifdef _WIN32
|
||||
if (fd == INVALID_SOCKET)
|
||||
#else
|
||||
if (fd == -1)
|
||||
#endif
|
||||
{
|
||||
throw std::invalid_argument{
|
||||
fmt::format("Failed to create UDP socket for unbound: {}", strerror(errno))};
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
#define CLOSE closesocket
|
||||
#else
|
||||
#define CLOSE close
|
||||
#endif
|
||||
if (0 != bind(fd, static_cast<const sockaddr*>(addr), addr.sockaddr_len()))
|
||||
{
|
||||
CLOSE(fd);
|
||||
throw std::invalid_argument{
|
||||
fmt::format("Failed to bind UDP socket for unbound: {}", strerror(errno))};
|
||||
}
|
||||
struct sockaddr_storage sas;
|
||||
auto* sa = reinterpret_cast<struct sockaddr*>(&sas);
|
||||
socklen_t sa_len = sizeof(sas);
|
||||
int rc = getsockname(fd, sa, &sa_len);
|
||||
CLOSE(fd);
|
||||
#undef CLOSE
|
||||
if (rc != 0)
|
||||
{
|
||||
throw std::invalid_argument{
|
||||
fmt::format("Failed to query UDP port for unbound: {}", strerror(errno))};
|
||||
}
|
||||
addr = SockAddr{*sa};
|
||||
}
|
||||
m_LocalAddr = addr;
|
||||
|
||||
log::info(logcat, "sending dns queries from {}:{}", host, addr.getPort());
|
||||
// set up query bind port if needed
|
||||
SetOpt("outgoing-interface:", host);
|
||||
SetOpt("outgoing-range:", "1");
|
||||
SetOpt("outgoing-port-avoid:", "0-65535");
|
||||
SetOpt("outgoing-port-permit:", "{}", addr.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SetOpt(const std::string& key, const std::string& val)
|
||||
{
|
||||
ub_ctx_set_option(m_ctx, key.c_str(), val.c_str());
|
||||
}
|
||||
|
||||
// Wrapper around the above that takes 3+ arguments: the 2nd arg gets formatted with the
|
||||
// remaining args, and the formatted string passed to the above as `val`.
|
||||
template <typename... FmtArgs, std::enable_if_t<sizeof...(FmtArgs), int> = 0>
|
||||
void
|
||||
SetOpt(const std::string& key, std::string_view format, FmtArgs&&... args)
|
||||
{
|
||||
SetOpt(key, fmt::format(format, std::forward<FmtArgs>(args)...));
|
||||
}
|
||||
|
||||
// Copy of the DNS config (a copy because on some platforms, like Apple, we change the applied
|
||||
// upstream DNS settings when turning on/off exit mode).
|
||||
llarp::DnsConfig m_conf;
|
||||
|
||||
public:
|
||||
explicit Resolver(const EventLoop_ptr& loop, llarp::DnsConfig conf)
|
||||
: m_Loop{loop}, m_conf{std::move(conf)}
|
||||
{
|
||||
Up(m_conf);
|
||||
}
|
||||
|
||||
~Resolver() override
|
||||
{
|
||||
Down();
|
||||
}
|
||||
|
||||
std::string_view
|
||||
ResolverName() const override
|
||||
{
|
||||
return "unbound";
|
||||
}
|
||||
|
||||
virtual std::optional<SockAddr>
|
||||
GetLocalAddr() const override
|
||||
{
|
||||
return m_LocalAddr;
|
||||
}
|
||||
|
||||
void
|
||||
Up(const llarp::DnsConfig& conf)
|
||||
{
|
||||
if (m_ctx)
|
||||
throw std::logic_error{"Internal error: attempt to Up() dns server multiple times"};
|
||||
|
||||
m_ctx = ::ub_ctx_create();
|
||||
// set libunbound settings
|
||||
|
||||
SetOpt("do-tcp:", "no");
|
||||
|
||||
for (const auto& [k, v] : conf.m_ExtraOpts)
|
||||
SetOpt(k, v);
|
||||
|
||||
// add host files
|
||||
for (const auto& file : conf.m_hostfiles)
|
||||
{
|
||||
const auto str = file.u8string();
|
||||
if (auto ret = ub_ctx_hosts(m_ctx, str.c_str()))
|
||||
{
|
||||
throw std::runtime_error{
|
||||
fmt::format("Failed to add host file {}: {}", file, ub_strerror(ret))};
|
||||
}
|
||||
}
|
||||
|
||||
ConfigureUpstream(conf);
|
||||
|
||||
// set async
|
||||
ub_ctx_async(m_ctx, 1);
|
||||
// setup mainloop
|
||||
#ifdef _WIN32
|
||||
running = true;
|
||||
runner = std::thread{[this]() {
|
||||
while (running)
|
||||
{
|
||||
ub_wait(m_ctx);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
ub_process(m_ctx);
|
||||
}};
|
||||
#else
|
||||
if (auto loop = m_Loop.lock())
|
||||
{
|
||||
if (auto loop_ptr = loop->MaybeGetUVWLoop())
|
||||
{
|
||||
m_Poller = loop_ptr->resource<uvw::PollHandle>(ub_fd(m_ctx));
|
||||
m_Poller->on<uvw::PollEvent>([this](auto&, auto&) { ub_process(m_ctx); });
|
||||
m_Poller->start(uvw::PollHandle::Event::READABLE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error{"no uvw loop"};
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Down() override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (running.exchange(false))
|
||||
runner.join();
|
||||
#else
|
||||
if (m_Poller)
|
||||
m_Poller->close();
|
||||
#endif
|
||||
if (m_ctx)
|
||||
{
|
||||
::ub_ctx_delete(m_ctx);
|
||||
m_ctx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
Rank() const override
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
void
|
||||
ResetResolver(std::optional<std::vector<SockAddr>> replace_upstream) override
|
||||
{
|
||||
Down();
|
||||
if (replace_upstream)
|
||||
m_conf.m_upstreamDNS = std::move(*replace_upstream);
|
||||
Up(m_conf);
|
||||
}
|
||||
|
||||
bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const override
|
||||
{
|
||||
#if defined(ANDROID)
|
||||
(void)to;
|
||||
(void)from;
|
||||
return false;
|
||||
#else
|
||||
const auto& vec = m_conf.m_upstreamDNS;
|
||||
return std::find(vec.begin(), vec.end(), to) != std::end(vec)
|
||||
or std::find(vec.begin(), vec.end(), from) != std::end(vec);
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename Callable>
|
||||
void
|
||||
call(Callable&& f)
|
||||
{
|
||||
if (auto loop = m_Loop.lock())
|
||||
loop->call(std::forward<Callable>(f));
|
||||
else
|
||||
log::critical(logcat, "no mainloop?");
|
||||
}
|
||||
|
||||
bool
|
||||
MaybeHookDNS(
|
||||
std::shared_ptr<PacketSource_Base> source,
|
||||
const Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) override
|
||||
{
|
||||
if (WouldLoop(to, from))
|
||||
return false;
|
||||
// we use this unique ptr to clean up on fail
|
||||
auto tmp = std::make_unique<Query>(weak_from_this(), query, source, to, from);
|
||||
// no questions, send fail
|
||||
if (query.questions.empty())
|
||||
{
|
||||
tmp->Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
for (const auto& q : query.questions)
|
||||
{
|
||||
// dont process .loki or .snode
|
||||
if (q.HasTLD(".loki") or q.HasTLD(".snode"))
|
||||
{
|
||||
tmp->Cancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
const auto& q = query.questions[0];
|
||||
if (auto err = ub_resolve_async(
|
||||
m_ctx,
|
||||
q.Name().c_str(),
|
||||
q.qtype,
|
||||
q.qclass,
|
||||
tmp.get(),
|
||||
&Resolver::Callback,
|
||||
nullptr))
|
||||
{
|
||||
log::warning(
|
||||
logcat, "failed to send upstream query with libunbound: {}", ub_strerror(err));
|
||||
tmp->Cancel();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Leak the bare pointer we gave to unbound; we'll recapture it in Callback
|
||||
(void)tmp.release();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
Query::SendReply(llarp::OwnedBuffer replyBuf) const
|
||||
{
|
||||
if (auto ptr = parent.lock())
|
||||
{
|
||||
ptr->call([src = src, from = resolverAddr, to = askerAddr, buf = replyBuf.copy()] {
|
||||
src->SendTo(to, from, OwnedBuffer::copy_from(buf));
|
||||
});
|
||||
}
|
||||
else
|
||||
log::error(logcat, "no source or parent");
|
||||
}
|
||||
} // namespace libunbound
|
||||
|
||||
Server::Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif)
|
||||
: 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>>
|
||||
Server::GetAllResolvers() const
|
||||
{
|
||||
return {m_Resolvers.begin(), m_Resolvers.end()};
|
||||
}
|
||||
|
||||
void
|
||||
Proxy::SendServerMessageBufferTo(
|
||||
const SockAddr& to, [[maybe_unused]] const SockAddr& from, llarp_buffer_t buf)
|
||||
Server::Start()
|
||||
{
|
||||
if (!m_Server->send(to, buf))
|
||||
llarp::LogError("dns reply failed");
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::IsUpstreamResolver(const SockAddr& to, [[maybe_unused]] const SockAddr& from) const
|
||||
{
|
||||
return m_Resolvers.count(to);
|
||||
}
|
||||
|
||||
bool
|
||||
PacketHandler::ShouldHandlePacket(
|
||||
const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) const
|
||||
{
|
||||
MessageHeader hdr;
|
||||
if (not hdr.Decode(&buf))
|
||||
// set up udp sockets
|
||||
for (const auto& addr : m_Config.m_bind)
|
||||
{
|
||||
return false;
|
||||
if (auto ptr = MakePacketSourceOn(addr, m_Config))
|
||||
AddPacketSource(std::move(ptr));
|
||||
}
|
||||
|
||||
Message msg{hdr};
|
||||
if (not msg.Decode(&buf))
|
||||
// add default resolver as needed
|
||||
if (auto ptr = MakeDefaultResolver())
|
||||
AddResolver(ptr);
|
||||
}
|
||||
|
||||
std::shared_ptr<I_Platform>
|
||||
Server::CreatePlatform() const
|
||||
{
|
||||
auto plat = std::make_shared<Multi_Platform>();
|
||||
if constexpr (llarp::platform::has_systemd)
|
||||
{
|
||||
return false;
|
||||
plat->add_impl(std::make_unique<SD_Platform_t>());
|
||||
plat->add_impl(std::make_unique<NM_Platform_t>());
|
||||
}
|
||||
if constexpr (llarp::platform::is_windows)
|
||||
{
|
||||
plat->add_impl(std::make_unique<Win32_Platform_t>());
|
||||
}
|
||||
return plat;
|
||||
}
|
||||
|
||||
std::shared_ptr<PacketSource_Base>
|
||||
Server::MakePacketSourceOn(const llarp::SockAddr& addr, const llarp::DnsConfig&)
|
||||
{
|
||||
return std::make_shared<UDPReader>(*this, m_Loop, addr);
|
||||
}
|
||||
|
||||
std::shared_ptr<Resolver_Base>
|
||||
Server::MakeDefaultResolver()
|
||||
{
|
||||
if (m_Config.m_upstreamDNS.empty())
|
||||
{
|
||||
log::info(
|
||||
logcat,
|
||||
"explicitly no upstream dns providers specified, we will not resolve anything but .loki "
|
||||
"and .snode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (m_QueryHandler and m_QueryHandler->ShouldHookDNSMessage(msg))
|
||||
return true;
|
||||
// If this request is going to an upstream resolver then we want to let it through (i.e. don't
|
||||
// handle it), and so want to return false. If we have something else then we want to
|
||||
// intercept it to route it through our caching libunbound server (which then redirects the
|
||||
// request to the lokinet-configured upstream, if not cached).
|
||||
return !IsUpstreamResolver(to, from);
|
||||
return std::make_shared<libunbound::Resolver>(m_Loop, m_Config);
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
Server::BoundPacketSourceAddrs() const
|
||||
{
|
||||
std::vector<SockAddr> addrs;
|
||||
for (const auto& src : m_PacketSources)
|
||||
{
|
||||
if (auto ptr = src.lock())
|
||||
if (auto maybe_addr = ptr->BoundOn())
|
||||
addrs.emplace_back(*maybe_addr);
|
||||
}
|
||||
return addrs;
|
||||
}
|
||||
|
||||
std::optional<SockAddr>
|
||||
Server::FirstBoundPacketSourceAddr() const
|
||||
{
|
||||
for (const auto& src : m_PacketSources)
|
||||
{
|
||||
if (auto ptr = src.lock())
|
||||
if (auto bound = ptr->BoundOn())
|
||||
return bound;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
PacketHandler::HandlePacket(const SockAddr& resolver, const SockAddr& from, llarp_buffer_t buf)
|
||||
Server::AddResolver(std::weak_ptr<Resolver_Base> resolver)
|
||||
{
|
||||
MessageHeader hdr;
|
||||
if (not hdr.Decode(&buf))
|
||||
m_Resolvers.insert(resolver);
|
||||
}
|
||||
|
||||
void
|
||||
Server::AddResolver(std::shared_ptr<Resolver_Base> resolver)
|
||||
{
|
||||
m_OwnedResolvers.insert(resolver);
|
||||
AddResolver(std::weak_ptr<Resolver_Base>{resolver});
|
||||
}
|
||||
|
||||
void
|
||||
Server::AddPacketSource(std::weak_ptr<PacketSource_Base> pkt)
|
||||
{
|
||||
m_PacketSources.push_back(pkt);
|
||||
}
|
||||
|
||||
void
|
||||
Server::AddPacketSource(std::shared_ptr<PacketSource_Base> pkt)
|
||||
{
|
||||
AddPacketSource(std::weak_ptr<PacketSource_Base>{pkt});
|
||||
m_OwnedPacketSources.push_back(std::move(pkt));
|
||||
}
|
||||
|
||||
void
|
||||
Server::Stop()
|
||||
{
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
llarp::LogWarn("failed to parse dns header from ", from);
|
||||
return;
|
||||
if (auto ptr = resolver.lock())
|
||||
ptr->Down();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Server::Reset()
|
||||
{
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
if (auto ptr = resolver.lock())
|
||||
ptr->ResetResolver();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Server::SetDNSMode(bool all_queries)
|
||||
{
|
||||
if (auto maybe_addr = FirstBoundPacketSourceAddr())
|
||||
m_Platform->set_resolver(m_NetIfIndex, *maybe_addr, all_queries);
|
||||
}
|
||||
|
||||
bool
|
||||
Server::MaybeHandlePacket(
|
||||
std::shared_ptr<PacketSource_Base> ptr,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from,
|
||||
llarp::OwnedBuffer buf)
|
||||
{
|
||||
// dont process to prevent feedback loop
|
||||
if (ptr->WouldLoop(to, from))
|
||||
{
|
||||
log::warning(logcat, "preventing dns packet replay to={} from={}", to, from);
|
||||
return false;
|
||||
}
|
||||
|
||||
Message msg(hdr);
|
||||
if (not msg.Decode(&buf))
|
||||
auto maybe = MaybeParseDNSMessage(buf);
|
||||
if (not maybe)
|
||||
{
|
||||
llarp::LogWarn("failed to parse dns message from ", from);
|
||||
return;
|
||||
log::warning(logcat, "invalid dns message format from {} to dns listener on {}", from, to);
|
||||
return false;
|
||||
}
|
||||
|
||||
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/loki-project/loki-network/issues/832
|
||||
// see: https://github.com/oxen-io/lokinet/issues/832
|
||||
for (const auto& q : msg.questions)
|
||||
{
|
||||
// is this firefox looking for their backdoor record?
|
||||
|
@ -163,32 +692,23 @@ namespace llarp::dns
|
|||
{
|
||||
// yea it is, let's turn off DoH because god is dead.
|
||||
msg.AddNXReply();
|
||||
// press F to pay respects
|
||||
SendServerMessageBufferTo(from, resolver, msg.ToBuffer());
|
||||
return;
|
||||
// press F to pay respects and send it back where it came from
|
||||
ptr->SendTo(from, to, msg.ToBuffer());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_QueryHandler && m_QueryHandler->ShouldHookDNSMessage(msg))
|
||||
for (const auto& resolver : m_Resolvers)
|
||||
{
|
||||
auto reply = [self = shared_from_this(), to = from, resolver](dns::Message msg) {
|
||||
self->SendServerMessageBufferTo(to, resolver, msg.ToBuffer());
|
||||
};
|
||||
if (!m_QueryHandler->HandleHookedDNSMessage(std::move(msg), reply))
|
||||
if (auto res_ptr = resolver.lock())
|
||||
{
|
||||
llarp::LogWarn("failed to handle hooked dns");
|
||||
log::debug(
|
||||
logcat, "check resolver {} for dns from {} to {}", res_ptr->ResolverName(), from, to);
|
||||
if (res_ptr->MaybeHookDNS(ptr, msg, to, from))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (not m_UnboundResolver)
|
||||
{
|
||||
// no upstream resolvers
|
||||
// let's serv fail it
|
||||
msg.AddServFail();
|
||||
SendServerMessageBufferTo(from, resolver, msg.ToBuffer());
|
||||
}
|
||||
else
|
||||
{
|
||||
m_UnboundResolver->Lookup(resolver, from, std::move(msg));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -1,99 +1,297 @@
|
|||
#pragma once
|
||||
|
||||
#include "message.hpp"
|
||||
#include "platform.hpp"
|
||||
#include <llarp/config/config.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include "unbound_resolver.hpp"
|
||||
#include <llarp/util/fs.hpp>
|
||||
#include <set>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace llarp
|
||||
namespace llarp::dns
|
||||
{
|
||||
namespace dns
|
||||
/// a job handling 1 dns query
|
||||
class QueryJob_Base
|
||||
{
|
||||
/// handler of dns query hooking
|
||||
class IQueryHandler
|
||||
protected:
|
||||
/// the original dns query
|
||||
Message m_Query;
|
||||
|
||||
public:
|
||||
explicit QueryJob_Base(Message query) : m_Query{std::move(query)}
|
||||
{}
|
||||
|
||||
virtual ~QueryJob_Base() = default;
|
||||
|
||||
Message&
|
||||
Underlying()
|
||||
{
|
||||
public:
|
||||
virtual ~IQueryHandler() = default;
|
||||
return m_Query;
|
||||
}
|
||||
|
||||
/// return true if we should hook this message
|
||||
virtual bool
|
||||
ShouldHookDNSMessage(const Message& msg) const = 0;
|
||||
|
||||
/// handle a hooked message
|
||||
virtual bool
|
||||
HandleHookedDNSMessage(Message query, std::function<void(Message)> sendReply) = 0;
|
||||
};
|
||||
|
||||
// Base class for DNS lookups
|
||||
class PacketHandler : public std::enable_shared_from_this<PacketHandler>
|
||||
const Message&
|
||||
Underlying() const
|
||||
{
|
||||
public:
|
||||
explicit PacketHandler(EventLoop_ptr loop, IQueryHandler* handler);
|
||||
return m_Query;
|
||||
}
|
||||
|
||||
virtual ~PacketHandler() = default;
|
||||
/// cancel this operation and inform anyone who cares
|
||||
void
|
||||
Cancel() const;
|
||||
|
||||
virtual bool
|
||||
Start(
|
||||
SockAddr localaddr,
|
||||
std::vector<SockAddr> upstreamResolvers,
|
||||
std::vector<fs::path> hostfiles);
|
||||
/// send a raw buffer back to the querier
|
||||
virtual void
|
||||
SendReply(llarp::OwnedBuffer replyBuf) const = 0;
|
||||
};
|
||||
|
||||
void
|
||||
Stop();
|
||||
class PacketSource_Base
|
||||
{
|
||||
public:
|
||||
virtual ~PacketSource_Base() = default;
|
||||
|
||||
void
|
||||
Restart();
|
||||
/// return true if traffic with source and dest addresses would cause a
|
||||
/// loop in resolution and thus should not be sent to query handlers
|
||||
virtual bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const = 0;
|
||||
|
||||
void
|
||||
HandlePacket(const SockAddr& resolver, const SockAddr& from, llarp_buffer_t buf);
|
||||
/// send packet with src and dst address containing buf on this packet source
|
||||
virtual void
|
||||
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const = 0;
|
||||
|
||||
bool
|
||||
ShouldHandlePacket(const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) const;
|
||||
/// stop reading packets and end operation
|
||||
virtual void
|
||||
Stop() = 0;
|
||||
|
||||
protected:
|
||||
virtual void
|
||||
SendServerMessageBufferTo(const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) = 0;
|
||||
/// returns the sockaddr we are bound on if applicable
|
||||
virtual std::optional<SockAddr>
|
||||
BoundOn() const = 0;
|
||||
};
|
||||
|
||||
// Returns true if this packet is something that looks like it's going to an upstream
|
||||
// resolver, i.e. matches a configured resolver.
|
||||
virtual bool
|
||||
IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const;
|
||||
/// a packet source which will override the sendto function of an wrapped packet source to
|
||||
/// construct a raw ip packet as a reply
|
||||
class PacketSource_Wrapper : public PacketSource_Base
|
||||
{
|
||||
std::weak_ptr<PacketSource_Base> m_Wrapped;
|
||||
std::function<void(net::IPPacket)> m_WritePacket;
|
||||
|
||||
private:
|
||||
void
|
||||
HandleUpstreamFailure(const SockAddr& from, const SockAddr& to, Message msg);
|
||||
public:
|
||||
explicit PacketSource_Wrapper(
|
||||
std::weak_ptr<PacketSource_Base> wrapped, std::function<void(net::IPPacket)> write_packet)
|
||||
: m_Wrapped{wrapped}, m_WritePacket{write_packet}
|
||||
{}
|
||||
|
||||
bool
|
||||
SetupUnboundResolver(std::vector<SockAddr> resolvers, std::vector<fs::path> hostfiles);
|
||||
|
||||
IQueryHandler* const m_QueryHandler;
|
||||
std::set<SockAddr> m_Resolvers;
|
||||
std::shared_ptr<UnboundResolver> m_UnboundResolver;
|
||||
EventLoop_ptr m_Loop;
|
||||
};
|
||||
|
||||
// Proxying DNS handler that listens on a UDP port for proper DNS requests.
|
||||
class Proxy : public PacketHandler
|
||||
bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const override
|
||||
{
|
||||
public:
|
||||
explicit Proxy(EventLoop_ptr loop, IQueryHandler* handler);
|
||||
if (auto ptr = m_Wrapped.lock())
|
||||
return ptr->WouldLoop(to, from);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Start(
|
||||
SockAddr localaddr,
|
||||
std::vector<SockAddr> upstreamResolvers,
|
||||
std::vector<fs::path> hostfiles) override;
|
||||
void
|
||||
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override
|
||||
{
|
||||
m_WritePacket(net::IPPacket::make_udp(from, to, std::move(buf)));
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
SendServerMessageBufferTo(
|
||||
const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) override;
|
||||
/// stop reading packets and end operation
|
||||
void
|
||||
Stop() override
|
||||
{
|
||||
if (auto ptr = m_Wrapped.lock())
|
||||
ptr->Stop();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<UDPHandle> m_Server;
|
||||
EventLoop_ptr m_Loop;
|
||||
};
|
||||
} // namespace dns
|
||||
} // namespace llarp
|
||||
/// returns the sockaddr we are bound on if applicable
|
||||
std::optional<SockAddr>
|
||||
BoundOn() const override
|
||||
{
|
||||
if (auto ptr = m_Wrapped.lock())
|
||||
return ptr->BoundOn();
|
||||
return std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
/// non complex implementation of QueryJob_Base for use in things that
|
||||
/// only ever called on the mainloop thread
|
||||
class QueryJob : public QueryJob_Base, std::enable_shared_from_this<QueryJob>
|
||||
{
|
||||
std::shared_ptr<PacketSource_Base> src;
|
||||
const SockAddr resolver;
|
||||
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_}
|
||||
{}
|
||||
|
||||
void
|
||||
SendReply(llarp::OwnedBuffer replyBuf) const override
|
||||
{
|
||||
src->SendTo(asker, resolver, std::move(replyBuf));
|
||||
}
|
||||
};
|
||||
|
||||
/// handler of dns query hooking
|
||||
/// intercepts dns for internal processing
|
||||
class Resolver_Base
|
||||
{
|
||||
protected:
|
||||
/// return the sorting order for this resolver
|
||||
/// lower means it will be tried first
|
||||
virtual int
|
||||
Rank() const = 0;
|
||||
|
||||
public:
|
||||
virtual ~Resolver_Base() = default;
|
||||
|
||||
/// less than via rank
|
||||
bool
|
||||
operator<(const Resolver_Base& other) const
|
||||
{
|
||||
return Rank() < other.Rank();
|
||||
}
|
||||
|
||||
/// greater than via rank
|
||||
bool
|
||||
operator>(const Resolver_Base& other) const
|
||||
{
|
||||
return Rank() > other.Rank();
|
||||
}
|
||||
|
||||
/// get local socket address that queries are sent from
|
||||
virtual std::optional<SockAddr>
|
||||
GetLocalAddr() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// get printable name
|
||||
virtual std::string_view
|
||||
ResolverName() const = 0;
|
||||
|
||||
/// reset the resolver state, optionally replace upstream info with new info. The default base
|
||||
/// implementation does nothing.
|
||||
virtual void
|
||||
ResetResolver(
|
||||
[[maybe_unused]] std::optional<std::vector<SockAddr>> replace_upstream = std::nullopt)
|
||||
{}
|
||||
|
||||
/// cancel all pending requests and cease further operation. Default operation is a no-op.
|
||||
virtual void
|
||||
Down()
|
||||
{}
|
||||
|
||||
/// attempt to handle a dns message
|
||||
/// returns true if we consumed this query and it should not be processed again
|
||||
virtual bool
|
||||
MaybeHookDNS(
|
||||
std::shared_ptr<PacketSource_Base> source,
|
||||
const Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) = 0;
|
||||
|
||||
/// Returns true if a packet with to and from addresses is something that would cause a
|
||||
/// resolution loop and thus should not be used on this resolver
|
||||
virtual bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const
|
||||
{
|
||||
(void)to;
|
||||
(void)from;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Base class for DNS proxy
|
||||
class Server : public std::enable_shared_from_this<Server>
|
||||
{
|
||||
protected:
|
||||
/// add a packet source to this server, does share ownership
|
||||
void
|
||||
AddPacketSource(std::shared_ptr<PacketSource_Base> resolver);
|
||||
/// add a resolver to this packet handler, does share ownership
|
||||
void
|
||||
AddResolver(std::shared_ptr<Resolver_Base> resolver);
|
||||
|
||||
/// create the platform dependant dns stuff
|
||||
virtual std::shared_ptr<I_Platform>
|
||||
CreatePlatform() const;
|
||||
|
||||
public:
|
||||
virtual ~Server() = default;
|
||||
|
||||
explicit Server(EventLoop_ptr loop, llarp::DnsConfig conf, unsigned int netif_index);
|
||||
|
||||
/// returns all sockaddr we have from all of our PacketSources
|
||||
std::vector<SockAddr>
|
||||
BoundPacketSourceAddrs() const;
|
||||
|
||||
/// returns the first sockaddr we have on our packet sources if we have one
|
||||
std::optional<SockAddr>
|
||||
FirstBoundPacketSourceAddr() const;
|
||||
|
||||
/// add a resolver to this packet handler, does not share ownership
|
||||
void
|
||||
AddResolver(std::weak_ptr<Resolver_Base> resolver);
|
||||
|
||||
/// add a packet source to this server, does not share ownership
|
||||
void
|
||||
AddPacketSource(std::weak_ptr<PacketSource_Base> resolver);
|
||||
|
||||
/// create a packet source bound on bindaddr but does not add it
|
||||
virtual std::shared_ptr<PacketSource_Base>
|
||||
MakePacketSourceOn(const SockAddr& bindaddr, const llarp::DnsConfig& conf);
|
||||
|
||||
/// sets up all internal binds and such and begins operation
|
||||
virtual void
|
||||
Start();
|
||||
|
||||
/// stops all operation
|
||||
virtual void
|
||||
Stop();
|
||||
|
||||
/// reset the internal state
|
||||
virtual void
|
||||
Reset();
|
||||
|
||||
/// create the default resolver for out config
|
||||
virtual std::shared_ptr<Resolver_Base>
|
||||
MakeDefaultResolver();
|
||||
|
||||
std::vector<std::weak_ptr<Resolver_Base>>
|
||||
GetAllResolvers() const;
|
||||
|
||||
/// feed a packet buffer from a packet source.
|
||||
/// returns true if we decided to process the packet and consumed it
|
||||
/// returns false if we dont want to process the packet
|
||||
bool
|
||||
MaybeHandlePacket(
|
||||
std::shared_ptr<PacketSource_Base> pktsource,
|
||||
const SockAddr& resolver,
|
||||
const SockAddr& from,
|
||||
llarp::OwnedBuffer buf);
|
||||
/// set which dns mode we are in.
|
||||
/// true for intercepting all queries. false for just .loki and .snode
|
||||
void
|
||||
SetDNSMode(bool all_queries);
|
||||
|
||||
protected:
|
||||
EventLoop_ptr m_Loop;
|
||||
llarp::DnsConfig m_Config;
|
||||
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;
|
||||
|
||||
std::vector<std::weak_ptr<PacketSource_Base>> m_PacketSources;
|
||||
std::vector<std::shared_ptr<PacketSource_Base>> m_OwnedPacketSources;
|
||||
};
|
||||
|
||||
} // namespace llarp::dns
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
namespace llarp::dns
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
bool
|
||||
SRVData::IsValid() const
|
||||
{
|
||||
|
@ -22,7 +24,7 @@ namespace llarp::dns
|
|||
// check target size is not absurd
|
||||
if (target.size() > TARGET_MAX_SIZE)
|
||||
{
|
||||
LogWarn("SRVData target larger than max size (", TARGET_MAX_SIZE, ")");
|
||||
log::warning(logcat, "SRVData target larger than max size ({})", TARGET_MAX_SIZE);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -41,7 +43,7 @@ namespace llarp::dns
|
|||
}
|
||||
|
||||
// if we're here, target is invalid
|
||||
LogWarn("SRVData invalid");
|
||||
log::warning(logcat, "SRVData invalid");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -64,14 +66,14 @@ namespace llarp::dns
|
|||
bool
|
||||
SRVData::fromString(std::string_view srvString)
|
||||
{
|
||||
LogDebug("SRVData::fromString(\"", srvString, "\")");
|
||||
log::debug(logcat, "SRVData::fromString(\"{}\")", srvString);
|
||||
|
||||
// split on spaces, discard trailing empty strings
|
||||
auto splits = split(srvString, " ", false);
|
||||
|
||||
if (splits.size() != 5 && splits.size() != 4)
|
||||
{
|
||||
LogWarn("SRV record should have either 4 or 5 space-separated parts");
|
||||
log::warning(logcat, "SRV record should have either 4 or 5 space-separated parts");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -79,19 +81,19 @@ namespace llarp::dns
|
|||
|
||||
if (not parse_int(splits[1], priority))
|
||||
{
|
||||
LogWarn("SRV record failed to parse \"", splits[1], "\" as uint16_t (priority)");
|
||||
log::warning(logcat, "SRV record failed to parse \"{}\" as uint16_t (priority)", splits[1]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not parse_int(splits[2], weight))
|
||||
{
|
||||
LogWarn("SRV record failed to parse \"", splits[2], "\" as uint16_t (weight)");
|
||||
log::warning(logcat, "SRV record failed to parse \"{}\" as uint16_t (weight)", splits[2]);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (not parse_int(splits[3], port))
|
||||
{
|
||||
LogWarn("SRV record failed to parse \"", splits[3], "\" as uint16_t (port)");
|
||||
log::warning(logcat, "SRV record failed to parse \"{}\" as uint16_t (port)", splits[3]);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
namespace llarp::dns
|
||||
{
|
||||
static auto logcat = log::Cat("dns");
|
||||
|
||||
struct PendingUnboundLookup
|
||||
{
|
||||
std::weak_ptr<UnboundResolver> resolver;
|
||||
|
@ -156,7 +158,7 @@ namespace llarp::dns
|
|||
upstream += std::to_string(port);
|
||||
}
|
||||
|
||||
LogError("Adding upstream resolver ", upstream);
|
||||
log::info("Adding upstream resolver ", upstream);
|
||||
if (ub_ctx_set_fwd(unboundContext, upstream.c_str()) != 0)
|
||||
{
|
||||
Reset();
|
||||
|
@ -198,17 +200,12 @@ namespace llarp::dns
|
|||
void
|
||||
UnboundResolver::AddHostsFile(const fs::path& file)
|
||||
{
|
||||
LogDebug("adding hosts file ", file);
|
||||
log::debug(logcat, "adding hosts file {}", file);
|
||||
const auto str = file.u8string();
|
||||
if (auto ret = ub_ctx_hosts(unboundContext, str.c_str()))
|
||||
{
|
||||
throw std::runtime_error{
|
||||
fmt::format("Failed to add host file {}: {}", file, ub_strerror(ret))};
|
||||
}
|
||||
else
|
||||
{
|
||||
LogInfo("added hosts file ", file);
|
||||
}
|
||||
log::info(logcat, "added hosts file {}", file);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
#include "win32_platform.hpp"
|
||||
#include <llarp/net/net.hpp>
|
||||
|
||||
namespace llarp::dns::win32
|
||||
{
|
||||
void
|
||||
Platform::set_resolver(unsigned int index, llarp::SockAddr dns, bool)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
|
||||
// clear any previous dns settings
|
||||
m_UndoDNS.clear();
|
||||
|
||||
auto interfaces = m_Loop->Net_ptr()->AllNetworkInterfaces();
|
||||
// remove dns
|
||||
{
|
||||
std::vector<llarp::win32::OneShotExec> jobs;
|
||||
for (const auto& ent : interfaces)
|
||||
{
|
||||
if (ent.index == index)
|
||||
continue;
|
||||
jobs.emplace_back(
|
||||
"netsh.exe", fmt::format("interface ipv4 delete dns \"{}\" all", ent.name));
|
||||
jobs.emplace_back(
|
||||
"netsh.exe", fmt::format("interface ipv6 delete dns \"{}\" all", ent.name));
|
||||
}
|
||||
}
|
||||
// add new dns
|
||||
{
|
||||
std::vector<llarp::win32::OneShotExec> jobs;
|
||||
for (const auto& ent : interfaces)
|
||||
{
|
||||
if (ent.index == index)
|
||||
continue;
|
||||
jobs.emplace_back(
|
||||
"netsh.exe",
|
||||
fmt::format("interface ipv4 add dns \"{}\" {} validate=no", ent.name, dns.asIPv4()));
|
||||
jobs.emplace_back(
|
||||
"netsh.exe",
|
||||
fmt::format("interface ipv6 add dns \"{}\" {} validate=no", ent.name, dns.asIPv6()));
|
||||
m_UndoDNS.emplace_back("netsh.exe", fmt::format("", index));
|
||||
}
|
||||
m_UndoDNS.emplace_back("netsh.exe", "winsock reset");
|
||||
}
|
||||
// flush dns
|
||||
llarp::win32::Exec("ipconfig.exe", "/flushdns");
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace llarp::dns::win32
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
#include "platform.hpp"
|
||||
|
||||
namespace llarp::dns
|
||||
{
|
||||
// TODO: implement me
|
||||
using Win32_Platform_t = Null_Platform;
|
||||
} // namespace llarp::dns
|
|
@ -23,6 +23,11 @@ namespace llarp
|
|||
class TunnelManager;
|
||||
}
|
||||
|
||||
namespace dns
|
||||
{
|
||||
class Server;
|
||||
}
|
||||
|
||||
class EndpointBase
|
||||
{
|
||||
std::unordered_set<dns::SRVData> m_SRVRecords;
|
||||
|
@ -72,6 +77,13 @@ namespace llarp
|
|||
void
|
||||
PutSRVRecord(dns::SRVData srv);
|
||||
|
||||
/// get dns serverr if we have on on this endpoint
|
||||
virtual std::shared_ptr<dns::Server>
|
||||
DNS() const
|
||||
{
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
/// called when srv data changes in some way
|
||||
virtual void
|
||||
SRVRecordsChanged() = 0;
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
#include <cstring>
|
||||
#include <string_view>
|
||||
|
||||
// We libuv now
|
||||
#include "ev_libuv.hpp"
|
||||
#include "libuv.hpp"
|
||||
#include <llarp/net/net.hpp>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
|
@ -16,4 +16,11 @@ namespace llarp
|
|||
{
|
||||
return std::make_shared<llarp::uv::Loop>(queueLength);
|
||||
}
|
||||
|
||||
const net::Platform*
|
||||
EventLoop::Net_ptr() const
|
||||
{
|
||||
return net::Platform::Default_ptr();
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
#include <llarp/util/time.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
#include <llarp/constants/evloop.hpp>
|
||||
|
||||
#include <llarp/net/interface_info.hpp>
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
|
@ -28,8 +28,10 @@ namespace llarp
|
|||
|
||||
namespace net
|
||||
{
|
||||
class Platform;
|
||||
|
||||
struct IPPacket;
|
||||
}
|
||||
} // namespace net
|
||||
|
||||
/// distinct event loop waker upper; used to idempotently schedule a task on the next event loop
|
||||
///
|
||||
|
@ -184,6 +186,9 @@ namespace llarp
|
|||
|
||||
virtual ~EventLoop() = default;
|
||||
|
||||
virtual const net::Platform*
|
||||
Net_ptr() const;
|
||||
|
||||
using UDPReceiveFunc = std::function<void(UDPHandle&, SockAddr src, llarp::OwnedBuffer buf)>;
|
||||
|
||||
// Constructs a UDP socket that can be used for sending and/or receiving
|
||||
|
@ -218,7 +223,6 @@ namespace llarp
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Triggers an event loop wakeup; use when something has been done that requires the event loop
|
||||
// to wake up (e.g. adding to queues). This is called implicitly by call() and call_soon().
|
||||
// Idempotent and thread-safe.
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
#include "ev_libuv.hpp"
|
||||
#include "vpn.hpp"
|
||||
#include "libuv.hpp"
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <cstring>
|
||||
|
||||
#include <llarp/util/exceptions.hpp>
|
||||
#include <llarp/util/thread/queue.hpp>
|
||||
#include <cstring>
|
||||
#include "ev.hpp"
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
|
||||
#include <uvw.hpp>
|
||||
|
||||
|
@ -72,6 +72,13 @@ namespace llarp::uv
|
|||
bool
|
||||
send(const SockAddr& dest, const llarp_buffer_t& buf) override;
|
||||
|
||||
std::optional<SockAddr>
|
||||
LocalAddr() const override
|
||||
{
|
||||
auto addr = handle->sock<uvw::IPv4>();
|
||||
return SockAddr{addr.ip, huint16_t{static_cast<uint16_t>(addr.port)}};
|
||||
}
|
||||
|
||||
std::optional<int>
|
||||
file_descriptor() override
|
||||
{
|
||||
|
@ -244,19 +251,21 @@ namespace llarp::uv
|
|||
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) {
|
||||
for (auto pkt = netif->ReadNextPacket(); pkt.sz > 0; pkt = netif->ReadNextPacket())
|
||||
for (auto pkt = netif->ReadNextPacket(); true; pkt = netif->ReadNextPacket())
|
||||
{
|
||||
LogDebug("got packet ", pkt.sz);
|
||||
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 this is a nop
|
||||
// on linux/android this is a nop
|
||||
netif->MaybeWakeUpperLayers();
|
||||
}
|
||||
});
|
|
@ -33,6 +33,10 @@ namespace llarp
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
/// returns the local address we are bound on
|
||||
virtual std::optional<SockAddr>
|
||||
LocalAddr() const = 0;
|
||||
|
||||
// Base class destructor
|
||||
virtual ~UDPHandle() = default;
|
||||
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace llarp
|
|||
|
||||
bool
|
||||
Endpoint::QueueOutboundTraffic(
|
||||
PathID_t path, ManagedBuffer buf, uint64_t counter, service::ProtocolType t)
|
||||
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)
|
||||
|
@ -118,18 +118,19 @@ namespace llarp
|
|||
auto quic = m_Parent->GetQUICTunnel();
|
||||
if (not quic)
|
||||
return false;
|
||||
quic->receive_packet(tag, buf.underlying);
|
||||
m_TxRate += buf.size();
|
||||
quic->receive_packet(tag, std::move(buf));
|
||||
m_LastActive = m_Parent->Now();
|
||||
m_TxRate += buf.underlying.sz;
|
||||
return true;
|
||||
}
|
||||
// queue overflow
|
||||
if (m_UpstreamQueue.size() > MaxUpstreamQueueSize)
|
||||
return false;
|
||||
|
||||
llarp::net::IPPacket pkt;
|
||||
if (!pkt.Load(buf.underlying))
|
||||
llarp::net::IPPacket pkt{std::move(buf)};
|
||||
if (pkt.empty())
|
||||
return false;
|
||||
|
||||
if (pkt.IsV6() && m_Parent->SupportsV6())
|
||||
{
|
||||
huint128_t dst;
|
||||
|
@ -152,24 +153,19 @@ namespace llarp
|
|||
{
|
||||
return false;
|
||||
}
|
||||
m_UpstreamQueue.emplace(pkt, counter);
|
||||
m_TxRate += buf.underlying.sz;
|
||||
m_TxRate += pkt.size();
|
||||
m_UpstreamQueue.emplace(std::move(pkt), counter);
|
||||
m_LastActive = m_Parent->Now();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Endpoint::QueueInboundTraffic(ManagedBuffer buf, service::ProtocolType type)
|
||||
Endpoint::QueueInboundTraffic(std::vector<byte_t> buf, service::ProtocolType type)
|
||||
{
|
||||
llarp::net::IPPacket pkt{};
|
||||
if (type == service::ProtocolType::QUIC)
|
||||
if (type != service::ProtocolType::QUIC)
|
||||
{
|
||||
pkt.sz = std::min(buf.underlying.sz, sizeof(pkt.buf));
|
||||
std::copy_n(buf.underlying.base, pkt.sz, pkt.buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!pkt.Load(buf.underlying))
|
||||
llarp::net::IPPacket pkt{std::move(buf)};
|
||||
if (pkt.empty())
|
||||
return false;
|
||||
|
||||
huint128_t src;
|
||||
|
@ -181,11 +177,11 @@ namespace llarp
|
|||
pkt.UpdateIPv6Address(src, m_IP);
|
||||
else
|
||||
pkt.UpdateIPv4Address(xhtonl(net::TruncateV6(src)), xhtonl(net::TruncateV6(m_IP)));
|
||||
}
|
||||
const auto _pktbuf = pkt.ConstBuffer();
|
||||
auto& pktbuf = _pktbuf.underlying;
|
||||
|
||||
const uint8_t queue_idx = pktbuf.sz / llarp::routing::ExitPadSize;
|
||||
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);
|
||||
|
@ -193,17 +189,17 @@ namespace llarp
|
|||
{
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = type;
|
||||
return queue.back().PutBuffer(pktbuf, m_Counter++);
|
||||
return queue.back().PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
auto& msg = queue.back();
|
||||
if (msg.Size() + pktbuf.sz > llarp::routing::ExitPadSize)
|
||||
if (msg.Size() + buf.size() > llarp::routing::ExitPadSize)
|
||||
{
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = type;
|
||||
return queue.back().PutBuffer(pktbuf, m_Counter++);
|
||||
return queue.back().PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
msg.protocol = type;
|
||||
return msg.PutBuffer(pktbuf, m_Counter++);
|
||||
return msg.PutBuffer(std::move(buf), m_Counter++);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -212,7 +208,8 @@ namespace llarp
|
|||
// flush upstream queue
|
||||
while (m_UpstreamQueue.size())
|
||||
{
|
||||
m_Parent->QueueOutboundTraffic(m_UpstreamQueue.top().pkt);
|
||||
m_Parent->QueueOutboundTraffic(
|
||||
const_cast<net::IPPacket&>(m_UpstreamQueue.top().pkt).steal());
|
||||
m_UpstreamQueue.pop();
|
||||
}
|
||||
// flush downstream queue
|
||||
|
|
|
@ -58,7 +58,7 @@ namespace llarp
|
|||
|
||||
/// queue traffic from service node / internet to be transmitted
|
||||
bool
|
||||
QueueInboundTraffic(ManagedBuffer buff, service::ProtocolType t);
|
||||
QueueInboundTraffic(std::vector<byte_t> data, service::ProtocolType t);
|
||||
|
||||
/// flush inbound and outbound traffic queues
|
||||
bool
|
||||
|
@ -68,7 +68,7 @@ namespace llarp
|
|||
/// does ip rewrite here
|
||||
bool
|
||||
QueueOutboundTraffic(
|
||||
PathID_t txid, ManagedBuffer pkt, uint64_t counter, service::ProtocolType t);
|
||||
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
|
||||
|
@ -122,7 +122,7 @@ namespace llarp
|
|||
|
||||
struct UpstreamBuffer
|
||||
{
|
||||
UpstreamBuffer(const llarp::net::IPPacket& p, uint64_t c) : pkt(p), counter(c)
|
||||
UpstreamBuffer(llarp::net::IPPacket p, uint64_t c) : pkt{std::move(p)}, counter(c)
|
||||
{}
|
||||
|
||||
llarp::net::IPPacket pkt;
|
||||
|
|
|
@ -213,8 +213,8 @@ namespace llarp
|
|||
|
||||
if (m_WritePacket)
|
||||
{
|
||||
llarp::net::IPPacket pkt;
|
||||
if (!pkt.Load(buf))
|
||||
llarp::net::IPPacket pkt{buf.view_all()};
|
||||
if (pkt.empty())
|
||||
return false;
|
||||
m_LastUse = m_router->Now();
|
||||
m_Downstream.emplace(counter, pkt);
|
||||
|
@ -235,9 +235,7 @@ namespace llarp
|
|||
BaseSession::QueueUpstreamTraffic(
|
||||
llarp::net::IPPacket pkt, const size_t N, service::ProtocolType t)
|
||||
{
|
||||
const auto pktbuf = pkt.ConstBuffer();
|
||||
const llarp_buffer_t& buf = pktbuf;
|
||||
auto& queue = m_Upstream[buf.sz / N];
|
||||
auto& queue = m_Upstream[pkt.size() / N];
|
||||
// queue overflow
|
||||
if (queue.size() >= MaxUpstreamQueueLength)
|
||||
return false;
|
||||
|
@ -245,18 +243,18 @@ namespace llarp
|
|||
{
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = t;
|
||||
return queue.back().PutBuffer(buf, m_Counter++);
|
||||
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
|
||||
}
|
||||
auto& back = queue.back();
|
||||
// pack to nearest N
|
||||
if (back.Size() + buf.sz > N)
|
||||
if (back.Size() + pkt.size() > N)
|
||||
{
|
||||
queue.emplace_back();
|
||||
queue.back().protocol = t;
|
||||
return queue.back().PutBuffer(buf, m_Counter++);
|
||||
return queue.back().PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
|
||||
}
|
||||
back.protocol = t;
|
||||
return back.PutBuffer(buf, m_Counter++);
|
||||
return back.PutBuffer(llarp_buffer_t{pkt}, m_Counter++);
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -333,7 +331,7 @@ namespace llarp
|
|||
while (m_Downstream.size())
|
||||
{
|
||||
if (m_WritePacket)
|
||||
m_WritePacket(m_Downstream.top().second.ConstBuffer());
|
||||
m_WritePacket(const_cast<net::IPPacket&>(m_Downstream.top().second).steal());
|
||||
m_Downstream.pop();
|
||||
}
|
||||
}
|
||||
|
@ -369,8 +367,8 @@ namespace llarp
|
|||
void
|
||||
SNodeSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
|
||||
{
|
||||
net::IPPacket pkt;
|
||||
if (not pkt.Load(buf))
|
||||
net::IPPacket pkt{buf.view_all()};
|
||||
if (pkt.empty())
|
||||
return;
|
||||
pkt.ZeroAddresses();
|
||||
QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t);
|
||||
|
@ -379,9 +377,10 @@ namespace llarp
|
|||
void
|
||||
ExitSession::SendPacketToRemote(const llarp_buffer_t& buf, service::ProtocolType t)
|
||||
{
|
||||
net::IPPacket pkt;
|
||||
if (not pkt.Load(buf))
|
||||
net::IPPacket pkt{buf.view_all()};
|
||||
if (pkt.empty())
|
||||
return;
|
||||
|
||||
pkt.ZeroSourceAddress();
|
||||
QueueUpstreamTraffic(std::move(pkt), llarp::routing::ExitPadSize, t);
|
||||
}
|
||||
|
|
|
@ -18,11 +18,7 @@ namespace llarp
|
|||
namespace handlers
|
||||
{
|
||||
ExitEndpoint::ExitEndpoint(std::string name, AbstractRouter* r)
|
||||
: m_Router(r)
|
||||
, m_Resolver(std::make_shared<dns::Proxy>(r->loop(), this))
|
||||
, m_Name(std::move(name))
|
||||
, m_LocalResolverAddr{"127.0.0.1:53"}
|
||||
, m_QUIC{std::make_shared<quic::TunnelManager>(*this)}
|
||||
: m_Router(r), m_Name(std::move(name)), m_QUIC{std::make_shared<quic::TunnelManager>(*this)}
|
||||
{
|
||||
m_ShouldInitTun = true;
|
||||
m_QUIC = std::make_shared<quic::TunnelManager>(*this);
|
||||
|
@ -113,7 +109,7 @@ namespace llarp
|
|||
{
|
||||
if (not itr->second->LooksDead(Now()))
|
||||
{
|
||||
if (itr->second->QueueInboundTraffic(ManagedBuffer{payload}, type))
|
||||
if (itr->second->QueueInboundTraffic(payload.copy(), type))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -121,10 +117,10 @@ namespace llarp
|
|||
if (not m_Router->PathToRouterAllowed(*rid))
|
||||
return false;
|
||||
|
||||
ObtainSNodeSession(*rid, [data = payload.copy(), type](auto session) {
|
||||
ObtainSNodeSession(*rid, [pkt = payload.copy(), type](auto session) mutable {
|
||||
if (session and session->IsReady())
|
||||
{
|
||||
session->SendPacketToRemote(data, type);
|
||||
session->SendPacketToRemote(std::move(pkt), type);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -211,6 +207,22 @@ namespace llarp
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ExitEndpoint::MaybeHookDNS(
|
||||
std::shared_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from)
|
||||
{
|
||||
if (not ShouldHookDNSMessage(query))
|
||||
return false;
|
||||
|
||||
auto job = std::make_shared<dns::QueryJob>(source, query, to, from);
|
||||
if (not HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); }))
|
||||
job->Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ExitEndpoint::HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)> reply)
|
||||
{
|
||||
|
@ -362,20 +374,24 @@ namespace llarp
|
|||
{
|
||||
while (not m_InetToNetwork.empty())
|
||||
{
|
||||
net::IPPacket pkt{m_InetToNetwork.top()};
|
||||
m_InetToNetwork.pop();
|
||||
auto& top = m_InetToNetwork.top();
|
||||
|
||||
PubKey pk;
|
||||
// get a session by public key
|
||||
std::optional<PubKey> maybe_pk;
|
||||
{
|
||||
auto itr = m_IPToKey.find(pkt.dstv6());
|
||||
if (itr == m_IPToKey.end())
|
||||
{
|
||||
// drop
|
||||
LogWarn(Name(), " dropping packet, has no session at ", pkt.dstv6());
|
||||
continue;
|
||||
}
|
||||
pk = itr->second;
|
||||
auto itr = m_IPToKey.find(top.dstv6());
|
||||
if (itr != m_IPToKey.end())
|
||||
maybe_pk = itr->second;
|
||||
}
|
||||
|
||||
auto buf = const_cast<net::IPPacket&>(top).steal();
|
||||
m_InetToNetwork.pop();
|
||||
// we have no session for public key so drop
|
||||
if (not maybe_pk)
|
||||
continue; // we are in a while loop
|
||||
|
||||
const auto& pk = *maybe_pk;
|
||||
|
||||
// check if this key is a service node
|
||||
if (m_SNodeKeys.count(pk))
|
||||
{
|
||||
|
@ -385,13 +401,14 @@ namespace llarp
|
|||
auto itr = m_SNodeSessions.find(pk);
|
||||
if (itr != m_SNodeSessions.end())
|
||||
{
|
||||
itr->second->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::TrafficV4);
|
||||
itr->second->SendPacketToRemote(std::move(buf), service::ProtocolType::TrafficV4);
|
||||
// we are in a while loop
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto tryFlushingTraffic = [&](exit::Endpoint* const ep) -> bool {
|
||||
if (!ep->QueueInboundTraffic(
|
||||
ManagedBuffer{pkt.Buffer()}, service::ProtocolType::TrafficV4))
|
||||
auto tryFlushingTraffic =
|
||||
[this, buf = std::move(buf), pk](exit::Endpoint* const ep) -> bool {
|
||||
if (!ep->QueueInboundTraffic(buf, service::ProtocolType::TrafficV4))
|
||||
{
|
||||
LogWarn(
|
||||
Name(),
|
||||
|
@ -439,13 +456,14 @@ namespace llarp
|
|||
m_IPToKey[ip] = us;
|
||||
m_IPActivity[ip] = std::numeric_limits<llarp_time_t>::max();
|
||||
m_SNodeKeys.insert(us);
|
||||
|
||||
if (m_ShouldInitTun)
|
||||
{
|
||||
vpn::InterfaceInfo info;
|
||||
info.ifname = m_ifname;
|
||||
info.addrs.emplace(m_OurRange);
|
||||
info.addrs.emplace_back(m_OurRange);
|
||||
|
||||
m_NetIf = GetRouter()->GetVPNPlatform()->ObtainInterface(std::move(info), m_Router);
|
||||
m_NetIf = GetRouter()->GetVPNPlatform()->CreateInterface(std::move(info), m_Router);
|
||||
if (not m_NetIf)
|
||||
{
|
||||
llarp::LogError("Could not create interface");
|
||||
|
@ -459,9 +477,12 @@ 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();
|
||||
|
||||
llarp::LogInfo("Trying to start resolver ", m_LocalResolverAddr);
|
||||
return m_Resolver->Start(m_LocalResolverAddr, m_UpstreamResolvers, {});
|
||||
#endif
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -586,7 +607,7 @@ namespace llarp
|
|||
rc.srvRecords.clear();
|
||||
for (auto& record : srvRecords)
|
||||
rc.srvRecords.emplace_back(record);
|
||||
// set the version to 1 because we have srv records
|
||||
// set the verssion to 1 because we have srv records
|
||||
rc.version = 1;
|
||||
return rc;
|
||||
});
|
||||
|
@ -641,8 +662,8 @@ namespace llarp
|
|||
bool
|
||||
ExitEndpoint::QueueSNodePacket(const llarp_buffer_t& buf, huint128_t from)
|
||||
{
|
||||
net::IPPacket pkt;
|
||||
if (!pkt.Load(buf))
|
||||
net::IPPacket pkt{buf.view_all()};
|
||||
if (pkt.empty())
|
||||
return false;
|
||||
// rewrite ip
|
||||
if (m_UseV6)
|
||||
|
@ -698,14 +719,14 @@ namespace llarp
|
|||
return true;
|
||||
}
|
||||
*/
|
||||
|
||||
m_DNSConf = dnsConfig;
|
||||
|
||||
if (networkConfig.m_endpointType == "null")
|
||||
{
|
||||
m_ShouldInitTun = false;
|
||||
}
|
||||
|
||||
m_LocalResolverAddr = dnsConfig.m_bind;
|
||||
m_UpstreamResolvers = dnsConfig.m_upstreamDNS;
|
||||
|
||||
m_OurRange = networkConfig.m_ifaddr;
|
||||
if (!m_OurRange.addr.h)
|
||||
{
|
||||
|
@ -736,8 +757,6 @@ namespace llarp
|
|||
return llarp::SockAddr{ifaddr, huint16_t{port}};
|
||||
});
|
||||
}
|
||||
// TODO: "exit-whitelist" and "exit-blacklist"
|
||||
// (which weren't originally implemented)
|
||||
}
|
||||
|
||||
huint128_t
|
||||
|
|
|
@ -10,8 +10,27 @@ namespace llarp
|
|||
struct AbstractRouter;
|
||||
namespace handlers
|
||||
{
|
||||
struct ExitEndpoint : public dns::IQueryHandler, public EndpointBase
|
||||
struct ExitEndpoint : public dns::Resolver_Base, public EndpointBase
|
||||
{
|
||||
int
|
||||
Rank() const override
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
std::string_view
|
||||
ResolverName() const override
|
||||
{
|
||||
return "snode";
|
||||
}
|
||||
|
||||
bool
|
||||
MaybeHookDNS(
|
||||
std::shared_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) override;
|
||||
|
||||
ExitEndpoint(std::string name, AbstractRouter* r);
|
||||
~ExitEndpoint() override;
|
||||
|
||||
|
@ -66,10 +85,10 @@ namespace llarp
|
|||
SupportsV6() const;
|
||||
|
||||
bool
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const override;
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const;
|
||||
|
||||
bool
|
||||
HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)>) override;
|
||||
HandleHookedDNSMessage(dns::Message msg, std::function<void(dns::Message)>);
|
||||
|
||||
void
|
||||
LookupServiceAsync(
|
||||
|
@ -174,7 +193,7 @@ namespace llarp
|
|||
KickIdentOffExit(const PubKey& pk);
|
||||
|
||||
AbstractRouter* m_Router;
|
||||
std::shared_ptr<dns::Proxy> m_Resolver;
|
||||
std::shared_ptr<dns::Server> m_Resolver;
|
||||
bool m_ShouldInitTun;
|
||||
std::string m_Name;
|
||||
bool m_PermitExit;
|
||||
|
@ -220,6 +239,7 @@ namespace llarp
|
|||
/// internet to llarp packet queue
|
||||
PacketQueue_t m_InetToNetwork;
|
||||
bool m_UseV6;
|
||||
DnsConfig m_DNSConf;
|
||||
};
|
||||
} // namespace handlers
|
||||
} // namespace llarp
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace llarp::handlers
|
|||
, m_PacketRouter{new vpn::EgresPacketRouter{[](auto from, auto pkt) {
|
||||
var::visit(
|
||||
[&pkt](auto&& from) {
|
||||
LogError("unhandled traffic from: ", from, " of ", pkt.sz, " bytes");
|
||||
LogError("unhandled traffic from: ", from, " of ", pkt.size(), " bytes");
|
||||
},
|
||||
from);
|
||||
}}}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/router/abstractrouter.hpp>
|
||||
#include <llarp/router/systemd_resolved.hpp>
|
||||
#include <llarp/router/route_poker.hpp>
|
||||
#include <llarp/service/context.hpp>
|
||||
#include <llarp/service/outbound_context.hpp>
|
||||
#include <llarp/service/endpoint_state.hpp>
|
||||
|
@ -24,7 +24,9 @@
|
|||
#include <llarp/quic/tunnel.hpp>
|
||||
#include <llarp/rpc/endpoint_rpc.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
#include <llarp/util/logging/buffer.hpp>
|
||||
#include <llarp/dns/srv_data.hpp>
|
||||
#include <llarp/constants/net.hpp>
|
||||
#include <llarp/constants/platform.hpp>
|
||||
|
||||
#include <oxenc/bt.h>
|
||||
|
@ -33,83 +35,180 @@ namespace llarp
|
|||
{
|
||||
namespace handlers
|
||||
{
|
||||
// Intercepts DNS IP packets going to an IP on the tun interface; this is currently used on
|
||||
// Android and macOS where binding to a DNS port (i.e. via llarp::dns::Proxy) isn't possible
|
||||
// because of OS restrictions, but a tun interface *is* available.
|
||||
class DnsInterceptor : public dns::PacketHandler
|
||||
bool
|
||||
TunEndpoint::MaybeHookDNS(
|
||||
std::shared_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from)
|
||||
{
|
||||
public:
|
||||
TunEndpoint* const m_Endpoint;
|
||||
if (not ShouldHookDNSMessage(query))
|
||||
return false;
|
||||
|
||||
explicit DnsInterceptor(AbstractRouter* router, TunEndpoint* ep)
|
||||
: dns::PacketHandler{router->loop(), ep}, m_Endpoint{ep} {};
|
||||
auto job = std::make_shared<dns::QueryJob>(source, query, to, from);
|
||||
if (HandleHookedDNSMessage(query, [job](auto msg) { job->SendReply(msg.ToBuffer()); }))
|
||||
Router()->TriggerPump();
|
||||
else
|
||||
job->Cancel();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Intercepts DNS IP packets on platforms where binding to a low port isn't viable.
|
||||
/// (windows/macos/ios/android ... aka everything that is not linux... funny that)
|
||||
class DnsInterceptor : public dns::PacketSource_Base
|
||||
{
|
||||
std::function<void(net::IPPacket)> m_Reply;
|
||||
net::ipaddr_t m_OurIP;
|
||||
llarp::DnsConfig m_Config;
|
||||
|
||||
public:
|
||||
explicit DnsInterceptor(
|
||||
std::function<void(net::IPPacket)> reply, net::ipaddr_t our_ip, llarp::DnsConfig conf)
|
||||
: m_Reply{std::move(reply)}, m_OurIP{std::move(our_ip)}, m_Config{std::move(conf)}
|
||||
{}
|
||||
|
||||
~DnsInterceptor() override = default;
|
||||
|
||||
void
|
||||
SendServerMessageBufferTo(
|
||||
const SockAddr& to, const SockAddr& from, llarp_buffer_t buf) override
|
||||
SendTo(const SockAddr& to, const SockAddr& from, OwnedBuffer buf) const override
|
||||
{
|
||||
const auto pkt = net::IPPacket::UDP(
|
||||
from.getIPv4(),
|
||||
ToNet(huint16_t{from.getPort()}),
|
||||
to.getIPv4(),
|
||||
ToNet(huint16_t{to.getPort()}),
|
||||
buf);
|
||||
auto pkt = net::IPPacket::make_udp(from, to, std::move(buf));
|
||||
|
||||
if (pkt.sz == 0)
|
||||
if (pkt.empty())
|
||||
return;
|
||||
m_Endpoint->HandleWriteIPPacket(
|
||||
pkt.ConstBuffer(), net::ExpandV4(from.asIPv4()), net::ExpandV4(to.asIPv4()), 0);
|
||||
m_Reply(std::move(pkt));
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
bool
|
||||
IsUpstreamResolver(const SockAddr&, const SockAddr&) const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
void
|
||||
Stop() override{};
|
||||
|
||||
#ifdef __APPLE__
|
||||
// DNS on Apple is a bit weird because in order for the NetworkExtension itself to send data
|
||||
// through the tunnel we have to proxy DNS requests through Apple APIs (and so our actual
|
||||
// upstream DNS won't be set in our resolvers, which is why the vanilla IsUpstreamResolver,
|
||||
// above, won't work for us). However when active the mac also only queries the main tunnel
|
||||
// IP for DNS, so we consider anything else to be upstream-bound DNS to let it through the
|
||||
// tunnel.
|
||||
bool
|
||||
IsUpstreamResolver(const SockAddr& to, const SockAddr& from) const override
|
||||
std::optional<SockAddr>
|
||||
BoundOn() const override
|
||||
{
|
||||
return to.asIPv6() != m_Endpoint->GetIfAddr();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool
|
||||
WouldLoop(const SockAddr& to, const SockAddr& from) const override
|
||||
{
|
||||
if constexpr (platform::is_apple)
|
||||
{
|
||||
// DNS on Apple is a bit weird because in order for the NetworkExtension itself to send
|
||||
// data through the tunnel we have to proxy DNS requests through Apple APIs (and so our
|
||||
// actual upstream DNS won't be set in our resolvers, which is why the vanilla WouldLoop
|
||||
// won't work for us). However when active the mac also only queries the main tunnel IP
|
||||
// for DNS, so we consider anything else to be upstream-bound DNS to let it through the
|
||||
// tunnel.
|
||||
return to.getIP() != m_OurIP;
|
||||
}
|
||||
else if (auto maybe_addr = m_Config.m_QueryBind)
|
||||
{
|
||||
const auto& addr = *maybe_addr;
|
||||
// omit traffic to and from our dns socket
|
||||
return addr == to or addr == from;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class TunDNS : public dns::Server
|
||||
{
|
||||
std::optional<SockAddr> m_QueryBind;
|
||||
net::ipaddr_t m_OurIP;
|
||||
TunEndpoint* const 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}
|
||||
, m_QueryBind{conf.m_QueryBind}
|
||||
, m_OurIP{ToNet(ep->GetIfAddr())}
|
||||
, m_Endpoint{ep}
|
||||
{}
|
||||
|
||||
std::shared_ptr<dns::PacketSource_Base>
|
||||
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);
|
||||
},
|
||||
m_OurIP,
|
||||
conf);
|
||||
PacketSource = ptr;
|
||||
return ptr;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
TunEndpoint::TunEndpoint(AbstractRouter* r, service::Context* parent)
|
||||
: service::Endpoint(r, parent)
|
||||
: service::Endpoint{r, parent}
|
||||
{
|
||||
m_PacketRouter = std::make_unique<vpn::PacketRouter>(
|
||||
m_PacketRouter = std::make_shared<vpn::PacketRouter>(
|
||||
[this](net::IPPacket pkt) { HandleGotUserPacket(std::move(pkt)); });
|
||||
#if defined(ANDROID) || (defined(__APPLE__) && !defined(MACOS_SYSTEM_EXTENSION))
|
||||
m_Resolver = std::make_shared<DnsInterceptor>(r, this);
|
||||
m_PacketRouter->AddUDPHandler(huint16_t{53}, [&](net::IPPacket pkt) {
|
||||
const size_t ip_header_size = (pkt.Header()->ihl * 4);
|
||||
}
|
||||
|
||||
const uint8_t* ptr = pkt.buf + ip_header_size;
|
||||
const auto dst = ToNet(pkt.dstv4());
|
||||
const auto src = ToNet(pkt.srcv4());
|
||||
const SockAddr laddr{src, nuint16_t{*reinterpret_cast<const uint16_t*>(ptr)}};
|
||||
const SockAddr raddr{dst, nuint16_t{*reinterpret_cast<const uint16_t*>(ptr + 2)}};
|
||||
void
|
||||
TunEndpoint::SetupDNS()
|
||||
{
|
||||
const auto& info = GetVPNInterface()->Info();
|
||||
if (m_DnsConfig.m_raw_dns)
|
||||
{
|
||||
auto dns = std::make_shared<TunDNS>(this, m_DnsConfig);
|
||||
m_DNS = dns;
|
||||
|
||||
m_PacketRouter->AddUDPHandler(huint16_t{53}, [this, dns](net::IPPacket pkt) {
|
||||
auto dns_pkt_src = dns->PacketSource;
|
||||
if (const auto& reply = pkt.reply)
|
||||
dns_pkt_src = std::make_shared<dns::PacketSource_Wrapper>(dns_pkt_src, reply);
|
||||
if (dns->MaybeHandlePacket(
|
||||
std::move(dns_pkt_src), pkt.dst(), pkt.src(), *pkt.L4OwnedBuffer()))
|
||||
return;
|
||||
|
||||
OwnedBuffer buf{pkt.sz - (8 + ip_header_size)};
|
||||
std::copy_n(ptr + 8, buf.sz, buf.buf.get());
|
||||
if (m_Resolver->ShouldHandlePacket(raddr, laddr, buf))
|
||||
m_Resolver->HandlePacket(raddr, laddr, buf);
|
||||
else
|
||||
HandleGotUserPacket(std::move(pkt));
|
||||
});
|
||||
#else
|
||||
m_Resolver = std::make_shared<dns::Proxy>(r->loop(), this);
|
||||
#endif
|
||||
});
|
||||
}
|
||||
else
|
||||
m_DNS = std::make_shared<dns::Server>(Loop(), m_DnsConfig, info.index);
|
||||
|
||||
m_DNS->AddResolver(weak_from_this());
|
||||
m_DNS->Start();
|
||||
|
||||
if (m_DnsConfig.m_raw_dns)
|
||||
{
|
||||
if (auto vpn = Router()->GetVPNPlatform())
|
||||
{
|
||||
// get the first local address we know of
|
||||
std::optional<SockAddr> localaddr;
|
||||
for (auto res : m_DNS->GetAllResolvers())
|
||||
{
|
||||
if (auto ptr = res.lock())
|
||||
{
|
||||
localaddr = ptr->GetLocalAddr();
|
||||
if (localaddr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (platform::is_windows)
|
||||
{
|
||||
auto dns_io = vpn->create_packet_io(0, localaddr);
|
||||
Router()->loop()->add_ticker([r = Router(), dns_io, handler = m_PacketRouter]() {
|
||||
net::IPPacket pkt = dns_io->ReadNextPacket();
|
||||
while (not pkt.empty())
|
||||
{
|
||||
handler->HandleIPPacket(std::move(pkt));
|
||||
pkt = dns_io->ReadNextPacket();
|
||||
}
|
||||
});
|
||||
m_RawDNS = dns_io;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_RawDNS)
|
||||
m_RawDNS->Start();
|
||||
}
|
||||
}
|
||||
|
||||
util::StatusObject
|
||||
|
@ -118,11 +217,21 @@ namespace llarp
|
|||
auto obj = service::Endpoint::ExtractStatus();
|
||||
obj["ifaddr"] = m_OurRange.ToString();
|
||||
obj["ifname"] = m_IfName;
|
||||
std::vector<std::string> resolvers;
|
||||
for (const auto& addr : m_UpstreamResolvers)
|
||||
resolvers.emplace_back(addr.ToString());
|
||||
obj["ustreamResolvers"] = resolvers;
|
||||
obj["localResolver"] = m_LocalResolverAddr.ToString();
|
||||
|
||||
std::vector<std::string> upstreamRes;
|
||||
for (const auto& ent : m_DnsConfig.m_upstreamDNS)
|
||||
upstreamRes.emplace_back(ent.ToString());
|
||||
obj["ustreamResolvers"] = upstreamRes;
|
||||
|
||||
std::vector<std::string> localRes;
|
||||
for (const auto& ent : m_DnsConfig.m_bind)
|
||||
localRes.emplace_back(ent.ToString());
|
||||
obj["localResolvers"] = localRes;
|
||||
|
||||
// for backwards compat
|
||||
if (not m_DnsConfig.m_bind.empty())
|
||||
obj["localResolver"] = localRes[0];
|
||||
|
||||
util::StatusObject ips{};
|
||||
for (const auto& item : m_IPActivity)
|
||||
{
|
||||
|
@ -147,19 +256,21 @@ namespace llarp
|
|||
void
|
||||
TunEndpoint::Thaw()
|
||||
{
|
||||
if (m_Resolver)
|
||||
m_Resolver->Restart();
|
||||
if (m_DNS)
|
||||
m_DNS->Reset();
|
||||
}
|
||||
|
||||
std::vector<SockAddr>
|
||||
void
|
||||
TunEndpoint::ReconfigureDNS(std::vector<SockAddr> servers)
|
||||
{
|
||||
std::swap(m_UpstreamResolvers, servers);
|
||||
m_Resolver->Stop();
|
||||
if (!m_Resolver->Start(
|
||||
m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers, m_hostfiles))
|
||||
llarp::LogError(Name(), " failed to reconfigure DNS server");
|
||||
return servers;
|
||||
if (m_DNS)
|
||||
{
|
||||
for (auto weak : m_DNS->GetAllResolvers())
|
||||
{
|
||||
if (auto ptr = weak.lock())
|
||||
ptr->ResetResolver(servers);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -199,13 +310,10 @@ namespace llarp
|
|||
m_AuthPolicy = std::move(auth);
|
||||
}
|
||||
|
||||
m_DnsConfig = dnsConf;
|
||||
m_TrafficPolicy = conf.m_TrafficPolicy;
|
||||
m_OwnedRanges = conf.m_OwnedRanges;
|
||||
|
||||
m_LocalResolverAddr = dnsConf.m_bind;
|
||||
m_UpstreamResolvers = dnsConf.m_upstreamDNS;
|
||||
m_hostfiles = dnsConf.m_hostfiles;
|
||||
|
||||
m_BaseV6Address = conf.m_baseV6Address;
|
||||
|
||||
if (conf.m_PathAlignmentTimeout)
|
||||
|
@ -354,7 +462,6 @@ namespace llarp
|
|||
return llarp::SockAddr{net::TruncateV6(GetIfAddr()), huint16_t{port}};
|
||||
});
|
||||
}
|
||||
|
||||
return Endpoint::Configure(conf, dnsConf);
|
||||
}
|
||||
|
||||
|
@ -763,20 +870,29 @@ namespace llarp
|
|||
}
|
||||
else if (msg.questions[0].qtype == dns::qTypeSRV)
|
||||
{
|
||||
llarp::service::Address addr;
|
||||
|
||||
auto srv_for = msg.questions[0].Subdomains();
|
||||
auto name = msg.questions[0].qname;
|
||||
if (is_localhost_loki(msg))
|
||||
{
|
||||
msg.AddSRVReply(introSet().GetMatchingSRVRecords(msg.questions[0].Subdomains()));
|
||||
msg.AddSRVReply(introSet().GetMatchingSRVRecords(srv_for));
|
||||
reply(msg);
|
||||
return true;
|
||||
}
|
||||
else if (addr.FromString(qname, ".loki"))
|
||||
{
|
||||
llarp::LogDebug("SRV request for: ", qname);
|
||||
|
||||
return ReplyToLokiSRVWhenReady(addr, std::make_shared<dns::Message>(msg));
|
||||
}
|
||||
LookupServiceAsync(
|
||||
name,
|
||||
srv_for,
|
||||
[reply, msg = std::make_shared<dns::Message>(std::move(msg))](auto records) {
|
||||
if (records.empty())
|
||||
{
|
||||
msg->AddNXReply();
|
||||
}
|
||||
else
|
||||
{
|
||||
msg->AddSRVReply(records);
|
||||
}
|
||||
reply(*msg);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -862,11 +978,8 @@ namespace llarp
|
|||
bool
|
||||
TunEndpoint::Start()
|
||||
{
|
||||
if (!Endpoint::Start())
|
||||
{
|
||||
llarp::LogWarn("Couldn't start endpoint");
|
||||
if (not Endpoint::Start())
|
||||
return false;
|
||||
}
|
||||
return SetupNetworking();
|
||||
}
|
||||
|
||||
|
@ -893,62 +1006,59 @@ namespace llarp
|
|||
}
|
||||
|
||||
vpn::InterfaceInfo info;
|
||||
info.addrs.emplace(m_OurRange);
|
||||
info.addrs.emplace_back(m_OurRange);
|
||||
|
||||
if (m_BaseV6Address)
|
||||
{
|
||||
IPRange v6range = m_OurRange;
|
||||
v6range.addr = (*m_BaseV6Address) | m_OurRange.addr;
|
||||
LogInfo(Name(), " using v6 range: ", v6range);
|
||||
info.addrs.emplace(v6range, AF_INET6);
|
||||
info.addrs.emplace_back(v6range, AF_INET6);
|
||||
}
|
||||
|
||||
info.ifname = m_IfName;
|
||||
info.dnsaddr.FromString(m_LocalResolverAddr.toHost());
|
||||
|
||||
LogInfo(Name(), " setting up network...");
|
||||
|
||||
try
|
||||
{
|
||||
m_NetIf = Router()->GetVPNPlatform()->ObtainInterface(std::move(info), Router());
|
||||
m_NetIf = Router()->GetVPNPlatform()->CreateInterface(std::move(info), Router());
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
LogError(Name(), " failed to set up network interface: ", ex.what());
|
||||
}
|
||||
if (not m_NetIf)
|
||||
{
|
||||
LogError(Name(), " failed to obtain network interface");
|
||||
return false;
|
||||
}
|
||||
m_IfName = m_NetIf->IfName();
|
||||
|
||||
m_IfName = m_NetIf->Info().ifname;
|
||||
LogInfo(Name(), " got network interface ", m_IfName);
|
||||
|
||||
if (not Router()->loop()->add_network_interface(m_NetIf, [this](net::IPPacket pkt) {
|
||||
m_PacketRouter->HandleIPPacket(std::move(pkt));
|
||||
}))
|
||||
auto handle_packet = [netif = m_NetIf, pkt_router = m_PacketRouter](auto pkt) {
|
||||
pkt.reply = [netif](auto pkt) { netif->WritePacket(std::move(pkt)); };
|
||||
pkt_router->HandleIPPacket(std::move(pkt));
|
||||
};
|
||||
|
||||
if (not Router()->loop()->add_network_interface(m_NetIf, std::move(handle_packet)))
|
||||
{
|
||||
LogError(Name(), " failed to add network interface");
|
||||
return false;
|
||||
}
|
||||
#ifdef __APPLE__
|
||||
|
||||
m_OurIPv6 = llarp::huint128_t{
|
||||
llarp::uint128_t{0xfd2e'6c6f'6b69'0000, llarp::net::TruncateV6(m_OurRange.addr).h}};
|
||||
#else
|
||||
const auto maybe = m_router->Net().GetInterfaceIPv6Address(m_IfName);
|
||||
if (maybe.has_value())
|
||||
|
||||
if constexpr (not llarp::platform::is_apple)
|
||||
{
|
||||
m_OurIPv6 = *maybe;
|
||||
LogInfo(Name(), " has ipv6 address ", m_OurIPv6);
|
||||
if (auto maybe = m_router->Net().GetInterfaceIPv6Address(m_IfName))
|
||||
{
|
||||
m_OurIPv6 = *maybe;
|
||||
LogInfo(Name(), " has ipv6 address ", m_OurIPv6);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Attempt to register DNS on the interface
|
||||
systemd_resolved_set_dns(
|
||||
m_IfName,
|
||||
m_LocalResolverAddr.createSockAddr(),
|
||||
false /* just .loki/.snode DNS initially */);
|
||||
|
||||
LogInfo(Name(), " setting up dns...");
|
||||
SetupDNS();
|
||||
Loop()->call_soon([this]() { m_router->routePoker()->SetDNSMode(false); });
|
||||
return HasAddress(ourAddr);
|
||||
}
|
||||
|
||||
|
@ -970,18 +1080,7 @@ namespace llarp
|
|||
TunEndpoint::SetupNetworking()
|
||||
{
|
||||
llarp::LogInfo("Set Up networking for ", Name());
|
||||
if (!SetupTun())
|
||||
{
|
||||
llarp::LogError(Name(), " failed to set up network interface");
|
||||
return false;
|
||||
}
|
||||
if (!m_Resolver->Start(
|
||||
m_LocalResolverAddr.createSockAddr(), m_UpstreamResolvers, m_hostfiles))
|
||||
{
|
||||
llarp::LogError(Name(), " failed to start DNS server");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return SetupTun();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -993,9 +1092,13 @@ namespace llarp
|
|||
bool
|
||||
TunEndpoint::Stop()
|
||||
{
|
||||
// stop vpn tunnel
|
||||
if (m_NetIf)
|
||||
m_NetIf->Stop();
|
||||
if (m_RawDNS)
|
||||
m_RawDNS->Stop();
|
||||
// save address map if applicable
|
||||
#ifndef ANDROID
|
||||
if (m_PersistAddrMapFile)
|
||||
if (m_PersistAddrMapFile and not platform::is_android)
|
||||
{
|
||||
const auto& file = *m_PersistAddrMapFile;
|
||||
LogInfo(Name(), " saving address map to ", file);
|
||||
|
@ -1015,9 +1118,8 @@ namespace llarp
|
|||
maybe->write(data.data(), data.size());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (m_Resolver)
|
||||
m_Resolver->Stop();
|
||||
if (m_DNS)
|
||||
m_DNS->Stop();
|
||||
return llarp::service::Endpoint::Stop();
|
||||
}
|
||||
|
||||
|
@ -1029,12 +1131,18 @@ namespace llarp
|
|||
// is it already mapped? return the mapping
|
||||
if (auto itr = m_ExitIPToExitAddress.find(ip); itr != m_ExitIPToExitAddress.end())
|
||||
return itr->second;
|
||||
|
||||
const auto& net = m_router->Net();
|
||||
const bool is_bogon = net.IsBogonIP(ip);
|
||||
// build up our candidates to choose
|
||||
|
||||
std::unordered_set<service::Address> candidates;
|
||||
for (const auto& entry : m_ExitMap.FindAllEntries(ip))
|
||||
{
|
||||
// make sure it is allowed by the range if the ip is a bogon
|
||||
if (not IsBogon(ip) or entry.first.BogonContains(ip))
|
||||
// in the event the exit's range is a bogon range, make sure the ip is located in that range
|
||||
// to allow it
|
||||
if ((is_bogon and net.IsBogonRange(entry.first) and entry.first.Contains(ip))
|
||||
or entry.first.Contains(ip))
|
||||
candidates.emplace(entry.second);
|
||||
}
|
||||
// no candidates? bail.
|
||||
|
@ -1096,13 +1204,20 @@ namespace llarp
|
|||
|
||||
return;
|
||||
}
|
||||
std::function<void(void)> extra_cb;
|
||||
if (not HasFlowToService(addr))
|
||||
{
|
||||
extra_cb = [poker = Router()->routePoker()]() { poker->Up(); };
|
||||
}
|
||||
pkt.ZeroSourceAddress();
|
||||
MarkAddressOutbound(addr);
|
||||
EnsurePathToService(
|
||||
addr,
|
||||
[pkt, this](service::Address addr, service::OutboundContext* ctx) {
|
||||
[pkt, extra_cb, this](service::Address addr, service::OutboundContext* ctx) {
|
||||
if (ctx)
|
||||
{
|
||||
if (extra_cb)
|
||||
extra_cb();
|
||||
ctx->SendPacketToRemote(pkt.ConstBuffer(), service::ProtocolType::Exit);
|
||||
Router()->TriggerPump();
|
||||
return;
|
||||
|
|
|
@ -2,33 +2,56 @@
|
|||
|
||||
#include <llarp/dns/server.hpp>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include <llarp/ev/vpn.hpp>
|
||||
#include <llarp/net/ip.hpp>
|
||||
#include <llarp/net/ip_packet.hpp>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <llarp/service/endpoint.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
#include <llarp/vpn/packet_router.hpp>
|
||||
|
||||
#include <future>
|
||||
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
#include <llarp/service/protocol_type.hpp>
|
||||
#include <llarp/util/priority_queue.hpp>
|
||||
#include <llarp/util/thread/threading.hpp>
|
||||
#include <llarp/vpn/packet_router.hpp>
|
||||
#include <llarp/vpn/platform.hpp>
|
||||
|
||||
#include <future>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
namespace handlers
|
||||
{
|
||||
struct TunEndpoint : public service::Endpoint,
|
||||
public dns::IQueryHandler,
|
||||
public dns::Resolver_Base,
|
||||
public std::enable_shared_from_this<TunEndpoint>
|
||||
{
|
||||
TunEndpoint(AbstractRouter* r, llarp::service::Context* parent);
|
||||
~TunEndpoint() override;
|
||||
|
||||
vpn::NetworkInterface*
|
||||
GetVPNInterface() override
|
||||
{
|
||||
return m_NetIf.get();
|
||||
}
|
||||
|
||||
int
|
||||
Rank() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string_view
|
||||
ResolverName() const override
|
||||
{
|
||||
return "lokinet";
|
||||
}
|
||||
|
||||
bool
|
||||
MaybeHookDNS(
|
||||
std::shared_ptr<dns::PacketSource_Base> source,
|
||||
const dns::Message& query,
|
||||
const SockAddr& to,
|
||||
const SockAddr& from) override;
|
||||
|
||||
path::PathSet_ptr
|
||||
GetSelf() override
|
||||
{
|
||||
|
@ -44,9 +67,8 @@ namespace llarp
|
|||
void
|
||||
Thaw() override;
|
||||
|
||||
// Reconfigures DNS servers and restarts libunbound with the new servers. Returns the old set
|
||||
// of configured dns servers.
|
||||
std::vector<SockAddr>
|
||||
// Reconfigures DNS servers and restarts libunbound with the new servers.
|
||||
void
|
||||
ReconfigureDNS(std::vector<SockAddr> servers);
|
||||
|
||||
bool
|
||||
|
@ -71,11 +93,10 @@ namespace llarp
|
|||
SupportsV6() const override;
|
||||
|
||||
bool
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const override;
|
||||
ShouldHookDNSMessage(const dns::Message& msg) const;
|
||||
|
||||
bool
|
||||
HandleHookedDNSMessage(
|
||||
dns::Message query, std::function<void(dns::Message)> sendreply) override;
|
||||
HandleHookedDNSMessage(dns::Message query, std::function<void(dns::Message)> sendreply);
|
||||
|
||||
void
|
||||
TickTun(llarp_time_t now);
|
||||
|
@ -96,6 +117,16 @@ namespace llarp
|
|||
bool
|
||||
SetupTun();
|
||||
|
||||
void
|
||||
SetupDNS();
|
||||
|
||||
/// overrides Endpoint
|
||||
std::shared_ptr<dns::Server>
|
||||
DNS() const override
|
||||
{
|
||||
return m_DNS;
|
||||
};
|
||||
|
||||
/// overrides Endpoint
|
||||
bool
|
||||
SetupNetworking() override;
|
||||
|
@ -249,8 +280,11 @@ namespace llarp
|
|||
query->AddNXReply();
|
||||
reply(*query);
|
||||
}
|
||||
/// our dns resolver
|
||||
std::shared_ptr<dns::PacketHandler> m_Resolver;
|
||||
|
||||
/// dns subsystem for this endpoint
|
||||
std::shared_ptr<dns::Server> m_DNS;
|
||||
|
||||
DnsConfig m_DnsConfig;
|
||||
|
||||
/// maps ip address to timestamp last active
|
||||
std::unordered_map<huint128_t, llarp_time_t> m_IPActivity;
|
||||
|
@ -265,12 +299,6 @@ namespace llarp
|
|||
huint128_t m_MaxIP;
|
||||
/// our ip range we are using
|
||||
llarp::IPRange m_OurRange;
|
||||
/// upstream dns resolver list
|
||||
std::vector<SockAddr> m_UpstreamResolvers;
|
||||
/// dns host files list
|
||||
std::vector<fs::path> m_hostfiles;
|
||||
/// local dns
|
||||
IpAddress m_LocalResolverAddr;
|
||||
/// list of strict connect addresses for hooks
|
||||
std::vector<IpAddress> m_StrictConnectAddrs;
|
||||
/// use v6?
|
||||
|
@ -281,7 +309,7 @@ namespace llarp
|
|||
|
||||
std::shared_ptr<vpn::NetworkInterface> m_NetIf;
|
||||
|
||||
std::unique_ptr<vpn::PacketRouter> m_PacketRouter;
|
||||
std::shared_ptr<vpn::PacketRouter> m_PacketRouter;
|
||||
|
||||
std::optional<net::TrafficPolicy> m_TrafficPolicy;
|
||||
/// ranges we advetise as reachable
|
||||
|
@ -291,6 +319,9 @@ namespace llarp
|
|||
|
||||
/// a file to load / store the ephemeral address map to
|
||||
std::optional<fs::path> m_PersistAddrMapFile;
|
||||
|
||||
/// for raw packet dns
|
||||
std::shared_ptr<vpn::I_Packet_IO> m_RawDNS;
|
||||
};
|
||||
|
||||
} // namespace handlers
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
#ifdef WITH_SYSTEMD
|
||||
#include "dbus.hpp"
|
||||
|
||||
namespace llarp::linux
|
||||
{
|
||||
system_bus_exception::system_bus_exception(int err)
|
||||
: std::runtime_error{"cannot connect to system bus: " + std::string{strerror(-err)}}
|
||||
{}
|
||||
|
||||
dbus_call_exception::dbus_call_exception(std::string meth, int err)
|
||||
: std::runtime_error{
|
||||
"failed to call dbus function '" + meth + "': " + std::string{strerror(-err)}}
|
||||
{}
|
||||
|
||||
DBUS::DBUS(std::string _interface, std::string _instance, std::string _call)
|
||||
: m_interface{std::move(_interface)}
|
||||
, m_instance{std::move(_instance)}
|
||||
, m_call{std::move(_call)}
|
||||
{
|
||||
sd_bus* bus{nullptr};
|
||||
if (auto err = sd_bus_open_system(&bus); err < 0)
|
||||
throw system_bus_exception{err};
|
||||
m_ptr.reset(bus);
|
||||
}
|
||||
} // namespace llarp::linux
|
||||
#endif
|
|
@ -0,0 +1,69 @@
|
|||
#pragma once
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include <systemd/sd-bus.h>
|
||||
}
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace llarp::linux
|
||||
{
|
||||
/// exception for connecting to system bus
|
||||
class system_bus_exception : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit system_bus_exception(int err);
|
||||
};
|
||||
|
||||
/// exception for a failed calling of a dbus method
|
||||
class dbus_call_exception : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit dbus_call_exception(std::string meth, int err);
|
||||
};
|
||||
|
||||
class DBUS
|
||||
{
|
||||
struct sd_bus_deleter
|
||||
{
|
||||
void
|
||||
operator()(sd_bus* ptr) const
|
||||
{
|
||||
sd_bus_unref(ptr);
|
||||
}
|
||||
};
|
||||
std::unique_ptr<sd_bus, sd_bus_deleter> m_ptr;
|
||||
const std::string m_interface;
|
||||
const std::string m_instance;
|
||||
const std::string m_call;
|
||||
|
||||
public:
|
||||
DBUS(std::string _interface, std::string _instance, std::string _call);
|
||||
|
||||
template <typename... T>
|
||||
void
|
||||
operator()(std::string method, const char* arg_format, T... args)
|
||||
{
|
||||
sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus_message* msg = nullptr;
|
||||
auto r = sd_bus_call_method(
|
||||
m_ptr.get(),
|
||||
m_interface.c_str(),
|
||||
m_instance.c_str(),
|
||||
m_call.c_str(),
|
||||
method.c_str(),
|
||||
&error,
|
||||
&msg,
|
||||
arg_format,
|
||||
args...);
|
||||
|
||||
if (r < 0)
|
||||
throw dbus_call_exception{std::move(method), r};
|
||||
|
||||
sd_bus_message_unref(msg);
|
||||
sd_bus_error_free(&error);
|
||||
}
|
||||
};
|
||||
} // namespace llarp::linux
|
|
@ -228,7 +228,7 @@ struct lokinet_context
|
|||
[[nodiscard]] std::optional<int>
|
||||
make_udp_handler(
|
||||
const std::shared_ptr<llarp::service::Endpoint>& ep,
|
||||
llarp::huint16_t exposePort,
|
||||
llarp::net::port_t exposePort,
|
||||
lokinet_udp_flow_filter filter,
|
||||
lokinet_udp_flow_recv_func recv,
|
||||
lokinet_udp_flow_timeout_func timeout,
|
||||
|
@ -245,16 +245,16 @@ struct lokinet_context
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
std::weak_ptr<llarp::service::Endpoint> weak{ep};
|
||||
auto udp = std::make_shared<UDPHandler>(
|
||||
next_socket_id(), llarp::ToNet(exposePort), filter, recv, timeout, user, std::weak_ptr{ep});
|
||||
next_socket_id(), exposePort, filter, recv, timeout, user, weak);
|
||||
auto id = udp->m_SocketID;
|
||||
std::promise<bool> result;
|
||||
|
||||
impl->router->loop()->call([ep, &result, udp, exposePort]() {
|
||||
if (auto pkt = ep->EgresPacketRouter())
|
||||
{
|
||||
pkt->AddUDPHandler(exposePort, [udp](auto from, auto pkt) {
|
||||
pkt->AddUDPHandler(llarp::net::ToHost(exposePort), [udp](auto from, auto pkt) {
|
||||
udp->HandlePacketFrom(std::move(from), std::move(pkt));
|
||||
});
|
||||
result.set_value(true);
|
||||
|
@ -903,8 +903,8 @@ extern "C"
|
|||
auto lock = ctx->acquire();
|
||||
if (auto ep = ctx->endpoint())
|
||||
{
|
||||
if (auto maybe =
|
||||
ctx->make_udp_handler(ep, llarp::huint16_t{exposedPort}, filter, recv, timeout, user))
|
||||
if (auto maybe = ctx->make_udp_handler(
|
||||
ep, llarp::net::port_t::from_host(exposedPort), filter, recv, timeout, user))
|
||||
{
|
||||
result->socket_id = *maybe;
|
||||
return 0;
|
||||
|
@ -934,7 +934,7 @@ extern "C"
|
|||
return EINVAL;
|
||||
std::shared_ptr<llarp::EndpointBase> ep;
|
||||
llarp::nuint16_t srcport{0};
|
||||
llarp::nuint16_t dstport{llarp::ToNet(llarp::huint16_t{remote->remote_port})};
|
||||
auto dstport = llarp::net::port_t::from_host(remote->remote_port);
|
||||
{
|
||||
auto lock = ctx->acquire();
|
||||
if (auto itr = ctx->udp_sockets.find(remote->socket_id); itr != ctx->udp_sockets.end())
|
||||
|
@ -954,7 +954,7 @@ extern "C"
|
|||
dstport,
|
||||
llarp_buffer_t{reinterpret_cast<const uint8_t*>(ptr), len});
|
||||
|
||||
if (pkt.sz == 0)
|
||||
if (pkt.empty())
|
||||
return EINVAL;
|
||||
std::promise<int> ret;
|
||||
ctx->impl->router->loop()->call([addr = *maybe, pkt = std::move(pkt), ep, &ret]() {
|
||||
|
|
|
@ -14,9 +14,9 @@ namespace llarp
|
|||
bool
|
||||
DHTImmediateMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
if (key == "m")
|
||||
if (key.startswith("m"))
|
||||
return llarp::dht::DecodeMesssageList(dht::Key_t(session->GetPubKey()), buf, msgs);
|
||||
if (key == "v")
|
||||
if (key.startswith("v"))
|
||||
{
|
||||
if (!bencode_read_integer(buf, &version))
|
||||
return false;
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace llarp
|
|||
bool
|
||||
DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf) override
|
||||
{
|
||||
if (key == "a")
|
||||
if (key.startswith("a"))
|
||||
{
|
||||
llarp_buffer_t strbuf;
|
||||
if (!bencode_read_string(buf, &strbuf))
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace llarp
|
|||
bool
|
||||
LinkIntroMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
if (key == "a")
|
||||
if (key.startswith("a"))
|
||||
{
|
||||
llarp_buffer_t strbuf;
|
||||
if (!bencode_read_string(buf, &strbuf))
|
||||
|
@ -20,18 +20,18 @@ namespace llarp
|
|||
return false;
|
||||
return *strbuf.cur == 'i';
|
||||
}
|
||||
if (key == "n")
|
||||
if (key.startswith("n"))
|
||||
{
|
||||
if (N.BDecode(buf))
|
||||
return true;
|
||||
llarp::LogWarn("failed to decode nonce in LIM");
|
||||
return false;
|
||||
}
|
||||
if (key == "p")
|
||||
if (key.startswith("p"))
|
||||
{
|
||||
return bencode_read_integer(buf, &P);
|
||||
}
|
||||
if (key == "r")
|
||||
if (key.startswith("r"))
|
||||
{
|
||||
if (rc.BDecode(buf))
|
||||
return true;
|
||||
|
@ -39,7 +39,7 @@ namespace llarp
|
|||
llarp::DumpBuffer(*buf);
|
||||
return false;
|
||||
}
|
||||
if (key == "v")
|
||||
if (key.startswith("v"))
|
||||
{
|
||||
if (!bencode_read_integer(buf, &version))
|
||||
return false;
|
||||
|
@ -52,7 +52,7 @@ namespace llarp
|
|||
llarp::LogDebug("LIM version ", version);
|
||||
return true;
|
||||
}
|
||||
if (key == "z")
|
||||
if (key.startswith("z"))
|
||||
{
|
||||
return Z.BDecode(buf);
|
||||
}
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace llarp
|
|||
if (!key)
|
||||
return false;
|
||||
// we are expecting the first key to be 'a'
|
||||
if (!(*key == "a"))
|
||||
if (!key->startswith("a"))
|
||||
{
|
||||
llarp::LogWarn("message has no message type");
|
||||
return false;
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace llarp
|
|||
bool
|
||||
LR_CommitMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
if (key == "c")
|
||||
if (key.startswith("c"))
|
||||
{
|
||||
/// so we dont put it into the shitty queue
|
||||
pathid.Fill('c');
|
||||
|
@ -131,7 +131,7 @@ namespace llarp
|
|||
return false;
|
||||
if (!BEncodeMaybeReadDictEntry("t", txid, read, *key, buffer))
|
||||
return false;
|
||||
if (*key == "u")
|
||||
if (key->startswith("u"))
|
||||
{
|
||||
nextRC = std::make_unique<RouterContact>();
|
||||
return nextRC->BDecode(buffer);
|
||||
|
@ -139,7 +139,7 @@ namespace llarp
|
|||
if (!BEncodeMaybeVerifyVersion(
|
||||
"v", version, llarp::constants::proto_version, read, *key, buffer))
|
||||
return false;
|
||||
if (*key == "w")
|
||||
if (key->startswith("w"))
|
||||
{
|
||||
// check for duplicate
|
||||
if (work)
|
||||
|
|
|
@ -60,25 +60,25 @@ namespace llarp
|
|||
LR_StatusMessage::DecodeKey(const llarp_buffer_t& key, llarp_buffer_t* buf)
|
||||
{
|
||||
bool read = false;
|
||||
if (key == "c")
|
||||
if (key.startswith("c"))
|
||||
{
|
||||
return BEncodeReadArray(frames, buf);
|
||||
}
|
||||
if (key == "p")
|
||||
if (key.startswith("p"))
|
||||
{
|
||||
if (!BEncodeMaybeReadDictEntry("p", pathid, read, key, buf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (key == "s")
|
||||
else if (key.startswith("s"))
|
||||
{
|
||||
if (!BEncodeMaybeReadDictInt("s", status, read, key, buf))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (key == "v")
|
||||
else if (key.startswith("v"))
|
||||
{
|
||||
if (!BEncodeMaybeVerifyVersion("v", version, llarp::constants::proto_version, read, key, buf))
|
||||
{
|
||||
|
|
|
@ -41,7 +41,7 @@ namespace llarp
|
|||
llarp_buffer_t strbuf;
|
||||
|
||||
// rank
|
||||
if (key == "c")
|
||||
if (key.startswith("c"))
|
||||
{
|
||||
if (!bencode_read_integer(buf, &i))
|
||||
return false;
|
||||
|
@ -54,7 +54,7 @@ namespace llarp
|
|||
}
|
||||
|
||||
// dialect
|
||||
if (key == "d")
|
||||
if (key.startswith("d"))
|
||||
{
|
||||
if (!bencode_read_string(buf, &strbuf))
|
||||
return false;
|
||||
|
@ -67,13 +67,13 @@ namespace llarp
|
|||
}
|
||||
|
||||
// encryption public key
|
||||
if (key == "e")
|
||||
if (key.startswith("e"))
|
||||
{
|
||||
return pubkey.BDecode(buf);
|
||||
}
|
||||
|
||||
// ip address
|
||||
if (key == "i")
|
||||
if (key.startswith("i"))
|
||||
{
|
||||
if (!bencode_read_string(buf, &strbuf))
|
||||
return false;
|
||||
|
@ -87,7 +87,7 @@ namespace llarp
|
|||
}
|
||||
|
||||
// port
|
||||
if (key == "p")
|
||||
if (key.startswith("p"))
|
||||
{
|
||||
if (!bencode_read_integer(buf, &i))
|
||||
return false;
|
||||
|
@ -100,7 +100,7 @@ namespace llarp
|
|||
}
|
||||
|
||||
// version
|
||||
if (key == "v")
|
||||
if (key.startswith("v"))
|
||||
{
|
||||
if (!bencode_read_integer(buf, &i))
|
||||
return false;
|
||||
|
|
|
@ -50,9 +50,19 @@ namespace llarp
|
|||
fromSockAddr(const SockAddr& address);
|
||||
|
||||
/// get this as an explicit v4 or explicit v6
|
||||
std::variant<nuint32_t, nuint128_t>
|
||||
net::ipaddr_t
|
||||
IP() const;
|
||||
|
||||
/// get this as an v4 or throw if it is not one
|
||||
inline net::ipv4addr_t
|
||||
IPv4() const
|
||||
{
|
||||
auto ip = IP();
|
||||
if (auto* ptr = std::get_if<net::ipv4addr_t>(&ip))
|
||||
return *ptr;
|
||||
throw std::runtime_error{"no ipv4 address found in address info"};
|
||||
}
|
||||
|
||||
std::string
|
||||
ToString() const;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
#include "ip_range.hpp"
|
||||
|
||||
namespace llarp
|
||||
{
|
||||
static constexpr std::array bogonRanges_v6 = {
|
||||
// zero
|
||||
IPRange{huint128_t{0}, netmask_ipv6_bits(128)},
|
||||
// loopback
|
||||
IPRange{huint128_t{1}, netmask_ipv6_bits(128)},
|
||||
// yggdrasil
|
||||
IPRange{huint128_t{uint128_t{0x0200'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(7)},
|
||||
// multicast
|
||||
IPRange{huint128_t{uint128_t{0xff00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
|
||||
// local
|
||||
IPRange{huint128_t{uint128_t{0xfc00'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)},
|
||||
// local
|
||||
IPRange{huint128_t{uint128_t{0xf800'0000'0000'0000UL, 0UL}}, netmask_ipv6_bits(8)}};
|
||||
|
||||
static constexpr std::array bogonRanges_v4 = {
|
||||
IPRange::FromIPv4(0, 0, 0, 0, 8),
|
||||
IPRange::FromIPv4(10, 0, 0, 0, 8),
|
||||
IPRange::FromIPv4(100, 64, 0, 0, 10),
|
||||
IPRange::FromIPv4(127, 0, 0, 0, 8),
|
||||
IPRange::FromIPv4(169, 254, 0, 0, 16),
|
||||
IPRange::FromIPv4(172, 16, 0, 0, 12),
|
||||
IPRange::FromIPv4(192, 0, 0, 0, 24),
|
||||
IPRange::FromIPv4(192, 0, 2, 0, 24),
|
||||
IPRange::FromIPv4(192, 88, 99, 0, 24),
|
||||
IPRange::FromIPv4(192, 168, 0, 0, 16),
|
||||
IPRange::FromIPv4(198, 18, 0, 0, 15),
|
||||
IPRange::FromIPv4(198, 51, 100, 0, 24),
|
||||
IPRange::FromIPv4(203, 0, 113, 0, 24),
|
||||
IPRange::FromIPv4(224, 0, 0, 0, 4),
|
||||
IPRange::FromIPv4(240, 0, 0, 0, 4)};
|
||||
} // namespace llarp
|
|
@ -69,7 +69,7 @@ namespace llarp
|
|||
return false;
|
||||
if (!BEncodeMaybeReadDictInt("v", version, read, k, buf))
|
||||
return false;
|
||||
if (k == "a")
|
||||
if (k.startswith("a"))
|
||||
{
|
||||
in6_addr tmp;
|
||||
if (not bdecode_ip_string(buf, tmp))
|
||||
|
@ -79,7 +79,7 @@ namespace llarp
|
|||
ipAddress = IpAddress(addr);
|
||||
return true;
|
||||
}
|
||||
if (k == "b")
|
||||
if (k.startswith("b"))
|
||||
{
|
||||
in6_addr tmp;
|
||||
if (not bdecode_ip_string(buf, tmp))
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "ip_range.hpp"
|
||||
|
||||
namespace llarp::net
|
||||
{
|
||||
/// info about a network interface lokinet does not own
|
||||
struct InterfaceInfo
|
||||
{
|
||||
/// human readable name of interface
|
||||
std::string name;
|
||||
/// interface's index
|
||||
int index;
|
||||
/// the addresses owned by this interface
|
||||
std::vector<IPRange> addrs;
|
||||
/// a gateway we can use if it exists
|
||||
std::optional<ipaddr_t> gateway;
|
||||
};
|
||||
} // namespace llarp::net
|
|
@ -127,7 +127,7 @@ namespace llarp
|
|||
SockAddr addr(m_ipAddress);
|
||||
const auto* addr6 = static_cast<const sockaddr_in6*>(addr);
|
||||
const uint8_t* raw = addr6->sin6_addr.s6_addr;
|
||||
return IsIPv4Bogon(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15]));
|
||||
return IPRange::V4MappedRange().Contains(ipaddr_ipv4_bits(raw[12], raw[13], raw[14], raw[15]));
|
||||
}
|
||||
|
||||
std::string
|
||||
|
|
|
@ -19,14 +19,12 @@ namespace llarp
|
|||
/// As a convenience, it can produce a SockAddr for dealing with network libraries which depend
|
||||
/// sockaddr structs. However, it does not keep this as a member variable and isn't responsible
|
||||
/// for its lifetime/memory/etc.
|
||||
///
|
||||
/// TODO: IPv6 is not currently supported.
|
||||
struct IpAddress
|
||||
struct [[deprecated("use llarp::SockAddr instead")]] IpAddress
|
||||
{
|
||||
/// Empty constructor.
|
||||
IpAddress() = default;
|
||||
/// move construtor
|
||||
IpAddress(IpAddress&&) = default;
|
||||
IpAddress(IpAddress &&) = default;
|
||||
/// copy construct
|
||||
IpAddress(const IpAddress&);
|
||||
|
||||
|
@ -56,80 +54,64 @@ namespace llarp
|
|||
/// @param addr is an SockAddr to initialize from.
|
||||
IpAddress(const SockAddr& addr);
|
||||
|
||||
IpAddress&
|
||||
operator=(const sockaddr& other);
|
||||
IpAddress& operator=(const sockaddr& other);
|
||||
|
||||
/// move assignment
|
||||
IpAddress&
|
||||
operator=(IpAddress&& other);
|
||||
IpAddress& operator=(IpAddress&& other);
|
||||
|
||||
/// copy assignment
|
||||
IpAddress&
|
||||
operator=(const IpAddress& other);
|
||||
IpAddress& operator=(const IpAddress& other);
|
||||
|
||||
/// Return the port. Returns -1 if no port has been provided.
|
||||
///
|
||||
/// @return the port, if present
|
||||
std::optional<uint16_t>
|
||||
getPort() const;
|
||||
std::optional<uint16_t> getPort() const;
|
||||
|
||||
/// Return true if we have a port set otherwise return false
|
||||
bool
|
||||
hasPort() const;
|
||||
bool hasPort() const;
|
||||
|
||||
/// Set the port.
|
||||
///
|
||||
/// @param port
|
||||
void
|
||||
setPort(std::optional<uint16_t> port);
|
||||
void setPort(std::optional<uint16_t> port);
|
||||
|
||||
/// Set the IP address. Follows the same logic as the constructor with the same signature, but
|
||||
/// doesn't overwrite the port if the port isn't present in the string.
|
||||
void
|
||||
setAddress(std::string_view str);
|
||||
void
|
||||
setAddress(std::string_view str, std::optional<uint16_t> port);
|
||||
void setAddress(std::string_view str);
|
||||
void setAddress(std::string_view str, std::optional<uint16_t> port);
|
||||
|
||||
/// Returns true if this is an IPv4 address (or an IPv6 address representing an IPv4 address)
|
||||
///
|
||||
/// TODO: could return an int (e.g. 4 or 6) or an enum
|
||||
///
|
||||
/// @return true if this is an IPv4 address, false otherwise
|
||||
bool
|
||||
isIPv4();
|
||||
bool isIPv4();
|
||||
|
||||
/// Returns true if this represents a valid IpAddress, false otherwise.
|
||||
///
|
||||
/// @return whether or not this IpAddress is empty
|
||||
bool
|
||||
isEmpty() const;
|
||||
bool isEmpty() const;
|
||||
|
||||
/// Creates an instance of SockAddr representing this IpAddress.
|
||||
///
|
||||
/// @return an instance of a SockAddr created from this IpAddress
|
||||
SockAddr
|
||||
createSockAddr() const;
|
||||
SockAddr createSockAddr() const;
|
||||
|
||||
/// Returns true if this IpAddress is a bogon, false otherwise
|
||||
///
|
||||
/// @return whether or not this IpAddress is a bogon
|
||||
bool
|
||||
isBogon() const;
|
||||
bool isBogon() const;
|
||||
|
||||
/// Returns a string representing this IpAddress
|
||||
///
|
||||
/// @return string representation of this IpAddress
|
||||
std::string
|
||||
ToString() const;
|
||||
std::string ToString() const;
|
||||
|
||||
std::string
|
||||
toHost() const;
|
||||
std::string toHost() const;
|
||||
|
||||
huint32_t
|
||||
toIP() const;
|
||||
huint32_t toIP() const;
|
||||
|
||||
huint128_t
|
||||
toIP6() const;
|
||||
huint128_t toIP6() const;
|
||||
|
||||
// TODO: other utility functions left over from Addr which may be useful
|
||||
// IsBogon() const;
|
||||
|
@ -137,11 +119,9 @@ namespace llarp
|
|||
// std::hash
|
||||
// to string / stream / etc
|
||||
|
||||
bool
|
||||
operator<(const IpAddress& other) const;
|
||||
bool operator<(const IpAddress& other) const;
|
||||
|
||||
bool
|
||||
operator==(const IpAddress& other) const;
|
||||
bool operator==(const IpAddress& other) const;
|
||||
|
||||
private:
|
||||
bool m_empty = true;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#include "ip_packet.hpp"
|
||||
#include "ip.hpp"
|
||||
|
||||
#include <llarp/constants/net.hpp>
|
||||
#include <llarp/util/buffer.hpp>
|
||||
#include <llarp/util/mem.hpp>
|
||||
#include <llarp/util/str.hpp>
|
||||
|
@ -17,22 +17,6 @@ namespace llarp::net
|
|||
{
|
||||
constexpr uint32_t ipv6_flowlabel_mask = 0b0000'0000'0000'1111'1111'1111'1111'1111;
|
||||
|
||||
template <bool little>
|
||||
struct ipv6_header_preamble
|
||||
{
|
||||
unsigned char pad_small : 4;
|
||||
unsigned char version : 4;
|
||||
uint8_t pad[3];
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ipv6_header_preamble<false>
|
||||
{
|
||||
unsigned char version : 4;
|
||||
unsigned char pad_small : 4;
|
||||
uint8_t pad[3];
|
||||
};
|
||||
|
||||
/// get 20 bit truncated flow label in network order
|
||||
llarp::nuint32_t
|
||||
ipv6_header::FlowLabel() const
|
||||
|
@ -75,7 +59,6 @@ namespace llarp::net
|
|||
}
|
||||
throw std::invalid_argument{"no such ip protocol: '" + data + "'"};
|
||||
}
|
||||
|
||||
inline static uint32_t*
|
||||
in6_uint32_ptr(in6_addr& addr)
|
||||
{
|
||||
|
@ -106,30 +89,55 @@ namespace llarp::net
|
|||
return ExpandV4(dstv4());
|
||||
}
|
||||
|
||||
bool
|
||||
IPPacket::Load(const llarp_buffer_t& pkt)
|
||||
IPPacket::IPPacket(byte_view_t view)
|
||||
{
|
||||
if (pkt.sz > sizeof(buf) or pkt.sz == 0)
|
||||
return false;
|
||||
sz = pkt.sz;
|
||||
std::copy_n(pkt.base, sz, buf);
|
||||
return true;
|
||||
if (view.size() < MinSize)
|
||||
{
|
||||
_buf.resize(0);
|
||||
return;
|
||||
}
|
||||
_buf.resize(view.size());
|
||||
std::copy_n(view.data(), size(), data());
|
||||
}
|
||||
|
||||
ManagedBuffer
|
||||
IPPacket::ConstBuffer() const
|
||||
IPPacket::IPPacket(size_t sz)
|
||||
{
|
||||
const byte_t* ptr = buf;
|
||||
llarp_buffer_t b(ptr, sz);
|
||||
return ManagedBuffer(b);
|
||||
if (sz and sz < MinSize)
|
||||
throw std::invalid_argument{"buffer size is too small to hold an ip packet"};
|
||||
_buf.resize(sz);
|
||||
}
|
||||
|
||||
ManagedBuffer
|
||||
IPPacket::Buffer()
|
||||
SockAddr
|
||||
IPPacket::src() const
|
||||
{
|
||||
byte_t* ptr = buf;
|
||||
llarp_buffer_t b(ptr, sz);
|
||||
return ManagedBuffer(b);
|
||||
const auto port = SrcPort().value_or(net::port_t{});
|
||||
|
||||
if (IsV4())
|
||||
return SockAddr{ToNet(srcv4()), port};
|
||||
else
|
||||
return SockAddr{ToNet(srcv6()), port};
|
||||
}
|
||||
|
||||
SockAddr
|
||||
IPPacket::dst() const
|
||||
{
|
||||
auto port = *DstPort();
|
||||
if (IsV4())
|
||||
return SockAddr{ToNet(dstv4()), port};
|
||||
else
|
||||
return SockAddr{ToNet(dstv6()), port};
|
||||
}
|
||||
|
||||
IPPacket::IPPacket(std::vector<byte_t>&& stolen) : _buf{stolen}
|
||||
{
|
||||
if (size() < MinSize)
|
||||
_buf.resize(0);
|
||||
}
|
||||
|
||||
byte_view_t
|
||||
IPPacket::view() const
|
||||
{
|
||||
return byte_view_t{data(), size()};
|
||||
}
|
||||
|
||||
std::optional<nuint16_t>
|
||||
|
@ -139,7 +147,7 @@ namespace llarp::net
|
|||
{
|
||||
case IPProtocol::TCP:
|
||||
case IPProtocol::UDP:
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(buf + (Header()->ihl * 4) + 2)};
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(data() + (Header()->ihl * 4) + 2)};
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -148,11 +156,12 @@ namespace llarp::net
|
|||
std::optional<nuint16_t>
|
||||
IPPacket::SrcPort() const
|
||||
{
|
||||
switch (IPProtocol{Header()->protocol})
|
||||
IPProtocol proto{Header()->protocol};
|
||||
switch (proto)
|
||||
{
|
||||
case IPProtocol::TCP:
|
||||
case IPProtocol::UDP:
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(buf + (Header()->ihl * 4))};
|
||||
return nuint16_t{*reinterpret_cast<const uint16_t*>(data() + (Header()->ihl * 4))};
|
||||
default:
|
||||
return std::nullopt;
|
||||
}
|
||||
|
@ -397,7 +406,8 @@ namespace llarp::net
|
|||
|
||||
auto oSrcIP = nuint32_t{hdr->saddr};
|
||||
auto oDstIP = nuint32_t{hdr->daddr};
|
||||
|
||||
auto* buf = data();
|
||||
auto sz = size();
|
||||
// L4 checksum
|
||||
auto ihs = size_t(hdr->ihl * 4);
|
||||
if (ihs <= sz)
|
||||
|
@ -435,7 +445,7 @@ namespace llarp::net
|
|||
IPPacket::UpdateIPv6Address(huint128_t src, huint128_t dst, std::optional<nuint32_t> flowlabel)
|
||||
{
|
||||
const size_t ihs = 4 + 4 + 16 + 16;
|
||||
|
||||
const auto sz = size();
|
||||
// XXX should've been checked at upper level?
|
||||
if (sz <= ihs)
|
||||
return;
|
||||
|
@ -459,11 +469,11 @@ namespace llarp::net
|
|||
const uint32_t* nDstIP = in6_uint32_ptr(hdr->dstaddr);
|
||||
|
||||
// TODO IPv6 header options
|
||||
auto pld = buf + ihs;
|
||||
auto* pld = data() + ihs;
|
||||
auto psz = sz - ihs;
|
||||
|
||||
size_t fragoff = 0;
|
||||
auto nextproto = hdr->proto;
|
||||
auto nextproto = hdr->protocol;
|
||||
for (;;)
|
||||
{
|
||||
switch (nextproto)
|
||||
|
@ -554,31 +564,27 @@ namespace llarp::net
|
|||
if (IsV4())
|
||||
{
|
||||
constexpr auto icmp_Header_size = 8;
|
||||
constexpr auto ip_Header_size = 20;
|
||||
net::IPPacket pkt{};
|
||||
auto* pkt_Header = pkt.Header();
|
||||
auto ip_Header_size = Header()->ihl * 4;
|
||||
auto pkt_size = (icmp_Header_size + ip_Header_size) * 2;
|
||||
net::IPPacket pkt{static_cast<size_t>(pkt_size)};
|
||||
|
||||
auto* pkt_Header = pkt.Header();
|
||||
pkt_Header->version = 4;
|
||||
pkt_Header->ihl = 0x05;
|
||||
pkt_Header->tos = 0;
|
||||
pkt_Header->check = 0;
|
||||
pkt_Header->tot_len = ntohs(icmp_Header_size + ip_Header_size);
|
||||
pkt_Header->tot_len = ntohs(pkt_size);
|
||||
pkt_Header->saddr = Header()->daddr;
|
||||
pkt_Header->daddr = Header()->saddr;
|
||||
pkt_Header->protocol = 1; // ICMP
|
||||
pkt_Header->ttl = 1;
|
||||
pkt_Header->ttl = Header()->ttl;
|
||||
pkt_Header->frag_off = htons(0b0100000000000000);
|
||||
// size pf ip header
|
||||
const size_t l3_HeaderSize = Header()->ihl * 4;
|
||||
// size of l4 packet to reflect back
|
||||
const size_t l4_PacketSize = 8;
|
||||
pkt_Header->tot_len += ntohs(l4_PacketSize + l3_HeaderSize);
|
||||
|
||||
uint16_t* checksum;
|
||||
uint8_t* itr = pkt.buf + (pkt_Header->ihl * 4);
|
||||
uint8_t* itr = pkt.data() + ip_Header_size;
|
||||
uint8_t* icmp_begin = itr; // type 'destination unreachable'
|
||||
*itr++ = 3;
|
||||
// code 'Destination host unknown error'
|
||||
// code 'Destination host unknown error'
|
||||
*itr++ = 7;
|
||||
// checksum + unused
|
||||
oxenc::write_host_as_big<uint32_t>(0, itr);
|
||||
|
@ -588,14 +594,13 @@ namespace llarp::net
|
|||
oxenc::write_host_as_big<uint16_t>(1500, itr);
|
||||
itr += 2;
|
||||
// copy ip header and first 8 bytes of datagram for icmp rject
|
||||
std::copy_n(buf, l4_PacketSize + l3_HeaderSize, itr);
|
||||
itr += l4_PacketSize + l3_HeaderSize;
|
||||
std::copy_n(data(), ip_Header_size + icmp_Header_size, itr);
|
||||
itr += ip_Header_size + icmp_Header_size;
|
||||
// calculate checksum of ip header
|
||||
pkt_Header->check = ipchksum(pkt.buf, pkt_Header->ihl * 4);
|
||||
pkt_Header->check = ipchksum(pkt.data(), ip_Header_size);
|
||||
const auto icmp_size = std::distance(icmp_begin, itr);
|
||||
// calculate icmp checksum
|
||||
*checksum = ipchksum(icmp_begin, icmp_size);
|
||||
pkt.sz = ntohs(pkt_Header->tot_len);
|
||||
return pkt;
|
||||
}
|
||||
return std::nullopt;
|
||||
|
@ -614,56 +619,85 @@ namespace llarp::net
|
|||
return std::nullopt;
|
||||
|
||||
// check for invalid size
|
||||
if (sz < (hdr->ihl * 4) + l4_HeaderSize)
|
||||
if (size() < (hdr->ihl * 4) + l4_HeaderSize)
|
||||
return std::nullopt;
|
||||
|
||||
const uint8_t* ptr = buf + ((hdr->ihl * 4) + l4_HeaderSize);
|
||||
return std::make_pair(reinterpret_cast<const char*>(ptr), std::distance(ptr, buf + sz));
|
||||
const uint8_t* ptr = data() + ((hdr->ihl * 4) + l4_HeaderSize);
|
||||
return std::make_pair(reinterpret_cast<const char*>(ptr), std::distance(ptr, data() + size()));
|
||||
}
|
||||
|
||||
IPPacket
|
||||
IPPacket::UDP(
|
||||
nuint32_t srcaddr,
|
||||
nuint16_t srcport,
|
||||
nuint32_t dstaddr,
|
||||
nuint16_t dstport,
|
||||
const llarp_buffer_t& buf)
|
||||
namespace
|
||||
{
|
||||
net::IPPacket pkt;
|
||||
|
||||
if (buf.sz + 28 > sizeof(pkt.buf))
|
||||
IPPacket
|
||||
make_ip4_udp(
|
||||
net::ipv4addr_t srcaddr,
|
||||
net::port_t srcport,
|
||||
net::ipv4addr_t dstaddr,
|
||||
net::port_t dstport,
|
||||
std::vector<byte_t> udp_data)
|
||||
{
|
||||
pkt.sz = 0;
|
||||
constexpr auto pkt_overhead = constants::udp_header_bytes + constants::ip_header_min_bytes;
|
||||
net::IPPacket pkt{udp_data.size() + pkt_overhead};
|
||||
|
||||
auto* hdr = pkt.Header();
|
||||
pkt.data()[1] = 0;
|
||||
hdr->version = 4;
|
||||
hdr->ihl = 5;
|
||||
hdr->tot_len = htons(pkt_overhead + udp_data.size());
|
||||
hdr->protocol = 0x11; // udp
|
||||
hdr->ttl = 64;
|
||||
hdr->frag_off = htons(0b0100000000000000);
|
||||
|
||||
hdr->saddr = srcaddr.n;
|
||||
hdr->daddr = dstaddr.n;
|
||||
|
||||
// make udp packet
|
||||
uint8_t* ptr = pkt.data() + constants::ip_header_min_bytes;
|
||||
std::memcpy(ptr, &srcport.n, 2);
|
||||
ptr += 2;
|
||||
std::memcpy(ptr, &dstport.n, 2);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(
|
||||
static_cast<uint16_t>(udp_data.size() + constants::udp_header_bytes), ptr);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum
|
||||
ptr += 2;
|
||||
std::copy_n(udp_data.data(), udp_data.size(), ptr);
|
||||
|
||||
hdr->check = 0;
|
||||
hdr->check = net::ipchksum(pkt.data(), 20);
|
||||
return pkt;
|
||||
}
|
||||
auto* hdr = pkt.Header();
|
||||
pkt.buf[1] = 0;
|
||||
hdr->version = 4;
|
||||
hdr->ihl = 5;
|
||||
hdr->tot_len = htons(buf.sz + 28);
|
||||
hdr->protocol = 0x11; // udp
|
||||
hdr->ttl = 64;
|
||||
hdr->frag_off = htons(0b0100000000000000);
|
||||
|
||||
hdr->saddr = srcaddr.n;
|
||||
hdr->daddr = dstaddr.n;
|
||||
|
||||
// make udp packet
|
||||
uint8_t* ptr = pkt.buf + 20;
|
||||
std::memcpy(ptr, &srcport.n, 2);
|
||||
ptr += 2;
|
||||
std::memcpy(ptr, &dstport.n, 2);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(static_cast<uint16_t>(buf.sz + 8), ptr);
|
||||
ptr += 2;
|
||||
oxenc::write_host_as_big(uint16_t{0}, ptr); // checksum
|
||||
ptr += 2;
|
||||
std::copy_n(buf.base, buf.sz, ptr);
|
||||
|
||||
hdr->check = 0;
|
||||
hdr->check = net::ipchksum(pkt.buf, 20);
|
||||
pkt.sz = 28 + buf.sz;
|
||||
return pkt;
|
||||
} // namespace
|
||||
IPPacket
|
||||
IPPacket::make_udp(
|
||||
net::ipaddr_t srcaddr,
|
||||
net::port_t srcport,
|
||||
net::ipaddr_t dstaddr,
|
||||
net::port_t dstport,
|
||||
std::vector<byte_t> udp_data)
|
||||
{
|
||||
auto getfam = [](auto&& v) {
|
||||
if (std::holds_alternative<net::ipv4addr_t>(v))
|
||||
return AF_INET;
|
||||
else if (std::holds_alternative<net::ipv6addr_t>(v))
|
||||
return AF_INET6;
|
||||
else
|
||||
return AF_UNSPEC;
|
||||
};
|
||||
auto fam = getfam(srcaddr);
|
||||
if (fam != getfam(dstaddr))
|
||||
return net::IPPacket{size_t{}};
|
||||
if (fam == AF_INET)
|
||||
{
|
||||
return make_ip4_udp(
|
||||
*std::get_if<net::ipv4addr_t>(&srcaddr),
|
||||
srcport,
|
||||
*std::get_if<net::ipv4addr_t>(&dstaddr),
|
||||
dstport,
|
||||
std::move(udp_data));
|
||||
}
|
||||
// TODO: ipv6
|
||||
return net::IPPacket{size_t{}};
|
||||
}
|
||||
|
||||
} // namespace llarp::net
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <oxenc/endian.h>
|
||||
#include <llarp/ev/ev.hpp>
|
||||
#include "net.hpp"
|
||||
#include <llarp/util/buffer.hpp>
|
||||
|
@ -10,11 +11,10 @@
|
|||
|
||||
namespace llarp::net
|
||||
{
|
||||
template <bool is_little_endian>
|
||||
struct ip_header_le
|
||||
{
|
||||
unsigned int ihl : 4;
|
||||
unsigned int version : 4;
|
||||
uint8_t ihl : 4;
|
||||
uint8_t version : 4;
|
||||
uint8_t tos;
|
||||
uint16_t tot_len;
|
||||
uint16_t id;
|
||||
|
@ -26,11 +26,10 @@ namespace llarp::net
|
|||
uint32_t daddr;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ip_header_le<false>
|
||||
struct ip_header_be
|
||||
{
|
||||
unsigned int version : 4;
|
||||
unsigned int ihl : 4;
|
||||
uint8_t version : 4;
|
||||
uint8_t ihl : 4;
|
||||
uint8_t tos;
|
||||
uint16_t tot_len;
|
||||
uint16_t id;
|
||||
|
@ -42,9 +41,10 @@ namespace llarp::net
|
|||
uint32_t daddr;
|
||||
};
|
||||
|
||||
using ip_header = ip_header_le<oxenc::little_endian>;
|
||||
using ip_header = std::conditional_t<oxenc::little_endian, ip_header_le, ip_header_be>;
|
||||
|
||||
static_assert(sizeof(ip_header) == 20);
|
||||
|
||||
template <bool little>
|
||||
struct ipv6_header_preamble_le
|
||||
{
|
||||
unsigned char pad_small : 4;
|
||||
|
@ -52,24 +52,28 @@ namespace llarp::net
|
|||
uint8_t pad[3];
|
||||
};
|
||||
|
||||
template <>
|
||||
struct ipv6_header_preamble_le<false>
|
||||
struct ipv6_header_preamble_be
|
||||
{
|
||||
unsigned char version : 4;
|
||||
unsigned char pad_small : 4;
|
||||
uint8_t pad[3];
|
||||
};
|
||||
|
||||
using ipv6_header_preamble =
|
||||
std::conditional_t<oxenc::little_endian, ipv6_header_preamble_le, ipv6_header_preamble_be>;
|
||||
|
||||
static_assert(sizeof(ipv6_header_preamble) == 4);
|
||||
|
||||
struct ipv6_header
|
||||
{
|
||||
union
|
||||
{
|
||||
ipv6_header_preamble_le<oxenc::little_endian> preamble;
|
||||
ipv6_header_preamble preamble;
|
||||
uint32_t flowlabel;
|
||||
} preamble;
|
||||
|
||||
uint16_t payload_len;
|
||||
uint8_t proto;
|
||||
uint8_t protocol;
|
||||
uint8_t hoplimit;
|
||||
in6_addr srcaddr;
|
||||
in6_addr dstaddr;
|
||||
|
@ -81,6 +85,8 @@ namespace llarp::net
|
|||
FlowLabel(llarp::nuint32_t label);
|
||||
};
|
||||
|
||||
static_assert(sizeof(ipv6_header) == 40);
|
||||
|
||||
/// "well known" ip protocols
|
||||
/// TODO: extend this to non "well known values"
|
||||
enum class IPProtocol : uint8_t
|
||||
|
@ -109,34 +115,119 @@ namespace llarp::net
|
|||
/// an Packet
|
||||
struct IPPacket
|
||||
{
|
||||
static constexpr size_t MaxSize = 1500;
|
||||
static constexpr size_t _max_size = 1500;
|
||||
llarp_time_t timestamp;
|
||||
size_t sz;
|
||||
std::vector<byte_t> _buf;
|
||||
|
||||
alignas(ip_header) byte_t buf[MaxSize];
|
||||
public:
|
||||
IPPacket() : IPPacket{size_t{}}
|
||||
{}
|
||||
/// create an ip packet buffer of all zeros of size sz
|
||||
explicit IPPacket(size_t sz);
|
||||
/// create an ip packet from a view
|
||||
explicit IPPacket(byte_view_t);
|
||||
/// create an ip packet from a vector we then own
|
||||
IPPacket(std::vector<byte_t>&&);
|
||||
|
||||
static IPPacket
|
||||
~IPPacket() = default;
|
||||
|
||||
static constexpr size_t MaxSize = _max_size;
|
||||
static constexpr size_t MinSize = 20;
|
||||
|
||||
[[deprecated("deprecated because of llarp_buffer_t")]] static IPPacket
|
||||
UDP(nuint32_t srcaddr,
|
||||
nuint16_t srcport,
|
||||
nuint32_t dstaddr,
|
||||
nuint16_t dstport,
|
||||
const llarp_buffer_t& data);
|
||||
const llarp_buffer_t& data)
|
||||
{
|
||||
return make_udp(srcaddr, srcport, dstaddr, dstport, data.copy());
|
||||
}
|
||||
|
||||
ManagedBuffer
|
||||
Buffer();
|
||||
static IPPacket
|
||||
make_udp(
|
||||
net::ipaddr_t srcaddr,
|
||||
net::port_t srcport,
|
||||
net::ipaddr_t dstaddr,
|
||||
net::port_t dstport,
|
||||
std::vector<byte_t> udp_body);
|
||||
|
||||
ManagedBuffer
|
||||
ConstBuffer() const;
|
||||
static inline IPPacket
|
||||
make_udp(SockAddr src, SockAddr dst, std::variant<OwnedBuffer, std::vector<byte_t>> udp_body)
|
||||
{
|
||||
if (auto* vec = std::get_if<std::vector<byte_t>>(&udp_body))
|
||||
return make_udp(src.getIP(), src.port(), dst.getIP(), dst.port(), std::move(*vec));
|
||||
else if (auto* buf = std::get_if<OwnedBuffer>(&udp_body))
|
||||
return make_udp(src, dst, buf->copy());
|
||||
else
|
||||
return net::IPPacket{size_t{}};
|
||||
}
|
||||
|
||||
bool
|
||||
Load(const llarp_buffer_t& buf);
|
||||
[[deprecated("deprecated because of llarp_buffer_t")]] inline bool
|
||||
Load(const llarp_buffer_t& buf)
|
||||
{
|
||||
_buf = buf.copy();
|
||||
if (size() >= MinSize)
|
||||
return true;
|
||||
_buf.resize(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
[[deprecated("deprecated because of llarp_buffer_t")]] inline llarp_buffer_t
|
||||
ConstBuffer() const
|
||||
{
|
||||
return llarp_buffer_t{_buf};
|
||||
}
|
||||
|
||||
/// steal the underlying vector
|
||||
inline std::vector<byte_t>
|
||||
steal()
|
||||
{
|
||||
std::vector<byte_t> buf;
|
||||
buf.resize(0);
|
||||
std::swap(_buf, buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
inline byte_t*
|
||||
data()
|
||||
{
|
||||
return _buf.data();
|
||||
}
|
||||
|
||||
inline const byte_t*
|
||||
data() const
|
||||
{
|
||||
return _buf.data();
|
||||
}
|
||||
|
||||
constexpr size_t
|
||||
capacity() const
|
||||
{
|
||||
return _max_size;
|
||||
}
|
||||
|
||||
inline size_t
|
||||
size() const
|
||||
{
|
||||
return _buf.size();
|
||||
}
|
||||
|
||||
inline bool
|
||||
empty() const
|
||||
{
|
||||
return _buf.empty();
|
||||
}
|
||||
|
||||
byte_view_t
|
||||
view() const;
|
||||
|
||||
struct CompareSize
|
||||
{
|
||||
bool
|
||||
operator()(const IPPacket& left, const IPPacket& right)
|
||||
{
|
||||
return left.sz < right.sz;
|
||||
return left.size() < right.size();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -152,25 +243,25 @@ namespace llarp::net
|
|||
inline ip_header*
|
||||
Header()
|
||||
{
|
||||
return reinterpret_cast<ip_header*>(&buf[0]);
|
||||
return reinterpret_cast<ip_header*>(data());
|
||||
}
|
||||
|
||||
inline const ip_header*
|
||||
Header() const
|
||||
{
|
||||
return reinterpret_cast<const ip_header*>(&buf[0]);
|
||||
return reinterpret_cast<const ip_header*>(data());
|
||||
}
|
||||
|
||||
inline ipv6_header*
|
||||
HeaderV6()
|
||||
{
|
||||
return reinterpret_cast<ipv6_header*>(&buf[0]);
|
||||
return reinterpret_cast<ipv6_header*>(data());
|
||||
}
|
||||
|
||||
inline const ipv6_header*
|
||||
HeaderV6() const
|
||||
{
|
||||
return reinterpret_cast<const ipv6_header*>(&buf[0]);
|
||||
return reinterpret_cast<const ipv6_header*>(data());
|
||||
}
|
||||
|
||||
inline int
|
||||
|
@ -179,6 +270,15 @@ namespace llarp::net
|
|||
return Header()->version;
|
||||
}
|
||||
|
||||
inline byte_t
|
||||
protocol() const
|
||||
{
|
||||
if (IsV4())
|
||||
return Header()->protocol;
|
||||
else
|
||||
return HeaderV6()->protocol;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsV4() const
|
||||
{
|
||||
|
@ -226,6 +326,12 @@ namespace llarp::net
|
|||
huint128_t
|
||||
dst4to6Lan() const;
|
||||
|
||||
SockAddr
|
||||
src() const;
|
||||
|
||||
SockAddr
|
||||
dst() const;
|
||||
|
||||
/// get destination port if applicable
|
||||
std::optional<nuint16_t>
|
||||
DstPort() const;
|
||||
|
@ -238,6 +344,14 @@ namespace llarp::net
|
|||
std::optional<std::pair<const char*, size_t>>
|
||||
L4Data() const;
|
||||
|
||||
inline std::optional<OwnedBuffer>
|
||||
L4OwnedBuffer() const
|
||||
{
|
||||
if (auto data = L4Data())
|
||||
return OwnedBuffer{reinterpret_cast<const byte_t*>(data->first), data->second};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateIPv4Address(nuint32_t src, nuint32_t dst);
|
||||
|
||||
|
@ -256,6 +370,8 @@ namespace llarp::net
|
|||
/// make an icmp unreachable reply packet based of this ip packet
|
||||
std::optional<IPPacket>
|
||||
MakeICMPUnreachable() const;
|
||||
|
||||
std::function<void(net::IPPacket)> reply;
|
||||
};
|
||||
|
||||
/// generate ip checksum
|
||||
|
|
|
@ -90,4 +90,15 @@ namespace llarp
|
|||
return addr.ToString();
|
||||
}
|
||||
|
||||
std::string
|
||||
IPRange::NetmaskString() const
|
||||
{
|
||||
if (IsV4())
|
||||
{
|
||||
const huint32_t mask = net::TruncateV6(netmask_bits);
|
||||
return mask.ToString();
|
||||
}
|
||||
return netmask_bits.ToString();
|
||||
}
|
||||
|
||||
} // namespace llarp
|
||||
|
|
|
@ -9,10 +9,6 @@
|
|||
|
||||
namespace llarp
|
||||
{
|
||||
/// forward declare
|
||||
bool
|
||||
IsBogon(huint128_t ip);
|
||||
|
||||
struct IPRange
|
||||
{
|
||||
using Addr_t = huint128_t;
|
||||
|
@ -25,6 +21,12 @@ namespace llarp
|
|||
: addr{std::move(address)}, netmask_bits{std::move(netmask)}
|
||||
{}
|
||||
|
||||
static constexpr IPRange
|
||||
V4MappedRange()
|
||||
{
|
||||
return IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)};
|
||||
}
|
||||
|
||||
static constexpr IPRange
|
||||
FromIPv4(byte_t a, byte_t b, byte_t c, byte_t d, byte_t mask)
|
||||
{
|
||||
|
@ -42,8 +44,7 @@ namespace llarp
|
|||
constexpr bool
|
||||
IsV4() const
|
||||
{
|
||||
constexpr auto ipv4_map = IPRange{huint128_t{0x0000'ffff'0000'0000UL}, netmask_ipv6_bits(96)};
|
||||
return ipv4_map.Contains(addr);
|
||||
return V4MappedRange().Contains(addr);
|
||||
}
|
||||
|
||||
/// get address family
|
||||
|
@ -55,27 +56,6 @@ namespace llarp
|
|||
return AF_INET6;
|
||||
}
|
||||
|
||||
/// return true if we intersect with a bogon range
|
||||
bool
|
||||
BogonRange() const
|
||||
{
|
||||
// special case for 0.0.0.0/0
|
||||
if (IsV4() and netmask_bits == netmask_ipv6_bits(96))
|
||||
return false;
|
||||
// special case for ::/0
|
||||
if (netmask_bits == huint128_t{0})
|
||||
return false;
|
||||
return IsBogon(addr) or IsBogon(HighestAddr());
|
||||
}
|
||||
|
||||
/// return true if we intersect with a bogon range *and* we contain the given address
|
||||
template <typename Addr>
|
||||
bool
|
||||
BogonContains(Addr&& addr) const
|
||||
{
|
||||
return BogonRange() and Contains(std::forward<Addr>(addr));
|
||||
}
|
||||
|
||||
/// return the number of bits set in the hostmask
|
||||
constexpr int
|
||||
HostmaskBits() const
|
||||
|
@ -117,6 +97,12 @@ namespace llarp
|
|||
return Contains(net::ExpandV4(ip));
|
||||
}
|
||||
|
||||
inline bool
|
||||
Contains(const net::ipaddr_t& ip) const
|
||||
{
|
||||
return var::visit([this](auto&& ip) { return Contains(ToHost(ip)); }, ip);
|
||||
}
|
||||
|
||||
/// get the highest address on this range
|
||||
constexpr huint128_t
|
||||
HighestAddr() const
|
||||
|
@ -147,6 +133,9 @@ namespace llarp
|
|||
std::string
|
||||
BaseAddressString() const;
|
||||
|
||||
std::string
|
||||
NetmaskString() const;
|
||||
|
||||
bool
|
||||
FromString(std::string str);
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue