Compare commits

..

14 Commits
dev ... v0.9.11

Author SHA1 Message Date
Jason Rhinelander 81a61f3d03
Merge pull request #2080 from oxen-io/dev
0.9.11 release
2 years ago
Jason Rhinelander 1e5e34bd24
Merge pull request #2052 from oxen-io/dev
v0.9.10
2 years ago
Jason Rhinelander 77fa34ca4d Bump apple network extension sub-version 2 years ago
Jason Rhinelander 67863cd9a4
Merge branch 'dev' into stable 2 years ago
Jason Rhinelander 2660761ce2
Merge pull request #2047 from oxen-io/dev
lokinet v0.9.10
2 years ago
Jason Rhinelander 440b547d2c
Merge pull request #1924 from oxen-io/dev
v0.9.9
2 years ago
Jeff ad201a48ac
Merge pull request #1800 from oxen-io/dev
v0.9.8
3 years ago
Jason Rhinelander 7792c9b463
Merge pull request #1776 from oxen-io/dev
v0.9.7
3 years ago
Jason Rhinelander 71663fafc1
Merge pull request #1731 from oxen-io/dev
v0.9.6
3 years ago
Jeff 44ad8ad3dd
Merge pull request #1694 from oxen-io/dev
0.9.5
3 years ago
Jeff 4723b532eb
Merge pull request #1681 from oxen-io/dev
v0.9.4
3 years ago
Jason Rhinelander a56308074d
Merge pull request #1664 from oxen-io/dev
dev -> stable for 0.9.3
3 years ago
Jeff 94376e0da0
Merge pull request #1649 from oxen-io/dev
v0.9.2
3 years ago
Jeff 9564e750d3
Merge pull request #1646 from oxen-io/dev
v0.9.1
3 years ago

@ -54,8 +54,3 @@ PointerAlignment: Left
# when wrapping function calls/declarations, force each parameter to have its own line # when wrapping function calls/declarations, force each parameter to have its own line
BinPackParameters: 'false' BinPackParameters: 'false'
BinPackArguments: 'false' BinPackArguments: 'false'
# TODO: uncomment me when we are reading to rearrange the header includes
# IncludeBlocks: Regroup
# IncludeCategories: 'llarp/'

@ -1,2 +1,2 @@
HeaderFilterRegex: 'llarp/.*' HeaderFilterRegex: 'llarp/.*'
Checks: 'readability-else-after-return,clang-analyzer-core-*,modernize-*,-modernize-use-trailing-return-type,-modernize-use-nodiscard,bugprone-*,-bugprone-easily-swappable-parameters' Checks: 'readability-else-after-return,clang-analyzer-core-*,modernize-*,-modernize-use-trailing-return-type,-modernize-use-nodiscard,bugprone-*'

@ -164,6 +164,8 @@ local windows_cross_pipeline(name,
apt_get_quiet + ' update', apt_get_quiet + ' update',
apt_get_quiet + ' install -y eatmydata', 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 icoutils automake libtool librsvg2-bin bison', 'eatmydata ' + apt_get_quiet + ' install --no-install-recommends -y build-essential cmake git pkg-config ccache g++-mingw-w64-x86-64-posix nsis zip icoutils automake libtool librsvg2-bin bison',
'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',
'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON -DGUI_EXE=$${DRONE_WORKSPACE}/gui/release/Lokinet-GUI_portable.exe' + 'JOBS=' + jobs + ' VERBOSE=1 ./contrib/windows.sh -DSTRIP_SYMBOLS=ON -DGUI_EXE=$${DRONE_WORKSPACE}/gui/release/Lokinet-GUI_portable.exe' +
ci_dep_mirror(local_mirror), ci_dep_mirror(local_mirror),
] + extra_cmds, ] + extra_cmds,
@ -381,16 +383,13 @@ local docs_pipeline(name, image, extra_cmds=[], allow_fail=false) = {
debian_pipeline('Debian stable (armhf)', docker_base + 'debian-stable/arm32v7', arch='arm64', jobs=4), debian_pipeline('Debian stable (armhf)', docker_base + 'debian-stable/arm32v7', arch='arm64', jobs=4),
// cross compile targets // cross compile targets
// Aug 11: these are exhibiting some dumb failures in libsodium and external deps, TOFIX later linux_cross_pipeline('Cross Compile (arm/arm64)', cross_targets=['arm-linux-gnueabihf', 'aarch64-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']),
//linux_cross_pipeline('Cross Compile (ppc64le)', cross_targets=['powerpc64le-linux-gnu']),
// Not currently building successfully: // Not currently building successfully:
//linux_cross_pipeline('Cross Compile (mips)', cross_targets=['mips-linux-gnu', 'mipsel-linux-gnu']), //linux_cross_pipeline('Cross Compile (mips)', cross_targets=['mips-linux-gnu', 'mipsel-linux-gnu']),
// android apk builder // android apk builder
// Aug 11: this is also failing in openssl, TOFIX later apk_builder('android apk', docker_base + 'flutter', extra_cmds=['UPLOAD_OS=android ./contrib/ci/drone-static-upload.sh']),
//apk_builder('android apk', docker_base + 'flutter', extra_cmds=['UPLOAD_OS=android ./contrib/ci/drone-static-upload.sh']),
// Windows builds (x64) // Windows builds (x64)
windows_cross_pipeline('Windows (amd64)', windows_cross_pipeline('Windows (amd64)',

@ -1,22 +0,0 @@
name: Close incomplete issues
on:
schedule:
- cron: "30 1 * * *"
jobs:
close-issues:
runs-on: ubuntu-latest
permissions:
issues: write
steps:
- uses: actions/stale@v4.1.1
with:
only-labels: incomplete
days-before-issue-stale: 14
days-before-issue-close: 7
stale-issue-label: "stale"
stale-issue-message: "This issue is stale because it has been 'incomplete' for 14 days with no activity."
close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as stale."
days-before-pr-stale: -1
days-before-pr-close: -1
repo-token: ${{ secrets.GITHUB_TOKEN }}

6
.gitmodules vendored

@ -1,6 +1,9 @@
[submodule "external/nlohmann"] [submodule "external/nlohmann"]
path = external/nlohmann path = external/nlohmann
url = https://github.com/nlohmann/json.git url = https://github.com/nlohmann/json.git
[submodule "external/cxxopts"]
path = external/cxxopts
url = https://github.com/jarro2783/cxxopts.git
[submodule "external/ghc-filesystem"] [submodule "external/ghc-filesystem"]
path = external/ghc-filesystem path = external/ghc-filesystem
url = https://github.com/gulrak/filesystem.git url = https://github.com/gulrak/filesystem.git
@ -36,6 +39,3 @@
[submodule "gui"] [submodule "gui"]
path = gui path = gui
url = https://github.com/oxen-io/lokinet-gui.git url = https://github.com/oxen-io/lokinet-gui.git
[submodule "external/CLI11"]
path = external/CLI11
url = https://github.com/CLIUtils/CLI11.git

@ -32,7 +32,7 @@ project(lokinet
if(APPLE) if(APPLE)
# Apple build number: must be incremented to submit a new build for the same lokinet version, # Apple build number: must be incremented to submit a new build for the same lokinet version,
# should be reset to 0 when the lokinet version increments. # should be reset to 0 when the lokinet version increments.
set(LOKINET_APPLE_BUILD 5) set(LOKINET_APPLE_BUILD 6)
endif() endif()
set(RELEASE_MOTTO "Our Lord And Savior" CACHE STRING "Release motto") set(RELEASE_MOTTO "Our Lord And Savior" CACHE STRING "Release motto")
@ -49,7 +49,8 @@ endif()
option(USE_AVX2 "enable avx2 code" OFF) option(USE_AVX2 "enable avx2 code" OFF)
option(USE_NETNS "enable networking namespace support. Linux only" OFF) option(USE_NETNS "enable networking namespace support. Linux only" OFF)
option(NATIVE_BUILD "optimise for host system and FPU" ON) option(NATIVE_BUILD "optimise for host system and FPU" ON)
option(WITH_EMBEDDED_LOKINET "build liblokinet.so for embedded lokinet" OFF) option(EMBEDDED_CFG "optimise for older hardware or embedded systems" OFF)
option(BUILD_LIBLOKINET "build liblokinet.so" ON)
option(XSAN "use sanitiser, if your system has it (requires -DCMAKE_BUILD_TYPE=Debug)" OFF) option(XSAN "use sanitiser, if your system has it (requires -DCMAKE_BUILD_TYPE=Debug)" OFF)
option(USE_JEMALLOC "Link to jemalloc for memory allocations, if found" ON) option(USE_JEMALLOC "Link to jemalloc for memory allocations, if found" ON)
option(TESTNET "testnet build" OFF) option(TESTNET "testnet build" OFF)
@ -148,6 +149,7 @@ endif()
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
if(NOT BUILD_STATIC_DEPS) if(NOT BUILD_STATIC_DEPS)
pkg_check_modules(LIBUV libuv>=1.18.0 IMPORTED_TARGET) pkg_check_modules(LIBUV libuv>=1.18.0 IMPORTED_TARGET)
endif() endif()

@ -242,7 +242,6 @@ set(openssl_system_env "")
set(openssl_arch "") set(openssl_arch "")
set(openssl_configure_command ./config) set(openssl_configure_command ./config)
set(openssl_flags "CFLAGS=${deps_CFLAGS}") set(openssl_flags "CFLAGS=${deps_CFLAGS}")
set(unbound_ldflags "")
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32) if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32)
set(openssl_arch mingw64) set(openssl_arch mingw64)
@ -259,12 +258,9 @@ if(CMAKE_CROSSCOMPILING)
set(openssl_arch linux-mips64) set(openssl_arch linux-mips64)
elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu) elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu)
set(openssl_arch linux-mips32) set(openssl_arch linux-mips32)
elseif(ARCH_TRIPLET STREQUAL mips-openwrt-linux)
set(unbound_ldflags "-latomic")
set(openssl_arch linux-mips32)
elseif(ARCH_TRIPLET STREQUAL mipsel-linux-gnu) elseif(ARCH_TRIPLET STREQUAL mipsel-linux-gnu)
set(openssl_arch linux-mips) set(openssl_arch linux-mips)
elseif(ARCH_TRIPLET STREQUAL aarch64-linux-gnu OR ARCH_TRIPLET STREQUAL aarch64-openwrt-linux-musl) elseif(ARCH_TRIPLET STREQUAL aarch64-linux-gnu)
# cross compile arm64 # cross compile arm64
set(openssl_arch linux-aarch64) set(openssl_arch linux-aarch64)
elseif(ARCH_TRIPLET MATCHES arm-linux) elseif(ARCH_TRIPLET MATCHES arm-linux)
@ -323,7 +319,7 @@ build_external(unbound
--enable-static --with-libunbound-only --with-pic --enable-static --with-libunbound-only --with-pic
--$<IF:$<BOOL:${WITH_LTO}>,enable,disable>-flto --with-ssl=${DEPS_DESTDIR} --$<IF:$<BOOL:${WITH_LTO}>,enable,disable>-flto --with-ssl=${DEPS_DESTDIR}
--with-libexpat=${DEPS_DESTDIR} --with-libexpat=${DEPS_DESTDIR}
"CC=${deps_cc}" "CFLAGS=${deps_CFLAGS}" "LDFLAGS=${unbound_ldflags}" "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS}"
) )
add_static_target(libunbound unbound_external libunbound.a) add_static_target(libunbound unbound_external libunbound.a)
if(NOT WIN32) if(NOT WIN32)

@ -47,5 +47,5 @@ else()
set(GHC_FILESYSTEM_WITH_INSTALL OFF CACHE INTERNAL "") set(GHC_FILESYSTEM_WITH_INSTALL OFF CACHE INTERNAL "")
add_subdirectory(external/ghc-filesystem) add_subdirectory(external/ghc-filesystem)
target_link_libraries(filesystem INTERFACE ghc_filesystem) target_link_libraries(filesystem INTERFACE ghc_filesystem)
target_compile_definitions(filesystem INTERFACE USE_GHC_FILESYSTEM CLI11_HAS_FILESYSTEM=0) target_compile_definitions(filesystem INTERFACE USE_GHC_FILESYSTEM)
endif() endif()

@ -150,7 +150,7 @@ if(BUILD_PACKAGE)
"${lokinet_installer}" "${lokinet_installer}"
COMMAND ./seticon lokinet.icns "${lokinet_installer}.dmg" COMMAND ./seticon lokinet.icns "${lokinet_installer}.dmg"
) )
add_custom_target(dmg DEPENDS "${lokinet_installer}.dmg") add_custom_target(package DEPENDS "${lokinet_installer}.dmg")
endif() endif()

@ -33,6 +33,7 @@ for abi in $build_abis; do
-DBUILD_PACKAGE=ON \ -DBUILD_PACKAGE=ON \
-DBUILD_SHARED_LIBS=OFF \ -DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \ -DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \ -DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \ -DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \ -DNATIVE_BUILD=OFF \

@ -11,13 +11,6 @@ elif len(sys.argv) != 2 or sys.argv[1].startswith('-'):
else: else:
f = open(sys.argv[1], 'rb') f = open(sys.argv[1], 'rb')
initial = f.peek(2)
is_hex = False
if initial.startswith(b'64') or initial.startswith(b'6c'):
print("Input looks like hex bencoded data; parsing as hex input", file=sys.stderr)
is_hex = True
class HexPrinter(): class HexPrinter():
def __init__(self, data): def __init__(self, data):
self.data = data self.data = data
@ -27,11 +20,6 @@ class HexPrinter():
def next_byte(): def next_byte():
if is_hex:
pair = f.read(2)
assert pair is not None and len(pair) == 2
b = int(pair, 16).to_bytes(1, 'big')
else:
b = f.read(1) b = f.read(1)
assert b is not None and len(b) == 1 assert b is not None and len(b) == 1
return b return b
@ -40,7 +28,7 @@ def next_byte():
def parse_int(): def parse_int():
s = b'' s = b''
x = next_byte() x = next_byte()
while x in b"0123456789-": while x in b"0123456789":
s += x s += x
x = next_byte() x = next_byte()
assert x == b'e' and len(s) > 0, "Invalid integer encoding" assert x == b'e' and len(s) > 0, "Invalid integer encoding"
@ -54,9 +42,6 @@ def parse_string(s):
x = next_byte() x = next_byte()
assert x == b':', "Invalid string encoding" assert x == b':', "Invalid string encoding"
s = int(s) s = int(s)
if is_hex:
data = bytes.fromhex(f.read(2*s).decode('ascii'))
else:
data = f.read(s) data = f.read(s)
assert len(data) == s, "Truncated string data" assert len(data) == s, "Truncated string data"
# If the string is ascii then convert to string: # If the string is ascii then convert to string:

@ -1,5 +1,5 @@
CLANG_FORMAT_DESIRED_VERSION=15 CLANG_FORMAT_DESIRED_VERSION=14
CLANG_FORMAT=$(command -v clang-format-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null) CLANG_FORMAT=$(command -v clang-format-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null)
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then

@ -13,6 +13,7 @@ cd build-mac
cmake \ cmake \
-G Ninja \ -G Ninja \
-DBUILD_STATIC_DEPS=ON \ -DBUILD_STATIC_DEPS=ON \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \ -DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \ -DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \ -DNATIVE_BUILD=OFF \

@ -19,7 +19,7 @@ fi
cd build-mac cd build-mac
rm -rf Lokinet\ * rm -rf Lokinet\ *
ninja -j${JOBS:-1} dmg ninja -j${JOBS:-1} package
cd .. cd ..
echo -e "Build complete, your app is here:\n" echo -e "Build complete, your app is here:\n"

@ -1,99 +0,0 @@
#!/usr/bin/env python3
import nacl.bindings as sodium
from nacl.public import PrivateKey
from nacl.signing import SigningKey, VerifyKey
import nacl.encoding
import requests
import zmq
import zmq.utils.z85
import sys
import re
import time
import random
import shutil
context = zmq.Context()
socket = context.socket(zmq.DEALER)
socket.setsockopt(zmq.CONNECT_TIMEOUT, 5000)
socket.setsockopt(zmq.HANDSHAKE_IVL, 5000)
#socket.setsockopt(zmq.IMMEDIATE, 1)
if len(sys.argv) > 1 and any(sys.argv[1].startswith(x) for x in ("ipc://", "tcp://", "curve://")):
remote = sys.argv[1]
del sys.argv[1]
else:
remote = "ipc://./rpc.sock"
curve_pubkey = b''
my_privkey, my_pubkey = b'', b''
# If given a curve://whatever/pubkey argument then transform it into 'tcp://whatever' and put the
# 'pubkey' back into argv to be handled below.
if remote.startswith("curve://"):
pos = remote.rfind('/')
pkhex = remote[pos+1:]
remote = "tcp://" + remote[8:pos]
if len(pkhex) != 64 or not all(x in "0123456789abcdefABCDEF" for x in pkhex):
print("curve:// addresses must be in the form curve://HOST:PORT/REMOTE_PUBKEY_HEX", file=sys.stderr)
sys.exit(1)
sys.argv[1:0] = [pkhex]
if len(sys.argv) > 1 and len(sys.argv[1]) == 64 and all(x in "0123456789abcdefABCDEF" for x in sys.argv[1]):
curve_pubkey = bytes.fromhex(sys.argv[1])
del sys.argv[1]
socket.curve_serverkey = curve_pubkey
if len(sys.argv) > 1 and len(sys.argv[1]) == 64 and all(x in "0123456789abcdefABCDEF" for x in sys.argv[1]):
my_privkey = bytes.fromhex(sys.argv[1])
del sys.argv[1]
my_pubkey = zmq.utils.z85.decode(zmq.curve_public(zmq.utils.z85.encode(my_privkey)))
else:
my_privkey = PrivateKey.generate()
my_pubkey = my_privkey.public_key.encode()
my_privkey = my_privkey.encode()
print("No curve client privkey given; generated a random one (pubkey: {}, privkey: {})".format(
my_pubkey.hex(), my_privkey.hex()), file=sys.stderr)
socket.curve_secretkey = my_privkey
socket.curve_publickey = my_pubkey
if not 2 <= len(sys.argv) <= 3 or any(x in y for x in ("--help", "-h") for y in sys.argv[1:]):
print("Usage: {} [ipc:///path/to/sock|tcp://1.2.3.4:5678] [SERVER_CURVE_PUBKEY [LOCAL_CURVE_PRIVKEY]] COMMAND ['JSON']".format(
sys.argv[0]), file=sys.stderr)
sys.exit(1)
beginning_of_time = time.clock_gettime(time.CLOCK_MONOTONIC)
print("Connecting to {}".format(remote), file=sys.stderr)
socket.connect(remote)
to_send = [sys.argv[1].encode(), b'tagxyz123']
to_send += (x.encode() for x in sys.argv[2:])
print("Sending {}".format(to_send[0]), file=sys.stderr)
socket.send_multipart(to_send)
if socket.poll(timeout=5000):
m = socket.recv_multipart()
recv_time = time.clock_gettime(time.CLOCK_MONOTONIC)
if len(m) < 3 or m[0:2] != [b'REPLY', b'tagxyz123']:
print("Received unexpected {}-part reply:".format(len(m)), file=sys.stderr)
for x in m:
print("- {}".format(x))
else: # m[2] is numeric value, m[3] is data part, and will become m[2] <- changed
print("Received reply in {:.6f}s:".format(recv_time - beginning_of_time), file=sys.stderr)
if len(m) < 3:
print("(empty reply data)", file=sys.stderr)
else:
for x in m[2:]:
print("{} bytes data part:".format(len(x)), file=sys.stderr)
if any(x.startswith(y) for y in (b'd', b'l', b'i')) and x.endswith(b'e'):
sys.stdout.buffer.write(x)
else:
print(x.decode(), end="\n\n")
else:
print("Request timed out", file=sys.stderr)
socket.close(linger=0)
sys.exit(1)
# sample usage:
# ./omq-rpc.py ipc://$HOME/.oxen/testnet/oxend.sock 'llarp.get_service_nodes' | jq

@ -32,6 +32,7 @@ cmake \
-DBUILD_PACKAGE=ON \ -DBUILD_PACKAGE=ON \
-DBUILD_SHARED_LIBS=OFF \ -DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \ -DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \ -DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \ -DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \ -DNATIVE_BUILD=OFF \

@ -20,7 +20,7 @@ add_library(lokinet-cryptography
libntrup/src/ref/rq.c libntrup/src/ref/rq.c
) )
target_include_directories(lokinet-cryptography PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/libntrup/include) target_include_directories(lokinet-cryptography PUBLIC libntrup/include)
# The avx implementation uses runtime CPU feature detection to enable itself, so we *always* want to # The avx implementation uses runtime CPU feature detection to enable itself, so we *always* want to
# compile it with avx2/fma support when supported by the compiler even if we aren't compiling with # compile it with avx2/fma support when supported by the compiler even if we aren't compiling with

@ -1,16 +1,11 @@
#include <oxenmq/oxenmq.h> #include <oxenmq/oxenmq.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <fmt/core.h> #include <fmt/core.h>
#include <cxxopts.hpp>
#include <future> #include <future>
#include <vector> #include <vector>
#include <array> #include <array>
#include <llarp/net/net.hpp> #include <llarp/net/net.hpp>
#include <string_view>
#include <CLI/App.hpp>
#include <CLI/Formatter.hpp>
#include <CLI/Config.hpp>
#include "oxenmq/address.h"
#ifdef _WIN32 #ifdef _WIN32
// add the unholy windows headers for iphlpapi // add the unholy windows headers for iphlpapi
@ -58,29 +53,25 @@ OMQ_Request(
namespace namespace
{ {
struct command_line_options template <typename T>
constexpr bool is_optional = false;
template <typename T>
constexpr bool is_optional<std::optional<T>> = true;
// Extracts a value from a cxxopts result and assigns it into `value` if present. The value can
// either be a plain value or a std::optional. If not present, `value` is not touched.
template <typename T>
void
extract_option(const cxxopts::ParseResult& r, const std::string& name, T& value)
{ {
// bool options if (r.count(name))
bool verbose = false; {
bool help = false; if constexpr (is_optional<T>)
bool vpnUp = false; value = r[name].as<typename T::value_type>();
bool vpnDown = false; else
bool swap = false; value = r[name].as<T>();
bool printStatus = false; }
bool killDaemon = false; }
// string options
std::string exitAddress;
std::string rpc;
std::string endpoint = "default";
std::string token;
std::optional<std::string> range;
std::vector<std::string> swapExits;
// oxenmq
oxenmq::address rpcURL{};
oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn;
};
// Takes a code, prints a message, and returns the code. Intended use is: // Takes a code, prints a message, and returns the code. Intended use is:
// return exit_error(1, "blah: {}", 42); // return exit_error(1, "blah: {}", 42);
@ -107,131 +98,127 @@ namespace
int int
main(int argc, char* argv[]) main(int argc, char* argv[])
{ {
CLI::App cli{"lokiNET vpn control utility", "lokinet-vpn"}; cxxopts::Options opts("lokinet-vpn", "LokiNET vpn control utility");
command_line_options options{};
// clang-format off
// flags: boolean values in command_line_options struct opts.add_options()
cli.add_flag("-v,--verbose", options.verbose, "Verbose"); ("v,verbose", "Verbose", cxxopts::value<bool>())
cli.add_flag("--add,--up", options.vpnUp, "Map VPN connection to exit node [--up is deprecated]"); ("h,help", "help", cxxopts::value<bool>())
cli.add_flag( ("kill", "kill the daemon", cxxopts::value<bool>())
"--remove,--down", ("up", "put vpn up", cxxopts::value<bool>())
options.vpnDown, ("down", "put vpn down", cxxopts::value<bool>())
"Unmap VPN connection to exit node [--down is deprecated]"); ("exit", "specify exit node address", cxxopts::value<std::string>())
cli.add_flag("--status", options.printStatus, "Print VPN status and exit"); ("rpc", "rpc url for lokinet", cxxopts::value<std::string>())
cli.add_flag("-k,--kill", options.killDaemon, "Kill lokinet daemon"); ("endpoint", "endpoint to use", cxxopts::value<std::string>())
("token", "exit auth token to use", cxxopts::value<std::string>())
// options: string values in command_line_options struct ("auth", "exit auth token to use", cxxopts::value<std::string>())
cli.add_option("--exit", options.exitAddress, "Specify exit node address")->capture_default_str(); ("status", "print status and exit", cxxopts::value<bool>())
cli.add_option("--endpoint", options.endpoint, "Endpoint to use")->capture_default_str(); ("range", "ip range to map", cxxopts::value<std::string>())
cli.add_option("--token,--auth", options.token, "Exit auth token to use")->capture_default_str(); ;
cli.add_option("--range", options.range, "IP range to map exit to")->capture_default_str(); // clang-format on
cli.add_option( oxenmq::address rpcURL("tcp://127.0.0.1:1190");
"--swap", options.swapExits, "Exit addresses to swap mapped connection to [old] [new]") std::string exitAddress;
->expected(2) std::string endpoint = "default";
->capture_default_str(); std::string token;
std::optional<std::string> range;
// options: oxenmq values in command_line_options struct oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn;
cli.add_option("--rpc", options.rpc, "Specify RPC URL for lokinet")->capture_default_str(); bool goUp = false;
cli.add_option( bool goDown = false;
"--log-level", options.logLevel, "Log verbosity level, see log levels for accepted values") bool printStatus = false;
->type_name("LEVEL") bool killDaemon = false;
->capture_default_str();
try try
{ {
cli.parse(argc, argv); const auto result = opts.parse(argc, argv);
}
catch (const CLI::ParseError& e) if (result.count("help") > 0)
{ {
return cli.exit(e); std::cout << opts.help() << std::endl;
return 0;
} }
try if (result.count("verbose") > 0)
{ {
if (options.verbose) logLevel = oxenmq::LogLevel::debug;
options.logLevel = oxenmq::LogLevel::debug;
} }
catch (const CLI::OptionNotFound& e) goUp = result.count("up") > 0;
goDown = result.count("down") > 0;
printStatus = result.count("status") > 0;
killDaemon = result.count("kill") > 0;
extract_option(result, "rpc", rpcURL);
extract_option(result, "exit", exitAddress);
extract_option(result, "endpoint", endpoint);
extract_option(result, "token", token);
extract_option(result, "auth", token);
extract_option(result, "range", range);
}
catch (const cxxopts::option_not_exists_exception& ex)
{ {
cli.exit(e); return exit_error(2, "{}\n{}", ex.what(), opts.help());
} }
catch (const CLI::Error& e) catch (std::exception& ex)
{ {
cli.exit(e); return exit_error(2, "{}", ex.what());
}; }
int numCommands = options.vpnUp + options.vpnDown + options.printStatus + options.killDaemon int num_commands = goUp + goDown + printStatus + killDaemon;
+ (not options.swapExits.empty());
switch (numCommands) if (num_commands == 0)
{ return exit_error(3, "One of --up/--down/--status/--kill must be specified");
case 0: if (num_commands != 1)
return exit_error(3, "One of --add/--remove/--swap/--status/--kill must be specified"); return exit_error(3, "Only one of --up/--down/--status/--kill may be specified");
case 1:
break;
default:
return exit_error(3, "Only one of --add/--remove/--swap/--status/--kill may be specified");
}
if (options.vpnUp and options.exitAddress.empty()) if (goUp and exitAddress.empty())
return exit_error("No exit address provided, must specify --exit <address>"); return exit_error("no exit address provided");
oxenmq::OxenMQ omq{ oxenmq::OxenMQ omq{
[](oxenmq::LogLevel lvl, const char* file, int line, std::string msg) { [](oxenmq::LogLevel lvl, const char* file, int line, std::string msg) {
std::cout << lvl << " [" << file << ":" << line << "] " << msg << std::endl; std::cout << lvl << " [" << file << ":" << line << "] " << msg << std::endl;
}, },
options.logLevel}; logLevel};
options.rpcURL = oxenmq::address{(options.rpc.empty()) ? "tcp://127.0.0.1:1190" : options.rpc};
omq.start(); omq.start();
std::promise<bool> connectPromise; std::promise<bool> connectPromise;
const auto connectionID = omq.connect_remote( const auto connID = omq.connect_remote(
options.rpcURL, rpcURL,
[&connectPromise](auto) { connectPromise.set_value(true); }, [&connectPromise](auto) { connectPromise.set_value(true); },
[&connectPromise](auto, std::string_view msg) { [&connectPromise](auto, std::string_view msg) {
std::cout << "Failed to connect to lokinet RPC: " << msg << std::endl; std::cout << "failed to connect to lokinet RPC: " << msg << std::endl;
connectPromise.set_value(false); connectPromise.set_value(false);
}); });
auto ftr = connectPromise.get_future(); auto ftr = connectPromise.get_future();
if (not ftr.get()) if (not ftr.get())
return 1;
if (options.killDaemon)
{ {
auto maybe_halt = OMQ_Request(omq, connectionID, "llarp.halt"); return 1;
}
if (not maybe_halt)
return exit_error("Call to llarp.halt failed");
if (auto err_it = maybe_halt->find("error"); if (killDaemon)
err_it != maybe_halt->end() and not err_it.value().is_null())
{ {
return exit_error("{}", err_it.value().dump()); if (not OMQ_Request(omq, connID, "llarp.halt"))
} return exit_error("call to llarp.halt failed");
return 0;
} }
if (options.printStatus) if (printStatus)
{ {
const auto maybe_status = OMQ_Request(omq, connectionID, "llarp.status"); const auto maybe_status = OMQ_Request(omq, connID, "llarp.status");
if (not maybe_status) if (not maybe_status)
return exit_error("Call to llarp.status failed"); return exit_error("call to llarp.status failed");
try try
{ {
const auto& ep = maybe_status->at("result").at("services").at(options.endpoint).at("exitMap"); const auto& ep = maybe_status->at("result").at("services").at(endpoint);
const auto exitMap = ep.at("exitMap");
if (ep.empty()) if (exitMap.empty())
{ {
std::cout << "No exits found" << std::endl; std::cout << "no exits" << std::endl;
} }
else else
{ {
for (const auto& [range, exit] : ep.items()) for (const auto& [range, exit] : exitMap.items())
{ {
std::cout << range << " via " << exit.get<std::string>() << std::endl; std::cout << range << " via " << exit.get<std::string>() << std::endl;
} }
@ -239,60 +226,34 @@ main(int argc, char* argv[])
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
return exit_error("Failed to parse result: {}", ex.what()); return exit_error("failed to parse result: {}", ex.what());
} }
return 0; return 0;
} }
if (goUp)
if (not options.swapExits.empty())
{ {
nlohmann::json opts{{"exit_addresses", std::move(options.swapExits)}}; nlohmann::json opts{{"exit", exitAddress}, {"token", token}};
if (range)
auto maybe_swap = OMQ_Request(omq, connectionID, "llarp.swap_exits", std::move(opts)); opts["range"] = *range;
if (not maybe_swap)
return exit_error("Failed to swap exit node connections");
if (auto err_it = maybe_swap->find("error"); auto maybe_result = OMQ_Request(omq, connID, "llarp.exit", std::move(opts));
err_it != maybe_swap->end() and not err_it.value().is_null())
{
return exit_error("{}", err_it.value().dump());
}
}
if (options.vpnUp)
{
nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}};
if (options.range)
opts["ip_range"] = *options.range;
auto maybe_result = OMQ_Request(omq, connectionID, "llarp.map_exit", std::move(opts));
if (not maybe_result) if (not maybe_result)
return exit_error("Could not add exit"); return exit_error("could not add exit");
if (auto err_it = maybe_result->find("error"); if (auto err_it = maybe_result->find("error");
err_it != maybe_result->end() and not err_it.value().is_null()) err_it != maybe_result->end() and not err_it.value().is_null())
{ {
return exit_error("{}", err_it.value().dump()); return exit_error("{}", err_it.value());
} }
} }
if (options.vpnDown) if (goDown)
{ {
nlohmann::json opts{{"unmap_exit", true}}; nlohmann::json opts{{"unmap", true}};
if (options.range) if (range)
opts["ip_range"] = *options.range; opts["range"] = *range;
if (not OMQ_Request(omq, connID, "llarp.exit", std::move(opts)))
auto maybe_down = OMQ_Request(omq, connectionID, "llarp.unmap_exit", std::move(opts)); return exit_error("failed to unmap exit");
if (not maybe_down)
return exit_error("Failed to unmap exit node connection");
if (auto err_it = maybe_down->find("error");
err_it != maybe_down->end() and not err_it.value().is_null())
{
return exit_error("{}", err_it.value().dump());
}
} }
return 0; return 0;

@ -15,60 +15,29 @@
#include <csignal> #include <csignal>
#include <cxxopts.hpp>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <thread>
#include <future> #include <future>
#include <CLI/App.hpp> int
#include <CLI/Formatter.hpp> lokinet_main(int, char**);
#include <CLI/Config.hpp>
namespace
{
struct command_line_options
{
// bool options
bool help = false;
bool version = false;
bool generate = false;
bool router = false;
bool config = false;
bool configOnly = false;
bool overwrite = false;
// string options
// TODO: change this to use a std::filesystem::path once we stop using ghc::filesystem on some
// platforms
std::string configPath;
// windows options #ifdef _WIN32
bool win_install = false; extern "C" LONG FAR PASCAL
bool win_remove = false; win32_signal_handler(EXCEPTION_POINTERS*);
}; extern "C" VOID FAR PASCAL
win32_daemon_entry(DWORD, LPTSTR*);
// windows-specific function declarations VOID
int insert_description();
startWinsock();
void
install_win32_daemon();
void
uninstall_win32_daemon();
// operational function definitions #endif
int
lokinet_main(int, char**);
void
handle_signal(int sig);
static void
run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts);
// variable declarations
static auto logcat = llarp::log::Cat("main"); static auto logcat = llarp::log::Cat("main");
std::shared_ptr<llarp::Context> ctx; std::shared_ptr<llarp::Context> ctx;
std::promise<int> exit_code; std::promise<int> exit_code;
// operational function definitions
void void
handle_signal(int sig) handle_signal(int sig)
{ {
@ -79,23 +48,7 @@ namespace
std::cerr << "Received signal " << sig << ", but have no context yet. Ignoring!" << std::endl; std::cerr << "Received signal " << sig << ", but have no context yet. Ignoring!" << std::endl;
} }
// Windows specific code
#ifdef _WIN32 #ifdef _WIN32
extern "C" LONG FAR PASCAL
win32_signal_handler(EXCEPTION_POINTERS*);
extern "C" VOID FAR PASCAL
win32_daemon_entry(DWORD, LPTSTR*);
VOID
insert_description();
extern "C" BOOL FAR PASCAL
handle_signal_win32(DWORD fdwCtrlType)
{
UNREFERENCED_PARAMETER(fdwCtrlType);
handle_signal(SIGINT);
return TRUE; // probably unreachable
};
int int
startWinsock() startWinsock()
{ {
@ -111,6 +64,14 @@ namespace
return 0; return 0;
} }
extern "C" BOOL FAR PASCAL
handle_signal_win32(DWORD fdwCtrlType)
{
UNREFERENCED_PARAMETER(fdwCtrlType);
handle_signal(SIGINT);
return TRUE; // probably unreachable
}
void void
install_win32_daemon() install_win32_daemon()
{ {
@ -260,6 +221,76 @@ namespace
CloseServiceHandle(schService); CloseServiceHandle(schService);
CloseServiceHandle(schSCManager); CloseServiceHandle(schSCManager);
} }
#endif
/// this sets up, configures and runs the main context
static void
run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts)
{
llarp::LogInfo(fmt::format("starting up {} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO));
try
{
std::shared_ptr<llarp::Config> conf;
if (confFile)
{
llarp::LogInfo("Using config file: ", *confFile);
conf = std::make_shared<llarp::Config>(confFile->parent_path());
}
else
{
conf = std::make_shared<llarp::Config>(llarp::GetDefaultDataDir());
}
if (not conf->Load(confFile, opts.isSNode))
{
llarp::LogError("failed to parse configuration");
exit_code.set_value(1);
return;
}
ctx = std::make_shared<llarp::Context>();
ctx->Configure(std::move(conf));
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
#ifndef _WIN32
signal(SIGHUP, handle_signal);
signal(SIGUSR1, handle_signal);
#endif
try
{
ctx->Setup(opts);
}
catch (llarp::util::bind_socket_error& ex)
{
llarp::LogError(fmt::format("{}, is lokinet already running? 🤔", ex.what()));
exit_code.set_value(1);
return;
}
catch (std::exception& ex)
{
llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what()));
exit_code.set_value(1);
return;
}
llarp::util::SetThreadName("llarp-mainloop");
auto result = ctx->Run(opts);
exit_code.set_value(result);
}
catch (std::exception& e)
{
llarp::LogError("Fatal: caught exception while running: ", e.what());
exit_code.set_exception(std::current_exception());
}
catch (...)
{
llarp::LogError("Fatal: caught non-standard exception while running");
exit_code.set_exception(std::current_exception());
}
}
#ifdef _WIN32
/// minidump generation for windows jizz /// minidump generation for windows jizz
/// will make a coredump when there is an unhandled exception /// will make a coredump when there is an unhandled exception
@ -295,57 +326,46 @@ namespace
return 1; return 1;
} }
VOID FAR PASCAL #endif
SvcCtrlHandler(DWORD dwCtrl)
{
// Handle the requested control code.
switch (dwCtrl) int
main(int argc, char* argv[])
{ {
case SERVICE_CONTROL_STOP: // Set up a default, stderr logging for very early logging; we'll replace this later once we read
// tell service we are stopping // the desired log info from config.
llarp::log::debug(logcat, "Windows service controller gave SERVICE_CONTROL_STOP"); llarp::log::add_sink(llarp::log::Type::Print, "stderr");
llarp::sys::service_manager->system_changed_our_state(llarp::sys::ServiceState::Stopping); llarp::log::reset_level(llarp::log::Level::info);
handle_signal(SIGINT);
return;
case SERVICE_CONTROL_INTERROGATE: llarp::logRingBuffer = std::make_shared<llarp::log::RingBufferSink>(100);
// report status llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO);
llarp::log::debug(logcat, "Got win32 service interrogate signal");
llarp::sys::service_manager->report_changed_state();
return;
default: #ifndef _WIN32
llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl); return lokinet_main(argc, argv);
break; #else
} SERVICE_TABLE_ENTRY DispatchTable[] = {
} {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
// The win32 daemon entry point is where we go when invoked as a windows service; we do the // Try first to run as a service; if this works it fires off to win32_daemon_entry and doesn't
// required service dance and then pretend we were invoked via main(). // return until the service enters STOPPED state.
VOID FAR PASCAL if (StartServiceCtrlDispatcher(DispatchTable))
win32_daemon_entry(DWORD, LPTSTR* argv) return 0;
{
// Register the handler function for the service
auto* svc = dynamic_cast<llarp::sys::SVC_Manager*>(llarp::sys::service_manager);
svc->handle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler);
if (svc->handle == nullptr) auto error = GetLastError();
// We'll get this error if not invoked as a service, which is fine: we can just run directly
if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
{ {
llarp::LogError("failed to register daemon control handler"); llarp::sys::service_manager->disable();
return; return lokinet_main(argc, argv);
} }
else
// we hard code the args to lokinet_main. {
// we yoink argv[0] (lokinet.exe path) and pass in the new args. llarp::log::critical(
std::array args = { logcat, "Error launching service: {}", std::system_category().message(error));
reinterpret_cast<char*>(argv[0]), return 1;
reinterpret_cast<char*>(strdup("c:\\programdata\\lokinet\\lokinet.ini")),
reinterpret_cast<char*>(0)};
lokinet_main(args.size() - 1, args.data());
} }
#endif #endif
}
int int
lokinet_main(int argc, char** argv) lokinet_main(int argc, char** argv)
@ -362,93 +382,98 @@ namespace
SetConsoleCtrlHandler(handle_signal_win32, TRUE); SetConsoleCtrlHandler(handle_signal_win32, TRUE);
#endif #endif
CLI::App cli{ cxxopts::Options options(
"LokiNET is a free, open source, private, decentralized, market-based sybil resistant and " "lokinet",
"IP " "LokiNET is a free, open source, private, "
"based onion routing network", "decentralized, \"market based sybil resistant\" "
"lokinet"}; "and IP based onion routing network");
command_line_options options{}; // clang-format off
options.add_options()
// flags: boolean values in command_line_options struct #ifdef _WIN32
cli.add_flag("--version", options.version, "Lokinet version"); ("install", "install win32 daemon to SCM", cxxopts::value<bool>())
cli.add_flag("-g,--generate", options.generate, "Generate default configuration and exit"); ("remove", "remove win32 daemon from SCM", cxxopts::value<bool>())
cli.add_flag( #endif
"-r,--router", options.router, "Run lokinet in routing mode instead of client-only mode"); ("h,help", "print this help message", cxxopts::value<bool>())
cli.add_flag("-f,--force", options.overwrite, "Force writing config even if file exists"); ("version", "print version string", cxxopts::value<bool>())
("g,generate", "generate default configuration and exit", cxxopts::value<bool>())
// options: string ("r,router", "run in routing mode instead of client only mode", cxxopts::value<bool>())
cli.add_option("config,--config", options.configPath, "Path to lokinet.ini configuration file") ("f,force", "force writing config even if it already exists", cxxopts::value<bool>())
->capture_default_str(); ("config", "path to lokinet.ini configuration file", cxxopts::value<std::string>())
;
if constexpr (llarp::platform::is_windows) // clang-format on
{
cli.add_flag("--install", options.win_install, "Install win32 daemon to SCM"); options.parse_positional("config");
cli.add_flag("--remove", options.win_remove, "Remove win32 daemon from SCM");
} bool genconfigOnly = false;
bool overwrite = false;
std::optional<fs::path> configFile;
try try
{ {
cli.parse(argc, argv); auto result = options.parse(argc, argv);
}
catch (const CLI::ParseError& e)
{
return cli.exit(e);
}
std::optional<fs::path> configFile; if (result.count("help"))
try
{ {
if (options.version) std::cout << options.help() << std::endl;
return 0;
}
if (result.count("version"))
{ {
std::cout << llarp::VERSION_FULL << std::endl; std::cout << llarp::VERSION_FULL << std::endl;
return 0; return 0;
} }
#ifdef _WIN32
if constexpr (llarp::platform::is_windows) if (result.count("install"))
{
if (options.win_install)
{ {
install_win32_daemon(); install_win32_daemon();
return 0; return 0;
} }
if (options.win_remove)
if (result.count("remove"))
{ {
uninstall_win32_daemon(); uninstall_win32_daemon();
return 0; return 0;
} }
#endif
if (result.count("generate") > 0)
{
genconfigOnly = true;
} }
opts.isSNode = options.router; if (result.count("router") > 0)
{
opts.isSNode = true;
}
if (options.generate) if (result.count("force") > 0)
{ {
options.configOnly = true; overwrite = true;
} }
if (not options.configPath.empty()) if (result.count("config") > 0)
{
auto arg = result["config"].as<std::string>();
if (!arg.empty())
{ {
configFile = options.configPath; configFile = arg;
} }
} }
catch (const CLI::OptionNotFound& e)
{
cli.exit(e);
} }
catch (const CLI::Error& e) catch (const cxxopts::option_not_exists_exception& ex)
{ {
cli.exit(e); std::cerr << ex.what();
}; std::cout << options.help() << std::endl;
return 1;
}
if (configFile.has_value()) if (configFile.has_value())
{ {
// when we have an explicit filepath // when we have an explicit filepath
fs::path basedir = configFile->parent_path(); fs::path basedir = configFile->parent_path();
if (options.configOnly) if (genconfigOnly)
{ {
try try
{ {
llarp::ensureConfig(basedir, *configFile, options.overwrite, opts.isSNode); llarp::ensureConfig(basedir, *configFile, overwrite, opts.isSNode);
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
@ -478,10 +503,7 @@ namespace
try try
{ {
llarp::ensureConfig( llarp::ensureConfig(
llarp::GetDefaultDataDir(), llarp::GetDefaultDataDir(), llarp::GetDefaultConfigPath(), overwrite, opts.isSNode);
llarp::GetDefaultConfigPath(),
options.overwrite,
opts.isSNode);
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
@ -491,8 +513,10 @@ namespace
configFile = llarp::GetDefaultConfigPath(); configFile = llarp::GetDefaultConfigPath();
} }
if (options.configOnly) if (genconfigOnly)
{
return 0; return 0;
}
#ifdef _WIN32 #ifdef _WIN32
SetUnhandledExceptionFilter(&GenerateDump); SetUnhandledExceptionFilter(&GenerateDump);
@ -564,114 +588,55 @@ namespace
return code; return code;
} }
// this sets up, configures and runs the main context #ifdef _WIN32
static void
run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts)
{
llarp::LogInfo(fmt::format("starting up {} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO));
try
{
std::shared_ptr<llarp::Config> conf;
if (confFile)
{
llarp::LogInfo("Using config file: ", *confFile);
conf = std::make_shared<llarp::Config>(confFile->parent_path());
}
else
{
conf = std::make_shared<llarp::Config>(llarp::GetDefaultDataDir());
}
if (not conf->Load(confFile, opts.isSNode))
{
llarp::LogError("failed to parse configuration");
exit_code.set_value(1);
return;
}
// change cwd to dataDir to support relative paths in config
fs::current_path(conf->router.m_dataDir);
ctx = std::make_shared<llarp::Context>();
ctx->Configure(std::move(conf));
signal(SIGINT, handle_signal);
signal(SIGTERM, handle_signal);
#ifndef _WIN32
signal(SIGHUP, handle_signal);
signal(SIGUSR1, handle_signal);
#endif
try VOID FAR PASCAL
SvcCtrlHandler(DWORD dwCtrl)
{ {
ctx->Setup(opts); // Handle the requested control code.
}
catch (llarp::util::bind_socket_error& ex) switch (dwCtrl)
{ {
llarp::LogError(fmt::format("{}, is lokinet already running? 🤔", ex.what())); case SERVICE_CONTROL_STOP:
exit_code.set_value(1); // tell service we are stopping
llarp::log::debug(logcat, "Windows service controller gave SERVICE_CONTROL_STOP");
llarp::sys::service_manager->system_changed_our_state(llarp::sys::ServiceState::Stopping);
handle_signal(SIGINT);
return; return;
}
catch (std::exception& ex) case SERVICE_CONTROL_INTERROGATE:
{ // report status
llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what())); llarp::log::debug(logcat, "Got win32 service interrogate signal");
exit_code.set_value(1); llarp::sys::service_manager->report_changed_state();
return; return;
}
llarp::util::SetThreadName("llarp-mainloop");
auto result = ctx->Run(opts); default:
exit_code.set_value(result); llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl);
} break;
catch (std::exception& e)
{
llarp::LogError("Fatal: caught exception while running: ", e.what());
exit_code.set_exception(std::current_exception());
}
catch (...)
{
llarp::LogError("Fatal: caught non-standard exception while running");
exit_code.set_exception(std::current_exception());
} }
} }
} // namespace // The win32 daemon entry point is where we go when invoked as a windows service; we do the required
// service dance and then pretend we were invoked via main().
int VOID FAR PASCAL
main(int argc, char* argv[]) win32_daemon_entry(DWORD, LPTSTR* argv)
{ {
// Set up a default, stderr logging for very early logging; we'll replace this later once we read // Register the handler function for the service
// the desired log info from config. auto* svc = dynamic_cast<llarp::sys::SVC_Manager*>(llarp::sys::service_manager);
llarp::log::add_sink(llarp::log::Type::Print, "stderr"); svc->handle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler);
llarp::log::reset_level(llarp::log::Level::info);
llarp::logRingBuffer = std::make_shared<llarp::log::RingBufferSink>(100);
llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO);
#ifndef _WIN32
return lokinet_main(argc, argv);
#else
SERVICE_TABLE_ENTRY DispatchTable[] = {
{strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
// Try first to run as a service; if this works it fires off to win32_daemon_entry and doesn't
// return until the service enters STOPPED state.
if (StartServiceCtrlDispatcher(DispatchTable))
return 0;
auto error = GetLastError();
// We'll get this error if not invoked as a service, which is fine: we can just run directly if (svc->handle == nullptr)
if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
{ {
llarp::sys::service_manager->disable(); llarp::LogError("failed to register daemon control handler");
return lokinet_main(argc, argv); return;
} }
else
{ // we hard code the args to lokinet_main.
llarp::log::critical( // we yoink argv[0] (lokinet.exe path) and pass in the new args.
logcat, "Error launching service: {}", std::system_category().message(error)); std::array args = {
return 1; reinterpret_cast<char*>(argv[0]),
reinterpret_cast<char*>(strdup("c:\\programdata\\lokinet\\lokinet.ini")),
reinterpret_cast<char*>(0)};
lokinet_main(args.size() - 1, args.data());
} }
#endif #endif
}

@ -1,97 +0,0 @@
# High Level Iterative Approach
the desired outcome of this refactor will be splitting the existing code up into a stack of new components.
a layer hides all functionality of the layer below it to reduce the complexity like the OSI stack intends to.
the refactor starts at the top layer, wiring up the old implementation piecewise to the top layer.
once the top layer is wired up to the old implementation we will move down to the next layer.
this will repeat until we reach the bottom layer.
once the old implementation is wired up into these new clearly defined layers, we can fixup or replace different parts of each layer one at a time as needed.
working down from each layer will let us pick apart the old implementation (if needed) that we would wire up to the new base classes for that layer we are defining now without worrying about what is below it (yet).
this refactor is very able to be split up into small work units that (ideally) do not confict with each other.
PDU: https://en.wikipedia.org/wiki/Protocol_data_unit
# The New Layers
from top to bottom the new layers are:
* Platform Layer
* Flow Layer
* Routing Layer
* Onion Layer
* Link Layer
* Wire Layer
## Platform Layer
this is the top layer, it is responsibile ONLY to act as a handler of reading data from the "user" (via tun interface or whatever) to forward to the flow layer as desired, and to take data from the flow layer and send it to the "user".
any kind of IP/dns mapping or traffic isolation details are done here. embedded lokinet would be implemented in this layer as well, as it is without a full tun interface.
Platform layer PDU are what the OS gives us and we internally convert them into flow layer PDU and hand them off to the flow layer.
## Flow Layer
this layer is tl;dr mean to multiplex data from the platform layer across the routing layer and propagating PDU from the routing to the platform layer if needed.
the flow layer is responsible for sending platform layer PDU across path we have already established.
this layer is informed by the routing layer below it of state changes in what paths are available for use.
the flow layer requests from the layer below to make new paths if it wishes to get new ones on demand.
this layer will recieve routing layer PDU from the routing layer and apply any congestion control needed to buffer things to the os if it is needed at all.
flow layer PDU are (data, ethertype, src-pubkey, dst-pubkey, isolation-metric) tuples.
data is the datum we are tunneling over lokinet. ethertype tells us what kind of datum this is, e.g. plainquic/ipv4/ipv6/auth/etc.
src-pubkey and dst-pubkey are public the ed25519 public keys of each end of the flow in use.
the isolation metric is a piece of metadata we use to distinguish unique flows (convotag). in this new seperation convotags explicitly do not hand over across paths.
## Routing Layer
this layer is tl;dr meant for path management but not path building.
the routing layer is responsible for sending/recieving flow layer PDU, DHT requests/responses, latency testing PDU and any other kind of PDU we send/recieve over the onion layer.
this layer will be responsible for managing paths we have already built across lokinet.
the routing layer will periodically measure path status/latency, and do any other kinds of perioidic path related tasks post build.
this layer when asked for a new path from the flow layer will use one that has been prebuilt already and if the number of prebuilt paths is below a threshold we will tell the onion layer to build more paths.
the routing layer will recieve path build results be their success/fail/timeout from the onion layer that were requested and apply any congestion control needed at the pivot router.
routing layer PDU are (data, src-path, dst-path) tuples.
data is the datum we are transferring between paths.
src-path and dst-path are (pathid, router id) tuples, the source being which path this routing layer PDU originated from, destination being which path it is going to.
in the old model, router id is always the router that recieves it as the pivot router, this remains the same unless we explicitly provide router-id.
this lets us propagate hints to DHT related PDU held inside the datum.
## Onion Layer
the onion layer is repsonsible for path builds, path selection logic and low level details of encrypted/decrypting PDU that are onion routed over paths.
this layer is requested by the routing layer to build a path to a pivot router with an optional additional constraints (e.g. unique cidr/operator/geoip/etc, latency constaints, hop length, path lifetime).
the onion layer will encrypt PDU and send them to link layer as (frame/edge router id) tuples, and recieve link layer frames from edge routers, decrypt them and propagate them as needed to the routing layer.
this layer also handles transit onion traffic and transit path build responsibilities as a snode and apply congestion control as needed per transit path.
the onion layer PDU are (data, src-path, dst-path) tuples.
src-path and dst-path are (router-id, path-id) tuples which contain the ed25519 pubkey of the node and the 128 bit path-id it was associated with.
data is some datum we are onion routing that we would apply symettric encryption as needed before propagating to upper or lower layers.
## Link Layer
the link layer is responsbile for transmission of frames between nodes.
this layer will handle queuing and congestion control between wire proto sessions between nodes.
the link layer is will initate and recieve wire session to/from remote nodes.
the link layer PDU is (data, src-router-id, dst-router-id) tuples.
data is a datum of a link layer frame.
src-router-id and dst-router-id are (ed25519-pubkey, net-addr, wire-proto-info) tuples.
the ed25519 pubkey is a .snode address, (clients have these too but they are ephemeral).
net-addr is an (ip, port) tuple the node is reachable via the wire protocol.
wire-proto-info is dialect specific wire protocol specific info.
## Wire Layer
the wire layer is responsible for transmitting link layer frames between nodes.
all details here are specific to each wire proto dialect.

1
external/CLI11 vendored

@ -1 +0,0 @@
Subproject commit 4c7c8ddc45d2ef74584e5cd945f7a4d27c987748

@ -1,3 +1,4 @@
option(SUBMODULE_CHECK "Enables checking that vendored library submodules are up to date" ON) option(SUBMODULE_CHECK "Enables checking that vendored library submodules are up to date" ON)
if(SUBMODULE_CHECK) if(SUBMODULE_CHECK)
find_package(Git) find_package(Git)
@ -25,6 +26,7 @@ if(SUBMODULE_CHECK)
message(STATUS "Checking submodules") message(STATUS "Checking submodules")
check_submodule(nlohmann) check_submodule(nlohmann)
check_submodule(cxxopts)
check_submodule(ghc-filesystem) check_submodule(ghc-filesystem)
check_submodule(oxen-logging fmt spdlog) check_submodule(oxen-logging fmt spdlog)
check_submodule(pybind11) check_submodule(pybind11)
@ -34,7 +36,6 @@ if(SUBMODULE_CHECK)
check_submodule(uvw) check_submodule(uvw)
check_submodule(cpr) check_submodule(cpr)
check_submodule(ngtcp2) check_submodule(ngtcp2)
check_submodule(CLI11)
endif() endif()
endif() endif()
@ -77,7 +78,7 @@ if(WITH_HIVE)
add_subdirectory(pybind11 EXCLUDE_FROM_ALL) add_subdirectory(pybind11 EXCLUDE_FROM_ALL)
endif() endif()
system_or_submodule(CLI11 CLI11 CLI11>=2.2.0 CLI11) add_subdirectory(cxxopts EXCLUDE_FROM_ALL)
if(WITH_PEERSTATS) if(WITH_PEERSTATS)
add_library(sqlite_orm INTERFACE) add_library(sqlite_orm INTERFACE)
@ -139,27 +140,3 @@ if(WITH_BOOTSTRAP)
endif() endif()
endif() endif()
# libcrypt defaults, only on with macos and non static linux
set(default_libcrypt OFF)
if(LINUX AND NOT STATIC_LINK)
set(default_libcrypt ON)
endif()
if(MACOS)
set(default_libcrypt ON)
endif()
option(WITH_LIBCRYPT "enable fast password hash with libcrypt" ${default_libcrypt})
add_library(lokinet-libcrypt INTERFACE)
if(WITH_LIBCRYPT)
pkg_check_modules(LIBCRYPT libcrypt IMPORTED_TARGET REQUIRED)
add_definitions(-DHAVE_CRYPT)
target_link_libraries(lokinet-libcrypt INTERFACE PkgConfig::LIBCRYPT)
message(STATUS "using libcrypt ${LIBCRYPT_VERSION}")
else()
# TODO static build lib crypt?
message(STATUS "not building with libcrypt")
endif()

1
external/cxxopts vendored

@ -0,0 +1 @@
Subproject commit c74846a891b3cc3bfa992d588b1295f528d43039

@ -1 +1 @@
Subproject commit b6e70ad3aa3b2d718f8607599864cd50a951166a Subproject commit 9f2323a2db5fc54fe8394892769eff859967f735

2
external/oxen-mq vendored

@ -1 +1 @@
Subproject commit 4f6dc35ea13722a5f9dcd0c3d65b6b7ac3d0f0c5 Subproject commit ac6ef82ff6fd20437b7d073466dbef82a95a2173

@ -1,7 +1,7 @@
directory for git submodules directory for git submodules
* CLI11: cli argument parser
* cpr: curl for people, used by lokinet-bootstrap toolchain (to be removed) * cpr: curl for people, used by lokinet-bootstrap toolchain (to be removed)
* cxxopts: cli argument parser (to be removed)
* ghc-filesystem: `std::filesystem` shim lib for older platforms (like macos) * ghc-filesystem: `std::filesystem` shim lib for older platforms (like macos)
* ngtcp2: quic implementation * ngtcp2: quic implementation
* nlohmann: json parser * nlohmann: json parser

@ -43,6 +43,7 @@ namespace llarp
std::shared_ptr<AbstractRouter> router = nullptr; std::shared_ptr<AbstractRouter> router = nullptr;
std::shared_ptr<EventLoop> loop = nullptr; std::shared_ptr<EventLoop> loop = nullptr;
std::shared_ptr<NodeDB> nodedb = nullptr; std::shared_ptr<NodeDB> nodedb = nullptr;
std::string nodedb_dir;
Context(); Context();
virtual ~Context() = default; virtual ~Context() = default;

@ -1,12 +1,5 @@
include(Version) include(Version)
target_sources(lokinet-cryptography PRIVATE
crypto/crypto_libsodium.cpp
crypto/crypto.cpp
crypto/encrypted_frame.cpp
crypto/types.cpp
)
add_library(lokinet-util add_library(lokinet-util
STATIC STATIC
${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp ${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp
@ -22,9 +15,19 @@ add_library(lokinet-util
util/thread/threading.cpp util/thread/threading.cpp
util/time.cpp) util/time.cpp)
add_dependencies(lokinet-util genversion) add_dependencies(lokinet-util genversion)
# lokinet-platform holds all platform specific code target_include_directories(lokinet-util PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR})
target_link_libraries(lokinet-util PUBLIC
lokinet-cryptography
nlohmann_json::nlohmann_json
filesystem
oxenc::oxenc
oxen::logging
)
add_library(lokinet-platform add_library(lokinet-platform
STATIC STATIC
# for networking # for networking
@ -42,6 +45,9 @@ add_library(lokinet-platform
vpn/platform.cpp vpn/platform.cpp
) )
target_link_libraries(lokinet-platform PUBLIC lokinet-cryptography lokinet-util Threads::Threads base_libs uvw)
target_link_libraries(lokinet-platform PRIVATE oxenmq::oxenmq)
if (ANDROID) if (ANDROID)
target_sources(lokinet-platform PRIVATE android/ifaddrs.c util/nop_service_manager.cpp) target_sources(lokinet-platform PRIVATE android/ifaddrs.c util/nop_service_manager.cpp)
endif() endif()
@ -60,26 +66,36 @@ if (WIN32)
net/win32.cpp net/win32.cpp
vpn/win32.cpp vpn/win32.cpp
win32/service_manager.cpp win32/service_manager.cpp
win32/exec.cpp win32/exec.cpp)
add_library(lokinet-win32 STATIC
win32/dll.cpp win32/dll.cpp
win32/exception.cpp win32/exception.cpp)
win32/wintun.cpp add_library(lokinet-wintun STATIC
win32/wintun.cpp)
add_library(lokinet-windivert STATIC
win32/windivert.cpp) win32/windivert.cpp)
target_include_directories(lokinet-platform PRIVATE ${CMAKE_BINARY_DIR}/wintun/include/ ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/include/)
# 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() else()
target_sources(lokinet-platform PRIVATE target_sources(lokinet-platform PRIVATE
net/posix.cpp) net/posix.cpp)
endif() endif()
if(APPLE)
add_subdirectory(apple) if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_sources(lokinet-platform PRIVATE util/nop_service_manager.cpp) target_include_directories(lokinet-platform SYSTEM PUBLIC /usr/local/include)
endif() endif()
# lokinet-dns is the dns parsing and hooking library that we use to
# parse modify and reconstitute dns wire proto, dns queries and RR
# should have no concept of dns caching, this is left as an implementation
# detail of dns resolvers (LATER: make separate lib for dns resolvers)
add_library(lokinet-dns add_library(lokinet-dns
STATIC STATIC
dns/message.cpp dns/message.cpp
@ -91,50 +107,13 @@ add_library(lokinet-dns
dns/server.cpp dns/server.cpp
dns/srv_data.cpp) dns/srv_data.cpp)
# platform specific bits and bobs for setting dns
add_library(lokinet-dns-platform INTERFACE)
if(WITH_SYSTEMD) if(WITH_SYSTEMD)
add_library(lokinet-dns-systemd STATIC dns/nm_platform.cpp dns/sd_platform.cpp) target_sources(lokinet-dns PRIVATE dns/nm_platform.cpp dns/sd_platform.cpp)
target_link_libraries(lokinet-dns-platform INTERFACE lokinet-dns-systemd)
endif() endif()
# lokinet-nodedb holds all types and logic for storing parsing and constructing target_link_libraries(lokinet-dns PUBLIC lokinet-platform uvw)
# nodedb data published to the network and versions of it stored locally target_link_libraries(lokinet-dns PRIVATE libunbound lokinet-config)
add_library(lokinet-nodedb
STATIC
bootstrap.cpp
net/address_info.cpp
net/exit_info.cpp
net/traffic_policy.cpp
nodedb.cpp
pow.cpp
profiling.cpp
router_contact.cpp
router_id.cpp
router_version.cpp
)
set(BOOTSTRAP_FALLBACKS)
foreach(bs IN ITEMS MAINNET TESTNET)
if(BOOTSTRAP_FALLBACK_${bs})
message(STATUS "Building with ${bs} fallback boostrap path \"${BOOTSTRAP_FALLBACK_${bs}}\"")
file(READ "${BOOTSTRAP_FALLBACK_${bs}}" bs_data HEX)
if(bs STREQUAL TESTNET)
set(network "gamma")
elseif(bs STREQUAL MAINNET)
set(network "lokinet")
else()
string(TOLOWER "${bs}" network)
endif()
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "\\\\x\\1" bs_data "${bs_data}")
set(BOOTSTRAP_FALLBACKS "${BOOTSTRAP_FALLBACKS}{\"${network}\"s, \"${bs_data}\"sv},\n")
endif()
endforeach()
configure_file("bootstrap-fallbacks.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/bootstrap-fallbacks.cpp" @ONLY)
target_sources(lokinet-nodedb PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/bootstrap-fallbacks.cpp")
# lokinet-config is for all configuration types and parsers
add_library(lokinet-config add_library(lokinet-config
STATIC STATIC
config/config.cpp config/config.cpp
@ -142,15 +121,18 @@ add_library(lokinet-config
config/ini.cpp config/ini.cpp
config/key_manager.cpp) config/key_manager.cpp)
# lokinet-consensus is for deriving and tracking network consensus state for both service nodes and clients target_link_libraries(lokinet-config PUBLIC lokinet-dns lokinet-platform oxenmq::oxenmq)
add_library(lokinet-consensus
add_library(lokinet-amalgum
STATIC STATIC
consensus/reachability_testing.cpp consensus/reachability_testing.cpp
)
# lokinet-dht holds all logic related to interacting with and participating in the DHT hashring bootstrap.cpp
add_library(lokinet-dht context.cpp
STATIC crypto/crypto_libsodium.cpp
crypto/crypto.cpp
crypto/encrypted_frame.cpp
crypto/types.cpp
dht/context.cpp dht/context.cpp
dht/dht.cpp dht/dht.cpp
dht/explorenetworkjob.cpp dht/explorenetworkjob.cpp
@ -169,56 +151,44 @@ add_library(lokinet-dht
dht/recursiverouterlookup.cpp dht/recursiverouterlookup.cpp
dht/serviceaddresslookup.cpp dht/serviceaddresslookup.cpp
dht/taglookup.cpp dht/taglookup.cpp
)
# lokinet-layer-flow is the flow layer which sits atop the routing layer which manages endpoint_base.cpp
# flows between lokinet snapp endpoints be they .loki or .snode
add_library(lokinet-layer-flow
STATIC
layers/flow/stub.cpp # todo: remove me
)
# lokinet-layer-onion is the "dumb" onion routing layer with builds manages and does i/o
# with onion paths. onion paths anonymize routing layer pdu.
add_library(lokinet-layer-onion
STATIC
path/ihophandler.cpp
path/path_context.cpp
path/path.cpp
path/pathbuilder.cpp
path/pathset.cpp
path/transit_hop.cpp
messages/relay.cpp
messages/relay_commit.cpp
messages/relay_status.cpp
)
# lokinet-layer-wire is a layer 1 analog which splits up exit/context.cpp
# layer 2 frames into layer 1 symbols which in the case of iwp are encrypted udp/ip packets exit/endpoint.cpp
add_library(lokinet-layer-wire exit/exit_messages.cpp
STATIC exit/policy.cpp
exit/session.cpp
handlers/exit.cpp
handlers/tun.cpp
iwp/iwp.cpp iwp/iwp.cpp
iwp/linklayer.cpp iwp/linklayer.cpp
iwp/message_buffer.cpp iwp/message_buffer.cpp
iwp/session.cpp iwp/session.cpp
)
# lokinet-layer-link is for our layer 2 analog which splits up layer 2 frames into
# a series of layer 1 symbols which are then transmitted between lokinet instances
add_library(lokinet-layer-link
STATIC
link/link_manager.cpp link/link_manager.cpp
link/session.cpp link/session.cpp
link/server.cpp link/server.cpp
messages/dht_immediate.cpp messages/dht_immediate.cpp
messages/link_intro.cpp messages/link_intro.cpp
messages/link_message_parser.cpp messages/link_message_parser.cpp
) messages/relay.cpp
messages/relay_commit.cpp
messages/relay_status.cpp
net/address_info.cpp
net/exit_info.cpp
net/traffic_policy.cpp
nodedb.cpp
path/ihophandler.cpp
path/path_context.cpp
path/path.cpp
path/pathbuilder.cpp
path/pathset.cpp
path/transit_hop.cpp
peerstats/peer_db.cpp
peerstats/types.cpp
pow.cpp
profiling.cpp
# lokinet-plainquic is for holding the tunneled plainquic code, not quic wire protocol code
add_library(lokinet-plainquic
STATIC
quic/address.cpp quic/address.cpp
quic/client.cpp quic/client.cpp
quic/connection.cpp quic/connection.cpp
@ -227,64 +197,27 @@ add_library(lokinet-plainquic
quic/server.cpp quic/server.cpp
quic/stream.cpp quic/stream.cpp
quic/tunnel.cpp quic/tunnel.cpp
)
# lokinet-context holds the contextualized god objects for a lokinet instance router_contact.cpp
# it is what any main function would link to in practice but it is hidden behind an interface library (lokinet-amalgum) router_id.cpp
add_library(lokinet-context router_version.cpp
STATIC service/name.cpp
context.cpp
link/link_manager.cpp
router/outbound_message_handler.cpp router/outbound_message_handler.cpp
router/outbound_session_maker.cpp router/outbound_session_maker.cpp
router/rc_lookup_handler.cpp router/rc_lookup_handler.cpp
router/rc_gossiper.cpp router/rc_gossiper.cpp
router/router.cpp router/router.cpp
router/route_poker.cpp router/route_poker.cpp
)
# lokinet-rpc holds all rpc related compilation units
add_library(lokinet-rpc
STATIC
rpc/json_binary_proxy.cpp
rpc/json_conversions.cpp
rpc/lokid_rpc_client.cpp
rpc/rpc_request_parser.cpp
rpc/rpc_server.cpp
rpc/endpoint_rpc.cpp
)
# optional peer stats library
add_library(lokinet-peerstats
STATIC
peerstats/peer_db.cpp
peerstats/types.cpp
)
# lokinet-layer-routing holds logic related to the routing layer
# routing layer is anonymized over the onion layer
add_library(lokinet-layer-routing
STATIC
routing/dht_message.cpp routing/dht_message.cpp
routing/message_parser.cpp routing/message_parser.cpp
routing/path_confirm_message.cpp routing/path_confirm_message.cpp
routing/path_latency_message.cpp routing/path_latency_message.cpp
routing/path_transfer_message.cpp routing/path_transfer_message.cpp
routing/transfer_traffic_message.cpp routing/transfer_traffic_message.cpp
) rpc/lokid_rpc_client.cpp
rpc/rpc_server.cpp
# kitchen sink to be removed after refactor rpc/endpoint_rpc.cpp
add_library(lokinet-service-deprecated-kitchensink
STATIC
endpoint_base.cpp
exit/context.cpp
exit/endpoint.cpp
exit/exit_messages.cpp
exit/policy.cpp
exit/session.cpp
handlers/exit.cpp
handlers/tun.cpp
service/name.cpp
service/address.cpp service/address.cpp
service/async_key_exchange.cpp service/async_key_exchange.cpp
service/auth.cpp service/auth.cpp
@ -309,216 +242,65 @@ add_library(lokinet-service-deprecated-kitchensink
service/tag.cpp service/tag.cpp
) )
add_library(lokinet-layer-platform set(BOOTSTRAP_FALLBACKS)
STATIC foreach(bs IN ITEMS MAINNET TESTNET)
layers/platform/stub.cpp # todo: remove me if(BOOTSTRAP_FALLBACK_${bs})
) message(STATUS "Building with ${bs} fallback boostrap path \"${BOOTSTRAP_FALLBACK_${bs}}\"")
file(READ "${BOOTSTRAP_FALLBACK_${bs}}" bs_data HEX)
if(bs STREQUAL TESTNET)
set(network "gamma")
elseif(bs STREQUAL MAINNET)
set(network "lokinet")
else()
string(TOLOWER "${bs}" network)
endif()
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "\\\\x\\1" bs_data "${bs_data}")
set(BOOTSTRAP_FALLBACKS "${BOOTSTRAP_FALLBACKS}{\"${network}\"s, \"${bs_data}\"sv},\n")
endif()
endforeach()
configure_file("bootstrap-fallbacks.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/bootstrap-fallbacks.cpp" @ONLY)
target_sources(lokinet-amalgum PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/bootstrap-fallbacks.cpp")
if(WITH_PEERSTATS_BACKEND)
target_compile_definitions(lokinet-amalgum PRIVATE -DLOKINET_PEERSTATS_BACKEND)
target_link_libraries(lokinet-amalgum PUBLIC sqlite_orm)
endif()
# interal tooling for pybind
add_library(lokinet-tooling INTERFACE)
if(WITH_HIVE) if(WITH_HIVE)
add_library(lokinet-hive-tooling target_sources(lokinet-amalgum PRIVATE
STATIC
tooling/router_hive.cpp tooling/router_hive.cpp
tooling/hive_router.cpp tooling/hive_router.cpp
tooling/hive_context.cpp tooling/hive_context.cpp
) )
target_link_libraries(lokinet-tooling INTERFACE lokinet-hive-tooling)
endif()
# interface library for setting commone includes, linkage and flags.
add_library(lokinet-base INTERFACE)
target_include_directories(lokinet-base
INTERFACE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/include
)
target_link_libraries(lokinet-base INTERFACE oxen::logging lokinet-cryptography)
if(WITH_PEERSTATS)
target_compile_definitions(lokinet-base INTERFACE -DLOKINET_PEERSTATS_BACKEND)
target_link_libraries(lokinet-base INTERFACE sqlite_orm)
endif() endif()
# interface libraries for internal linkage # TODO: make libunbound hidden behind a feature flag like sqlite for embedded lokinet
add_library(lokinet-layers INTERFACE) target_link_libraries(lokinet-amalgum PRIVATE libunbound)
add_library(lokinet-amalgum INTERFACE)
# helper function to link a library to lokinet-base, enable lto, add to lokinet-amalgum and then link to other libs
function(lokinet_link_lib libname)
message(DEBUG "created target: ${libname}")
enable_lto(${libname})
target_link_libraries(${libname} PUBLIC lokinet-base ${ARGN})
target_link_libraries(lokinet-amalgum INTERFACE ${libname})
endfunction()
# internal public linkages of components
lokinet_link_lib(lokinet-util)
lokinet_link_lib(lokinet-cryptography lokinet-libcrypt lokinet-util)
lokinet_link_lib(lokinet-peerstats lokinet-context)
lokinet_link_lib(lokinet-consensus lokinet-context)
lokinet_link_lib(lokinet-layer-link lokinet-peerstats)
if(TARGET lokinet-hive-tooling)
lokinet_link_lib(lokinet-hive-tooling lokinet-context)
endif()
if(TARGET lokinet-dns-systemd)
lokinet_link_lib(lokinet-dns-systemd
lokinet-dns
lokinet-platform
)
endif()
lokinet_link_lib(lokinet-platform lokinet-util)
lokinet_link_lib(lokinet-config
lokinet-util
lokinet-nodedb
lokinet-dns
lokinet-platform
)
lokinet_link_lib(lokinet-context
lokinet-config
lokinet-platform
lokinet-peerstats
lokinet-layers
lokinet-consensus
lokinet-rpc
)
lokinet_link_lib(lokinet-dht
lokinet-util
lokinet-nodedb
)
lokinet_link_lib(lokinet-plainquic
lokinet-platform
lokinet-config
)
lokinet_link_lib(lokinet-dns target_link_libraries(lokinet-amalgum PUBLIC
cxxopts
oxenc::oxenc
lokinet-platform lokinet-platform
lokinet-dns-platform
lokinet-config lokinet-config
) lokinet-dns
lokinet_link_lib(lokinet-nodedb
lokinet-util
lokinet-platform
)
lokinet_link_lib(lokinet-util
lokinet-nodedb
lokinet-platform
)
lokinet_link_lib(lokinet-rpc
lokinet-context
lokinet-peerstats
lokinet-util lokinet-util
) lokinet-cryptography
ngtcp2_static
oxenmq::oxenmq)
# inter lokinet-layer public/private linkage. enable_lto(lokinet-util lokinet-platform lokinet-dns lokinet-config lokinet-amalgum)
# when linking each layer, we consider the layer directly below private linkage and the layer above public linkage.
# this lets us hide functionality of layers below us when depended on by another component.
#
# from highest to lowest layer, the above layers are stacked as follows:
#
# platform (what lokinet snapps interact with, be it l3 os interaction or embedded lokinet)
# flow (how we want to route and stripe over our onion routing)
# routing (what we are onion routing)
# onion (how the onion routing happens)
# link (what we want to send over the wire and to where)
# wire (what is actually sent over the wire)
#
function(link_lokinet_layers)
set(lib ${ARGV0})
if(${ARGC} GREATER 1)
lokinet_link_lib(${ARGV1} ${lib})
list(REMOVE_AT ARGV 1)
target_link_libraries(${lib} PRIVATE ${ARGV1})
# recursion :D
link_lokinet_layers(${ARGV})
else()
lokinet_link_lib(${lib})
endif()
endfunction()
link_lokinet_layers(
lokinet-layer-platform
lokinet-layer-flow
lokinet-layer-routing
lokinet-layer-onion
lokinet-layer-link
lokinet-layer-wire
)
# set me to OFF to disable old codepath pkg_check_modules(CRYPT libcrypt IMPORTED_TARGET)
set(use_old_impl ON) if(CRYPT_FOUND AND NOT CMAKE_CROSSCOMPILING)
if(use_old_impl) add_definitions(-DHAVE_CRYPT)
# flow layer deprecated-kitchensink (remove me after refactor) add_library(libcrypt INTERFACE)
lokinet_link_lib(lokinet-service-deprecated-kitchensink target_link_libraries(libcrypt INTERFACE PkgConfig::CRYPT)
lokinet-dns target_link_libraries(lokinet-amalgum PRIVATE libcrypt)
lokinet-nodedb message(STATUS "using libcrypt ${CRYPT_VERSION}")
lokinet-context
lokinet-plainquic
lokinet-layer-routing
lokinet-layer-onion
lokinet-dht
lokinet-platform
lokinet-rpc
)
target_link_libraries(lokinet-layers INTERFACE lokinet-service-deprecated-kitchensink)
endif() endif()
target_link_libraries(lokinet-layers INTERFACE
lokinet-layer-platform
lokinet-layer-flow
lokinet-layer-routing
lokinet-layer-onion
lokinet-layer-link
lokinet-layer-wire
)
# per component external deps
target_link_libraries(lokinet-config PUBLIC oxenmq::oxenmq)
target_link_libraries(lokinet-platform PUBLIC oxenmq::oxenmq)
target_link_libraries(lokinet-dns PUBLIC libunbound)
target_link_libraries(lokinet-cryptography PUBLIC
oxenc::oxenc
sodium
)
target_link_libraries(lokinet-context PUBLIC
CLI11
oxenmq::oxenmq
uvw
)
target_link_libraries(lokinet-platform PUBLIC
Threads::Threads
base_libs
uvw
)
target_link_libraries(lokinet-util PUBLIC
nlohmann_json::nlohmann_json
filesystem
oxenc::oxenc
)
target_link_libraries(lokinet-plainquic PUBLIC if(BUILD_LIBLOKINET)
ngtcp2_static
uvw
)
if(WITH_EMBEDDED_LOKINET)
include(GNUInstallDirs) include(GNUInstallDirs)
add_library(lokinet-shared SHARED lokinet_shared.cpp) add_library(lokinet-shared SHARED lokinet_shared.cpp)
target_link_libraries(lokinet-shared PUBLIC lokinet-amalgum) target_link_libraries(lokinet-shared PUBLIC lokinet-amalgum)
@ -534,5 +316,11 @@ if(WITH_EMBEDDED_LOKINET)
endif() endif()
endif() endif()
if(APPLE)
add_subdirectory(apple)
target_sources(lokinet-platform PRIVATE util/nop_service_manager.cpp)
endif()
file(GLOB_RECURSE docs_SRC */*.hpp *.hpp) file(GLOB_RECURSE docs_SRC */*.hpp *.hpp)
set(DOCS_SRC ${docs_SRC} PARENT_SCOPE) set(DOCS_SRC ${docs_SRC} PARENT_SCOPE)

@ -1,7 +1,8 @@
#include <chrono>
#include "config.hpp" #include "config.hpp"
#include "definition.hpp"
#include "ini.hpp"
#include "config/definition.hpp"
#include "ini.hpp"
#include <llarp/constants/files.hpp> #include <llarp/constants/files.hpp>
#include <llarp/constants/platform.hpp> #include <llarp/constants/platform.hpp>
#include <llarp/constants/version.hpp> #include <llarp/constants/version.hpp>
@ -17,7 +18,6 @@
#include <llarp/service/name.hpp> #include <llarp/service/name.hpp>
#include <chrono>
#include <cstdlib> #include <cstdlib>
#include <ios> #include <ios>
#include <iostream> #include <iostream>
@ -609,7 +609,7 @@ namespace llarp
"ifname", "ifname",
Comment{ Comment{
"Interface name for lokinet traffic. If unset lokinet will look for a free name", "Interface name for lokinet traffic. If unset lokinet will look for a free name",
"matching 'lokitunN', starting at N=0 (e.g. lokitun0, lokitun1, ...).", "matching 'lokinetN', starting at N=0 (e.g. lokinet0, lokinet1, ...).",
}, },
AssignmentAcceptor(m_ifname)); AssignmentAcceptor(m_ifname));
@ -1114,12 +1114,7 @@ namespace llarp
void void
ApiConfig::defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params) ApiConfig::defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params)
{ {
constexpr std::array DefaultRPCBind{ constexpr Default DefaultRPCBindAddr{"tcp://127.0.0.1:1190"};
Default{"tcp://127.0.0.1:1190"},
#ifndef _WIN32
Default{"ipc://rpc.sock"},
#endif
};
conf.defineOption<bool>( conf.defineOption<bool>(
"api", "api",
@ -1133,22 +1128,20 @@ namespace llarp
conf.defineOption<std::string>( conf.defineOption<std::string>(
"api", "api",
"bind", "bind",
DefaultRPCBind, DefaultRPCBindAddr,
MultiValue, [this](std::string arg) {
[this, first = true](std::string arg) mutable { if (arg.empty())
if (first)
{ {
m_rpcBindAddresses.clear(); arg = DefaultRPCBindAddr.val;
first = false;
} }
if (arg.find("://") == std::string::npos) if (arg.find("://") == std::string::npos)
{ {
arg = "tcp://" + arg; arg = "tcp://" + arg;
} }
m_rpcBindAddresses.emplace_back(arg); m_rpcBindAddr = std::move(arg);
}, },
Comment{ Comment{
"IP addresses and ports to bind to.", "IP address and port to bind to.",
"Recommend localhost-only for security purposes.", "Recommend localhost-only for security purposes.",
}); });

@ -1,15 +1,14 @@
#pragma once #pragma once
#include "ini.hpp"
#include "definition.hpp"
#include <chrono> #include <chrono>
#include <llarp/bootstrap.hpp> #include <llarp/bootstrap.hpp>
#include <llarp/crypto/types.hpp> #include <llarp/crypto/types.hpp>
#include <llarp/router_contact.hpp> #include <llarp/router_contact.hpp>
#include <llarp/util/fs.hpp> #include <llarp/util/fs.hpp>
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include "ini.hpp"
#include "definition.hpp"
#include <llarp/constants/files.hpp> #include <llarp/constants/files.hpp>
#include <llarp/net/ip_address.hpp> #include <llarp/net/ip_address.hpp>
#include <llarp/net/net_int.hpp> #include <llarp/net/net_int.hpp>
@ -17,6 +16,7 @@
#include <llarp/service/address.hpp> #include <llarp/service/address.hpp>
#include <llarp/service/auth.hpp> #include <llarp/service/auth.hpp>
#include <llarp/dns/srv_data.hpp> #include <llarp/dns/srv_data.hpp>
#include <llarp/router_contact.hpp> #include <llarp/router_contact.hpp>
#include <cstdlib> #include <cstdlib>
@ -189,7 +189,7 @@ namespace llarp
struct ApiConfig struct ApiConfig
{ {
bool m_enableRPCServer = false; bool m_enableRPCServer = false;
std::vector<oxenmq::address> m_rpcBindAddresses; std::string m_rpcBindAddr;
void void
defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params); defineConfigOptions(ConfigDefinition& conf, const ConfigGenParameters& params);

@ -105,9 +105,11 @@ namespace llarp
constexpr bool is_default_array<U&> = is_default_array<remove_cvref_t<U>>; constexpr bool is_default_array<U&> = is_default_array<remove_cvref_t<U>>;
template <typename T, typename Option> template <typename T, typename Option>
constexpr bool is_option = std::is_base_of_v<option_flag, remove_cvref_t<Option>> constexpr bool is_option =
or std::is_same_v<Comment, Option> or is_default<Option> or is_default_array<Option> std::is_base_of_v<
or std::is_invocable_v<remove_cvref_t<Option>, T>; option_flag,
remove_cvref_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 } // namespace config
/// A base class for specifying config options and their constraints. The basic to/from string /// A base class for specifying config options and their constraints. The basic to/from string
@ -296,12 +298,7 @@ namespace llarp
std::vector<std::string> def_strs; std::vector<std::string> def_strs;
def_strs.reserve(defaultValues.size()); def_strs.reserve(defaultValues.size());
for (const auto& v : defaultValues) for (const auto& v : defaultValues)
{
if constexpr (std::is_same_v<bool, T>)
def_strs.push_back(fmt::format("{}", (bool)v));
else
def_strs.push_back(fmt::format("{}", v)); def_strs.push_back(fmt::format("{}", v));
}
return def_strs; return def_strs;
} }
} }
@ -311,7 +308,8 @@ namespace llarp
{ {
if (not multiValued and parsedValues.size() > 0) if (not multiValued and parsedValues.size() > 0)
{ {
throw std::invalid_argument{fmt::format("duplicate value for {}", name)}; throw std::invalid_argument{
fmt::format("duplicate value for {}, previous value: {}", name, parsedValues[0])};
} }
parsedValues.emplace_back(fromString(input)); parsedValues.emplace_back(fromString(input));
@ -331,6 +329,7 @@ namespace llarp
iss >> t; iss >> t;
if (iss.fail()) if (iss.fail())
throw std::invalid_argument{fmt::format("{} is not a valid {}", input, typeid(T).name())}; throw std::invalid_argument{fmt::format("{} is not a valid {}", input, typeid(T).name())};
else
return t; return t;
} }
} }
@ -343,12 +342,7 @@ namespace llarp
std::vector<std::string> result; std::vector<std::string> result;
result.reserve(parsedValues.size()); result.reserve(parsedValues.size());
for (const auto& v : parsedValues) for (const auto& v : parsedValues)
{
if constexpr (std::is_same_v<bool, T>)
result.push_back(fmt::format("{}", (bool)v));
else
result.push_back(fmt::format("{}", v)); result.push_back(fmt::format("{}", v));
}
return result; return result;
} }

@ -9,7 +9,6 @@
#include <list> #include <list>
#include <iostream> #include <iostream>
#include <cassert> #include <cassert>
#include <stdexcept>
namespace llarp namespace llarp
{ {
@ -31,14 +30,6 @@ namespace llarp
return Parse(); return Parse();
} }
bool
ConfigParser::LoadNewFromStr(std::string_view str)
{
m_Data.resize(str.size());
std::copy(str.begin(), str.end(), m_Data.begin());
return ParseAll();
}
bool bool
ConfigParser::LoadFromStr(std::string_view str) ConfigParser::LoadFromStr(std::string_view str)
{ {
@ -61,78 +52,6 @@ namespace llarp
return std::isspace(static_cast<unsigned char>(ch)) != 0; return std::isspace(static_cast<unsigned char>(ch)) != 0;
} }
/// Differs from Parse() as ParseAll() does NOT skip comments
/// ParseAll() is only used by RPC endpoint 'config' for
/// reading new .ini files from string and writing them
bool
ConfigParser::ParseAll()
{
std::list<std::string_view> lines;
{
auto itr = m_Data.begin();
// split into lines
while (itr != m_Data.end())
{
auto beg = itr;
while (itr != m_Data.end() && *itr != '\n' && *itr != '\r')
++itr;
lines.emplace_back(std::addressof(*beg), std::distance(beg, itr));
if (itr == m_Data.end())
break;
++itr;
}
}
std::string_view sectName;
size_t lineno = 0;
for (auto line : lines)
{
lineno++;
// Trim whitespace
while (!line.empty() && whitespace(line.front()))
line.remove_prefix(1);
while (!line.empty() && whitespace(line.back()))
line.remove_suffix(1);
// Skip blank lines but NOT comments
if (line.empty())
continue;
if (line.front() == '[' && line.back() == ']')
{
// section header
line.remove_prefix(1);
line.remove_suffix(1);
sectName = line;
}
else if (auto kvDelim = line.find('='); kvDelim != std::string_view::npos)
{
// key value pair
std::string_view k = line.substr(0, kvDelim);
std::string_view v = line.substr(kvDelim + 1);
// Trim inner whitespace
while (!k.empty() && whitespace(k.back()))
k.remove_suffix(1);
while (!v.empty() && whitespace(v.front()))
v.remove_prefix(1);
if (k.empty())
{
throw std::runtime_error(
fmt::format("{} invalid line ({}): '{}'", m_FileName, lineno, line));
}
LogDebug(m_FileName, ": [", sectName, "]:", k, "=", v);
m_Config[std::string{sectName}].emplace(k, v);
}
else // malformed?
{
throw std::runtime_error(
fmt::format("{} invalid line ({}): '{}'", m_FileName, lineno, line));
}
}
return true;
}
bool bool
ConfigParser::Parse() ConfigParser::Parse()
{ {
@ -163,7 +82,7 @@ namespace llarp
while (!line.empty() && whitespace(line.back())) while (!line.empty() && whitespace(line.back()))
line.remove_suffix(1); line.remove_suffix(1);
// Skip blank lines // Skip blank lines and comments
if (line.empty() or line.front() == ';' or line.front() == '#') if (line.empty() or line.front() == ';' or line.front() == '#')
continue; continue;
@ -187,16 +106,16 @@ namespace llarp
if (k.empty()) if (k.empty())
{ {
throw std::runtime_error( LogError(m_FileName, " invalid line (", lineno, "): '", line, "'");
fmt::format("{} invalid line ({}): '{}'", m_FileName, lineno, line)); return false;
} }
LogDebug(m_FileName, ": [", sectName, "]:", k, "=", v); LogDebug(m_FileName, ": [", sectName, "]:", k, "=", v);
m_Config[std::string{sectName}].emplace(k, v); m_Config[std::string{sectName}].emplace(k, v);
} }
else // malformed? else // malformed?
{ {
throw std::runtime_error( LogError(m_FileName, " invalid line (", lineno, "): '", line, "'");
fmt::format("{} invalid line ({}): '{}'", m_FileName, lineno, line)); return false;
} }
} }
return true; return true;
@ -249,31 +168,4 @@ namespace llarp
m_Overrides.clear(); m_Overrides.clear();
} }
void
ConfigParser::SaveNew() const
{
if (not m_Overrides.empty())
{
throw std::invalid_argument("Override specified when attempting new .ini save");
}
if (m_Config.empty())
{
throw std::invalid_argument("New config not loaded when attempting new .ini save");
}
if (m_FileName.empty())
{
throw std::invalid_argument("New config cannot be saved with filepath specified");
}
std::ofstream ofs(m_FileName);
for (const auto& [section, values] : m_Config)
{
ofs << std::endl << "[" << section << "]" << std::endl;
for (const auto& [key, value] : values)
{
ofs << key << "=" << value << std::endl;
}
}
}
} // namespace llarp } // namespace llarp

@ -24,12 +24,6 @@ namespace llarp
bool bool
LoadFile(const fs::path& fname); LoadFile(const fs::path& fname);
/// load new .ini file from string (calls ParseAll() rather than Parse())
/// return true on success
/// return false on error
bool
LoadNewFromStr(std::string_view str);
/// load from string /// load from string
/// return true on success /// return true on success
/// return false on error /// return false on error
@ -53,10 +47,6 @@ namespace llarp
void void
Save(); Save();
/// save new .ini config file to path
void
SaveNew() const;
inline void inline void
Filename(fs::path f) Filename(fs::path f)
{ {
@ -64,9 +54,6 @@ namespace llarp
}; };
private: private:
bool
ParseAll();
bool bool
Parse(); Parse();

@ -1,5 +1,5 @@
#include <llarp/constants/version.hpp> #include <constants/version.hpp>
#include <llarp/constants/proto.hpp> #include <constants/proto.hpp>
namespace llarp namespace llarp
{ {

@ -14,10 +14,7 @@
#include <llarp/util/service_manager.hpp> #include <llarp/util/service_manager.hpp>
#include <CLI/App.hpp> #include <cxxopts.hpp>
#include <CLI/Formatter.hpp>
#include <CLI/Config.hpp>
#include <csignal> #include <csignal>
#include <stdexcept> #include <stdexcept>
@ -25,10 +22,10 @@
#include <pthread_np.h> #include <pthread_np.h>
#endif #endif
namespace llarp
{
static auto logcat = llarp::log::Cat("llarp-context"); static auto logcat = llarp::log::Cat("llarp-context");
namespace llarp
{
bool bool
Context::CallSafe(std::function<void(void)> f) Context::CallSafe(std::function<void(void)> f)
{ {
@ -45,6 +42,8 @@ namespace llarp
throw std::runtime_error("Config already exists"); throw std::runtime_error("Config already exists");
config = std::move(conf); config = std::move(conf);
nodedb_dir = fs::path{config->router.m_dataDir / nodedb_dirname}.string();
} }
bool bool
@ -90,7 +89,7 @@ namespace llarp
Context::makeNodeDB() Context::makeNodeDB()
{ {
return std::make_shared<NodeDB>( return std::make_shared<NodeDB>(
nodedb_dirname, [r = router.get()](auto call) { r->QueueDiskIO(std::move(call)); }); nodedb_dir, [r = router.get()](auto call) { r->QueueDiskIO(std::move(call)); });
} }
std::shared_ptr<AbstractRouter> std::shared_ptr<AbstractRouter>

@ -1,6 +1,6 @@
#include "rr.hpp" #include "rr.hpp"
#include "dns.hpp" #include "dns.hpp"
#include <llarp/util/formattable.hpp> #include "util/formattable.hpp"
#include <llarp/util/mem.hpp> #include <llarp/util/mem.hpp>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>

@ -177,7 +177,10 @@ namespace llarp::dns
void void
AddUpstreamResolver(const SockAddr& dns) AddUpstreamResolver(const SockAddr& dns)
{ {
std::string str = fmt::format("{}@{}", dns.hostString(false), dns.getPort()); 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())) if (auto err = ub_ctx_set_fwd(m_ctx, str.c_str()))
{ {

@ -75,11 +75,8 @@ namespace llarp::uv
std::optional<SockAddr> std::optional<SockAddr>
LocalAddr() const override LocalAddr() const override
{ {
if (auto addr = handle->sock<uvw::IPv4>(); not addr.ip.empty()) auto addr = handle->sock<uvw::IPv4>();
return SockAddr{addr.ip, huint16_t{static_cast<uint16_t>(addr.port)}}; return SockAddr{addr.ip, huint16_t{static_cast<uint16_t>(addr.port)}};
if (auto addr = handle->sock<uvw::IPv6>(); not addr.ip.empty())
return SockAddr{addr.ip, huint16_t{static_cast<uint16_t>(addr.port)}};
return std::nullopt;
} }
std::optional<int> std::optional<int>

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "exit_messages.hpp" #include "exit_messages.hpp"
#include <llarp/service/protocol_type.hpp> #include "service/protocol_type.hpp"
#include <llarp/net/ip_packet.hpp> #include <llarp/net/ip_packet.hpp>
#include <llarp/path/pathbuilder.hpp> #include <llarp/path/pathbuilder.hpp>
#include <llarp/routing/transfer_traffic_message.hpp> #include <llarp/routing/transfer_traffic_message.hpp>

@ -11,7 +11,7 @@
#include <llarp/router/i_rc_lookup_handler.hpp> #include <llarp/router/i_rc_lookup_handler.hpp>
#include <cassert> #include <cassert>
#include <llarp/service/protocol_type.hpp> #include "service/protocol_type.hpp"
namespace llarp namespace llarp
{ {

@ -2,7 +2,7 @@
#include <llarp/crypto/encrypted_frame.hpp> #include <llarp/crypto/encrypted_frame.hpp>
#include <llarp/crypto/types.hpp> #include <llarp/crypto/types.hpp>
#include <llarp/messages/link_message.hpp> #include "link_message.hpp"
#include <llarp/path/path_types.hpp> #include <llarp/path/path_types.hpp>
#include <llarp/pow.hpp> #include <llarp/pow.hpp>

@ -8,7 +8,6 @@
#include <list> #include <list>
#include <optional> #include <optional>
#include <stdexcept>
#include <string> #include <string>
namespace llarp namespace llarp
@ -25,12 +24,6 @@ namespace llarp
: addr{std::move(address)}, netmask_bits{std::move(netmask)} : addr{std::move(address)}, netmask_bits{std::move(netmask)}
{} {}
explicit IPRange(std::string _range)
{
if (not FromString(_range))
throw std::invalid_argument{"IP string '{}' cannot be parsed as IP range"_format(_range)};
}
static constexpr IPRange static constexpr IPRange
V4MappedRange() V4MappedRange()
{ {
@ -47,8 +40,7 @@ namespace llarp
FromIPv4(net::ipv4addr_t addr, net::ipv4addr_t netmask) FromIPv4(net::ipv4addr_t addr, net::ipv4addr_t netmask)
{ {
return IPRange{ return IPRange{
net::ExpandV4(llarp::net::ToHost(addr)), net::ExpandV4(ToHost(addr)), netmask_ipv6_bits(bits::count_bits(netmask) + 96)};
netmask_ipv6_bits(bits::count_bits(netmask) + 96)};
} }
/// return true if this iprange is in the IPv4 mapping range for containing ipv4 addresses /// return true if this iprange is in the IPv4 mapping range for containing ipv4 addresses
@ -111,7 +103,7 @@ namespace llarp
inline bool inline bool
Contains(const net::ipaddr_t& ip) const Contains(const net::ipaddr_t& ip) const
{ {
return var::visit([this](auto&& ip) { return Contains(llarp::net::ToHost(ip)); }, ip); return var::visit([this](auto&& ip) { return Contains(ToHost(ip)); }, ip);
} }
/// get the highest address on this range /// get the highest address on this range

@ -2,7 +2,6 @@
#include "ip_range.hpp" #include "ip_range.hpp"
#include <llarp/util/status.hpp> #include <llarp/util/status.hpp>
#include <set>
#include <vector> #include <vector>
namespace llarp namespace llarp

@ -290,21 +290,25 @@ namespace llarp
} }
std::string std::string
SockAddr::hostString(bool ipv6_brackets) const SockAddr::hostString() const
{ {
std::array<char, 128> buf{}; std::string str;
char buf[INET6_ADDRSTRLEN] = {0x0};
if (isIPv4()) if (isIPv4())
{ {
// IPv4 mapped addrs // handle IPv4 mapped addrs
inet_ntop(AF_INET, &m_addr4.sin_addr.s_addr, buf.data(), buf.size()); inet_ntop(AF_INET, &m_addr4.sin_addr.s_addr, buf, sizeof(buf));
return buf.data(); str = buf;
} }
else
inet_ntop(AF_INET6, &m_addr.sin6_addr.s6_addr, buf.data(), buf.size()); {
if (not ipv6_brackets) inet_ntop(AF_INET6, &m_addr.sin6_addr.s6_addr, buf, sizeof(buf));
return buf.data(); str.reserve(std::strlen(buf) + 2);
str.append("[");
return fmt::format("[{}]", buf.data()); str.append(buf);
str.append("]");
}
return str;
} }
bool bool

@ -86,10 +86,8 @@ namespace llarp
std::string std::string
ToString() const; ToString() const;
/// convert ip address to string; ipv6_brackets - if true or omitted we add [...] around the
/// IPv6 address, otherwise we return it bare.
std::string std::string
hostString(bool ipv6_brackets = true) const; hostString() const;
inline int inline int
Family() const Family() const

@ -1,10 +1,10 @@
#include "pathbuilder.hpp" #include "pathbuilder.hpp"
#include "path_context.hpp"
#include <llarp/crypto/crypto.hpp> #include <llarp/crypto/crypto.hpp>
#include <llarp/messages/relay_commit.hpp> #include <llarp/messages/relay_commit.hpp>
#include <llarp/nodedb.hpp> #include <llarp/nodedb.hpp>
#include <llarp/util/logging.hpp> #include "path_context.hpp"
#include "util/logging.hpp"
#include <llarp/profiling.hpp> #include <llarp/profiling.hpp>
#include <llarp/router/abstractrouter.hpp> #include <llarp/router/abstractrouter.hpp>
#include <llarp/router/i_rc_lookup_handler.hpp> #include <llarp/router/i_rc_lookup_handler.hpp>

@ -1,7 +1,7 @@
#pragma once #pragma once
#include "path_types.hpp" #include "path_types.hpp"
#include <llarp/service/protocol_type.hpp> #include "service/protocol_type.hpp"
#include <llarp/router_id.hpp> #include <llarp/router_id.hpp>
#include <llarp/routing/message.hpp> #include <llarp/routing/message.hpp>
#include <llarp/service/intro_set.hpp> #include <llarp/service/intro_set.hpp>

@ -33,9 +33,7 @@ namespace llarp
} }
TransitHop::TransitHop() TransitHop::TransitHop()
: IHopHandler{} : m_UpstreamGather(transit_hop_queue_size), m_DownstreamGather(transit_hop_queue_size)
, m_UpstreamGather{transit_hop_queue_size}
, m_DownstreamGather{transit_hop_queue_size}
{ {
m_UpstreamGather.enable(); m_UpstreamGather.enable();
m_DownstreamGather.enable(); m_DownstreamGather.enable();

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <llarp/constants/path.hpp> #include <llarp/constants/path.hpp>
#include <llarp/path/ihophandler.hpp> #include "ihophandler.hpp"
#include <llarp/path/path_types.hpp> #include "path_types.hpp"
#include <llarp/routing/handler.hpp> #include <llarp/routing/handler.hpp>
#include <llarp/router_id.hpp> #include <llarp/router_id.hpp>
#include <llarp/util/compare_ptr.hpp> #include <llarp/util/compare_ptr.hpp>

@ -90,7 +90,7 @@ namespace llarp::quic
void* user_data) void* user_data)
{ {
std::basic_string_view data{rawdata, rawdatalen}; std::basic_string_view data{rawdata, rawdatalen};
log::trace(log_cat, "Receiving crypto data: {}", buffer_printer{data}); LogTrace("Receiving crypto data @ level ", crypto_level, " ", buffer_printer{data});
auto& conn = *static_cast<Connection*>(user_data); auto& conn = *static_cast<Connection*>(user_data);
switch (crypto_level) switch (crypto_level)
@ -101,7 +101,6 @@ namespace llarp::quic
return FAIL; return FAIL;
case NGTCP2_CRYPTO_LEVEL_INITIAL: case NGTCP2_CRYPTO_LEVEL_INITIAL:
log::trace(log_cat, "Receiving initial crypto...");
// "Initial" level means we are still handshaking; if we are server then we receive // "Initial" level means we are still handshaking; if we are server then we receive
// the client's transport params (sent in client_initial, above) and blast ours // the client's transport params (sent in client_initial, above) and blast ours
// back. If we are a client then getting here means we received a response from the // back. If we are a client then getting here means we received a response from the
@ -121,7 +120,6 @@ namespace llarp::quic
break; break;
case NGTCP2_CRYPTO_LEVEL_HANDSHAKE: case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
log::trace(log_cat, "Receiving handshake crypto...");
if (!ngtcp2_conn_is_server(conn)) if (!ngtcp2_conn_is_server(conn))
{ {
if (auto rv = conn.recv_transport_params(data); rv != 0) if (auto rv = conn.recv_transport_params(data); rv != 0)
@ -146,8 +144,13 @@ namespace llarp::quic
conn.complete_handshake(); conn.complete_handshake();
break; break;
case NGTCP2_CRYPTO_LEVEL_APPLICATION:
// if (!conn.init_tx_key())
// return FAIL;
break;
default: default:
LogWarn("Unhandled crypto_level"); LogWarn("Unhandled crypto_level ", crypto_level);
return FAIL; return FAIL;
} }
conn.io_ready(); conn.io_ready();

@ -21,11 +21,6 @@ extern "C"
#include <uvw/poll.h> #include <uvw/poll.h>
#include <uvw/timer.h> #include <uvw/timer.h>
namespace
{
static auto log_cat = llarp::log::Cat("lokinet.quic");
} // namespace
namespace llarp::quic namespace llarp::quic
{ {
// We send and verify this in the initial connection and handshake; this is designed to allow // We send and verify this in the initial connection and handshake; this is designed to allow

@ -1,7 +1,7 @@
#include "tunnel.hpp" #include "tunnel.hpp"
#include <llarp/service/convotag.hpp> #include "service/convotag.hpp"
#include <llarp/service/endpoint.hpp> #include "service/endpoint.hpp"
#include <llarp/service/name.hpp> #include "service/name.hpp"
#include "stream.hpp" #include "stream.hpp"
#include <limits> #include <limits>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
@ -196,10 +196,9 @@ namespace llarp::quic
auto peer = client.peer(); auto peer = client.peer();
LogDebug("Closing connection to ", peer.ip, ":", peer.port); LogDebug("Closing connection to ", peer.ip, ":", peer.port);
client.clear(); client.clear();
// TOFIX: this logic if (error_code)
// if (error_code) client.close();
// client.close(); else
// else
client.close(); client.close();
} }
@ -266,7 +265,7 @@ namespace llarp::quic
LogInfo("quic stream from ", lokinet_addr, " to ", port, " tunnelling to ", *tunnel_to); LogInfo("quic stream from ", lokinet_addr, " to ", port, " tunnelling to ", *tunnel_to);
auto tcp = get_loop()->resource<uvw::TCPHandle>(); auto tcp = get_loop()->resource<uvw::TCPHandle>();
[[maybe_unused]] auto error_handler = tcp->once<uvw::ErrorEvent>( auto error_handler = tcp->once<uvw::ErrorEvent>(
[&stream, to = *tunnel_to](const uvw::ErrorEvent&, uvw::TCPHandle&) { [&stream, to = *tunnel_to](const uvw::ErrorEvent&, uvw::TCPHandle&) {
LogWarn("Failed to connect to ", to, ", shutting down quic stream"); LogWarn("Failed to connect to ", to, ", shutting down quic stream");
stream.close(tunnel::ERROR_CONNECT); stream.close(tunnel::ERROR_CONNECT);
@ -276,7 +275,8 @@ namespace llarp::quic
// tunnel to let the other end know the connection was successful, then set up regular // tunnel to let the other end know the connection was successful, then set up regular
// stream handling to handle any other to/from data. // stream handling to handle any other to/from data.
tcp->once<uvw::ConnectEvent>( tcp->once<uvw::ConnectEvent>(
[streamw = stream.weak_from_this()](const uvw::ConnectEvent&, uvw::TCPHandle& tcp) { [streamw = stream.weak_from_this(), error_handler = std::move(error_handler)](
const uvw::ConnectEvent&, uvw::TCPHandle& tcp) {
auto peer = tcp.peer(); auto peer = tcp.peer();
auto stream = streamw.lock(); auto stream = streamw.lock();
if (!stream) if (!stream)

@ -1,6 +1,6 @@
#include "route_poker.hpp" #include "route_poker.hpp"
#include <llarp/router/abstractrouter.hpp> #include "abstractrouter.hpp"
#include <llarp/net/sock_addr.hpp> #include "net/sock_addr.hpp"
#include <llarp/service/context.hpp> #include <llarp/service/context.hpp>
#include <llarp/dns/platform.hpp> #include <llarp/dns/platform.hpp>
#include <unordered_set> #include <unordered_set>

@ -361,7 +361,6 @@ namespace llarp
_identity = RpcClient()->ObtainIdentityKey(); _identity = RpcClient()->ObtainIdentityKey();
const RouterID pk{pubkey()}; const RouterID pk{pubkey()};
LogWarn("Obtained lokid identity key: ", pk); LogWarn("Obtained lokid identity key: ", pk);
RpcClient()->StartPings();
break; break;
} }
catch (const std::exception& e) catch (const std::exception& e)
@ -412,10 +411,12 @@ namespace llarp
if (log::get_level_default() != log::Level::off) if (log::get_level_default() != log::Level::off)
log::reset_level(conf.logging.m_logLevel); log::reset_level(conf.logging.m_logLevel);
log::clear_sinks(); log::clear_sinks();
log::add_sink(log_type, log_type == log::Type::System ? "lokinet" : conf.logging.m_logFile); log::add_sink(log_type, conf.logging.m_logFile);
enableRPCServer = conf.api.m_enableRPCServer;
// re-add rpc log sink if rpc enabled, else free it // re-add rpc log sink if rpc enabled, else free it
if (m_Config->api.m_enableRPCServer and llarp::logRingBuffer) if (enableRPCServer and llarp::logRingBuffer)
log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO); log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO);
else else
llarp::logRingBuffer = nullptr; llarp::logRingBuffer = nullptr;
@ -429,6 +430,9 @@ namespace llarp
m_lokidRpcClient = std::make_shared<rpc::LokidRpcClient>(m_lmq, weak_from_this()); m_lokidRpcClient = std::make_shared<rpc::LokidRpcClient>(m_lmq, weak_from_this());
} }
if (enableRPCServer)
rpcBindAddr = oxenmq::address(conf.api.m_rpcBindAddr);
log::debug(logcat, "Starting RPC server"); log::debug(logcat, "Starting RPC server");
if (not StartRpcServer()) if (not StartRpcServer())
throw std::runtime_error("Failed to start rpc server"); throw std::runtime_error("Failed to start rpc server");
@ -624,6 +628,7 @@ namespace llarp
} }
if (IsServiceNode()) if (IsServiceNode())
return SaveRC(); return SaveRC();
else
return true; return true;
} }
@ -1255,8 +1260,12 @@ namespace llarp
bool bool
Router::StartRpcServer() Router::StartRpcServer()
{ {
if (m_Config->api.m_enableRPCServer) if (enableRPCServer)
m_RPCServer = std::make_unique<rpc::RPCServer>(m_lmq, *this); {
m_RPCServer.reset(new rpc::RpcServer{m_lmq, this});
m_RPCServer->AsyncServeRPC(rpcBindAddr);
LogInfo("Bound RPC server to ", rpcBindAddr.full_address());
}
return true; return true;
} }

@ -298,7 +298,10 @@ namespace llarp
void void
PumpLL(); PumpLL();
std::unique_ptr<rpc::RPCServer> m_RPCServer; const oxenmq::address DefaultRPCBindAddr = oxenmq::address::tcp("127.0.0.1", 1190);
bool enableRPCServer = false;
oxenmq::address rpcBindAddr = DefaultRPCBindAddr;
std::unique_ptr<rpc::RpcServer> m_RPCServer;
const llarp_time_t _randomStartDelay; const llarp_time_t _randomStartDelay;

@ -1,63 +0,0 @@
#include "json_binary_proxy.hpp"
#include <oxenc/hex.h>
#include <oxenc/base64.h>
namespace llarp::rpc
{
void
load_binary_parameter_impl(
std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data)
{
if (allow_raw && bytes.size() == raw_size)
{
std::memcpy(val_data, bytes.data(), bytes.size());
return;
}
else if (bytes.size() == raw_size * 2)
{
if (oxenc::is_hex(bytes))
{
oxenc::from_hex(bytes.begin(), bytes.end(), val_data);
return;
}
}
else
{
const size_t b64_padded = (raw_size + 2) / 3 * 4;
const size_t b64_padding = raw_size % 3 == 1 ? 2 : raw_size % 3 == 2 ? 1 : 0;
const size_t b64_unpadded = b64_padded - b64_padding;
const std::string_view b64_padding_string = b64_padding == 2 ? "=="sv
: b64_padding == 1 ? "="sv
: ""sv;
if (bytes.size() == b64_unpadded
|| (b64_padding > 0 && bytes.size() == b64_padded
&& bytes.substr(b64_unpadded) == b64_padding_string))
{
if (oxenc::is_base64(bytes))
{
oxenc::from_base64(bytes.begin(), bytes.end(), val_data);
return;
}
}
}
throw std::runtime_error{"Invalid binary value: unexpected size and/or encoding"};
}
nlohmann::json&
json_binary_proxy::operator=(std::string_view binary_data)
{
switch (format)
{
case fmt::bt:
return e = binary_data;
case fmt::hex:
return e = oxenc::to_hex(binary_data);
case fmt::base64:
return e = oxenc::to_base64(binary_data);
}
throw std::runtime_error{"Internal error: invalid binary encoding"};
}
} // namespace llarp::rpc

@ -1,158 +0,0 @@
#pragma once
#include <string_view>
#include <nlohmann/json.hpp>
#include <unordered_set>
using namespace std::literals;
namespace llarp::rpc
{
// Binary types that we support for rpc input/output. For json, these must be specified as hex or
// base64; for bt-encoded requests these can be accepted as binary, hex, or base64.
template <typename T>
inline constexpr bool json_is_binary = false;
template <typename T>
inline constexpr bool json_is_binary_container = false;
template <typename T>
inline constexpr bool json_is_binary_container<std::vector<T>> = json_is_binary<T>;
template <typename T>
inline constexpr bool json_is_binary_container<std::unordered_set<T>> = json_is_binary<T>;
// De-referencing wrappers around the above:
template <typename T>
inline constexpr bool json_is_binary<const T&> = json_is_binary<T>;
template <typename T>
inline constexpr bool json_is_binary<T&&> = json_is_binary<T>;
template <typename T>
inline constexpr bool json_is_binary_container<const T&> = json_is_binary_container<T>;
template <typename T>
inline constexpr bool json_is_binary_container<T&&> = json_is_binary_container<T>;
void
load_binary_parameter_impl(
std::string_view bytes, size_t raw_size, bool allow_raw, uint8_t* val_data);
// Loads a binary value from a string_view which may contain hex, base64, and (optionally) raw
// bytes.
template <typename T, typename = std::enable_if_t<json_is_binary<T>>>
void
load_binary_parameter(std::string_view bytes, bool allow_raw, T& val)
{
load_binary_parameter_impl(bytes, sizeof(T), allow_raw, reinterpret_cast<uint8_t*>(&val));
}
// Wrapper around a nlohmann::json that assigns a binary value either as binary (for bt-encoding);
// or as hex or base64 (for json-encoding).
class json_binary_proxy
{
public:
nlohmann::json& e;
enum class fmt
{
bt,
hex,
base64
} format;
explicit json_binary_proxy(nlohmann::json& elem, fmt format) : e{elem}, format{format}
{}
json_binary_proxy() = delete;
json_binary_proxy(const json_binary_proxy&) = default;
json_binary_proxy(json_binary_proxy&&) = default;
/// Dereferencing a proxy element accesses the underlying nlohmann::json
nlohmann::json&
operator*()
{
return e;
}
nlohmann::json*
operator->()
{
return &e;
}
/// Descends into the json object, returning a new binary value proxy around the child element.
template <typename T>
json_binary_proxy
operator[](T&& key)
{
return json_binary_proxy{e[std::forward<T>(key)], format};
}
/// Returns a binary value proxy around the first/last element (requires an underlying list)
json_binary_proxy
front()
{
return json_binary_proxy{e.front(), format};
}
json_binary_proxy
back()
{
return json_binary_proxy{e.back(), format};
}
/// Assigns binary data from a string_view/string/etc.
nlohmann::json&
operator=(std::string_view binary_data);
/// Assigns binary data from a string_view over a 1-byte, non-char type (e.g. unsigned char or
/// uint8_t).
template <
typename Char,
std::enable_if_t<sizeof(Char) == 1 && !std::is_same_v<Char, char>, int> = 0>
nlohmann::json&
operator=(std::basic_string_view<Char> binary_data)
{
return *this = std::string_view{
reinterpret_cast<const char*>(binary_data.data()), binary_data.size()};
}
/// Takes a trivial, no-padding data structure (e.g. a crypto::hash) as the value and dumps its
/// contents as the binary value.
template <typename T, std::enable_if_t<json_is_binary<T>, int> = 0>
nlohmann::json&
operator=(const T& val)
{
return *this = std::string_view{reinterpret_cast<const char*>(&val), sizeof(val)};
}
/// Takes a vector of some json_binary_proxy-assignable type and builds an array by assigning
/// each one into a new array of binary values.
template <typename T, std::enable_if_t<json_is_binary_container<T>, int> = 0>
nlohmann::json&
operator=(const T& vals)
{
auto a = nlohmann::json::array();
for (auto& val : vals)
json_binary_proxy{a.emplace_back(), format} = val;
return e = std::move(a);
}
/// Emplaces a new nlohman::json to the end of an underlying list and returns a
/// json_binary_proxy wrapping it.
///
/// Example:
///
/// auto child = wrappedelem.emplace_back({"key1": 1}, {"key2": 2});
/// child["binary-key"] = some_binary_thing;
template <typename... Args>
json_binary_proxy
emplace_back(Args&&... args)
{
return json_binary_proxy{e.emplace_back(std::forward<Args>(args)...), format};
}
/// Adds an element to an underlying list, then copies or moves the given argument onto it via
/// json_binary_proxy assignment.
template <typename T>
void
push_back(T&& val)
{
emplace_back() = std::forward<T>(val);
}
};
} // namespace llarp::rpc

@ -1,46 +0,0 @@
#pragma once
#include <nlohmann/json.hpp>
#include <oxenc/bt_value.h>
using nlohmann::json;
namespace llarp::rpc
{
inline oxenc::bt_value
json_to_bt(json&& j)
{
if (j.is_object())
{
oxenc::bt_dict res;
for (auto& [k, v] : j.items())
{
if (v.is_null())
continue; // skip k-v pairs with a null v (for other nulls we fail).
res[k] = json_to_bt(std::move(v));
}
return res;
}
if (j.is_array())
{
oxenc::bt_list res;
for (auto& v : j)
res.push_back(json_to_bt(std::move(v)));
return res;
}
if (j.is_string())
{
return std::move(j.get_ref<std::string&>());
}
if (j.is_boolean())
return j.get<bool>() ? 1 : 0;
if (j.is_number_unsigned())
return j.get<uint64_t>();
if (j.is_number_integer())
return j.get<int64_t>();
throw std::domain_error{
"internal error: encountered some unhandled/invalid type in json-to-bt translation"};
}
} // namespace llarp::rpc

@ -1,18 +0,0 @@
#include "json_conversions.hpp"
#include <nlohmann/json.hpp>
namespace llarp
{
void
to_json(nlohmann::json& j, const IPRange& ipr)
{
j = ipr.ToString();
}
void
from_json(const nlohmann::json& j, IPRange& ipr)
{
ipr = IPRange{j.get<std::string>()};
}
} // namespace llarp

@ -1,37 +0,0 @@
#pragma once
#include <llarp/net/ip_range.hpp>
#include <nlohmann/json_fwd.hpp>
#include "json_binary_proxy.hpp"
namespace llarp
{
void
to_json(nlohmann::json& j, const IPRange& ipr);
void
from_json(const nlohmann::json& j, IPRange& ipr);
} // namespace llarp
namespace nlohmann
{
// Specializations of binary types for deserialization; when receiving these from json we expect
// them encoded in hex or base64. These may *not* be used for serialization, and will throw if so
// invoked; for serialization you need to use RPC_COMMAND::response_hex (or _b64) instead.
template <typename T>
struct adl_serializer<T, std::enable_if_t<llarp::rpc::json_is_binary<T>>>
{
static_assert(std::is_trivially_copyable_v<T> && std::has_unique_object_representations_v<T>);
static void
to_json(json&, const T&)
{
throw std::logic_error{"Internal error: binary types are not directly serializable"};
}
static void
from_json(const json& j, T& val)
{
llarp::rpc::load_binary_parameter(j.get<std::string_view>(), false /*no raw*/, val);
}
};
} // namespace nlohmann

@ -64,7 +64,7 @@ namespace llarp
LogInfo("connecting to lokid via LMQ at ", url.full_address()); LogInfo("connecting to lokid via LMQ at ", url.full_address());
m_Connection = m_lokiMQ->connect_remote( m_Connection = m_lokiMQ->connect_remote(
url, url,
[](oxenmq::ConnectionID) {}, [self = shared_from_this()](oxenmq::ConnectionID) { self->Connected(); },
[self = shared_from_this(), url](oxenmq::ConnectionID, std::string_view f) { [self = shared_from_this(), url](oxenmq::ConnectionID, std::string_view f) {
llarp::LogWarn("Failed to connect to lokid: ", f); llarp::LogWarn("Failed to connect to lokid: ", f);
if (auto router = self->m_Router.lock()) if (auto router = self->m_Router.lock())
@ -126,7 +126,7 @@ namespace llarp
}}, }},
}; };
if (!m_LastUpdateHash.empty()) if (!m_LastUpdateHash.empty())
request["poll_block_hash"] = m_LastUpdateHash; request["fields"]["poll_block_hash"] = m_LastUpdateHash;
Request( Request(
"rpc.get_service_nodes", "rpc.get_service_nodes",
@ -168,15 +168,10 @@ namespace llarp
} }
void void
LokidRpcClient::StartPings() LokidRpcClient::Connected()
{ {
constexpr auto PingInterval = 30s; constexpr auto PingInterval = 30s;
auto makePingRequest = [self = shared_from_this()]() {
auto router = m_Router.lock();
if (not router)
return;
auto makePingRequest = router->loop()->make_caller([self = shared_from_this()]() {
// send a ping // send a ping
PubKey pk{}; PubKey pk{};
auto r = self->m_Router.lock(); auto r = self->m_Router.lock();
@ -213,11 +208,10 @@ namespace llarp
// reason (e.g. oxend restarts and loses the subscription); we poll using the last known // reason (e.g. oxend restarts and loses the subscription); we poll using the last known
// hash so that the poll is very cheap (basically empty) if the block hasn't advanced. // hash so that the poll is very cheap (basically empty) if the block hasn't advanced.
self->UpdateServiceNodeList(); self->UpdateServiceNodeList();
}); };
// Fire one ping off right away to get things going. // Fire one ping off right away to get things going.
makePingRequest(); makePingRequest();
m_lokiMQ->add_timer(std::move(makePingRequest), PingInterval); m_lokiMQ->add_timer(makePingRequest, PingInterval);
} }
void void

@ -46,10 +46,11 @@ namespace llarp
void void
InformConnection(RouterID router, bool success); InformConnection(RouterID router, bool success);
private:
/// called when we have connected to lokid via lokimq
void void
StartPings(); Connected();
private:
/// do a lmq command on the current connection /// do a lmq command on the current connection
void void
Command(std::string_view cmd); Command(std::string_view cmd);

@ -1,367 +0,0 @@
#pragma once
#include "json_binary_proxy.hpp"
#include "json_bt.hpp"
#include "json_conversions.hpp"
#include <oxenc/bt_serialize.h>
#include <nlohmann/json.hpp>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <optional>
namespace llarp::rpc
{
using json_range = std::pair<nlohmann::json::const_iterator, nlohmann::json::const_iterator>;
using rpc_input = std::variant<std::monostate, nlohmann::json, oxenc::bt_dict_consumer>;
// Checks that key names are given in ascending order
template <typename... Ignore>
void
check_ascending_names(std::string_view name1, std::string_view name2, const Ignore&...)
{
if (!(name2 > name1))
throw std::runtime_error{
"Internal error: request values must be retrieved in ascending order"};
}
// Wrapper around a reference for get_values that is used to indicate that the value is
// required, in which case an exception will be raised if the value is not found. Usage:
//
// int a_optional = 0, b_required;
// get_values(input,
// "a", a_optional,
// "b", required{b_required},
// // ...
// );
template <typename T>
struct required
{
T& value;
required(T& ref) : value{ref}
{}
};
template <typename T>
constexpr bool is_required_wrapper = false;
template <typename T>
constexpr bool is_required_wrapper<required<T>> = true;
template <typename T>
constexpr bool is_std_optional = false;
template <typename T>
constexpr bool is_std_optional<std::optional<T>> = true;
// Wrapper around a reference for get_values that adds special handling to act as if the value was
// not given at all if the value is given as an empty string. This sucks, but is necessary for
// backwards compatibility (especially with wallet2 clients).
//
// Usage:
//
// std::string x;
// get_values(input,
// "x", ignore_empty_string{x},
// // ...
// );
template <typename T>
struct ignore_empty_string
{
T& value;
ignore_empty_string(T& ref) : value{ref}
{}
bool
should_ignore(oxenc::bt_dict_consumer& d)
{
if (d.is_string())
{
auto d2{d}; // Copy because we want to leave d intact
if (d2.consume_string_view().empty())
return true;
}
return false;
}
bool
should_ignore(json_range& it_range)
{
auto& e = *it_range.first;
return (e.is_string() && e.get<std::string_view>().empty());
}
};
template <typename T>
constexpr bool is_ignore_empty_string_wrapper = false;
template <typename T>
constexpr bool is_ignore_empty_string_wrapper<ignore_empty_string<T>> = true;
// Advances the dict consumer to the first element >= the given name. Returns true if found,
// false if it advanced beyond the requested name. This is exactly the same as
// `d.skip_until(name)`, but is here so we can also overload an equivalent function for json
// iteration.
inline bool
skip_until(oxenc::bt_dict_consumer& d, std::string_view name)
{
return d.skip_until(name);
}
// Equivalent to the above but for a json object iterator.
inline bool
skip_until(json_range& it_range, std::string_view name)
{
auto& [it, end] = it_range;
while (it != end && it.key() < name)
++it;
return it != end && it.key() == name;
}
// List types that are expandable; for these we emplace_back for each element of the input
template <typename T>
constexpr bool is_expandable_list = false;
template <typename T>
constexpr bool is_expandable_list<std::vector<T>> = true;
// Types that are constructible from string
template <typename T>
constexpr bool is_string_constructible = false;
template <>
inline constexpr bool is_string_constructible<IPRange> = true;
// Fixed size elements: tuples, pairs, and std::array's; we accept list input as long as the
// list length matches exactly.
template <typename T>
constexpr bool is_tuple_like = false;
template <typename T, size_t N>
constexpr bool is_tuple_like<std::array<T, N>> = true;
template <typename S, typename T>
constexpr bool is_tuple_like<std::pair<S, T>> = true;
template <typename... T>
constexpr bool is_tuple_like<std::tuple<T...>> = true;
// True if T is a `std::unordered_map<std::string, ANYTHING...>`
template <typename T>
constexpr bool is_unordered_string_map = false;
template <typename... ValueEtc>
constexpr bool is_unordered_string_map<std::unordered_map<std::string, ValueEtc...>> = true;
template <typename TupleLike, size_t... Is>
void
load_tuple_values(oxenc::bt_list_consumer&, TupleLike&, std::index_sequence<Is...>);
// Consumes the next value from the dict consumer into `val`
template <
typename BTConsumer,
typename T,
std::enable_if_t<
std::is_same_v<BTConsumer, oxenc::bt_dict_consumer>
|| std::is_same_v<BTConsumer, oxenc::bt_list_consumer>,
int> = 0>
void
load_value(BTConsumer& c, T& target)
{
if constexpr (std::is_integral_v<T>)
target = c.template consume_integer<T>();
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
target = c.consume_string_view();
else if constexpr (is_string_constructible<T>)
target = T{c.consume_string()};
else if constexpr (llarp::rpc::json_is_binary<T>)
llarp::rpc::load_binary_parameter(c.consume_string_view(), true /*allow raw*/, target);
else if constexpr (is_expandable_list<T>)
{
auto lc = c.consume_list_consumer();
target.clear();
while (!lc.is_finished())
load_value(lc, target.emplace_back());
}
else if constexpr (is_tuple_like<T>)
{
auto lc = c.consume_list_consumer();
load_tuple_values(lc, target, std::make_index_sequence<std::tuple_size_v<T>>{});
}
else if constexpr (is_unordered_string_map<T>)
{
auto dc = c.consume_dict_consumer();
target.clear();
while (!dc.is_finished())
load_value(dc, target[std::string{dc.key()}]);
}
else
static_assert(std::is_same_v<T, void>, "Unsupported load_value type");
}
// Copies the next value from the json range into `val`, and advances the iterator. Throws
// on unconvertible values.
template <typename T>
void
load_value(json_range& range_itr, T& target)
{
auto& key = range_itr.first.key();
auto& current = *range_itr.first; // value currently pointed to by range_itr.first
if constexpr (std::is_same_v<T, bool>)
{
if (current.is_boolean())
target = current.get<bool>();
else if (current.is_number_unsigned())
{
// Also accept 0 or 1 for bools (mainly to be compatible with bt-encoding which doesn't
// have a distinct bool type).
auto b = current.get<uint64_t>();
if (b <= 1)
target = b;
else
throw std::domain_error{"Invalid value for '" + key + "': expected boolean"};
}
else
{
throw std::domain_error{"Invalid value for '" + key + "': expected boolean"};
}
}
else if constexpr (std::is_unsigned_v<T>)
{
if (!current.is_number_unsigned())
throw std::domain_error{"Invalid value for '" + key + "': non-negative value required"};
auto i = current.get<uint64_t>();
if (sizeof(T) < sizeof(uint64_t) && i > std::numeric_limits<T>::max())
throw std::domain_error{"Invalid value for '" + key + "': value too large"};
target = i;
}
else if constexpr (std::is_integral_v<T>)
{
if (!current.is_number_integer())
throw std::domain_error{"Invalid value for '" + key + "': value is not an integer"};
auto i = current.get<int64_t>();
if (sizeof(T) < sizeof(int64_t))
{
if (i < std::numeric_limits<T>::lowest())
throw std::domain_error{
"Invalid value for '" + key + "': negative value magnitude is too large"};
if (i > std::numeric_limits<T>::max())
throw std::domain_error{"Invalid value for '" + key + "': value is too large"};
}
target = i;
}
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
{
target = current.get<std::string_view>();
}
else if constexpr (
llarp::rpc::json_is_binary<T> || is_expandable_list<T> || is_tuple_like<T>
|| is_unordered_string_map<T>)
{
try
{
current.get_to(target);
}
catch (const std::exception& e)
{
throw std::domain_error{"Invalid values in '" + key + "'"};
}
}
else
{
static_assert(std::is_same_v<T, void>, "Unsupported load type");
}
++range_itr.first;
}
template <typename TupleLike, size_t... Is>
void
load_tuple_values(oxenc::bt_list_consumer& c, TupleLike& val, std::index_sequence<Is...>)
{
(load_value(c, std::get<Is>(val)), ...);
}
// Takes a json object iterator or bt_dict_consumer and loads the current value at the iterator.
// This calls itself recursively, if needed, to unwrap optional/required/ignore_empty_string
// wrappers.
template <typename In, typename T>
void
load_curr_value(In& in, T& val)
{
if constexpr (is_required_wrapper<T>)
{
load_curr_value(in, val.value);
}
else if constexpr (is_ignore_empty_string_wrapper<T>)
{
if (!val.should_ignore(in))
load_curr_value(in, val.value);
}
else if constexpr (is_std_optional<T>)
{
load_curr_value(in, val.emplace());
}
else
{
load_value(in, val);
}
}
// Gets the next value from a json object iterator or bt_dict_consumer. Leaves the iterator at
// the next value, i.e. found + 1 if found, or the next greater value if not found. (NB:
// nlohmann::json objects are backed by an *ordered* map and so both nlohmann iterators and
// bt_dict_consumer behave analogously here).
template <typename In, typename T>
void
get_next_value(In& in, [[maybe_unused]] std::string_view name, T& val)
{
if constexpr (std::is_same_v<std::monostate, In>)
;
else if (skip_until(in, name))
load_curr_value(in, val);
else if constexpr (is_required_wrapper<T>)
throw std::runtime_error{"Required key '" + std::string{name} + "' not found"};
}
// Accessor for simple, flat value retrieval from a json or bt_dict_consumer. In the later
// case note that the given bt_dict_consumer will be advanced, so you *must* take care to
// process keys in order, both for the keys passed in here *and* for use before and after this
// call.
template <typename Input, typename T, typename... More>
void
get_values(Input& in, std::string_view name, T&& val, More&&... more)
{
if constexpr (std::is_same_v<rpc_input, Input>)
{
if (auto* json_in = std::get_if<nlohmann::json>(&in))
{
json_range r{json_in->cbegin(), json_in->cend()};
get_values(r, name, val, std::forward<More>(more)...);
}
else if (auto* dict = std::get_if<oxenc::bt_dict_consumer>(&in))
{
get_values(*dict, name, val, std::forward<More>(more)...);
}
else
{
// A monostate indicates that no parameters field was provided at all
get_values(var::get<std::monostate>(in), name, val, std::forward<More>(more)...);
}
}
else if constexpr (std::is_same_v<std::string_view, Input>)
{
if (in.front() == 'd')
{
oxenc::bt_dict_consumer d{in};
get_values(d, name, val, std::forward<More>(more)...);
}
else
{
auto json_in = nlohmann::json::parse(in);
json_range r{json_in.cbegin(), json_in.cend()};
get_values(r, name, val, std::forward<More>(more)...);
}
}
else
{
static_assert(
std::is_same_v<json_range, Input> || std::is_same_v<oxenc::bt_dict_consumer, Input>
|| std::is_same_v<std::monostate, Input>);
get_next_value(in, name, val);
if constexpr (sizeof...(More) > 0)
{
check_ascending_names(name, more...);
get_values(in, std::forward<More>(more)...);
}
}
}
} // namespace llarp::rpc

@ -1,71 +0,0 @@
#pragma once
#include "rpc_server.hpp"
#include "rpc_request_parser.hpp"
#include "rpc_request_decorators.hpp"
#include "rpc_request_definitions.hpp"
#include "json_bt.hpp"
#include <string_view>
#include <llarp/config/config.hpp>
#include <llarp/router/abstractrouter.hpp>
#include <oxenmq/oxenmq.h>
#include <oxenmq/address.h>
#include <oxen/log/omq_logger.hpp>
namespace llarp::rpc
{
using nlohmann::json;
template <typename RPC>
auto
make_invoke()
{
return [](oxenmq::Message& m, RPCServer& server) {
EndpointHandler<RPC> handler{server, m.send_later()};
auto& rpc = handler.rpc;
if (m.data.size() > 1)
{
m.send_reply(nlohmann::json{
{"error",
"Bad Request: RPC requests must have at most one data part (received {})"_format(
m.data.size())}}
.dump());
return;
}
// parsing input as bt or json
// hand off to parse_request (overloaded versions)
try
{
if (m.data.empty() or m.data[0].empty())
{
parse_request(rpc, nlohmann::json::object());
}
else if (m.data[0].front() == 'd')
{
rpc.set_bt();
parse_request(rpc, oxenc::bt_dict_consumer{m.data[0]});
}
else
{
parse_request(rpc, nlohmann::json::parse(m.data[0]));
}
}
catch (const std::exception& e)
{
m.send_reply(nlohmann::json{{"Failed to parse request parameters: "s + e.what()}}.dump());
return;
}
if (not std::is_base_of_v<Immediate, RPC>)
{
server.m_Router.loop()->call_soon(std::move(handler));
}
else
{
handler();
}
};
}
} // namespace llarp::rpc

@ -1,128 +0,0 @@
#pragma once
#include "json_binary_proxy.hpp"
#include "json_bt.hpp"
#include <nlohmann/json_fwd.hpp>
#include <string_view>
#include <llarp/config/config.hpp>
#include <oxenmq/oxenmq.h>
#include <oxenmq/address.h>
#include <oxen/log/omq_logger.hpp>
namespace tools
{
// Type wrapper that contains an arbitrary list of types.
template <typename...>
struct type_list
{};
} // namespace tools
namespace llarp::rpc
{
// Base class that all RPC requests will expand for each endpoint type
struct RPCRequest
{
private:
bool bt = false;
public:
// Returns true if response is bt-encoded, and false for json
// Note: do not set value
bool
is_bt() const
{
return bt;
}
// Callable method to indicate request is bt-encoded
void
set_bt()
{
bt = true;
response_b64.format = llarp::rpc::json_binary_proxy::fmt::bt;
response_hex.format = llarp::rpc::json_binary_proxy::fmt::bt;
}
// Invoked if this.replier is still present. If it is "stolen" by endpoint (moved from
// RPC struct), then endpoint handles sending reply
void
send_response()
{
replier->reply(
is_bt() ? oxenc::bt_serialize(json_to_bt(std::move(response))) : response.dump());
}
void
send_response(nlohmann::json _response)
{
response = std::move(_response);
send_response();
}
// Response Data:
// bt-encoded are converted in real-time
// - bool becomes 0 or 1
// - key:value where value == null are omitted
// - other nulls will raise an exception if found in json
// - no doubles
// - to store doubles: encode bt in endpoint-specific way
// - binary strings will fail json serialization; caller must
//
// std::string binary = some_binary_data();
// request.response["binary_value"] = is_bt ? binary : oxenmq::to_hex(binary)
//
nlohmann::json response;
// Proxy Object:
// Sets binary data in "response"
// - if return type is json, encodes as hex
// - if return type is bt, then binary is untouched
//
// Usage:
// std::string data = "abc";
// request.response_hex["foo"]["bar"] = data; // json: "616263", bt: "abc"
//
llarp::rpc::json_binary_proxy response_hex{response, llarp::rpc::json_binary_proxy::fmt::hex};
// Proxy Object:
// Encodes binary data as base_64 for json-encoded responses, leaves as binary for bt-encoded
// responses
//
// Usage:
// std::string data = "abc"
// request.response_b64["foo"]["bar"] = data; json: "YWJj", bt: "abc"
//
llarp::rpc::json_binary_proxy response_b64{
response, llarp::rpc::json_binary_proxy::fmt::base64};
// The oxenmq deferred send object into which the response will be sent when the `invoke`
// method returns. If the response needs to happen later (i.e. not immediately after `invoke`
// returns) then you should call `defer()` to extract and clear this and then send the response
// via the returned DeferredSend object yourself.
std::optional<oxenmq::Message::DeferredSend> replier;
// Called to clear the current replier and return it. After this call the automatic reply will
// not be generated; the caller is responsible for calling `->reply` on the returned optional
// itself. This is typically used where a call has to be deferred, for example because it
// depends on some network response to build the reply.
oxenmq::Message::DeferredSend
move()
{
auto r{std::move(*replier)};
replier.reset();
return r;
}
};
// Tag types that are inherited to set RPC endpoint properties
// RPC call wil take no input arguments
// Parameter dict can be passed, but will be ignored
struct NoArgs : virtual RPCRequest
{};
// RPC call will be executed immediately
struct Immediate : virtual RPCRequest
{};
} // namespace llarp::rpc

@ -1,311 +0,0 @@
#pragma once
#include "rpc_request_decorators.hpp"
#include "llarp/net/ip_range.hpp"
#include "llarp/router/abstractrouter.hpp"
#include "llarp/router/route_poker.hpp"
#include "llarp/service/address.hpp"
#include "llarp/service/endpoint.hpp"
#include "llarp/service/outbound_context.hpp"
#include <string_view>
#include <llarp/config/config.hpp>
#include <oxenmq/oxenmq.h>
#include <oxenmq/address.h>
#include <oxen/log/omq_logger.hpp>
#include <unordered_map>
#include <vector>
namespace llarp::rpc
{
// RPC: halt
// Stops lokinet router
//
// Inputs: none
//
struct Halt : NoArgs, Immediate
{
static constexpr auto name = "halt"sv;
};
// RPC: version
// Returns version and uptime information
//
// Inputs: none
//
// Returns: "OK"
// "uptime"
// "version"
//
struct Version : NoArgs, Immediate
{
static constexpr auto name = "version"sv;
};
// RPC: status
// Returns that current activity status of lokinet router
// Calls router::extractstatus
//
// Inputs: none
//
// Returns: massive dump of status info including
// "running"
// "numNodesKnown"
// "dht"
// "services"
// "exit"
// "links"
// "outboundMessages"
// etc
//
struct Status : NoArgs
{
static constexpr auto name = "status"sv;
};
// RPC: get_status
// Returns current summary status
//
// Inputs: none
//
// Returns: slightly smaller dump of status info including
// "authcodes"
// "exitMap"
// "lokiAddress"
// "networkReady"
// "numPathsBuilt"
// "numPeersConnected"
// etc
//
struct GetStatus : NoArgs
{
static constexpr auto name = "get_status"sv;
};
// RPC: quic_connect
// Initializes QUIC connection tunnel
// Passes request parameters in nlohmann::json format
//
// Inputs:
// "endpoint" : endpoint id (string)
// "bindAddr" : bind address (string, ex: "127.0.0.1:1142")
// "host" : remote host ID (string)
// "port" : port to bind to (int)
// "close" : close connection to port or host ID
//
// Returns:
// "id" : connection ID
// "addr" : connection local address
//
struct QuicConnect : RPCRequest
{
static constexpr auto name = "quic_connect"sv;
struct request_parameters
{
std::string bindAddr;
int closeID;
std::string endpoint;
uint16_t port;
std::string remoteHost;
} request;
};
// RPC: quic_listener
// Connects to QUIC interface on local endpoint
// Passes request parameters in nlohmann::json format
//
// Inputs:
// "endpoint" : endpoint id (string)
// "host" : remote host ID (string)
// "port" : port to bind to (int)
// "close" : close connection to port or host ID
// "srv-proto" :
//
// Returns:
// "id" : connection ID
// "addr" : connection local address
//
struct QuicListener : RPCRequest
{
static constexpr auto name = "quic_listener"sv;
struct request_parameters
{
int closeID;
std::string endpoint;
uint16_t port;
std::string remoteHost;
std::string srvProto;
} request;
};
// RPC: lookup_snode
// Look up service node
// Passes request parameters in nlohmann::json format
//
// Inputs:
// "routerID" : router ID to query (string)
//
// Returns:
// "ip" : snode IP address
//
struct LookupSnode : RPCRequest
{
static constexpr auto name = "lookup_snode"sv;
struct request_parameters
{
std::string routerID;
} request;
};
// RPC: map_exit
// Map a new connection to an exit node
//
// Inputs:
// "address" : ID of endpoint to map
// "range" : IP range to map to exit node
// "token" : auth token
//
// Returns:
//
struct MapExit : RPCRequest
{
MapExit()
{
if constexpr (platform::supports_ipv6)
request.ip_range.emplace_back("::/0");
else
request.ip_range.emplace_back("0.0.0.0/0");
}
static constexpr auto name = "map_exit"sv;
struct request_parameters
{
std::string address;
std::vector<IPRange> ip_range;
std::string token;
} request;
};
// RPC: list_exits
// List all currently mapped exit node connections
//
// Inputs: none
//
// Returns:
//
struct ListExits : NoArgs
{
static constexpr auto name = "list_exits"sv;
};
// RPC: unmap_exit
// Unmap a connection to an exit node
//
// Inputs:
// "endpoint" : ID of endpoint to map
// "range" : IP range to map to exit node
// "token" : auth token
//
// Returns:
//
struct UnmapExit : RPCRequest
{
UnmapExit()
{
if constexpr (platform::supports_ipv6)
request.ip_range.emplace_back("::/0");
else
request.ip_range.emplace_back("0.0.0.0/0");
}
static constexpr auto name = "unmap_exit"sv;
struct request_parameters
{
std::vector<IPRange> ip_range;
} request;
};
// RPC: swap_exit
// Swap a connection from one exit to another
//
// Inputs:
// "exits" : exit nodes to swap mappings from (index 0 = old exit, index 1 = new exit)
//
// Returns:
//
struct SwapExits : RPCRequest
{
static constexpr auto name = "swap_exits"sv;
struct request_parameters
{
std::vector<std::string> exit_addresses;
std::string token;
} request;
};
// RPC: dns_query
// Attempts to query endpoint by domain name
//
// Inputs:
// "endpoint" : endpoint ID to query (string)
// "qname" : query name (string)
// "qtype" : query type (int)
//
// Returns:
//
struct DNSQuery : Immediate
{
static constexpr auto name = "dns_query"sv;
struct request_parameters
{
std::string endpoint;
uint16_t qtype;
std::string qname;
} request;
};
// RPC: config
// Runs lokinet router using .ini config file passed as path
//
// Inputs:
// "filename" : name of .ini file to either save or delete
// "ini" : .ini chunk to save in new file
// "del" : boolean specifying whether to delete file "filename" or save it
//
// Returns:
//
struct Config : Immediate
{
static constexpr auto name = "config"sv;
struct request_parameters
{
bool del;
std::string filename;
std::string ini;
} request;
};
// List of all RPC request structs to allow compile-time enumeration of all supported types
using rpc_request_types = tools::type_list<
Halt,
Version,
Status,
GetStatus,
QuicConnect,
QuicListener,
LookupSnode,
MapExit,
ListExits,
SwapExits,
UnmapExit,
DNSQuery,
Config>;
} // namespace llarp::rpc

@ -1,110 +0,0 @@
#include "rpc_request_parser.hpp"
#include "llarp/rpc/rpc_request_definitions.hpp"
#include "param_parser.hpp"
#include <string_view>
#include <llarp/config/config.hpp>
#include <oxenmq/oxenmq.h>
#include <oxenmq/address.h>
#include <oxen/log/omq_logger.hpp>
namespace llarp::rpc
{
using nlohmann::json;
void
parse_request(QuicConnect& quicconnect, rpc_input input)
{
get_values(
input,
"bindAddr",
quicconnect.request.bindAddr,
"closeID",
quicconnect.request.closeID,
"endpoint",
quicconnect.request.endpoint,
"port",
quicconnect.request.port,
"remoteHost",
quicconnect.request.remoteHost);
}
void
parse_request(QuicListener& quiclistener, rpc_input input)
{
get_values(
input,
"closeID",
quiclistener.request.closeID,
"endpoint",
quiclistener.request.endpoint,
"port",
quiclistener.request.port,
"remoteHost",
quiclistener.request.remoteHost,
"srvProto",
quiclistener.request.srvProto);
}
void
parse_request(LookupSnode& lookupsnode, rpc_input input)
{
get_values(input, "routerID", lookupsnode.request.routerID);
}
void
parse_request(MapExit& mapexit, rpc_input input)
{
get_values(
input,
"address",
mapexit.request.address,
"ip_range",
mapexit.request.ip_range,
"token",
mapexit.request.token);
}
void
parse_request(UnmapExit& unmapexit, rpc_input input)
{
get_values(input, "ip_range", unmapexit.request.ip_range);
}
void
parse_request(SwapExits& swapexits, rpc_input input)
{
get_values(
input,
"exit_addresses",
swapexits.request.exit_addresses,
"token",
swapexits.request.token);
}
void
parse_request(DNSQuery& dnsquery, rpc_input input)
{
get_values(
input,
"endpoint",
dnsquery.request.endpoint,
"qname",
dnsquery.request.qname,
"qtype",
dnsquery.request.qtype);
}
void
parse_request(Config& config, rpc_input input)
{
get_values(
input,
"delete",
config.request.del,
"filename",
config.request.filename,
"ini",
config.request.ini);
}
} // namespace llarp::rpc

@ -1,35 +0,0 @@
#pragma once
#include "rpc_request_definitions.hpp"
#include <string_view>
#include <llarp/config/config.hpp>
#include <oxenmq/oxenmq.h>
#include <oxenmq/address.h>
#include <oxen/log/omq_logger.hpp>
namespace llarp::rpc
{
using rpc_input = std::variant<std::monostate, nlohmann::json, oxenc::bt_dict_consumer>;
inline void
parse_request(NoArgs&, rpc_input)
{}
void
parse_request(QuicConnect& quicconnect, rpc_input input);
void
parse_request(QuicListener& quiclistener, rpc_input input);
void
parse_request(LookupSnode& lookupsnode, rpc_input input);
void
parse_request(MapExit& mapexit, rpc_input input);
void
parse_request(UnmapExit& unmapexit, rpc_input input);
void
parse_request(SwapExits& swapexits, rpc_input input);
void
parse_request(DNSQuery& dnsquery, rpc_input input);
void
parse_request(Config& config, rpc_input input);
} // namespace llarp::rpc

File diff suppressed because it is too large Load Diff

@ -1,165 +1,33 @@
#pragma once #pragma once
#include "rpc_request_definitions.hpp"
#include "json_bt.hpp"
#include <nlohmann/json_fwd.hpp>
#include <stdexcept>
#include <string_view> #include <string_view>
#include <llarp/config/config.hpp>
#include <oxenmq/oxenmq.h> #include <oxenmq/oxenmq.h>
#include <oxenmq/message.h>
#include <oxenmq/address.h> #include <oxenmq/address.h>
#include <oxen/log/omq_logger.hpp> #include <oxen/log/omq_logger.hpp>
namespace llarp namespace llarp
{ {
struct AbstractRouter; struct AbstractRouter;
} // namespace llarp }
namespace
{
static auto logcat = llarp::log::Cat("lokinet.rpc");
} // namespace
namespace llarp::rpc namespace llarp::rpc
{ {
using LMQ_ptr = std::shared_ptr<oxenmq::OxenMQ>; using LMQ_ptr = std::shared_ptr<oxenmq::OxenMQ>;
using DeferredSend = oxenmq::Message::DeferredSend;
class RPCServer;
// Stores RPC request callback
struct rpc_callback
{
using result_type = std::variant<oxenc::bt_value, nlohmann::json, std::string>;
// calls with incoming request data; returns response body or throws exception
void (*invoke)(oxenmq::Message&, RPCServer&);
};
// RPC request registration struct RpcServer
// Stores references to RPC requests in a unordered map for ease of reference
// when adding to server. To add endpoints, define in rpc_request_definitions.hpp
// and register in rpc_server.cpp
extern const std::unordered_map<std::string, rpc_callback> rpc_request_map;
// Exception used to signal various types of errors with a request back to the caller. This
// exception indicates that the caller did something wrong: bad data, invalid value, etc., but
// don't indicate a local problem (and so we'll log them only at debug). For more serious,
// internal errors a command should throw some other stl error (e.g. std::runtime_error or
// perhaps std::logic_error), which will result in a local daemon warning (and a generic internal
// error response to the user).
//
// For JSON RPC these become an error response with the code as the error.code value and the
// string as the error.message.
// For HTTP JSON these become a 500 Internal Server Error response with the message as the body.
// For OxenMQ the code becomes the first part of the response and the message becomes the
// second part of the response.
struct rpc_error : std::runtime_error
{ {
/// \param message - a message to send along with the error code (see general description explicit RpcServer(LMQ_ptr, AbstractRouter*);
/// above). ~RpcServer() = default;
rpc_error(std::string message)
: std::runtime_error{"RPC error: " + message}, message{std::move(message)}
{}
std::string message;
};
template <typename Result_t>
void void
SetJSONResponse(Result_t result, json& j) AsyncServeRPC(oxenmq::address addr);
{
j["result"] = result;
}
inline void
SetJSONError(std::string_view msg, json& j)
{
j["error"] = msg;
}
class RPCServer
{
public:
explicit RPCServer(LMQ_ptr, AbstractRouter&);
~RPCServer() = default;
private:
void void
HandleLogsSubRequest(oxenmq::Message& m); HandleLogsSubRequest(oxenmq::Message& m);
void
AddCategories();
void
invoke(Halt& halt);
void
invoke(Version& version);
void
invoke(Status& status);
void
invoke(GetStatus& getstatus);
void
invoke(QuicConnect& quicconnect);
void
invoke(QuicListener& quiclistener);
void
invoke(LookupSnode& lookupsnode);
void
invoke(MapExit& mapexit);
void
invoke(ListExits& listexits);
void
invoke(UnmapExit& unmapexit);
void
invoke(SwapExits& swapexits);
void
invoke(DNSQuery& dnsquery);
void
invoke(Config& config);
LMQ_ptr m_LMQ; LMQ_ptr m_LMQ;
AbstractRouter& m_Router; AbstractRouter* const m_Router;
oxen::log::PubsubLogger log_subs;
};
template <typename RPC>
class EndpointHandler
{
public:
RPCServer& server;
RPC rpc{};
EndpointHandler(RPCServer& _server, DeferredSend _replier) : server{_server} oxen::log::PubsubLogger log_subs;
{
rpc.replier.emplace(std::move(_replier));
}
void
operator()()
{
try
{
server.invoke(rpc);
}
catch (const rpc_error& e)
{
log::info(logcat, "RPC request 'rpc.{}' failed with: {}", rpc.name, e.what());
SetJSONError(
fmt::format("RPC request 'rpc.{}' failed with: {}", rpc.name, e.what()), rpc.response);
}
catch (const std::exception& e)
{
log::info(logcat, "RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what());
SetJSONError(
fmt::format("RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what()),
rpc.response);
}
if (rpc.replier.has_value())
{
rpc.send_response();
}
}
}; };
} // namespace llarp::rpc } // namespace llarp::rpc

@ -1,5 +1,5 @@
#include "convotag.hpp" #include "convotag.hpp"
#include <llarp/net/ip.hpp> #include "net/ip.hpp"
namespace llarp::service namespace llarp::service
{ {

@ -1,16 +1,7 @@
#include <chrono>
#include <memory>
#include "endpoint.hpp" #include "endpoint.hpp"
#include "endpoint_state.hpp"
#include "endpoint_util.hpp"
#include "hidden_service_address_lookup.hpp"
#include "auth.hpp"
#include "llarp/util/logging.hpp"
#include "outbound_context.hpp"
#include "protocol.hpp"
#include "info.hpp"
#include "protocol_type.hpp"
#include <llarp/net/ip.hpp>
#include <llarp/net/ip_range.hpp>
#include <llarp/dht/context.hpp> #include <llarp/dht/context.hpp>
#include <llarp/dht/key.hpp> #include <llarp/dht/key.hpp>
#include <llarp/dht/messages/findintro.hpp> #include <llarp/dht/messages/findintro.hpp>
@ -23,22 +14,29 @@
#include <llarp/nodedb.hpp> #include <llarp/nodedb.hpp>
#include <llarp/profiling.hpp> #include <llarp/profiling.hpp>
#include <llarp/router/abstractrouter.hpp> #include <llarp/router/abstractrouter.hpp>
#include <llarp/router/route_poker.hpp>
#include <llarp/routing/dht_message.hpp> #include <llarp/routing/dht_message.hpp>
#include <llarp/routing/path_transfer_message.hpp> #include <llarp/routing/path_transfer_message.hpp>
#include "endpoint_state.hpp"
#include "endpoint_util.hpp"
#include "hidden_service_address_lookup.hpp"
#include "net/ip.hpp"
#include "outbound_context.hpp"
#include "protocol.hpp"
#include "service/info.hpp"
#include "service/protocol_type.hpp"
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
#include <llarp/util/buffer.hpp> #include <llarp/util/buffer.hpp>
#include <llarp/util/meta/memfn.hpp> #include <llarp/util/meta/memfn.hpp>
#include <llarp/link/link_manager.hpp> #include <llarp/link/link_manager.hpp>
#include <llarp/tooling/dht_event.hpp> #include <llarp/tooling/dht_event.hpp>
#include <llarp/quic/server.hpp>
#include <llarp/quic/tunnel.hpp> #include <llarp/quic/tunnel.hpp>
#include <llarp/util/priority_queue.hpp> #include <llarp/util/priority_queue.hpp>
#include <optional> #include <optional>
#include <type_traits>
#include <utility> #include <utility>
#include <llarp/quic/server.hpp>
#include <llarp/quic/tunnel.hpp>
#include <uvw.hpp> #include <uvw.hpp>
#include <variant> #include <variant>
@ -218,75 +216,6 @@ namespace llarp
return std::nullopt; return std::nullopt;
} }
void
Endpoint::map_exit(
std::string name,
std::string token,
std::vector<IPRange> ranges,
std::function<void(bool, std::string)> result_handler)
{
if (ranges.empty())
{
result_handler(false, "no ranges provided");
return;
}
LookupNameAsync(
name,
[ptr = std::static_pointer_cast<Endpoint>(GetSelf()),
name,
auth = AuthInfo{token},
ranges,
result_handler,
poker = m_router->routePoker()](auto maybe_addr) {
if (not maybe_addr)
{
result_handler(false, "exit not found: {}"_format(name));
return;
}
if (auto* addr_ptr = std::get_if<Address>(&*maybe_addr))
{
Address addr{*addr_ptr};
ptr->SetAuthInfoForEndpoint(addr, auth);
ptr->MarkAddressOutbound(addr);
auto result = ptr->EnsurePathToService(
addr,
[ptr, name, ranges, result_handler, poker](auto addr, auto* ctx) {
if (ctx == nullptr)
{
result_handler(false, "could not establish flow to {}"_format(name));
return;
}
// make a lambda that sends the reply after doing auth
auto apply_result =
[ptr, poker, addr, result_handler, ranges](AuthResult result) {
if (result.code != AuthResultCode::eAuthAccepted)
{
result_handler(false, result.reason);
return;
}
for (const auto& range : ranges)
ptr->MapExitRange(range, addr);
if (poker)
poker->Up();
result_handler(true, result.reason);
};
ctx->AsyncSendAuth(apply_result);
},
ptr->PathAlignmentTimeout());
if (not result)
result_handler(false, "did not build path to {}"_format(name));
}
else
result_handler(false, "exit via snode not supported");
});
}
void void
Endpoint::LookupServiceAsync( Endpoint::LookupServiceAsync(
std::string name, std::string name,
@ -2158,11 +2087,6 @@ namespace llarp
void void
Endpoint::SetAuthInfoForEndpoint(Address addr, AuthInfo info) Endpoint::SetAuthInfoForEndpoint(Address addr, AuthInfo info)
{ {
if (info.token.empty())
{
m_RemoteAuthInfos.erase(addr);
return;
}
m_RemoteAuthInfos[addr] = std::move(info); m_RemoteAuthInfos[addr] = std::move(info);
} }
@ -2189,26 +2113,6 @@ namespace llarp
LogInfo(Name(), " unmap ", item.first, " exit range mapping"); LogInfo(Name(), " unmap ", item.first, " exit range mapping");
return true; return true;
}); });
if (m_ExitMap.Empty())
m_router->routePoker()->Down();
}
void
Endpoint::UnmapRangeByExit(IPRange range, std::string exit)
{
// unmap all ranges that match the given exit when hot swapping
m_ExitMap.RemoveIf([&](const auto& item) -> bool {
if ((range.Contains(item.first)) and (item.second.ToString() == exit))
{
log::info(logcat, "{} unmap {} range mapping to exit node {}", Name(), item.first, exit);
return true;
}
return false;
});
if (m_ExitMap.Empty())
m_router->routePoker()->Down();
} }
std::optional<AuthInfo> std::optional<AuthInfo>

@ -6,27 +6,24 @@
#include <llarp/net/net.hpp> #include <llarp/net/net.hpp>
#include <llarp/path/path.hpp> #include <llarp/path/path.hpp>
#include <llarp/path/pathbuilder.hpp> #include <llarp/path/pathbuilder.hpp>
#include "address.hpp"
#include "handler.hpp"
#include "identity.hpp"
#include "pendingbuffer.hpp"
#include "protocol.hpp"
#include "sendcontext.hpp"
#include "service/protocol_type.hpp"
#include "session.hpp"
#include "lookup.hpp"
#include <llarp/util/compare_ptr.hpp> #include <llarp/util/compare_ptr.hpp>
// --- begin kitchen sink headers ----
#include <llarp/service/address.hpp>
#include <llarp/service/handler.hpp>
#include <llarp/service/identity.hpp>
#include <llarp/service/pendingbuffer.hpp>
#include <llarp/service/protocol.hpp>
#include <llarp/service/sendcontext.hpp>
#include <llarp/service/protocol_type.hpp>
#include <llarp/service/session.hpp>
#include <llarp/service/lookup.hpp>
#include <llarp/service/endpoint_types.hpp>
#include <llarp/endpoint_base.hpp>
#include <llarp/service/auth.hpp>
// ----- end kitchen sink headers -----
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
#include <variant> #include <variant>
#include <oxenc/variant.h> #include <oxenc/variant.h>
#include "endpoint_types.hpp"
#include "llarp/endpoint_base.hpp"
#include "auth.hpp"
#include <llarp/vpn/egres_packet_router.hpp> #include <llarp/vpn/egres_packet_router.hpp>
#include <llarp/dns/server.hpp> #include <llarp/dns/server.hpp>
@ -284,16 +281,6 @@ namespace llarp
void void
UnmapExitRange(IPRange range); UnmapExitRange(IPRange range);
void
UnmapRangeByExit(IPRange range, std::string exit);
void
map_exit(
std::string name,
std::string token,
std::vector<IPRange> ranges,
std::function<void(bool, std::string)> result);
void void
PutLookup(IServiceLookup* lookup, uint64_t txid) override; PutLookup(IServiceLookup* lookup, uint64_t txid) override;

@ -1,5 +1,5 @@
#include "intro.hpp" #include "intro.hpp"
#include <llarp/util/time.hpp> #include "util/time.hpp"
namespace llarp namespace llarp
{ {

@ -1,15 +1,16 @@
#include "outbound_context.hpp" #include "outbound_context.hpp"
#include <llarp/router/abstractrouter.hpp>
#include "async_key_exchange.hpp" #include "async_key_exchange.hpp"
#include "hidden_service_address_lookup.hpp" #include "hidden_service_address_lookup.hpp"
#include "endpoint.hpp" #include "endpoint.hpp"
#include "endpoint_util.hpp"
#include "protocol_type.hpp"
#include <llarp/router/abstractrouter.hpp>
#include <llarp/nodedb.hpp> #include <llarp/nodedb.hpp>
#include <llarp/profiling.hpp> #include <llarp/profiling.hpp>
#include <llarp/util/meta/memfn.hpp> #include <llarp/util/meta/memfn.hpp>
#include "endpoint_util.hpp"
#include "service/protocol_type.hpp"
#include <random> #include <random>
#include <algorithm> #include <algorithm>

@ -125,7 +125,8 @@ namespace llarp
void void
SendContext::AsyncSendAuth(std::function<void(AuthResult)> resultHandler) SendContext::AsyncSendAuth(std::function<void(AuthResult)> resultHandler)
{ {
if (const auto maybe = m_Endpoint->MaybeGetAuthInfoForEndpoint(remoteIdent.Addr())) const auto maybe = m_Endpoint->MaybeGetAuthInfoForEndpoint(remoteIdent.Addr());
if (maybe.has_value())
{ {
// send auth message // send auth message
const llarp_buffer_t authdata{maybe->token}; const llarp_buffer_t authdata{maybe->token};
@ -133,7 +134,7 @@ namespace llarp
authResultListener = resultHandler; authResultListener = resultHandler;
} }
else else
resultHandler({AuthResultCode::eAuthAccepted, "no auth needed"}); resultHandler({AuthResultCode::eAuthFailed, "no auth for given endpoint"});
} }
void void

@ -3,8 +3,8 @@
#include "router_event.hpp" #include "router_event.hpp"
#include <llarp.hpp> #include <llarp.hpp>
#include <llarp/config/config.hpp> #include <config/config.hpp>
#include <llarp/tooling/hive_context.hpp> #include <tooling/hive_context.hpp>
#include <vector> #include <vector>
#include <deque> #include <deque>

@ -1,5 +1,5 @@
#pragma once #pragma once
#include <cstdint>
#include <algorithm> #include <algorithm>
#include <atomic> #include <atomic>
#include <cassert> #include <cassert>

@ -1,4 +1,4 @@
#include "win32.hpp" #include "vpn/win32.hpp"
#include <llarp/win32/windivert.hpp> #include <llarp/win32/windivert.hpp>
#include <llarp/win32/wintun.hpp> #include <llarp/win32/wintun.hpp>
#include <fmt/core.h> #include <fmt/core.h>

@ -80,7 +80,7 @@ namespace llarp
py::class_<ApiConfig>(mod, "ApiConfig") py::class_<ApiConfig>(mod, "ApiConfig")
.def(py::init<>()) .def(py::init<>())
.def_readwrite("enableRPCServer", &ApiConfig::m_enableRPCServer) .def_readwrite("enableRPCServer", &ApiConfig::m_enableRPCServer)
.def_readwrite("rpcBindAddresses", &ApiConfig::m_rpcBindAddresses); .def_readwrite("rpcBindAddr", &ApiConfig::m_rpcBindAddr);
py::class_<LokidConfig>(mod, "LokidConfig") py::class_<LokidConfig>(mod, "LokidConfig")
.def(py::init<>()) .def(py::init<>())

@ -3,8 +3,7 @@
#include <llarp/tooling/hive_context.hpp> #include <llarp/tooling/hive_context.hpp>
#include <llarp/router/router.hpp> #include <llarp/router/router.hpp>
#include <llarp/handlers/pyhandler.hpp> #include <llarp/handlers/pyhandler.hpp>
#include <llarp/service/protocol_type.hpp> #include "service/protocol_type.hpp"
namespace llarp namespace llarp
{ {
void void

@ -60,4 +60,8 @@ if(WIN32)
target_link_libraries(testAll PUBLIC ws2_32 iphlpapi shlwapi) target_link_libraries(testAll PUBLIC ws2_32 iphlpapi shlwapi)
endif() endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
target_link_directories(testAll PRIVATE /usr/local/lib)
endif()
add_custom_target(check COMMAND testAll) add_custom_target(check COMMAND testAll)

@ -1,8 +1,8 @@
#define CATCH_CONFIG_RUNNER #define CATCH_CONFIG_RUNNER
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <llarp/util/logging.hpp> #include <util/logging.hpp>
#include <llarp/util/service_manager.hpp> #include <util/service_manager.hpp>
#ifdef _WIN32 #ifdef _WIN32
#include <winsock2.h> #include <winsock2.h>

@ -1,4 +1,4 @@
#include <llarp/config/definition.hpp> #include <config/definition.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>

@ -1,4 +1,4 @@
#include <llarp/config/ini.hpp> #include <config/ini.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
@ -42,12 +42,12 @@ TEST_CASE("ConfigParser", "[config]")
SECTION("No key") SECTION("No key")
{ {
REQUIRE_THROWS(parser.LoadFromStr("[test]\n=1090\n")); REQUIRE_FALSE(parser.LoadFromStr("[test]\n=1090\n"));
} }
SECTION("Parse invalid") SECTION("Parse invalid")
{ {
REQUIRE_THROWS( REQUIRE_FALSE(
parser.LoadFromStr("srged5ghe5\nf34wtge5\nw34tgfs4ygsd5yg=4;\n#" parser.LoadFromStr("srged5ghe5\nf34wtge5\nw34tgfs4ygsd5yg=4;\n#"
"g4syhgd5\n")); "g4syhgd5\n"));
} }

@ -1,4 +1,4 @@
#include <llarp/config/definition.hpp> #include <config/definition.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>

@ -1,4 +1,4 @@
#include <llarp/crypto/crypto_libsodium.hpp> #include <crypto/crypto_libsodium.hpp>
#include <iostream> #include <iostream>

@ -1,9 +1,9 @@
#include <llarp/crypto/types.hpp> #include <crypto/types.hpp>
#include <fstream> #include <fstream>
#include <string> #include <string>
#include "test_util.hpp" #include <test_util.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
extern "C" { extern "C" {

@ -1,15 +1,14 @@
#include "llarp_test.hpp" #include <config/key_manager.hpp>
#include "test_util.hpp"
#include <llarp/config/key_manager.hpp> #include <crypto/crypto.hpp>
#include <crypto/crypto_libsodium.hpp>
#include <llarp/crypto/crypto.hpp> #include <llarp_test.hpp>
#include <llarp/crypto/crypto_libsodium.hpp>
#include <functional> #include <functional>
#include <random> #include <random>
#include <string> #include <string>
#include <test_util.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
using namespace ::llarp; using namespace ::llarp;

@ -1,11 +1,11 @@
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <llarp/dns/dns.hpp> #include <dns/dns.hpp>
#include <llarp/dns/message.hpp> #include <dns/message.hpp>
#include <llarp/dns/name.hpp> #include <dns/name.hpp>
#include <llarp/dns/rr.hpp> #include <dns/rr.hpp>
#include <llarp/net/net.hpp> #include <net/net.hpp>
#include <llarp/net/ip.hpp> #include <net/ip.hpp>
#include <llarp/util/buffer.hpp> #include <util/buffer.hpp>
#include <algorithm> #include <algorithm>

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <llarp/crypto/crypto_libsodium.hpp> #include <crypto/crypto_libsodium.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
namespace llarp::test namespace llarp::test

@ -1,4 +1,4 @@
#include <llarp/net/ip_address.hpp> #include <net/ip_address.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>

@ -1,7 +1,7 @@
#include <llarp/net/net_int.hpp> #include <net/net_int.hpp>
#include <llarp/net/ip.hpp> #include <net/ip.hpp>
#include <llarp/net/ip_range.hpp> #include <net/ip_range.hpp>
#include <llarp/net/net.hpp> #include <net/net.hpp>
#include <oxenc/hex.h> #include <oxenc/hex.h>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>

@ -1,6 +1,6 @@
#include <llarp/util/mem.hpp> #include <util/mem.hpp>
#include <llarp/net/sock_addr.hpp> #include <net/sock_addr.hpp>
#include <llarp/net/net_if.hpp> #include <net/net_if.hpp>
#include <llarp/util/logging.hpp> #include <llarp/util/logging.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>

@ -1,8 +1,8 @@
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include "config/config.hpp"
#include <llarp/config/config.hpp> #include <router_contact.hpp>
#include <llarp/router_contact.hpp> #include <nodedb.hpp>
#include <llarp/nodedb.hpp>
using llarp_nodedb = llarp::NodeDB; using llarp_nodedb = llarp::NodeDB;

@ -1,4 +1,4 @@
#include <llarp/path/path.hpp> #include <path/path.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
using Path_t = llarp::path::Path; using Path_t = llarp::path::Path;

@ -1,12 +1,12 @@
#include <llarp/peerstats/peer_db.hpp> #include <peerstats/peer_db.hpp>
#include <test_util.hpp> #include <test_util.hpp>
#include <numeric> #include <numeric>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>
#include <llarp/peerstats/types.hpp> #include "peerstats/types.hpp"
#include <llarp/router_contact.hpp> #include "router_contact.hpp"
#include <llarp/util/logging.hpp> #include "util/logging.hpp"
#include <llarp/util/time.hpp> #include "util/time.hpp"
TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]") TEST_CASE("Test PeerDb PeerStats memory storage", "[PeerDb]")
{ {

@ -1,5 +1,5 @@
#include <numeric> #include <numeric>
#include <llarp/peerstats/types.hpp> #include <peerstats/types.hpp>
#include <test_util.hpp> #include <test_util.hpp>
#include <catch2/catch.hpp> #include <catch2/catch.hpp>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save