Merge pull request #2090 from necro-nemesis/fedora/36

Fedora/36
fedora/36
Jason Rhinelander 1 year ago committed by GitHub
commit 333df898e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,46 +1,46 @@
local distro = "fedora-35"; local distro = 'fedora-36';
local distro_name = 'Fedora 35'; local distro_name = 'Fedora 36';
local distro_docker = 'fedora:35'; local distro_docker = 'fedora:36';
local submodules = { local submodules = {
name: 'submodules', name: 'submodules',
image: 'drone/git', image: 'drone/git',
commands: ['git fetch --tags', 'git submodule update --init --recursive --depth=1'] commands: ['git fetch --tags', 'git submodule update --init --recursive --depth=1'],
}; };
local dnf(arch) = 'dnf -y --setopt install_weak_deps=False --setopt cachedir=/cache/'+distro+'/'+arch+'/${DRONE_STAGE_MACHINE} '; local dnf(arch) = 'dnf -y --setopt install_weak_deps=False --setopt cachedir=/cache/' + distro + '/' + arch + '/${DRONE_STAGE_MACHINE} ';
local rpm_pipeline(image, buildarch='amd64', rpmarch='x86_64', jobs=6) = { local rpm_pipeline(image, buildarch='amd64', rpmarch='x86_64', jobs=6) = {
kind: 'pipeline', kind: 'pipeline',
type: 'docker', type: 'docker',
name: distro_name + ' (' + rpmarch + ')', name: distro_name + ' (' + rpmarch + ')',
platform: { arch: buildarch }, platform: { arch: buildarch },
steps: [ steps: [
submodules, submodules,
{ {
name: 'build', name: 'build',
image: image, image: image,
environment: { environment: {
SSH_KEY: { from_secret: "SSH_KEY" }, SSH_KEY: { from_secret: 'SSH_KEY' },
RPM_BUILD_NCPUS: jobs RPM_BUILD_NCPUS: jobs,
}, },
commands: [ commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"', 'echo "Building on ${DRONE_STAGE_MACHINE}"',
dnf(rpmarch) + 'distro-sync', dnf(rpmarch) + 'distro-sync',
dnf(rpmarch) + 'install rpm-build git-archive-all dnf-plugins-core ccache', dnf(rpmarch) + 'install rpm-build git-archive-all dnf-plugins-core ccache',
dnf(rpmarch) + 'config-manager --add-repo https://rpm.oxen.io/fedora/oxen.repo', dnf(rpmarch) + 'config-manager --add-repo https://rpm.oxen.io/fedora/oxen.repo',
'pkg_src_base="$(rpm -q --queryformat=\'%{NAME}-%{VERSION}\n\' --specfile SPECS/lokinet.spec | head -n 1)"', 'pkg_src_base="$(rpm -q --queryformat=\'%{NAME}-%{VERSION}\n\' --specfile SPECS/lokinet.spec | head -n 1)"',
'git-archive-all --prefix $pkg_src_base/ SOURCES/$pkg_src_base.src.tar.gz', 'git-archive-all --prefix $pkg_src_base/ SOURCES/$pkg_src_base.src.tar.gz',
dnf(rpmarch) + 'builddep --spec SPECS/lokinet.spec', dnf(rpmarch) + 'builddep --spec SPECS/lokinet.spec',
'if [ -n "$CCACHE_DIR" ]; then mkdir -pv ~/.cache; ln -sv "$CCACHE_DIR" ~/.cache/ccache; fi', 'if [ -n "$CCACHE_DIR" ]; then mkdir -pv ~/.cache; ln -sv "$CCACHE_DIR" ~/.cache/ccache; fi',
'rpmbuild --define "_topdir $(pwd)" -bb SPECS/lokinet.spec', 'rpmbuild --define "_topdir $(pwd)" -bb SPECS/lokinet.spec',
'./SPECS/ci-upload.sh ' + distro + ' ' + rpmarch, './SPECS/ci-upload.sh ' + distro + ' ' + rpmarch,
], ],
} },
] ],
}; };
[ [
rpm_pipeline(distro_docker), rpm_pipeline(distro_docker),
rpm_pipeline("arm64v8/" + distro_docker, buildarch='arm64', rpmarch="aarch64", jobs=4) rpm_pipeline('arm64v8/' + distro_docker, buildarch='arm64', rpmarch='aarch64', jobs=3),
] ]

9
.gitmodules vendored

@ -10,9 +10,6 @@
[submodule "test/Catch2"] [submodule "test/Catch2"]
path = test/Catch2 path = test/Catch2
url = https://github.com/catchorg/Catch2 url = https://github.com/catchorg/Catch2
[submodule "external/date"]
path = external/date
url = https://github.com/HowardHinnant/date.git
[submodule "external/pybind11"] [submodule "external/pybind11"]
path = external/pybind11 path = external/pybind11
url = https://github.com/pybind/pybind11 url = https://github.com/pybind/pybind11
@ -36,3 +33,9 @@
[submodule "external/oxen-encoding"] [submodule "external/oxen-encoding"]
path = external/oxen-encoding path = external/oxen-encoding
url = https://github.com/oxen-io/oxen-encoding.git url = https://github.com/oxen-io/oxen-encoding.git
[submodule "external/oxen-logging"]
path = external/oxen-logging
url = https://github.com/oxen-io/oxen-logging.git
[submodule "gui"]
path = gui
url = https://github.com/oxen-io/lokinet-gui.git

@ -1,9 +1,9 @@
cmake_minimum_required(VERSION 3.10) # bionic's cmake version cmake_minimum_required(VERSION 3.13...3.24) # 3.13 is buster's version
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
# Has to be set before `project()`, and ignored on non-macos: # Has to be set before `project()`, and ignored on non-macos:
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)") set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15 CACHE STRING "macOS deployment target (Apple clang only)")
option(BUILD_DAEMON "build lokinet daemon and associated utils" ON) option(BUILD_DAEMON "build lokinet daemon and associated utils" ON)
@ -25,17 +25,17 @@ endif()
project(lokinet project(lokinet
VERSION 0.9.9 VERSION 0.9.11
DESCRIPTION "lokinet - IP packet onion router" DESCRIPTION "lokinet - IP packet onion router"
LANGUAGES ${LANGS}) LANGUAGES ${LANGS})
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 0) set(LOKINET_APPLE_BUILD 6)
endif() endif()
set(RELEASE_MOTTO "Gluten Free Edition" CACHE STRING "Release motto") set(RELEASE_MOTTO "Our Lord And Savior" CACHE STRING "Release motto")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")
@ -51,18 +51,20 @@ 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(EMBEDDED_CFG "optimise for older hardware or embedded systems" OFF) option(EMBEDDED_CFG "optimise for older hardware or embedded systems" OFF)
option(BUILD_LIBLOKINET "build liblokinet.so" ON) option(BUILD_LIBLOKINET "build liblokinet.so" ON)
option(SHADOW "use shadow testing framework. linux only" OFF)
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)
option(WITH_COVERAGE "generate coverage data" OFF) option(WITH_COVERAGE "generate coverage data" OFF)
option(USE_SHELLHOOKS "enable shell hooks on compile time (dangerous)" OFF)
option(WARNINGS_AS_ERRORS "treat all warnings as errors. turn off for development, on for release" OFF) option(WARNINGS_AS_ERRORS "treat all warnings as errors. turn off for development, on for release" OFF)
option(TRACY_ROOT "include tracy profiler source" OFF)
option(WITH_TESTS "build unit tests" OFF) option(WITH_TESTS "build unit tests" OFF)
option(WITH_HIVE "build simulation stubs" OFF) option(WITH_HIVE "build simulation stubs" OFF)
option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF) option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF)
option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP}) option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP})
option(WITH_PEERSTATS "build with experimental peerstats db support" OFF)
option(STRIP_SYMBOLS "strip off all debug symbols into an external archive for all executables built" OFF)
set(BOOTSTRAP_FALLBACK_MAINNET "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed" CACHE PATH "Fallback bootstrap path (mainnet)")
set(BOOTSTRAP_FALLBACK_TESTNET "${PROJECT_SOURCE_DIR}/contrib/bootstrap/testnet.signed" CACHE PATH "Fallback bootstrap path (testnet)")
include(cmake/enable_lto.cmake) include(cmake/enable_lto.cmake)
@ -83,9 +85,17 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo) set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif() endif()
set(debug OFF)
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
set(debug ON)
add_definitions(-DLOKINET_DEBUG)
endif()
option(WARN_DEPRECATED "show deprecation warnings" ${debug})
if(BUILD_STATIC_DEPS AND STATIC_LINK) if(BUILD_STATIC_DEPS AND STATIC_LINK)
message(STATUS "we are building static deps so we won't build shared libs") message(STATUS "we are building static deps so we won't build shared libs")
set(BUILD_SHARED_LIBS OFF) set(BUILD_SHARED_LIBS OFF CACHE BOOL "")
endif() endif()
include(CheckCXXSourceCompiles) include(CheckCXXSourceCompiles)
@ -99,7 +109,6 @@ set(CMAKE_C_EXTENSIONS OFF)
include(cmake/target_link_libraries_system.cmake) include(cmake/target_link_libraries_system.cmake)
include(cmake/add_import_library.cmake) include(cmake/add_import_library.cmake)
include(cmake/add_log_tag.cmake)
include(cmake/libatomic.cmake) include(cmake/libatomic.cmake)
if (STATIC_LINK) if (STATIC_LINK)
@ -107,10 +116,11 @@ if (STATIC_LINK)
message(STATUS "setting static library suffix search") message(STATUS "setting static library suffix search")
endif() endif()
add_definitions(-D${CMAKE_SYSTEM_NAME}) include(cmake/gui-option.cmake)
include(cmake/solaris.cmake) include(cmake/solaris.cmake)
include(cmake/win32.cmake) include(cmake/win32.cmake)
include(cmake/macos.cmake)
# No in-source building # No in-source building
include(MacroEnsureOutOfSourceBuild) include(MacroEnsureOutOfSourceBuild)
@ -175,49 +185,19 @@ if(NOT TARGET sodium)
export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake) export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake)
endif() endif()
option(FORCE_OXENC_SUBMODULE "force using oxen-encoding submodule" OFF) set(warning_flags -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Werror=vla)
if(NOT FORCE_OXENC_SUBMODULE) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
pkg_check_modules(OXENC liboxenc>=1.0.3 IMPORTED_TARGET) list(APPEND warning_flags -Wno-unknown-warning-option)
endif()
if(OXENC_FOUND)
if(NOT TARGET PkgConfig::OXENC AND CMAKE_VERSION VERSION_LESS "3.21")
# Work around cmake bug 22180 (PkgConfig::OXENC not set if no flags needed):
add_library(_empty_oxenc INTERFACE)
add_library(oxenc::oxenc ALIAS _empty_oxenc)
else()
add_library(oxenc::oxenc ALIAS PkgConfig::OXENC)
endif()
message(STATUS "Found system liboxenc ${OXENC_VERSION}")
else()
message(STATUS "using oxen-encoding submodule")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/oxen-encoding)
add_library(oxenc::oxenc ALIAS oxenc)
endif()
option(FORCE_OXENMQ_SUBMODULE "force using oxenmq submodule" OFF)
if(NOT FORCE_OXENMQ_SUBMODULE)
pkg_check_modules(OXENMQ liboxenmq>=1.2.12 IMPORTED_TARGET)
endif() endif()
if(OXENMQ_FOUND) if(WARN_DEPRECATED)
add_library(oxenmq::oxenmq ALIAS PkgConfig::OXENMQ) list(APPEND warning_flags -Wdeprecated-declarations)
message(STATUS "Found system liboxenmq ${OXENMQ_VERSION}")
else() else()
message(STATUS "using oxenmq submodule") list(APPEND warning_flags -Wno-deprecated-declarations)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/oxen-mq)
endif() endif()
# If we blindly add these directly as compile_options then they get passed to swiftc on Apple and
if(NOT APPLE) # break, so we use a generate expression to set them only for C++/C/ObjC
add_compile_options(-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations -Werror=vla) add_compile_options("$<$<OR:$<COMPILE_LANGUAGE:CXX>,$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:OBJC>>:${warning_flags}>")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-unknown-warning-option)
endif()
endif()
if (NOT CMAKE_SYSTEM_NAME MATCHES "Linux" AND SHADOW)
message( FATAL_ERROR "shadow-framework is Linux only" )
endif()
if(XSAN) if(XSAN)
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fsanitize=${XSAN} -fno-omit-frame-pointer -fno-sanitize-recover") string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fsanitize=${XSAN} -fno-omit-frame-pointer -fno-sanitize-recover")
@ -227,20 +207,6 @@ if(XSAN)
message(STATUS "Doing a ${XSAN} sanitizer build") message(STATUS "Doing a ${XSAN} sanitizer build")
endif() endif()
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
add_definitions(-DLOKINET_DEBUG=1)
endif()
if(WITH_SHELLHOOKS)
add_definitions(-DENABLE_SHELLHOOKS)
endif()
if(TRACY_ROOT)
include_directories(${TRACY_ROOT})
add_definitions(-DTRACY_ENABLE)
endif()
include(cmake/coverage.cmake) include(cmake/coverage.cmake)
# these vars are set by the cmake toolchain spec # these vars are set by the cmake toolchain spec
@ -269,22 +235,12 @@ set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
set(THREADS_PREFER_PTHREAD_FLAG TRUE) set(THREADS_PREFER_PTHREAD_FLAG TRUE)
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
if(USE_NETNS)
add_definitions(-DNETNS=1)
else()
add_definitions(-DNETNS=0)
endif()
if(TESTNET) if(TESTNET)
add_definitions(-DTESTNET=1) add_definitions(-DTESTNET)
# 5 times slower than realtime # 5 times slower than realtime
# add_definitions(-DTESTNET_SPEED=5) # add_definitions(-DTESTNET_SPEED=5)
endif() endif()
if(SHADOW)
include(cmake/shadow.cmake)
endif()
unset(GIT_VERSION) unset(GIT_VERSION)
unset(GIT_VERSION_REAL) unset(GIT_VERSION_REAL)
@ -319,7 +275,6 @@ if(WITH_SYSTEMD AND (NOT ANDROID))
endif() endif()
add_subdirectory(external) add_subdirectory(external)
include_directories(SYSTEM external/sqlite_orm/include)
if(USE_JEMALLOC AND NOT STATIC_LINK) if(USE_JEMALLOC AND NOT STATIC_LINK)
pkg_check_modules(JEMALLOC jemalloc IMPORTED_TARGET) pkg_check_modules(JEMALLOC jemalloc IMPORTED_TARGET)
@ -339,12 +294,8 @@ if(ANDROID)
set(ANDROID_PLATFORM_SRC android/ifaddrs.c) set(ANDROID_PLATFORM_SRC android/ifaddrs.c)
endif() endif()
if(TRACY_ROOT)
target_link_libraries(base_libs INTERFACE dl)
endif()
if(WITH_HIVE) if(WITH_HIVE)
add_definitions(-DLOKINET_HIVE=1) add_definitions(-DLOKINET_HIVE)
endif() endif()
add_subdirectory(crypto) add_subdirectory(crypto)
@ -356,17 +307,21 @@ endif()
if(WITH_HIVE) if(WITH_HIVE)
add_subdirectory(pybind) add_subdirectory(pybind)
endif() endif()
if (NOT SHADOW) if(WITH_TESTS OR WITH_HIVE)
if(WITH_TESTS OR WITH_HIVE) add_subdirectory(test)
add_subdirectory(test) endif()
endif() if(ANDROID)
if(ANDROID) add_subdirectory(jni)
add_subdirectory(jni)
endif()
endif() endif()
add_subdirectory(docs) add_subdirectory(docs)
include(cmake/gui.cmake)
if(APPLE)
macos_target_setup()
endif()
# uninstall target # uninstall target
if(NOT TARGET uninstall) if(NOT TARGET uninstall)
configure_file( configure_file(
@ -378,7 +333,10 @@ if(NOT TARGET uninstall)
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif() endif()
if(BUILD_PACKAGE AND NOT APPLE) if(BUILD_PACKAGE AND NOT APPLE)
include(cmake/installer.cmake) include(cmake/installer.cmake)
endif()
if(TARGET package)
add_dependencies(package assemble_gui)
endif() endif()

@ -1,28 +0,0 @@
{
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"variables": []
},
{
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"buildRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\build\\${name}",
"installRoot": "${env.USERPROFILE}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": "",
"inheritEnvironments": [ "msvc_x64_x64" ],
"variables": []
}
]
}

@ -3,7 +3,7 @@
* Act like a responsible adult. * Act like a responsible adult.
* RUN `make format` BEFORE COMMITING ALWAYS. * RUN `./contrib/format.sh` BEFORE COMMITING ALWAYS.
# Do NOT # Do NOT

@ -1,27 +0,0 @@
From cdad3e7f093c4b0c69f73580e4fbacc24067db96 Mon Sep 17 00:00:00 2001
From: necro-nemsis <necro_nemesis@hotmail.com>
Date: Sun, 19 Jun 2022 06:47:01 -0400
Subject: [PATCH] Change default port to 953
---
llarp/config/config.cpp | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp
index 6bff9611..9c89bce6 100644
--- a/llarp/config/config.cpp
+++ b/llarp/config/config.cpp
@@ -760,7 +760,10 @@ namespace llarp
// can bind to other 127.* IPs to avoid conflicting with something else that may be listening on
// 127.0.0.1:53.
#ifdef __linux__
- constexpr Default DefaultDNSBind{"127.3.2.1:53"};
+ // Fedora's systemd-resolved seems unable to connect to 127.3.2.1 for unknown reasons,
+ // however since systemd-resolved is perfectly happy with a different port so listen on
+ // localhost:953 as a workaround.
+ constexpr Default DefaultDNSBind{"127.0.0.1:953"};
#else
constexpr Default DefaultDNSBind{"127.0.0.1:53"};
#endif
--
2.30.2

@ -1,6 +1,5 @@
[Unit] [Unit]
Description=LokiNET: Anonymous Network layer thingydoo, client Description=LokiNET: Anonymous Network layer thingydoo, client
AssertFileNotEmpty=/var/lib/lokinet/bootstrap.signed
Wants=network-online.target Wants=network-online.target
After=network-online.target After=network-online.target

@ -1,5 +1,5 @@
Name: lokinet Name: lokinet
Version: 0.9.9 Version: 0.9.11
Release: 1%{?dist} Release: 1%{?dist}
Summary: Lokinet anonymous, decentralized overlay network Summary: Lokinet anonymous, decentralized overlay network
@ -19,11 +19,6 @@ BuildRequires: systemd-devel
BuildRequires: systemd-rpm-macros BuildRequires: systemd-rpm-macros
BuildRequires: libcurl-devel BuildRequires: libcurl-devel
BuildRequires: jemalloc-devel BuildRequires: jemalloc-devel
BuildRequires: libsqlite3x-devel
# Changes the default dns listener to 127.0.0.1:953 because Fedora's systemd-resolved doesn't like
# talking to 127.3.2.1:53 for unknown reasons.
Patch1: default-dns.patch
Requires: lokinet-bin = %{version}-%{release} Requires: lokinet-bin = %{version}-%{release}
%{?systemd_requires} %{?systemd_requires}
@ -92,7 +87,7 @@ install -m755 contrib/py/admin/lokinetmon $RPM_BUILD_ROOT/%{_bindir}/
install -Dm644 SOURCES/lokinet.service $RPM_BUILD_ROOT/%{_unitdir}/lokinet.service install -Dm644 SOURCES/lokinet.service $RPM_BUILD_ROOT/%{_unitdir}/lokinet.service
install -Dm644 contrib/systemd-resolved/lokinet.rules $RPM_BUILD_ROOT/%{_datadir}/polkit-1/rules.d/50-lokinet.rules install -Dm644 contrib/systemd-resolved/lokinet.rules $RPM_BUILD_ROOT/%{_datadir}/polkit-1/rules.d/50-lokinet.rules
install -Dm644 SOURCES/dnssec-lokinet.negative $RPM_BUILD_ROOT%{_exec_prefix}/lib/dnssec-trust-anchors.d/lokinet.negative install -Dm644 SOURCES/dnssec-lokinet.negative $RPM_BUILD_ROOT%{_exec_prefix}/lib/dnssec-trust-anchors.d/lokinet.negative
install -Dm644 SOURCES/bootstrap.signed $RPM_BUILD_ROOT%{_sharedstatedir}/lokinet/bootstrap.signed install -Dm644 contrib/bootstrap/mainnet.signed $RPM_BUILD_ROOT%{_sharedstatedir}/lokinet/bootstrap.signed
%files %files
@ -122,7 +117,7 @@ if ! getent group _loki >/dev/null; then
groupadd --system _loki groupadd --system _loki
fi fi
if ! getent passwd _lokinet >/dev/null; then if ! getent passwd _lokinet >/dev/null; then
useradd --badnames --system --home-dir /var/lib/lokinet --group _loki --comment "Lokinet system user" _lokinet useradd --badname --system --home-dir /var/lib/lokinet --group _loki --comment "Lokinet system user" _lokinet
fi fi
# Make sure the _lokinet user is part of the _loki group (in case it already existed) # Make sure the _lokinet user is part of the _loki group (in case it already existed)
@ -153,6 +148,14 @@ fi
%systemd_postun lokinet.service %systemd_postun lokinet.service
%changelog %changelog
* Thu Dec 15 2022 Technical Tumbleweed <necro_nemesis@hotmail.com> - 0.9.11-1
- bump version
- remove dns Patch1
- remove external/date submodule
- external/oxen-logging & gui submodule added during merge
- [jason@oxen.io] remove no-longer-rerequired sqlite dependency
- [jason@oxen.io] remove no-longer-wanted assertion from service file
* Wed Jun 29 2022 Technical Tumbleweed <necro_nemesis@hotmail.com> - 0.9.9-1 * Wed Jun 29 2022 Technical Tumbleweed <necro_nemesis@hotmail.com> - 0.9.9-1
- bump version - bump version
- cmake flags for no system library search - cmake flags for no system library search

@ -5,31 +5,31 @@
set(LOCAL_MIRROR "" CACHE STRING "local mirror path/URL for lib downloads") set(LOCAL_MIRROR "" CACHE STRING "local mirror path/URL for lib downloads")
set(OPENSSL_VERSION 1.1.1o CACHE STRING "openssl version") set(OPENSSL_VERSION 3.0.7 CACHE STRING "openssl version")
set(OPENSSL_MIRROR ${LOCAL_MIRROR} https://www.openssl.org/source CACHE STRING "openssl download mirror(s)") set(OPENSSL_MIRROR ${LOCAL_MIRROR} https://www.openssl.org/source CACHE STRING "openssl download mirror(s)")
set(OPENSSL_SOURCE openssl-${OPENSSL_VERSION}.tar.gz) set(OPENSSL_SOURCE openssl-${OPENSSL_VERSION}.tar.gz)
set(OPENSSL_HASH SHA256=9384a2b0570dd80358841464677115df785edb941c71211f75076d72fe6b438f set(OPENSSL_HASH SHA256=83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e
CACHE STRING "openssl source hash") CACHE STRING "openssl source hash")
set(EXPAT_VERSION 2.4.8 CACHE STRING "expat version") set(EXPAT_VERSION 2.5.0 CACHE STRING "expat version")
string(REPLACE "." "_" EXPAT_TAG "R_${EXPAT_VERSION}") string(REPLACE "." "_" EXPAT_TAG "R_${EXPAT_VERSION}")
set(EXPAT_MIRROR ${LOCAL_MIRROR} https://github.com/libexpat/libexpat/releases/download/${EXPAT_TAG} set(EXPAT_MIRROR ${LOCAL_MIRROR} https://github.com/libexpat/libexpat/releases/download/${EXPAT_TAG}
CACHE STRING "expat download mirror(s)") CACHE STRING "expat download mirror(s)")
set(EXPAT_SOURCE expat-${EXPAT_VERSION}.tar.xz) set(EXPAT_SOURCE expat-${EXPAT_VERSION}.tar.xz)
set(EXPAT_HASH SHA256=f79b8f904b749e3e0d20afeadecf8249c55b2e32d4ebb089ae378df479dcaf25 set(EXPAT_HASH SHA512=2da73b991b7c0c54440485c787e5edeb3567230204e31b3cac1c3a6713ec6f9f1554d3afffc0f8336168dfd5df02db4a69bcf21b4d959723d14162d13ab87516
CACHE STRING "expat source hash") CACHE STRING "expat source hash")
set(UNBOUND_VERSION 1.15.0 CACHE STRING "unbound version") set(UNBOUND_VERSION 1.17.0 CACHE STRING "unbound version")
set(UNBOUND_MIRROR ${LOCAL_MIRROR} https://nlnetlabs.nl/downloads/unbound CACHE STRING "unbound download mirror(s)") set(UNBOUND_MIRROR ${LOCAL_MIRROR} https://nlnetlabs.nl/downloads/unbound CACHE STRING "unbound download mirror(s)")
set(UNBOUND_SOURCE unbound-${UNBOUND_VERSION}.tar.gz) set(UNBOUND_SOURCE unbound-${UNBOUND_VERSION}.tar.gz)
set(UNBOUND_HASH SHA256=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f set(UNBOUND_HASH SHA512=f6b9f279330fb19b5feca09524959940aad8c4e064528aa82b369c726d77e9e8e5ca23f366f6e9edcf2c061b96f482ed7a2c26ac70fc15ae5762b3d7e36a5284
CACHE STRING "unbound source hash") CACHE STRING "unbound source hash")
set(SQLITE3_VERSION 3380500 CACHE STRING "sqlite3 version") set(SQLITE3_VERSION 3400000 CACHE STRING "sqlite3 version")
set(SQLITE3_MIRROR ${LOCAL_MIRROR} https://www.sqlite.org/2022 set(SQLITE3_MIRROR ${LOCAL_MIRROR} https://www.sqlite.org/2022
CACHE STRING "sqlite3 download mirror(s)") CACHE STRING "sqlite3 download mirror(s)")
set(SQLITE3_SOURCE sqlite-autoconf-${SQLITE3_VERSION}.tar.gz) set(SQLITE3_SOURCE sqlite-autoconf-${SQLITE3_VERSION}.tar.gz)
set(SQLITE3_HASH SHA3_256=ab649fea76f49a6ec7f907f001d87b8bd76dec0679c783e3992284c5a882a98c set(SQLITE3_HASH SHA3_256=7ee8f02b21edb4489df5082b5cf5b7ef47bcebcdb0e209bf14240db69633c878
CACHE STRING "sqlite3 source hash") CACHE STRING "sqlite3 source hash")
set(SODIUM_VERSION 1.0.18 CACHE STRING "libsodium version") set(SODIUM_VERSION 1.0.18 CACHE STRING "libsodium version")
@ -48,25 +48,25 @@ set(ZMQ_SOURCE zeromq-${ZMQ_VERSION}.tar.gz)
set(ZMQ_HASH SHA512=e198ef9f82d392754caadd547537666d4fba0afd7d027749b3adae450516bcf284d241d4616cad3cb4ad9af8c10373d456de92dc6d115b037941659f141e7c0e set(ZMQ_HASH SHA512=e198ef9f82d392754caadd547537666d4fba0afd7d027749b3adae450516bcf284d241d4616cad3cb4ad9af8c10373d456de92dc6d115b037941659f141e7c0e
CACHE STRING "libzmq source hash") CACHE STRING "libzmq source hash")
set(LIBUV_VERSION 1.44.1 CACHE STRING "libuv version") set(LIBUV_VERSION 1.44.2 CACHE STRING "libuv version")
set(LIBUV_MIRROR ${LOCAL_MIRROR} https://dist.libuv.org/dist/v${LIBUV_VERSION} set(LIBUV_MIRROR ${LOCAL_MIRROR} https://dist.libuv.org/dist/v${LIBUV_VERSION}
CACHE STRING "libuv mirror(s)") CACHE STRING "libuv mirror(s)")
set(LIBUV_SOURCE libuv-v${LIBUV_VERSION}.tar.gz) set(LIBUV_SOURCE libuv-v${LIBUV_VERSION}.tar.gz)
set(LIBUV_HASH SHA512=b4f8944e2c79e3a6a31ded6cccbe4c0eeada50db6bc8a448d7015642795012a4b80ffeef7ca455bb093c59a8950d0e1430566c3c2fa87b73f82699098162d834 set(LIBUV_HASH SHA512=91197ff9303112567bbb915bbb88058050e2ad1c048815a3b57c054635d5dc7df458b956089d785475290132236cb0edcfae830f5d749de29a9a3213eeaf0b20
CACHE STRING "libuv source hash") CACHE STRING "libuv source hash")
set(ZLIB_VERSION 1.2.12 CACHE STRING "zlib version") set(ZLIB_VERSION 1.2.13 CACHE STRING "zlib version")
set(ZLIB_MIRROR ${LOCAL_MIRROR} https://zlib.net set(ZLIB_MIRROR ${LOCAL_MIRROR} https://zlib.net
CACHE STRING "zlib mirror(s)") CACHE STRING "zlib mirror(s)")
set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.gz) set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.xz)
set(ZLIB_HASH SHA256=91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9 set(ZLIB_HASH SHA256=d14c38e313afc35a9a8760dadf26042f51ea0f5d154b0630a31da0540107fb98
CACHE STRING "zlib source hash") CACHE STRING "zlib source hash")
set(CURL_VERSION 7.83.1 CACHE STRING "curl version") set(CURL_VERSION 7.86.0 CACHE STRING "curl version")
set(CURL_MIRROR ${LOCAL_MIRROR} https://curl.haxx.se/download https://curl.askapache.com set(CURL_MIRROR ${LOCAL_MIRROR} https://curl.haxx.se/download https://curl.askapache.com
CACHE STRING "curl mirror(s)") CACHE STRING "curl mirror(s)")
set(CURL_SOURCE curl-${CURL_VERSION}.tar.xz) set(CURL_SOURCE curl-${CURL_VERSION}.tar.xz)
set(CURL_HASH SHA256=2cb9c2356e7263a1272fd1435ef7cdebf2cd21400ec287b068396deb705c22c4 set(CURL_HASH SHA512=18e03a3c00f22125e07bddb18becbf5acdca22baeb7b29f45ef189a5c56f95b2d51247813f7a9a90f04eb051739e9aa7d3a1c5be397bae75d763a2b918d1b656
CACHE STRING "curl source hash") CACHE STRING "curl source hash")
include(ExternalProject) include(ExternalProject)
@ -125,21 +125,21 @@ if(ANDROID)
set(android_toolchain_prefix x86_64) set(android_toolchain_prefix x86_64)
set(android_toolchain_suffix linux-android) set(android_toolchain_suffix linux-android)
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES x86) elseif(CMAKE_ANDROID_ARCH_ABI MATCHES x86)
set(android_machine i686) set(android_machine x86)
set(cross_host "--host=i686-linux-android") set(cross_host "--host=i686-linux-android")
set(android_compiler_prefix i686) set(android_compiler_prefix i686)
set(android_compiler_suffix linux-android23) set(android_compiler_suffix linux-android23)
set(android_toolchain_prefix i686) set(android_toolchain_prefix i686)
set(android_toolchain_suffix linux-android) set(android_toolchain_suffix linux-android)
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES armeabi-v7a) elseif(CMAKE_ANDROID_ARCH_ABI MATCHES armeabi-v7a)
set(android_machine armv7) set(android_machine arm)
set(cross_host "--host=armv7a-linux-androideabi") set(cross_host "--host=armv7a-linux-androideabi")
set(android_compiler_prefix armv7a) set(android_compiler_prefix armv7a)
set(android_compiler_suffix linux-androideabi23) set(android_compiler_suffix linux-androideabi23)
set(android_toolchain_prefix arm) set(android_toolchain_prefix arm)
set(android_toolchain_suffix linux-androideabi) set(android_toolchain_suffix linux-androideabi)
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES arm64-v8a) elseif(CMAKE_ANDROID_ARCH_ABI MATCHES arm64-v8a)
set(android_machine aarch64) set(android_machine arm64)
set(cross_host "--host=aarch64-linux-android") set(cross_host "--host=aarch64-linux-android")
set(android_compiler_prefix aarch64) set(android_compiler_prefix aarch64)
set(android_compiler_suffix linux-android23) set(android_compiler_suffix linux-android23)
@ -167,6 +167,11 @@ if(APPLE)
set(deps_CXXFLAGS "${deps_CXXFLAGS} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}") set(deps_CXXFLAGS "${deps_CXXFLAGS} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
endif() endif()
if(_winver)
set(deps_CFLAGS "${deps_CFLAGS} -D_WIN32_WINNT=${_winver}")
set(deps_CXXFLAGS "${deps_CXXFLAGS} -D_WIN32_WINNT=${_winver}")
endif()
if("${CMAKE_GENERATOR}" STREQUAL "Unix Makefiles") if("${CMAKE_GENERATOR}" STREQUAL "Unix Makefiles")
set(_make $(MAKE)) set(_make $(MAKE))
@ -223,7 +228,7 @@ build_external(libuv
add_static_target(libuv libuv_external libuv.a) add_static_target(libuv libuv_external libuv.a)
target_link_libraries(libuv INTERFACE ${CMAKE_DL_LIBS}) target_link_libraries(libuv INTERFACE ${CMAKE_DL_LIBS})
build_external(zlib build_external(zlib
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS} -fPIC" ${cross_extra} ./configure --prefix=${DEPS_DESTDIR} --static CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS} -fPIC" ${cross_extra} ./configure --prefix=${DEPS_DESTDIR} --static
BUILD_BYPRODUCTS BUILD_BYPRODUCTS
@ -234,43 +239,51 @@ add_static_target(zlib zlib_external libz.a)
set(openssl_system_env "") set(openssl_system_env "")
set(openssl_arch "")
set(openssl_configure_command ./config) set(openssl_configure_command ./config)
set(openssl_flags "CFLAGS=${deps_CFLAGS}")
if(CMAKE_CROSSCOMPILING) if(CMAKE_CROSSCOMPILING)
if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32) if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32)
set(openssl_system_env SYSTEM=MINGW64 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib) set(openssl_arch mingw64)
set(openssl_system_env RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
elseif(ARCH_TRIPLET STREQUAL i686-w64-mingw32) elseif(ARCH_TRIPLET STREQUAL i686-w64-mingw32)
set(openssl_system_env SYSTEM=MINGW32 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib) set(openssl_arch mingw)
set(openssl_system_env RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
elseif(ANDROID) elseif(ANDROID)
set(openssl_system_env SYSTEM=Linux MACHINE=${android_machine} LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar}) set(openssl_arch android-${android_machine})
set(openssl_system_env LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar} ANDROID_NDK_ROOT=${CMAKE_ANDROID_NDK} "PATH=${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin:$ENV{PATH}")
list(APPEND openssl_flags "CPPFLAGS=-D__ANDROID_API__=${ANDROID_API}")
set(openssl_extra_opts no-asm) set(openssl_extra_opts no-asm)
elseif(ARCH_TRIPLET STREQUAL mips64-linux-gnuabi64) elseif(ARCH_TRIPLET STREQUAL mips64-linux-gnuabi64)
set(openssl_system_env SYSTEM=Linux MACHINE=mips64) set(openssl_arch linux-mips64)
set(openssl_configure_command ./Configure linux64-mips64)
elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu) elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu)
set(openssl_system_env SYSTEM=Linux MACHINE=mips) set(openssl_arch linux-mips32)
elseif(ARCH_TRIPLET STREQUAL mipsel-linux-gnu) elseif(ARCH_TRIPLET STREQUAL mipsel-linux-gnu)
set(openssl_system_env SYSTEM=Linux MACHINE=mipsel) set(openssl_arch linux-mips)
elseif(ARCH_TRIPLET STREQUAL aarch64-linux-gnu) elseif(ARCH_TRIPLET STREQUAL aarch64-linux-gnu)
# cross compile arm64 # cross compile arm64
set(openssl_system_env SYSTEM=Linux MACHINE=aarch64) set(openssl_arch linux-aarch64)
elseif(ARCH_TRIPLET MATCHES arm-linux) elseif(ARCH_TRIPLET MATCHES arm-linux)
# cross compile armhf # cross compile armhf
set(openssl_system_env SYSTEM=Linux MACHINE=armv4) set(openssl_arch linux-armv4)
elseif(ARCH_TRIPLET MATCHES powerpc64le) elseif(ARCH_TRIPLET MATCHES powerpc64le)
# cross compile ppc64le # cross compile ppc64le
set(openssl_system_env SYSTEM=Linux MACHINE=ppc64le) set(openssl_arch linux-ppc64le)
endif() endif()
elseif(CMAKE_C_FLAGS MATCHES "-march=armv7") elseif(CMAKE_C_FLAGS MATCHES "-march=armv7")
# Help openssl figure out that we're building from armv7 even if on armv8 hardware: # Help openssl figure out that we're building from armv7 even if on armv8 hardware:
set(openssl_system_env SYSTEM=Linux MACHINE=armv7) set(openssl_arch linux-armv4)
endif() endif()
build_external(openssl build_external(openssl
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CC=${deps_cc} ${openssl_system_env} ${openssl_configure_command} CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CC=${deps_cc} ${openssl_system_env} ${openssl_configure_command}
--prefix=${DEPS_DESTDIR} ${openssl_extra_opts} no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost --prefix=${DEPS_DESTDIR} --libdir=lib ${openssl_extra_opts}
no-heartbeats no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl2 no-ssl3 no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost
no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic "CFLAGS=${deps_CFLAGS}" no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl3
no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic ${openssl_flags}
${openssl_arch}
BUILD_COMMAND ${CMAKE_COMMAND} -E env ${openssl_system_env} ${_make}
INSTALL_COMMAND ${_make} install_sw INSTALL_COMMAND ${_make} install_sw
BUILD_BYPRODUCTS BUILD_BYPRODUCTS
${DEPS_DESTDIR}/lib/libssl.a ${DEPS_DESTDIR}/lib/libcrypto.a ${DEPS_DESTDIR}/lib/libssl.a ${DEPS_DESTDIR}/lib/libcrypto.a
@ -284,7 +297,6 @@ endif()
set(OPENSSL_INCLUDE_DIR ${DEPS_DESTDIR}/include) set(OPENSSL_INCLUDE_DIR ${DEPS_DESTDIR}/include)
set(OPENSSL_CRYPTO_LIBRARY ${DEPS_DESTDIR}/lib/libcrypto.a ${DEPS_DESTDIR}/lib/libssl.a) set(OPENSSL_CRYPTO_LIBRARY ${DEPS_DESTDIR}/lib/libcrypto.a ${DEPS_DESTDIR}/lib/libssl.a)
set(OPENSSL_VERSION 1.1.1)
set(OPENSSL_ROOT_DIR ${DEPS_DESTDIR}) set(OPENSSL_ROOT_DIR ${DEPS_DESTDIR})
build_external(expat build_external(expat
@ -294,8 +306,15 @@ build_external(expat
) )
add_static_target(expat expat_external libexpat.a) add_static_target(expat expat_external libexpat.a)
if(WIN32)
set(unbound_patch
PATCH_COMMAND ${PROJECT_SOURCE_DIR}/contrib/apply-patches.sh
${PROJECT_SOURCE_DIR}/contrib/patches/unbound-delete-crash-fix.patch)
endif()
build_external(unbound build_external(unbound
DEPENDS openssl_external expat_external DEPENDS openssl_external expat_external
${unbound_patch}
CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --disable-shared CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --disable-shared
--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}
@ -315,8 +334,11 @@ build_external(sodium CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --
--enable-static --with-pic "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS}") --enable-static --with-pic "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS}")
add_static_target(sodium sodium_external libsodium.a) add_static_target(sodium sodium_external libsodium.a)
build_external(sqlite3)
add_static_target(sqlite3 sqlite3_external libsqlite3.a) if(WITH_PEERSTATS_BACKEND)
build_external(sqlite3)
add_static_target(sqlite3 sqlite3_external libsqlite3.a)
endif()
if(ARCH_TRIPLET MATCHES mingw) if(ARCH_TRIPLET MATCHES mingw)
@ -328,7 +350,9 @@ endif()
if(CMAKE_CROSSCOMPILING AND ARCH_TRIPLET MATCHES mingw) if(CMAKE_CROSSCOMPILING AND ARCH_TRIPLET MATCHES mingw)
set(zmq_patch set(zmq_patch
PATCH_COMMAND ${PROJECT_SOURCE_DIR}/contrib/apply-patches.sh ${PROJECT_SOURCE_DIR}/contrib/patches/libzmq-mingw-wepoll.patch ${PROJECT_SOURCE_DIR}/contrib/patches/libzmq-mingw-closesocket.patch) PATCH_COMMAND ${PROJECT_SOURCE_DIR}/contrib/apply-patches.sh
${PROJECT_SOURCE_DIR}/contrib/patches/libzmq-mingw-wepoll.patch
${PROJECT_SOURCE_DIR}/contrib/patches/libzmq-mingw-unistd.patch)
endif() endif()
build_external(zmq build_external(zmq
@ -351,6 +375,15 @@ set_target_properties(libzmq PROPERTIES
INTERFACE_LINK_LIBRARIES "${libzmq_link_libs}" INTERFACE_LINK_LIBRARIES "${libzmq_link_libs}"
INTERFACE_COMPILE_DEFINITIONS "ZMQ_STATIC") INTERFACE_COMPILE_DEFINITIONS "ZMQ_STATIC")
#
#
#
# Everything that follows is *only* for lokinet-bootstrap (i.e. if adding new deps put them *above*
# this).
#
#
#
if(NOT WITH_BOOTSTRAP) if(NOT WITH_BOOTSTRAP)
return() return()
endif() endif()
@ -420,7 +453,7 @@ foreach(curl_arch ${curl_arches})
list(APPEND curl_lib_outputs ${curl_prefix}/lib/libcurl.a) list(APPEND curl_lib_outputs ${curl_prefix}/lib/libcurl.a)
endforeach() endforeach()
message(STATUS "TARGETS: ${curl_lib_targets}")
if(IOS AND num_arches GREATER 1) if(IOS AND num_arches GREATER 1)
# We are building multiple architectures for different iOS devices, so we need to glue the # We are building multiple architectures for different iOS devices, so we need to glue the

@ -1,26 +1,31 @@
# We do this via a custom command that re-invokes a cmake script because we need the DEPENDS on .git/index so that we will re-run it (to regenerate the commit tag in the version) whenever the current commit changes. If we used a configure_file directly here, it would only re-run when something else causes cmake to re-run. # We do this via a custom command that re-invokes a cmake script because we need the DEPENDS on .git/index so that we will re-run it (to regenerate the commit tag in the version) whenever the current commit changes. If we used a configure_file directly here, it would only re-run when something else causes cmake to re-run.
set(VERSIONTAG "${GIT_VERSION}") if(LOKINET_VERSIONTAG)
set(GIT_INDEX_FILE "${PROJECT_SOURCE_DIR}/.git/index") set(VERSIONTAG "${LOKINET_VERSIONTAG}")
find_package(Git)
if(EXISTS "${GIT_INDEX_FILE}" AND ( GIT_FOUND OR Git_FOUND) )
message(STATUS "Found Git: ${GIT_EXECUTABLE}")
set(genversion_args "-DGIT=${GIT_EXECUTABLE}")
foreach(v lokinet_VERSION lokinet_VERSION_MAJOR lokinet_VERSION_MINOR lokinet_VERSION_PATCH RELEASE_MOTTO)
list(APPEND genversion_args "-D${v}=${${v}}")
endforeach()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp"
COMMAND "${CMAKE_COMMAND}"
${genversion_args}
"-D" "SRC=${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in"
"-D" "DEST=${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp"
"-P" "${CMAKE_CURRENT_LIST_DIR}/GenVersion.cmake"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in"
"${GIT_INDEX_FILE}")
else()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp" @ONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp" @ONLY)
else()
set(VERSIONTAG "${GIT_VERSION}")
set(GIT_INDEX_FILE "${PROJECT_SOURCE_DIR}/.git/index")
find_package(Git)
if(EXISTS "${GIT_INDEX_FILE}" AND ( GIT_FOUND OR Git_FOUND) )
message(STATUS "Found Git: ${GIT_EXECUTABLE}")
set(genversion_args "-DGIT=${GIT_EXECUTABLE}")
foreach(v lokinet_VERSION lokinet_VERSION_MAJOR lokinet_VERSION_MINOR lokinet_VERSION_PATCH RELEASE_MOTTO)
list(APPEND genversion_args "-D${v}=${${v}}")
endforeach()
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp"
COMMAND "${CMAKE_COMMAND}"
${genversion_args}
"-D" "SRC=${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in"
"-D" "DEST=${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp"
"-P" "${CMAKE_CURRENT_LIST_DIR}/GenVersion.cmake"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in"
"${GIT_INDEX_FILE}")
else()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/constants/version.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/constants/version.cpp" @ONLY)
endif()
endif() endif()

@ -1,7 +0,0 @@
function(add_log_tag target)
get_target_property(TARGET_SRCS ${target} SOURCES)
foreach(F ${TARGET_SRCS})
get_filename_component(fpath "${F}" ABSOLUTE)
set_property(SOURCE ${F} APPEND PROPERTY COMPILE_DEFINITIONS SOURCE_ROOT=\"${PROJECT_SOURCE_DIR}\")
endforeach()
endfunction()

@ -44,6 +44,7 @@ if(filesystem_is_good EQUAL 1)
else() else()
# Probably broken AF macos # Probably broken AF macos
message(STATUS "std::filesystem is not available, apparently this compiler isn't C++17 compliant; falling back to ghc::filesystem") message(STATUS "std::filesystem is not available, apparently this compiler isn't C++17 compliant; falling back to ghc::filesystem")
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) target_compile_definitions(filesystem INTERFACE USE_GHC_FILESYSTEM)

@ -2,6 +2,9 @@
include(CheckIPOSupported) include(CheckIPOSupported)
option(WITH_LTO "enable lto on compile time" ON) option(WITH_LTO "enable lto on compile time" ON)
if(WITH_LTO) if(WITH_LTO)
if(WIN32)
message(FATAL_ERROR "LTO not supported on win32 targets, please set -DWITH_LTO=OFF")
endif()
check_ipo_supported(RESULT IPO_ENABLED OUTPUT ipo_error) check_ipo_supported(RESULT IPO_ENABLED OUTPUT ipo_error)
if(IPO_ENABLED) if(IPO_ENABLED)
message(STATUS "LTO enabled") message(STATUS "LTO enabled")

@ -0,0 +1,17 @@
set(default_build_gui OFF)
if(APPLE OR WIN32)
set(default_build_gui ON)
endif()
if(WIN32)
set(GUI_EXE "" CACHE FILEPATH "path to a pre-built Windows GUI .exe to use (implies -DBUILD_GUI=OFF)")
if(GUI_EXE)
set(default_build_gui OFF)
endif()
endif()
option(BUILD_GUI "build electron gui from 'gui' submodule source" ${default_build_gui})
if(BUILD_GUI AND GUI_EXE)
message(FATAL_ERROR "-DGUI_EXE=... and -DBUILD_GUI=ON are mutually exclusive")
endif()

@ -0,0 +1,67 @@
if(WIN32 AND GUI_EXE)
message(STATUS "using pre-built lokinet gui executable: ${GUI_EXE}")
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GUI_EXE}" "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe")
elseif(BUILD_GUI)
message(STATUS "Building lokinet-gui from source")
set(default_gui_target pack)
if(APPLE)
set(default_gui_target macos:raw)
elseif(WIN32)
set(default_gui_target win32)
endif()
set(GUI_YARN_TARGET "${default_gui_target}" CACHE STRING "yarn target for building the GUI")
set(GUI_YARN_EXTRA_OPTS "" CACHE STRING "extra options to pass into the yarn build command")
# allow manually specifying yarn with -DYARN=
if(NOT YARN)
find_program(YARN NAMES yarnpkg yarn REQUIRED)
endif()
message(STATUS "Building lokinet-gui with yarn ${YARN}, target ${GUI_YARN_TARGET}")
if(NOT WIN32)
add_custom_target(lokinet-gui
COMMAND ${YARN} install --frozen-lockfile &&
${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
endif()
if(APPLE)
add_custom_target(assemble_gui ALL
DEPENDS assemble lokinet-gui
COMMAND mkdir "${lokinet_app}/Contents/Helpers"
COMMAND cp -a "${PROJECT_SOURCE_DIR}/gui/release/mac/Lokinet-GUI.app" "${lokinet_app}/Contents/Helpers/"
COMMAND mkdir -p "${lokinet_app}/Contents/Resources/en.lproj"
COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Resources/en.lproj/"
COMMAND cp "${lokinet_app}/Contents/Resources/icon.icns" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/icon.icns"
COMMAND cp "${PROJECT_SOURCE_DIR}/contrib/macos/InfoPlist.strings" "${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Resources/en.lproj/"
COMMAND /usr/libexec/PlistBuddy
-c "Delete :CFBundleDisplayName"
-c "Add :LSHasLocalizedDisplayName bool true"
-c "Add :CFBundleDevelopmentRegion string en"
-c "Set :CFBundleShortVersionString ${lokinet_VERSION}"
-c "Set :CFBundleVersion ${lokinet_VERSION}.${LOKINET_APPLE_BUILD}"
"${lokinet_app}/Contents/Helpers/Lokinet-GUI.app/Contents/Info.plist"
)
elseif(WIN32)
file(MAKE_DIRECTORY "${PROJECT_BINARY_DIR}/gui")
add_custom_command(OUTPUT "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
COMMAND ${YARN} install --frozen-lockfile &&
USE_SYSTEM_7ZA=true DISPLAY= WINEDEBUG=-all WINEPREFIX="${PROJECT_BINARY_DIR}/wineprefix" ${YARN} ${GUI_YARN_EXTRA_OPTS} ${GUI_YARN_TARGET}
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${PROJECT_SOURCE_DIR}/gui/release/Lokinet-GUI_portable.exe"
"${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe"
WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/gui")
add_custom_target(assemble_gui ALL COMMAND "true" DEPENDS "${PROJECT_BINARY_DIR}/gui/lokinet-gui.exe")
else()
message(FATAL_ERROR "Building/bundling the GUI from this repository is not supported on this platform")
endif()
else()
message(STATUS "not building gui")
endif()
if(NOT TARGET assemble_gui)
add_custom_target(assemble_gui COMMAND "true")
endif()

@ -5,9 +5,42 @@ set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")
if(WIN32) if(WIN32)
include(cmake/win32_installer_deps.cmake) include(cmake/win32_installer_deps.cmake)
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-exit.ini DESTINATION share/conf.d COMPONENT exit_configs)
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-keyfile.ini DESTINATION share/conf.d COMPONENT keyfile_configs)
install(FILES ${CMAKE_SOURCE_DIR}/contrib/configs/00-debug-log.ini DESTINATION share/conf.d COMPONENT debug_configs)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified" "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs")
list(APPEND CPACK_COMPONENTS_ALL "lokinet" "gui" "exit_configs" "keyfile_configs" "debug_configs")
elseif(APPLE)
set(CPACK_GENERATOR DragNDrop;ZIP)
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
endif() endif()
# This must always be last!
include(CPack) include(CPack)
if(WIN32)
cpack_add_component(lokinet
DISPLAY_NAME "lokinet"
DESCRIPTION "core required lokinet files"
REQUIRED)
cpack_add_component(gui
DISPLAY_NAME "lokinet gui"
DESCRIPTION "electron based control panel for lokinet")
cpack_add_component(exit_configs
DISPLAY_NAME "auto-enable exit"
DESCRIPTION "automatically enable usage of exit.loki as an exit node\n"
DISABLED)
cpack_add_component(keyfile_configs
DISPLAY_NAME "persist address"
DESCRIPTION "persist .loki address across restarts of lokinet\nnot recommended when enabling exit nodes"
DISABLED)
cpack_add_component(debug_configs
DISPLAY_NAME "debug logging"
DESCRIPTION "enable debug spew log level by default"
DISABLED)
endif()

@ -0,0 +1,214 @@
if(NOT APPLE)
return()
endif()
option(MACOS_SYSTEM_EXTENSION
"Build the network extension as a system extension rather than a plugin. This must be ON for non-app store release builds, and must be OFF for dev builds and Mac App Store distribution builds"
OFF)
option(CODESIGN "codesign the resulting app and extension" ON)
set(CODESIGN_ID "" CACHE STRING "codesign the macos app using this key identity; if empty we'll try to guess")
set(default_profile_type "dev")
if(MACOS_SYSTEM_EXTENSION)
set(default_profile_type "release")
endif()
set(CODESIGN_PROFILE "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.${default_profile_type}.provisionprofile" CACHE FILEPATH
"Path to a .provisionprofile to use for the main app")
set(CODESIGN_EXT_PROFILE "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet-extension.${default_profile_type}.provisionprofile" CACHE FILEPATH
"Path to a .provisionprofile to use for the lokinet extension")
if(CODESIGN AND NOT CODESIGN_ID)
if(MACOS_SYSTEM_EXTENSION)
set(codesign_cert_pattern "Developer ID Application")
else()
set(codesign_cert_pattern "Apple Development")
endif()
execute_process(
COMMAND security find-identity -v -p codesigning
COMMAND sed -n "s/^ *[0-9][0-9]*) *\\([A-F0-9]\\{40\\}\\) *\"\\(${codesign_cert_pattern}.*\\)\"\$/\\1 \\2/p"
RESULT_VARIABLE find_id_exit_code
OUTPUT_VARIABLE find_id_output)
if(NOT find_id_exit_code EQUAL 0)
message(FATAL_ERROR "Finding signing identities with security find-identity failed; try specifying an id using -DCODESIGN_ID=...")
endif()
string(REGEX MATCHALL "(^|\n)[0-9A-F]+" find_id_sign_id "${find_id_output}")
if(NOT find_id_sign_id)
message(FATAL_ERROR "Did not find any \"${codesign_cert_pattern}\" identity; try specifying an id using -DCODESIGN_ID=...")
endif()
if (find_id_sign_id MATCHES ";")
message(FATAL_ERROR "Found multiple \"${codesign_cert_pattern}\" identities:\n${find_id_output}\nSpecify an identify using -DCODESIGN_ID=...")
endif()
set(CODESIGN_ID "${find_id_sign_id}" CACHE STRING "" FORCE)
endif()
if(CODESIGN)
message(STATUS "Codesigning using ${CODESIGN_ID}")
if (NOT MACOS_NOTARIZE_USER AND NOT MACOS_NOTARIZE_PASS AND NOT MACOS_NOTARIZE_ASC AND EXISTS "$ENV{HOME}/.notarization.cmake")
message(STATUS "Loading notarization info from ~/.notarization.cmake")
include("$ENV{HOME}/.notarization.cmake")
endif()
if (MACOS_NOTARIZE_USER AND MACOS_NOTARIZE_PASS AND MACOS_NOTARIZE_ASC)
message(STATUS "Enabling notarization with account ${MACOS_NOTARIZE_ASC}/${MACOS_NOTARIZE_USER}")
else()
message(WARNING "You have not set one or more of MACOS_NOTARIZE_USER, MACOS_NOTARIZE_PASS, MACOS_NOTARIZE_ASC: notarization will fail; see contrib/macos/README.txt")
endif()
else()
message(WARNING "Codesigning disabled; the resulting build will not run on most macOS systems")
endif()
foreach(prof IN ITEMS CODESIGN_PROFILE CODESIGN_EXT_PROFILE)
if(NOT ${prof})
message(WARNING "Missing a ${prof} provisioning profile: Apple will most likely log an uninformative error message to the system log and then kill harmless kittens if you try to run the result")
elseif(NOT EXISTS "${${prof}}")
message(FATAL_ERROR "Provisioning profile ${${prof}} does not exist; fix your -D${prof} path")
endif()
endforeach()
message(STATUS "Using ${CODESIGN_PROFILE} app provisioning profile")
message(STATUS "Using ${CODESIGN_EXT_PROFILE} extension provisioning profile")
set(lokinet_installer "${PROJECT_BINARY_DIR}/Lokinet ${PROJECT_VERSION}")
if(NOT CODESIGN)
set(lokinet_installer "${lokinet_installer}-UNSIGNED")
endif()
set(lokinet_app "${lokinet_installer}/Lokinet.app")
if(MACOS_SYSTEM_EXTENSION)
set(lokinet_ext_dir Contents/Library/SystemExtensions)
else()
set(lokinet_ext_dir Contents/PlugIns)
endif()
if(CODESIGN)
if(MACOS_SYSTEM_EXTENSION)
set(LOKINET_ENTITLEMENTS_TYPE sysext)
set(notarize_py_is_sysext True)
else()
set(LOKINET_ENTITLEMENTS_TYPE plugin)
set(notarize_py_is_sysext False)
endif()
configure_file(
"${PROJECT_SOURCE_DIR}/contrib/macos/sign.sh.in"
"${PROJECT_BINARY_DIR}/sign.sh"
@ONLY)
add_custom_target(
sign
DEPENDS "${PROJECT_BINARY_DIR}/sign.sh"
COMMAND "${PROJECT_BINARY_DIR}/sign.sh"
)
if(MACOS_NOTARIZE_USER AND MACOS_NOTARIZE_PASS AND MACOS_NOTARIZE_ASC)
configure_file(
"${PROJECT_SOURCE_DIR}/contrib/macos/notarize.py.in"
"${PROJECT_BINARY_DIR}/notarize.py"
@ONLY)
add_custom_target(
notarize
DEPENDS "${PROJECT_BINARY_DIR}/notarize.py" sign
COMMAND "${PROJECT_BINARY_DIR}/notarize.py"
)
else()
message(WARNING "You have not set one or more of MACOS_NOTARIZE_USER, MACOS_NOTARIZE_PASS, MACOS_NOTARIZE_ASC: notarization disabled")
endif()
else()
add_custom_target(sign COMMAND "true")
add_custom_target(notarize DEPENDS sign COMMAND "true")
endif()
set(mac_icon "${PROJECT_BINARY_DIR}/lokinet.icns")
add_custom_command(OUTPUT "${mac_icon}"
COMMAND ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh ${PROJECT_SOURCE_DIR}/contrib/lokinet-mac.svg "${mac_icon}"
DEPENDS ${PROJECT_SOURCE_DIR}/contrib/lokinet-mac.svg ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh)
add_custom_target(icon DEPENDS "${mac_icon}")
if(BUILD_PACKAGE)
add_executable(seticon "${PROJECT_SOURCE_DIR}/contrib/macos/seticon.swift")
add_custom_command(OUTPUT "${lokinet_installer}.dmg"
DEPENDS notarize seticon
COMMAND create-dmg
--volname "Lokinet ${PROJECT_VERSION}"
--volicon lokinet.icns
--background "${PROJECT_SOURCE_DIR}/contrib/macos/installer.tiff"
--text-size 16
--icon-size 128
--window-size 555 440
--icon Lokinet.app 151 196
--hide-extension Lokinet.app
--app-drop-link 403 196
--eula "${PROJECT_SOURCE_DIR}/LICENSE"
--no-internet-enable
"${lokinet_installer}.dmg"
"${lokinet_installer}"
COMMAND ./seticon lokinet.icns "${lokinet_installer}.dmg"
)
add_custom_target(package DEPENDS "${lokinet_installer}.dmg")
endif()
# Called later to set things up, after the main lokinet targets are set up
function(macos_target_setup)
if(MACOS_SYSTEM_EXTENSION)
target_compile_definitions(lokinet PRIVATE MACOS_SYSTEM_EXTENSION)
endif()
set_target_properties(lokinet
PROPERTIES
OUTPUT_NAME Lokinet
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_STRING "Lokinet IP Packet Onion Router"
MACOSX_BUNDLE_BUNDLE_NAME "Lokinet"
MACOSX_BUNDLE_BUNDLE_VERSION "${lokinet_VERSION}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${lokinet_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${lokinet_VERSION_MAJOR}.${lokinet_VERSION_MINOR}"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.lokinet"
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.Info.plist.in"
MACOSX_BUNDLE_COPYRIGHT "© 2022, The Oxen Project"
)
add_custom_target(copy_bootstrap
DEPENDS lokinet-extension
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed
$<TARGET_BUNDLE_DIR:lokinet-extension>/Contents/Resources/bootstrap.signed
)
add_dependencies(lokinet lokinet-extension icon)
if(CODESIGN_PROFILE)
add_custom_target(copy_prov_prof
DEPENDS lokinet
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${CODESIGN_PROFILE}
$<TARGET_BUNDLE_DIR:lokinet>/Contents/embedded.provisionprofile
)
else()
add_custom_target(copy_prov_prof COMMAND true)
endif()
add_custom_target(assemble ALL
DEPENDS lokinet lokinet-extension icon copy_prov_prof copy_bootstrap
COMMAND rm -rf "${lokinet_app}"
COMMAND mkdir -p "${lokinet_installer}"
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet> "${lokinet_app}"
COMMAND mkdir -p "${lokinet_app}/${lokinet_ext_dir}"
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet-extension> "${lokinet_app}/${lokinet_ext_dir}/"
COMMAND mkdir -p "${lokinet_app}/Contents/Resources"
COMMAND cp -a "${mac_icon}" "${lokinet_app}/Contents/Resources/icon.icns"
)
if(BUILD_GUI)
add_dependencies(sign assemble_gui)
else()
add_dependencies(sign assemble)
endif()
endfunction()

@ -1,18 +0,0 @@
set(WITH_STATIC OFF)
set(WITH_SHARED ON)
if("${SHADOW_ROOT}" STREQUAL "")
set(SHADOW_ROOT "$ENV{HOME}/.shadow")
endif("${SHADOW_ROOT}" STREQUAL "")
if(EXISTS "${SHADOW_ROOT}")
message(STATUS "SHADOW_ROOT = ${SHADOW_ROOT}")
else()
message(FATAL_ERROR "SHADOW_ROOT path does not exist: '${SHADOW_ROOT}'")
endif(EXISTS "${SHADOW_ROOT}")
set(CMAKE_MODULE_PATH "${SHADOW_ROOT}/share/cmake/Modules")
include_directories(${CMAKE_MODULE_PATH})
include(ShadowTools)
add_compile_options(-fno-inline -fno-strict-aliasing )
add_definitions(-DTESTNET=1)
add_definitions(-DLOKINET_SHADOW)
include_directories(${SHADOW_ROOT}/include)

@ -1,32 +1,49 @@
if(NOT WIN32) if(NOT WIN32)
return() return()
endif() endif()
if (NOT STATIC_LINK)
message(FATAL_ERROR "windows requires static builds (thanks balmer)")
endif()
enable_language(RC) enable_language(RC)
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) option(WITH_WINDOWS_32 "build 32 bit windows" OFF)
if(NOT MSVC_VERSION) # unlike unix where you get a *single* compiler ID string in .comment
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-bad-function-cast>) # GNU ld sees fit to merge *all* the .ident sections in object files
add_compile_options($<$<COMPILE_LANGUAGE:C>:-Wno-cast-function-type>) # to .r[o]data section one after the other!
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:-fpermissive>) add_compile_options(-fno-ident -Wa,-mbig-obj)
# unlike unix where you get a *single* compiler ID string in .comment
# GNU ld sees fit to merge *all* the .ident sections in object files
# to .r[o]data section one after the other!
add_compile_options(-fno-ident -Wa,-mbig-obj)
link_libraries( -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
# the minimum windows version, set to 6 rn because supporting older windows is hell
set(_winver 0x0600)
add_definitions(-DWINVER=${_winver} -D_WIN32_WINNT=${_winver})
endif()
if(EMBEDDED_CFG) if(EMBEDDED_CFG)
link_libatomic() link_libatomic()
endif() endif()
add_definitions(-DWIN32_LEAN_AND_MEAN -DWIN32) set(WINTUN_VERSION 0.14.1 CACHE STRING "wintun version")
set(WINTUN_MIRROR https://www.wintun.net/builds
CACHE STRING "wintun mirror(s)")
set(WINTUN_SOURCE wintun-${WINTUN_VERSION}.zip)
set(WINTUN_HASH SHA256=07c256185d6ee3652e09fa55c0b673e2624b565e02c4b9091c79ca7d2f24ef51
CACHE STRING "wintun source hash")
if (NOT STATIC_LINK AND NOT MSVC) set(WINDIVERT_VERSION 2.2.0-A CACHE STRING "windivert version")
message("must ship compiler runtime libraries with this build: libwinpthread-1.dll, libgcc_s_dw2-1.dll, and libstdc++-6.dll") set(WINDIVERT_MIRROR https://reqrypt.org/download
message("for release builds, turn on STATIC_LINK in cmake options") CACHE STRING "windivert mirror(s)")
endif() set(WINDIVERT_SOURCE WinDivert-${WINDIVERT_VERSION}.zip)
set(WINDIVERT_HASH SHA256=2a7630aac0914746fbc565ac862fa096e3e54233883ac52d17c83107496b7a7f
CACHE STRING "windivert source hash")
set(WINTUN_URL ${WINTUN_MIRROR}/${WINTUN_SOURCE}
CACHE STRING "wintun download url")
set(WINDIVERT_URL ${WINDIVERT_MIRROR}/${WINDIVERT_SOURCE}
CACHE STRING "windivert download url")
message(STATUS "Downloading wintun from ${WINTUN_URL}")
file(DOWNLOAD ${WINTUN_URL} ${CMAKE_BINARY_DIR}/wintun.zip EXPECTED_HASH ${WINTUN_HASH})
message(STATUS "Downloading windivert from ${WINDIVERT_URL}")
file(DOWNLOAD ${WINDIVERT_URL} ${CMAKE_BINARY_DIR}/windivert.zip EXPECTED_HASH ${WINDIVERT_HASH})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/wintun.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar x ${CMAKE_BINARY_DIR}/windivert.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})

@ -1,34 +1,29 @@
if(NOT GUI_ZIP_URL) install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui)
set(GUI_ZIP_URL "https://oxen.rocks/oxen-io/lokinet-gui/dev/lokinet-windows-x64-20220331T180338Z-569f90ad8.zip")
set(GUI_ZIP_HASH_OPTS EXPECTED_HASH SHA256=316f10489f5907bfa9c74b21f8ef2fdd7b7c7e6a0f5bcedaed2ee5f4004eab52) if(WITH_WINDOWS_32)
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/x86/wintun.dll DESTINATION bin COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.sys DESTINATION lib COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.dll DESTINATION bin COMPONENT lokinet)
else()
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/amd64/wintun.dll DESTINATION bin COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert64.sys DESTINATION lib COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert.dll DESTINATION bin COMPONENT lokinet)
endif() endif()
set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe")
set(TUNTAP_EXE "${CMAKE_BINARY_DIR}/tuntap-install.exe")
set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed") set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed")
file(DOWNLOAD
${TUNTAP_URL}
${TUNTAP_EXE})
file(DOWNLOAD
${GUI_ZIP_URL}
${CMAKE_BINARY_DIR}/lokinet-gui.zip
${GUI_ZIP_HASH_OPTS})
execute_process(COMMAND ${CMAKE_COMMAND} -E tar xf ${CMAKE_BINARY_DIR}/lokinet-gui.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui)
install(PROGRAMS ${TUNTAP_EXE} DESTINATION bin COMPONENT tuntap)
install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet RENAME bootstrap.signed) install(FILES ${BOOTSTRAP_FILE} DESTINATION share COMPONENT lokinet RENAME bootstrap.signed)
set(win_ico "${PROJECT_BINARY_DIR}/lokinet.ico")
add_custom_command(OUTPUT "${win_ico}"
COMMAND ${PROJECT_SOURCE_DIR}/contrib/make-ico.sh ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg "${win_ico}"
DEPENDS ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${PROJECT_SOURCE_DIR}/contrib/make-ico.sh)
add_custom_target(icon ALL DEPENDS "${win_ico}")
set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet") set(CPACK_PACKAGE_INSTALL_DIRECTORY "Lokinet")
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/win32-setup/lokinet.ico") set(CPACK_NSIS_MUI_ICON "${PROJECT_BINARY_DIR}/lokinet.ico")
set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin") set(CPACK_NSIS_DEFINES "RequestExecutionLevel admin")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON) set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
function(read_nsis_file filename outvar) function(read_nsis_file filename outvar)
file(STRINGS "${filename}" _outvar) file(STRINGS "${filename}" _outvar)
list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\") list(TRANSFORM _outvar REPLACE "\\\\" "\\\\\\\\")
@ -44,9 +39,8 @@ read_nsis_file("${CMAKE_SOURCE_DIR}/win32-setup/extra_delete_icons.nsis" _extra_
set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}") set(CPACK_NSIS_EXTRA_PREINSTALL_COMMANDS "${_extra_preinstall}")
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}") set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS "${_extra_install}")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}") set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}") set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}")
set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}") set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}")
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS) set(CPACK_NSIS_COMPRESSOR "/SOLID lzma")
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")

@ -1,17 +1,18 @@
#!/bin/bash #!/bin/bash
set -e set -e
set +x
default_abis="armeabi-v7a arm64-v8a x86_64" default_abis="armeabi-v7a arm64-v8a x86_64"
build_abis=${ABIS:-$default_abis} build_abis=${ABIS:-$default_abis}
test x$NDK = x && echo "NDK env var not set" test x$NDK = x && test -e /usr/lib/android-ndk && export NDK=/usr/lib/android-ndk
test x$NDK = x && exit 1 test x$NDK = x && exit 1
echo "building abis: $build_abis" echo "building abis: $build_abis"
root="$(readlink -f $(dirname $0)/../)" root=$(readlink -f "$1")
build=$root/build-android shift
build=$(readlink -f "$1")
shift
mkdir -p $build mkdir -p $build
cd $build cd $build
@ -19,11 +20,13 @@ for abi in $build_abis; do
mkdir -p build-$abi mkdir -p build-$abi
cd build-$abi cd build-$abi
cmake \ cmake \
-S "$root" -B . \
-G 'Unix Makefiles' \ -G 'Unix Makefiles' \
-DANDROID=ON \ -DANDROID=ON \
-DANDROID_ABI=$abi \ -DANDROID_ABI=$abi \
-DANDROID_ARM_MODE=arm \ -DANDROID_ARM_MODE=arm \
-DANDROID_PLATFORM=android-23 \ -DANDROID_PLATFORM=android-23 \
-DANDROID_API=23 \
-DANDROID_STL=c++_static \ -DANDROID_STL=c++_static \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \ -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DBUILD_STATIC_DEPS=ON \ -DBUILD_STATIC_DEPS=ON \
@ -32,15 +35,19 @@ for abi in $build_abis; do
-DBUILD_TESTING=OFF \ -DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \ -DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \ -DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \ -DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \ -DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \ -DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \ -DFORCE_OXENMQ_SUBMODULE=ON \
-DFORCE_OXENC_SUBMODULE=ON \ -DFORCE_OXENC_SUBMODULE=ON \
-DFORCE_FMT_SUBMODULE=ON \
-DFORCE_SPDLOG_SUBMODULE=ON \
-DFORCE_NLOHMANN_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \ -DSUBMODULE_CHECK=OFF \
-DWITH_LTO=OFF \ -DWITH_LTO=OFF \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
$@ $root "$@"
cd - cd -
done done
rm -f $build/Makefile rm -f $build/Makefile
@ -51,4 +58,11 @@ for abi in $build_abis; do
echo -ne '$(MAKE) -C ' >> $build/Makefile echo -ne '$(MAKE) -C ' >> $build/Makefile
echo "build-$abi lokinet-android" >> $build/Makefile echo "build-$abi lokinet-android" >> $build/Makefile
echo -ne "\tmkdir -p out/$abi && cp build-$abi/jni/liblokinet-android.so out/$abi/liblokinet-android.so\n\n" >> $build/Makefile echo -ne "\tmkdir -p out/$abi && cp build-$abi/jni/liblokinet-android.so out/$abi/liblokinet-android.so\n\n" >> $build/Makefile
echo -ne "clean-$abi:\n\t" >> $build/Makefile
echo -ne '$(MAKE) -C ' >> $build/Makefile
echo "build-$abi clean" >> $build/Makefile
done done
echo -ne "clean:" >> $build/Makefile
for targ in $build_abis ; do echo -ne " clean-$targ" >> $build/Makefile ; done
echo "" >> $build/Makefile

@ -2,9 +2,7 @@
set -e set -e
set +x set +x
test x$NDK = x && echo "NDK env var not set"
test x$NDK = x && exit 1
root="$(readlink -f $(dirname $0)/../)" root="$(readlink -f $(dirname $0)/../)"
cd "$root" cd "$root"
./contrib/android-configure.sh $@ ./contrib/android-configure.sh . build-android "$@"
make -C build-android -j ${JOBS:-$(nproc)} make -C build-android -j ${JOBS:-$(nproc)}

@ -1 +1 @@
d1:ald1:ci2e1:d3:iwp1:e32:9ãxÚsX«l%ìû€ê<,sØ›•©÷ïå_1:i21:::ffff:144.76.164.2021:pi1666e1:vi0eee1:i5:gamma1:k32:ÞÊðòm=o„ZÐ1ÿßmcŒ%»¸ÿû¾™SĹ1:p32:!EÏâéz˜ý:Š‹úý… /0¡Ú„ Ãݪ„µNçB1:rli0ei0ei8ei3ee1:ui1614788310454e1:vi0e1:xle1:z64:Œ¤u G¿”D“=Œxµ¢{ïÌ51þ`í߀ùEâw m)q2Øg¯±˜øš ï³À)˜TÑP•´ò³ö—Á1e ld1:ald1:ci2e1:d3:iwp1:e32:9ãxÚsX«l%ìû€ê<,sØ›•©÷ïå_1:i21:::ffff:144.76.164.2021:pi1666e1:vi0eee1:i5:gamma1:k32:ÞÊðòm=o„ZÐ1ÿßmcŒ%»¸ÿû¾™SĹ1:p32:!EÏâéz˜ý:Š‹úý… /0¡Ú„ Ãݪ„µNçB1:rli0ei0ei8ei3ee1:ui1614788310454e1:vi0e1:xle1:z64:Œ¤u G¿”D“=Œxµ¢{ïÌ51þ`í߀ùEâw m)q2Øg¯±˜øš ï³À)˜TÑP•´ò³ö—Á1ee

@ -7,7 +7,8 @@ set -o errexit
bad= bad=
if [ "$DRONE_STAGE_OS" == "darwin" ]; then if [ "$DRONE_STAGE_OS" == "darwin" ]; then
if otool -L daemon/lokinet | grep -Ev '^daemon/lokinet:|^\t(/usr/lib/libSystem\.|/usr/lib/libc\+\+\.|/System/Library/Frameworks/CoreFoundation)'; then if otool -L llarp/apple/org.lokinet.network-extension.systemextension/Contents/MacOS/org.lokinet.network-extension | \
grep -Ev '^llarp/apple:|^\t(/usr/lib/lib(System\.|c\+\+|objc))|/System/Library/Frameworks/(CoreFoundation|NetworkExtension|Foundation|Network)\.framework'; then
bad=1 bad=1
fi fi
elif [ "$DRONE_STAGE_OS" == "linux" ]; then elif [ "$DRONE_STAGE_OS" == "linux" ]; then

@ -1,6 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
test "x$IGNORE" != "x" && exit 0 test "x$IGNORE" != "x" && exit 0
. $(dirname $0)/../format-version.sh
repo=$(readlink -e $(dirname $0)/../../) repo=$(readlink -e $(dirname $0)/../../)
clang-format-11 -i $(find $repo/jni $repo/daemon $repo/llarp $repo/include $repo/pybind | grep -E '\.[hc](pp)?$') $CLANG_FORMAT -i $(find $repo/jni $repo/daemon $repo/llarp $repo/include $repo/pybind | grep -E '\.[hc](pp)?$')
jsonnetfmt -i $repo/.drone.jsonnet jsonnetfmt -i $repo/.drone.jsonnet
git --no-pager diff --exit-code --color || (echo -ne '\n\n\e[31;1mLint check failed; please run ./contrib/format.sh\e[0m\n\n' ; exit 1) git --no-pager diff --exit-code --color || (echo -ne '\n\n\e[31;1mLint check failed; please run ./contrib/format.sh\e[0m\n\n' ; exit 1)

@ -19,9 +19,15 @@ set -o xtrace # Don't start tracing until *after* we write the ssh key
chmod 600 ssh_key chmod 600 ssh_key
os="${UPLOAD_OS:-$DRONE_STAGE_OS-$DRONE_STAGE_ARCH}" os="$UPLOAD_OS"
if [ -n "$WINDOWS_BUILD_NAME" ]; then if [ -z "$os" ]; then
os="windows-$WINDOWS_BUILD_NAME" if [ "$DRONE_STAGE_OS" == "darwin" ]; then
os="macos-$DRONE_STAGE_ARCH"
elif [ -n "$WINDOWS_BUILD_NAME" ]; then
os="windows-$WINDOWS_BUILD_NAME"
else
os="$DRONE_STAGE_OS-$DRONE_STAGE_ARCH"
fi
fi fi
if [ -n "$DRONE_TAG" ]; then if [ -n "$DRONE_TAG" ]; then
@ -34,8 +40,11 @@ else
fi fi
mkdir -v "$base" mkdir -v "$base"
if [ -e build-windows ]; then if [ -e build/win32 ]; then
cp -av build-windows/lokinet-*.exe "$base" # save debug symbols
cp -av build/win32/daemon/debug-symbols.tar.xz "$base-debug-symbols.tar.xz"
# save installer
cp -av build/win32/*.exe "$base"
# zipit up yo # zipit up yo
archive="$base.zip" archive="$base.zip"
zip -r "$archive" "$base" zip -r "$archive" "$base"
@ -47,9 +56,13 @@ elif [ -e build-docs ]; then
archive="$base.tar.xz" archive="$base.tar.xz"
cp -av build-docs/docs/mkdocs.yml build-docs/docs/markdown "$base" cp -av build-docs/docs/mkdocs.yml build-docs/docs/markdown "$base"
tar cJvf "$archive" "$base" tar cJvf "$archive" "$base"
elif [ -e build-mac ]; then
archive="$base.tar.xz"
mv build-mac/Lokinet*/ "$base"
tar cJvf "$archive" "$base"
else else
cp -av daemon/lokinet daemon/lokinet-vpn "$base" cp -av build/daemon/lokinet{,-vpn} "$base"
cp -av ../contrib/bootstrap/mainnet.signed "$base/bootstrap.signed" cp -av contrib/bootstrap/mainnet.signed "$base/bootstrap.signed"
# tar dat shiz up yo # tar dat shiz up yo
archive="$base.tar.xz" archive="$base.tar.xz"
tar cJvf "$archive" "$base" tar cJvf "$archive" "$base"
@ -61,6 +74,7 @@ upload_to="oxen.rocks/${DRONE_REPO// /_}/${DRONE_BRANCH// /_}"
# -mkdir a/, -mkdir a/b/, -mkdir a/b/c/, ... commands. The leading `-` allows the command to fail # -mkdir a/, -mkdir a/b/, -mkdir a/b/c/, ... commands. The leading `-` allows the command to fail
# without error. # without error.
upload_dirs=(${upload_to//\// }) upload_dirs=(${upload_to//\// })
put_debug=
mkdirs= mkdirs=
dir_tmp="" dir_tmp=""
for p in "${upload_dirs[@]}"; do for p in "${upload_dirs[@]}"; do
@ -68,10 +82,13 @@ for p in "${upload_dirs[@]}"; do
mkdirs="$mkdirs mkdirs="$mkdirs
-mkdir $dir_tmp" -mkdir $dir_tmp"
done done
if [ -e "$base-debug-symbols.tar.xz" ] ; then
put_debug="put $base-debug-symbols.tar.xz $upload_to"
fi
sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks <<SFTP sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks <<SFTP
$mkdirs $mkdirs
put $archive $upload_to put $archive $upload_to
$put_debug
SFTP SFTP
set +o xtrace set +o xtrace

@ -0,0 +1,2 @@
[logging]
level=debug

@ -0,0 +1,5 @@
#
# "suggested" default exit node config
#
[network]
exit-node=exit.loki

@ -0,0 +1,5 @@
#
# persist .loki address in a private key file in the data dir
#
[network]
keyfile=lokinet-addr.privkey

@ -15,27 +15,13 @@ root="$(readlink -e $(dirname $0)/../)"
cd $root cd $root
mkdir -p build-cross mkdir -p build-cross
cmake_opts="-DBUILD_STATIC_DEPS=ON \
-DSTATIC_LINK=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=OFF \
-DWITH_BOOTSTRAP=OFF \
-DCMAKE_BUILD_TYPE=RelWithDeb"
targets=() targets=()
cmake_extra=()
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
if [ "$1" = "--" ]; then if [ "$1" = "--" ]; then
shift shift
cmake_opts=$@ cmake_extra=("$@")
break break
fi fi
targets+=("$1") targets+=("$1")
@ -55,7 +41,21 @@ for arch in $archs ; do
-DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \ -DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \
-DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \ -DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \
-DCMAKE_TOOLCHAIN_FILE=$root/contrib/cross/cross.toolchain.cmake \ -DCMAKE_TOOLCHAIN_FILE=$root/contrib/cross/cross.toolchain.cmake \
$cmake_opts \ -DBUILD_STATIC_DEPS=ON \
-DSTATIC_LINK=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=OFF \
-DWITH_BOOTSTRAP=OFF \
-DCMAKE_BUILD_TYPE=RelWithDeb \
"${cmake_extra[@]}" \
$root $root
cd $root/build-cross cd $root/build-cross
echo -ne "$arch:\n\t\$(MAKE) -C build-$arch\n" >> $root/build-cross/Makefile echo -ne "$arch:\n\t\$(MAKE) -C build-$arch\n" >> $root/build-cross/Makefile

@ -1,4 +1,8 @@
set(CMAKE_SYSTEM_VERSION 5.0) set(CMAKE_SYSTEM_VERSION 6.0)
# the minimum windows version, set to 6 rn because supporting older windows is hell
set(_winver 0x0600)
add_definitions(-D_WIN32_WINNT=${_winver})
# target environment on the build host system # target environment on the build host system
# second one is for non-root installs # second one is for non-root installs

@ -0,0 +1,19 @@
CLANG_FORMAT_DESIRED_VERSION=14
CLANG_FORMAT=$(command -v clang-format-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null)
if [ $? -ne 0 ]; then
CLANG_FORMAT=$(command -v clang-format-mp-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null)
fi
if [ $? -ne 0 ]; then
CLANG_FORMAT=$(command -v clang-format 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Please install clang-format version $CLANG_FORMAT_DESIRED_VERSION and re-run this script."
exit 1
fi
version=$(clang-format --version)
if [[ ! $version == *"clang-format version $CLANG_FORMAT_DESIRED_VERSION"* ]]; then
echo "Please install clang-format version $CLANG_FORMAT_DESIRED_VERSION and re-run this script."
exit 1
fi
fi

@ -1,43 +1,27 @@
#!/usr/bin/env bash #!/usr/bin/env bash
CLANG_FORMAT_DESIRED_VERSION=11 . $(dirname $0)/format-version.sh
binary=$(command -v clang-format-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null)
if [ $? -ne 0 ]; then
binary=$(command -v clang-format-mp-$CLANG_FORMAT_DESIRED_VERSION 2>/dev/null)
fi
if [ $? -ne 0 ]; then
binary=$(command -v clang-format 2>/dev/null)
if [ $? -ne 0 ]; then
echo "Please install clang-format version $CLANG_FORMAT_DESIRED_VERSION and re-run this script."
exit 1
fi
version=$(clang-format --version)
if [[ ! $version == *"clang-format version $CLANG_FORMAT_DESIRED_VERSION"* ]]; then
echo "Please install clang-format version $CLANG_FORMAT_DESIRED_VERSION and re-run this script."
exit 1
fi
fi
cd "$(dirname $0)/../" cd "$(dirname $0)/../"
if [ "$1" = "verify" ] ; then if [ "$1" = "verify" ] ; then
if [ $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|mm?)$' | grep -v '\#') | grep '</replacement>' | wc -l) -ne 0 ] ; then if [ $($CLANG_FORMAT --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|m(m)?)$' | grep -v '#') | grep '</replacement>' | wc -l) -ne 0 ] ; then
exit 2 exit 2
fi fi
else else
$binary -i $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|mm)$' | grep -v '\#') &> /dev/null $CLANG_FORMAT -i $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|m(m)?)$' | grep -v '#') &> /dev/null
fi fi
swift_format=$(command -v swiftformat 2>/dev/null) swift_format=$(command -v swiftformat 2>/dev/null)
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
if [ "$1" = "verify" ] ; then if [ "$1" = "verify" ] ; then
for f in $(find daemon | grep -E '\.swift$' | grep -v '\#') ; do for f in $(find daemon | grep -E '\.swift$' | grep -v '#') ; do
if [ $($swift_format --quiet --dryrun < "$f" | diff "$f" - | wc -l) -ne 0 ] ; then if [ $($swift_format --quiet --dryrun < "$f" | diff "$f" - | wc -l) -ne 0 ] ; then
exit 3 exit 3
fi fi
done done
else else
$swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '\#') $swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '#')
fi fi
fi fi

@ -0,0 +1,26 @@
#!/usr/bin/env python3
#
# .loki secret key generator script
# makes keyfile contents
#
# usage: python3 keygen.py out.private
# python3 keygen.py > /some/where/over/the/rainbow
#
from nacl.bindings import crypto_sign_keypair
import sys
out = sys.stdout
close_out = lambda : None
args = sys.argv[1:]
if args and args[0] != '-':
out = open(args[0], 'wb')
close_out = out.close
pk, sk = crypto_sign_keypair()
out.write(b'64:')
out.write(sk)
out.flush()
close_out()

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- our size/viewbox is positioned such that 0,0 is the center of the image (to simplify scaling and rotation). -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="-512px" y="-512px"
viewBox="-512 -512 1024 1024" style="enable-background:new -512 -512 1024 1024;" xml:space="preserve">
<style type="text/css">
.bg{fill:#FFFFFF;}
</style>
<!--
Draw the background shape in a 2x2 box (from -1 to 1 in each dimension), then scale it up
(but not all the way to 512, because we want some padding around the outside.
-->
<g transform="scale(415)">
<path class="bg" d="
M 0.5 1
H -0.5
C -0.81,1 -1,0.81 -1,0.5
V -0.5
C -1,-0.81 -0.81,-1 -0.5,-1
H 0.5
C 0.81,-1 1,-0.81 1,-0.5
V 0.5
C 1,0.81 0.81,1 0.5,1
z
"/>
</g>
<g id="shape0">
<!--
Start with a simple 3x2 shape, where each unit we draw corresponds to 1 block edge length in the
final diagram, and shift it so that 2.5x2.5 becomes the new origin (around which we will rotate).
Then we rotate and scale it to the desired size.
We can then copy that at 90, 180, 270 degree rotations to complete the logo.
-->
<g transform="rotate(45) scale(85) translate(-2.5, -2.5)">
<polygon points="0,0 2,0 2,1 1,1 1,2 0,2"/>
<rect x="1" y="2" width="1" height="1"/>
</g>
</g>
<use xlink:href="#shape0" transform="rotate(90)"/>
<use xlink:href="#shape0" transform="rotate(180)"/>
<use xlink:href="#shape0" transform="rotate(270)"/>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -1,21 +1,34 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" <!-- our size/viewbox is positioned such that 0,0 is the center of the image (to simplify scaling and rotation). -->
viewBox="0 0 189.4 189.4" style="enable-background:new 0 0 189.4 189.4;" xml:space="preserve"> <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="-512px" y="-512px"
viewBox="-512 -512 1024 1024" style="enable-background:new -512 -512 1024 1024;" xml:space="preserve">
<style type="text/css"> <style type="text/css">
.st0{fill:#FFFFFF;} .bg{fill:#FFFFFF;}
</style> </style>
<g>
<polygon class="st0" points="113.6,132.6 94.7,151.5 75.8,132.6 56.8,151.5 94.7,189.4 132.6,151.5 "/> <!--
<polygon class="st0" points="132.6,113.6 151.5,94.7 132.6,75.8 151.5,56.8 189.4,94.7 151.5,132.6 "/> Draw the background shape in a 2x2 box (from -1 to 1 in each dimension), then scale it up
<polygon class="st0" points="56.8,75.8 37.9,94.7 56.8,113.6 37.9,132.6 0,94.7 37.9,56.8 "/> (but not all the way to 512, because we want some padding around the outside.
<polygon class="st0" points="75.8,56.8 94.7,37.9 113.6,56.8 132.6,37.9 94.7,0 56.8,37.9 "/> -->
<g transform="scale(512)">
<rect x="100.2" y="100.2" transform="matrix(0.7071 0.7071 -0.7071 0.7071 113.6329 -47.0683)" class="st0" width="26.8" height="26.8"/> <circle r="1" class="bg"/>
<rect x="62.4" y="62.4" transform="matrix(0.7071 0.7071 -0.7071 0.7071 75.7552 -31.3789)" class="st0" width="26.8" height="26.8"/>
<rect x="100.2" y="62.4" transform="matrix(0.7071 0.7071 -0.7071 0.7071 86.8493 -58.1624)" class="st0" width="26.8" height="26.8"/>
<rect x="62.4" y="100.2" transform="matrix(0.7071 0.7071 -0.7071 0.7071 102.5388 -20.2848)" class="st0" width="26.8" height="26.8"/>
</g> </g>
<g id="shape0">
<!--
Start with a simple 3x2 shape, where each unit we draw corresponds to 1 block edge length in the
final diagram, and shift it so that 2.5x2.5 becomes the new origin (around which we will rotate).
Then we rotate and scale it to the desired size.
We can then copy that at 90, 180, 270 degree rotations to complete the logo.
-->
<g transform="rotate(45) scale(105) translate(-2.5, -2.5)">
<polygon points="0,0 2,0 2,1 1,1 1,2 0,2"/>
<rect x="1" y="2" width="1" height="1"/>
</g>
</g>
<use xlink:href="#shape0" transform="rotate(90)"/>
<use xlink:href="#shape0" transform="rotate(180)"/>
<use xlink:href="#shape0" transform="rotate(270)"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

@ -0,0 +1,28 @@
#!/bin/bash
set -e
set -x
if ! [ -f LICENSE ] || ! [ -d llarp ]; then
echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory" >&2
exit 1
fi
mkdir -p build-mac
cd build-mac
cmake \
-G Ninja \
-DBUILD_STATIC_DEPS=ON \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \
-DWITH_LTO=ON \
-DCMAKE_BUILD_TYPE=Release \
-DMACOS_SYSTEM_EXTENSION=ON \
-DCODESIGN=ON \
-DBUILD_PACKAGE=ON \
"$@" \
..
echo "cmake build configured in build-mac"

@ -8,32 +8,20 @@
# #
set -e set -e
set +x set -x
if ! [ -f LICENSE ] || ! [ -d llarp ]; then if ! [ -f LICENSE ] || ! [ -d llarp ]; then
echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory" echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory" >&2
exit 1
fi fi
mkdir -p build-mac ./contrib/mac-configure.sh "$@"
cd build-mac cd build-mac
cmake \ rm -rf Lokinet\ *
-G Ninja \ ninja -j${JOBS:-1} package
-DBUILD_STATIC_DEPS=ON \ cd ..
-DBUILD_PACKAGE=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=ON \
-DCMAKE_BUILD_TYPE=Release \
"$@" \
..
ninja sign
echo -e "Build complete, your app is here:\n" echo -e "Build complete, your app is here:\n"
ls -lad $(pwd)/daemon/lokinet.app ls -lad $(pwd)/build-mac/Lokinet\ *
echo "" echo ""

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Lokinet</string>
<key>CFBundleExecutable</key>
<string>MacOS/lokinet</string>
<key>CFBundleIdentifier</key>
<string>com.loki-project.lokinet</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>lokinet</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>@lokinet_VERSION@</string>
<key>CFBundleVersion</key>
<string>@lokinet_VERSION@.@LOKINET_APPLE_BUILD@</string>
</dict>
</plist>

Binary file not shown.

@ -1,40 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>Lokinet</string>
<key>CFBundleExecutable</key>
<string>lokinet-extension</string>
<key>CFBundleIdentifier</key>
<string>com.loki-project.lokinet.network-extension</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleName</key>
<string>lokinet</string>
<key>CFBundleVersion</key>
<string>@lokinet_VERSION@</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.networkextension.packet-tunnel</string>
<key>NSExtensionPrincipalClass</key>
<string>LLARPPacketTunnel</string>
</dict>
</dict>
</plist>

@ -1,38 +0,0 @@
This directory contains the magical incantations and random voodoo symbols needed to coax an Apple
build. There's no reason builds have to be this stupid, except that Apple wants to funnel everyone
into the no-CI, no-help, undocumented, non-toy-apps-need-not-apply modern Apple culture.
This is disgusting.
But it gets worse.
The following two files, in particular, are the very worst manifestations of this already toxic
Apple cancer: they are required for proper permissions to run on macOS, are undocumented, and can
only be regenerated through the entirely closed source Apple Developer backend, for which you have
to pay money first to get a team account (a personal account will not work), and they lock the
resulting binaries to only run on individually selected Apple computers selected at the time the
profile is provisioned (with no ability to allow it to run anywhere).
lokinet.provisionprofile
lokinet-extension.provisionprofile
This is actively hostile to open source development, but that is nothing new for Apple.
In order to make things work, you'll have to replace these provisioning profiles with your own
(after paying Apple for the privilege of developing on their platform, of course) and change all the
team/application/bundle IDs to reference your own team, matching the provisioning profiles. The
provisioning profiles must be a "macOS Development" provisioning profile, and must include the
signing keys and the authorized devices on which you want to run it. (The profiles bundled in this
repository contains the lokinet team's "Apple Development" keys associated with the Oxen project,
and mac dev boxes. This is *useless* for anyone else).
Also take note that you *must not* put a development build `lokinet.app` inside /Applications
because if you do, it won't work because *on top* of the ridiculous signing and entitlement bullshit
that Apple makes you jump through, the rules *also* differ for binaries placed in /Applications
versus binaries placed elsewhere, but like everything else here, it is entirely undocumented.
If you are reading this to try to build Lokinet for yourself for an Apple operating system and
simultaneously care about open source, privacy, or freedom then you, my friend, are a walking
contradiction: you are trying to get Lokinet to work on a platform that actively despises open
source, privacy, and freedom. Even Windows is a better choice in all of these categories than
Apple.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

@ -0,0 +1,64 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleDisplayName</key>
<string>Lokinet Network Extension</string>
<key>CFBundleExecutable</key>
<string>org.lokinet.network-extension</string>
<key>CFBundleIdentifier</key>
<string>org.lokinet.network-extension</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>SYSX</string>
<key>CFBundleName</key>
<string>org.lokinet.network-extension</string>
<key>CFBundleVersion</key>
<string>@lokinet_VERSION@.@LOKINET_APPLE_BUILD@</string>
<key>CFBundleShortVersionString</key>
<string>@lokinet_VERSION@</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2022 The Oxen Project, licensed under GPLv3-or-later</string>
<key>NSSystemExtensionUsageDescription</key>
<string>Provides Lokinet Network connectivity.</string>
<key>NetworkExtension</key>
<dict>
<key>NEMachServiceName</key>
<string>SUQ8J2PCT7.org.lokinet.network-extension</string>
<key>NEProviderClasses</key>
<dict>
<key>com.apple.networkextension.packet-tunnel</key>
<string>LLARPPacketTunnel</string>
<key>com.apple.networkextension.dns-proxy</key>
<string>LLARPDNSProxy</string>
</dict>
</dict>
</dict>
</plist>

@ -3,11 +3,12 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.application-identifier</key> <key>com.apple.application-identifier</key>
<string>SUQ8J2PCT7.com.loki-project.lokinet.network-extension</string> <string>SUQ8J2PCT7.org.lokinet.network-extension</string>
<key>com.apple.developer.networking.networkextension</key> <key>com.apple.developer.networking.networkextension</key>
<array> <array>
<string>packet-tunnel-provider</string> <string>packet-tunnel-provider</string>
<string>dns-proxy</string>
</array> </array>
<key>com.apple.developer.team-identifier</key> <key>com.apple.developer.team-identifier</key>
@ -16,9 +17,6 @@
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.network.client</key> <key>com.apple.security.network.client</key>
<true/> <true/>

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string>SUQ8J2PCT7.org.lokinet.network-extension</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider-systemextension</string>
<string>dns-proxy-systemextension</string>
</array>
<key>com.apple.developer.team-identifier</key>
<string>SUQ8J2PCT7</string>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>SUQ8J2PCT7.org.lokinet</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>Lokinet</string>
<key>CFBundleIdentifier</key>
<string>org.lokinet</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Lokinet</string>
<key>CFBundleIconFile</key>
<string>icon.icns</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>@lokinet_VERSION@</string>
<key>CFBundleVersion</key>
<string>@lokinet_VERSION@.@LOKINET_APPLE_BUILD@</string>
<key>LSMinimumSystemVersion</key>
<string>10.15</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2022 The Oxen Project, licensed under GPLv3-or-later</string>
<key>LSUIElement</key>
<true/>
<key>LSHasLocalizedDisplayName</key>
<true/>
</dict>
</plist>

@ -3,13 +3,13 @@
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.application-identifier</key> <key>com.apple.application-identifier</key>
<string>SUQ8J2PCT7.com.loki-project.lokinet</string> <string>SUQ8J2PCT7.org.lokinet</string>
<key>com.apple.developer.networking.networkextension</key> <key>com.apple.developer.networking.networkextension</key>
<array> <array>
<string>packet-tunnel-provider</string> <string>packet-tunnel-provider</string>
<string>dns-proxy</string> <string>dns-proxy</string>
<string>dns-settings</string> <string>dns-settings</string>
</array> </array>
<key>com.apple.developer.team-identifier</key> <key>com.apple.developer.team-identifier</key>
@ -18,13 +18,10 @@
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.get-task-allow</key> <key>com.apple.security.network.client</key>
<true/> <true/>
<key>com.apple.security.network.client</key> <key>com.apple.security.network.server</key>
<true/>
<key>com.apple.security.network.server</key>
<true/> <true/>
</dict> </dict>

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.application-identifier</key>
<string>SUQ8J2PCT7.org.lokinet</string>
<key>com.apple.developer.networking.networkextension</key>
<array>
<string>packet-tunnel-provider-systemextension</string>
<string>dns-proxy-systemextension</string>
<string>dns-settings</string>
</array>
<key>com.apple.developer.team-identifier</key>
<string>SUQ8J2PCT7</string>
<key>com.apple.developer.system-extension.install</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.application-groups</key>
<array>
<string>SUQ8J2PCT7.org.lokinet</string>
</array>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<true/>
</dict>
</plist>

@ -1,45 +0,0 @@
#!/bin/sh
scutil_query()
{
key=$1
scutil<<EOT
open
get $key
d.show
close
EOT
}
SERVICE_GUID=`scutil_query State:/Network/Global/IPv4 \
| grep "PrimaryService" \
| awk '{print $3}'`
SERVICE_NAME=`scutil_query Setup:/Network/Service/$SERVICE_GUID \
| grep "UserDefinedName" \
| awk -F': ' '{print $2}'`
OLD_SERVERS="$(networksetup -getdnsservers "$SERVICE_NAME" \
| tr '\n' ' ' \
| sed 's/ $//')"
# <3 Apple
#
# if there were no explicit DNS servers, this will return:
# "There aren't any DNS Servers set on Ethernet."
# This might be internationalized, so we'll suffice it to see if there's a space
pattern=" |'"
if [[ $OLD_SERVERS =~ $pattern ]]
then
# and when there aren't any explicit servers set, we want to pass the literal
# string "empty"
OLD_SERVERS="empty"
fi
networksetup -setdnsservers "$SERVICE_NAME" 127.0.0.1
trap "networksetup -setdnsservers \"$SERVICE_NAME\" $OLD_SERVERS" INT TERM EXIT
/opt/lokinet/bin/lokinet /var/lib/lokinet/lokinet.ini

@ -1,53 +0,0 @@
#!/bin/sh
set -x
test `whoami` == root || exit 1
# this is for dns tomfoolery
scutil_query()
{
key=$1
scutil<<EOT
open
get $key
d.show
close
EOT
}
# get guid for service
SERVICE_GUID=`scutil_query State:/Network/Global/IPv4 \
| grep "PrimaryService" \
| awk '{print $3}'`
# get name of network service
SERVICE_NAME=`scutil_query Setup:/Network/Service/$SERVICE_GUID \
| grep "UserDefinedName" \
| awk -F': ' '{print $2}'`
# tell dns to be "empty" so that it's reset
networksetup -setdnsservers "$SERVICE_NAME" empty
# Prevent restarting on exit
touch /var/lib/lokinet/suspend-launchd-service
# shut off lokinet gracefully
pgrep lokinet$ && /opt/lokinet/bin/lokinet-vpn --kill
# kill the gui and such
killall LokinetGUI
killall lokinet
# if the launch daemon is there kill it
/bin/launchctl stop network.loki.lokinet.daemon
/bin/launchctl unload /Library/LaunchDaemons/network.loki.lokinet.daemon.plist
# kill it and make sure it's dead
killall -9 lokinet
rm -rf /Library/LaunchDaemons/network.loki.lokinet.daemon.plist
rm -rf /Applications/Lokinet/
rm -rf /Applications/LokinetGUI.app
rm -rf /var/lib/lokinet
rm -rf /usr/local/lokinet/
rm -rf /opt/lokinet
rm -f /etc/newsyslog.d/lokinet.conf

@ -7,14 +7,73 @@ outdir="${out/%.icns/.iconset}"
set -e set -e
# Apple's PNG encoding/decoding is buggy and likes to inject yellow lines, particularly for the
# smaller images. This is apparently a known issue since macOS 11 that apple just doesn't give a
# shit about fixing (https://en.wikipedia.org/wiki/Apple_Icon_Image_format#Known_issues).
#
# So moral of the story: we have to arse around and edit the png to put a tranparent pixel in the
# bottom-left corner but that pixel *must* be different from the preceeding color, otherwise Apple's
# garbage breaks exposing the dumpster fire that lies beneath and drops the blue channel from the
# last pixel (or run of pixels, if they are the same color (ignoring transparency). So, just to be
# consistent, we make *all* 4 corners transparent yellow, because it seems unlikely for our logo to
# have full-on yellow in the corner, and the color itself is irrelevant because it is fully
# transparent.
#
# Why is there so much broken, buggy crap in the macOS core???
no_r_kelly() {
size=$1
last=$((size - 1))
for x in 0 $last; do
for y in 0 $last; do
echo -n "color $x,$y point "
done
done
}
mkdir -p "${outdir}" mkdir -p "${outdir}"
for size in 16 32 64 128 256 512 1024; do for size in 32 64 128 256 512 1024; do
convert -background none -resize "${size}x${size}" "$svg" -strip "png32:${outdir}/icon_${size}x${size}.png" # Yay Apple thanks for this utter trash OS.
last=$((size - 1))
convert -background none -resize "${size}x${size}" "$svg" \
-fill '#ff00' -draw "$(no_r_kelly $size)" \
-strip "png32:${outdir}/icon_${size}x${size}.png"
done done
# Outputs the imagemagick -draw command to color the corner-adjacent pixels as half-transparent
# white. We use this for the 16x16 (the others pick up corner transparency from the svg).
semitransparent_off_corners() {
size=$1
for x in 1 $((size - 2)); do
for y in 0 $((size - 1)); do
echo -n "color $x,$y point "
done
done
for x in 0 $((size -1)); do
for y in 1 $((size - 2)); do
echo -n "color $x,$y point "
done
done
}
# For 16x16 we crop the image to 5/8 of its regular size before resizing which effectively zooms in
# on it a bit because if we resize the full icon it ends up a fuzzy mess, while the crop and resize
# lets us retain some detail of the logo. (We don't do this for the 16x16@2x because that is really
# 32x32 where it retains enough detail).
convert -background none -resize 512x512 "$svg" -gravity Center -extent 320x320 -resize 16x16 \
-fill '#ff00' -draw "$(no_r_kelly 16)" \
-fill '#fff8' -draw "$(semitransparent_off_corners 16)" \
-strip "png32:$outdir/icon_16x16.png"
# Create all the "@2x" versions which are just the double-size versions
rm -f "${outdir}/icon_*@2x.png"
mv "${outdir}/icon_1024x1024.png" "${outdir}/icon_512x512@2x.png" mv "${outdir}/icon_1024x1024.png" "${outdir}/icon_512x512@2x.png"
for size in 16 32 128 256; do for size in 16 32 128 256; do
double=$((size * 2)) double=$((size * 2))
cp "${outdir}/icon_${double}x${double}.png" "${outdir}/icon_${size}x${size}@2x.png" ln -f "${outdir}/icon_${double}x${double}.png" "${outdir}/icon_${size}x${size}@2x.png"
done done
iconutil -c icns "${outdir}" iconutil -c icns "${outdir}"

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>network.loki.lokinet.daemon</string>
<key>ProgramArguments</key>
<array>
<string>/var/lib/lokinet/lokinet_macos_daemon_script.sh</string>
</array>
<!-- Keep Lokinet alive unless magic file exists -->
<key>KeepAlive</key>
<dict>
<key>PathState</key>
<dict>
<key>/var/lib/lokinet/suspend-launchd-service</key>
<false/>
</dict>
</dict>
<key>StandardOutPath</key>
<string>/var/log/lokinet.log</string>
</dict>
</plist>

@ -4,21 +4,47 @@ import sys
import plistlib import plistlib
import subprocess import subprocess
import time import time
import os
import os.path
def bold_red(x):
return "\x1b[31;1m" + x + "\x1b[0m"
if not @notarize_py_is_sysext@:
print(bold_red("\nUnable to notarize: this lokinet is not built as a system extension\n"), file=sys.stderr)
sys.exit(1)
if not all(("@MACOS_NOTARIZE_USER@", "@MACOS_NOTARIZE_PASS@", "@MACOS_NOTARIZE_ASC@")):
print(bold_red("\nUnable to notarize: one or more required notarization variable not set; see contrib/macos/README.txt\n") +
" Called with -DMACOS_NOTARIZE_USER=@MACOS_NOTARIZE_USER@\n"
" -DMACOS_NOTARIZE_PASS=@MACOS_NOTARIZE_PASS@\n"
" -DMACOS_NOTARIZE_ASC=@MACOS_NOTARIZE_ASC@\n",
file=sys.stderr)
sys.exit(1)
os.chdir("@PROJECT_BINARY_DIR@")
app = "@lokinet_app@"
zipfile = f"Lokinet.app.notarize.zip"
print(f"Creating {zipfile} from {app}")
if os.path.exists(zipfile):
os.remove(zipfile)
subprocess.run(['ditto', '-v', '-c', '-k', '--sequesterRsrc', '--keepParent', app, zipfile])
pkg = "lokinet-@PROJECT_VERSION@-Darwin.pkg"
userpass = ('--username', "@MACOS_NOTARIZE_USER@", '--password', "@MACOS_NOTARIZE_PASS@") userpass = ('--username', "@MACOS_NOTARIZE_USER@", '--password', "@MACOS_NOTARIZE_PASS@")
print("Submitting {} for notarization; this may take a minute...".format(pkg)) print("Submitting {} for notarization; this may take a minute...".format(zipfile))
started = time.time() started = time.time()
result = subprocess.run([ command = [
'xcrun', 'altool', 'xcrun', 'altool',
'--notarize-app', '--notarize-app',
'--primary-bundle-id', 'org.lokinet.lokinet.pkg.@PROJECT_VERSION@', '--primary-bundle-id', 'org.lokinet.@PROJECT_VERSION@',
*userpass, *userpass,
'--asc-provider', "@MACOS_NOTARIZE_ASC@", '--asc-provider', "@MACOS_NOTARIZE_ASC@",
'--file', pkg, '--file', zipfile,
'--output-format', 'xml' '--output-format', 'xml'
], stdout=subprocess.PIPE) ]
print(command)
result = subprocess.run(command, stdout=subprocess.PIPE)
data = plistlib.loads(result.stdout) data = plistlib.loads(result.stdout)
if 'success-message' not in data or 'notarization-upload' not in data or 'RequestUUID' not in data['notarization-upload']: if 'success-message' not in data or 'notarization-upload' not in data or 'RequestUUID' not in data['notarization-upload']:
@ -42,24 +68,27 @@ while not done:
'--notarization-info', uuid, '--notarization-info', uuid,
*userpass, *userpass,
'--output-format', 'xml' '--output-format', 'xml'
], stdout=subprocess.PIPE) ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
result.check_returncode() if result.returncode == 1 and b'Gateway Timeout' in result.stderr:
data = plistlib.loads(result.stdout) status = "Apple's servers are trash (aka Gateway Timeout)"
if 'notarization-info' not in data or 'Status' not in data['notarization-info']:
status = 'Request failed'
else: else:
status = data['notarization-info']['Status Message'] if 'Status Message' in data['notarization-info'] else '' result.check_returncode()
st = data['notarization-info']['Status'] data = plistlib.loads(result.stdout)
if st == 'success': if 'notarization-info' not in data or 'Status' not in data['notarization-info']:
success = True status = 'Request failed'
done = True else:
elif st == 'invalid': status = data['notarization-info']['Status Message'] if 'Status Message' in data['notarization-info'] else ''
done = True st = data['notarization-info']['Status']
elif st == 'in progress' and len(status) == 0: if st == 'success':
status = 'Notarization in progress' success = True
done = True
if done and 'LogFileURL' in data['notarization-info']: elif st == 'invalid':
status += '\n\nlog file: {}'.format(data['notarization-info']['LogFileURL']) done = True
elif st == 'in progress' and len(status) == 0:
status = 'Notarization in progress'
if done and 'LogFileURL' in data['notarization-info']:
status += '\n\nlog file: {}'.format(data['notarization-info']['LogFileURL'])
elapsed = time.time() - started_waiting elapsed = time.time() - started_waiting
mins, secs = int(elapsed // 60), int(elapsed % 60) mins, secs = int(elapsed // 60), int(elapsed % 60)
@ -70,7 +99,15 @@ print("\n")
if not success: if not success:
sys.exit(42) sys.exit(42)
print("Stapling {}".format(pkg)) if os.path.exists(zipfile):
result = subprocess.run(['xcrun', 'stapler', 'staple', pkg]) os.remove(zipfile)
print("Stapling {}...".format(app), end='')
result = subprocess.run(['xcrun', 'stapler', 'staple', app])
result.check_returncode() result.check_returncode()
with open("macos-notarized.stamp", 'w'):
pass
print(" success.\n")

@ -1,38 +0,0 @@
#!/bin/sh
PERMS_OWNER=root
PERMS_GROUP=admin
CHOWN=$PERMS_OWNER:$PERMS_GROUP
# set up lokinet data dir
[ -e /var/lib/lokinet/ ] || mkdir /var/lib/lokinet
chown $CHOWN /var/lib/lokinet
chmod g+w /var/lib/lokinet
# mv files copied into $INSTALL_PREFIX/extra/ to their proper locations
mv /opt/lokinet/extra/lokinet_macos_daemon_script.sh /var/lib/lokinet
chown $CHOWN /var/lib/lokinet/lokinet_macos_daemon_script.sh
chmod 770 /var/lib/lokinet/lokinet_macos_daemon_script.sh
mv /opt/lokinet/extra/network.loki.lokinet.daemon.plist /Library/LaunchDaemons/
chown $CHOWN /Library/LaunchDaemons/network.loki.lokinet.daemon.plist
chmod 640 /Library/LaunchDaemons/network.loki.lokinet.daemon.plist
mv /opt/lokinet/extra/lokinet-newsyslog.conf /etc/newsyslog.d/lokinet.conf
chown $CHOWN /etc/newsyslog.d/lokinet.conf
chmod 640 /etc/newsyslog.d/lokinet.conf
# clean up by removing 'extra/' (so long as it's empty)
rmdir /opt/lokinet/extra/
# bootstrap
/opt/lokinet/bin/lokinet-bootstrap mainnet /var/lib/lokinet/bootstrap.signed
chown $CHOWN /var/lib/lokinet/bootstrap.signed
# generate configs
/opt/lokinet/bin/lokinet -g /var/lib/lokinet/lokinet.ini
chown $CHOWN /var/lib/lokinet/lokinet.ini
# register with launchd and start
launchctl load /Library/LaunchDaemons/network.loki.lokinet.daemon.plist
launchctl start network.loki.lokinet.daemon

@ -1,46 +0,0 @@
#!/bin/sh
# this is for dns tomfoolery
scutil_query()
{
key=$1
scutil<<EOT
open
get $key
d.show
close
EOT
}
# get guid for service
SERVICE_GUID=`scutil_query State:/Network/Global/IPv4 \
| grep "PrimaryService" \
| awk '{print $3}'`
# get name of network service
SERVICE_NAME=`scutil_query Setup:/Network/Service/$SERVICE_GUID \
| grep "UserDefinedName" \
| awk -F': ' '{print $2}'`
# tell dns to be "empty" so that it's reset
networksetup -setdnsservers "$SERVICE_NAME" empty
# suspend existing lokinet if it's there
[ -e /var/lib/lokinet ] && touch /var/lib/lokinet/suspend-launchd-service
# kill it
killall lokinet || true
# wait a sec
sleep 1
# make sure it's fucking dead
killall -9 lokinet || true
# check for prexisting lokinet and kill it if it's there
[ -e /Library/LaunchDaemons/network.loki.lokinet.daemon.plist ] && (
launchctl stop network.loki.lokinet.daemon ;
launchctl unload /Library/LaunchDaemons/network.loki.lokinet.daemon.plist ;
rm -rf /Library/LaunchDaemons/network.loki.lokinet.daemon.plist
)
# clear out the install dir beforehand
rm -rf /opt/lokinet

@ -0,0 +1,26 @@
import Foundation
import AppKit
// Apple deprecated their command line tools to set images on things and replaced them with a
// barely-documented swift function. Yay!
// Usage: ./seticon /path/to/my.icns /path/to/some.dmg
let args = CommandLine.arguments
if args.count != 3 {
print("Error: usage: ./seticon /path/to/my.icns /path/to/some.dmg")
exit(1)
}
var icns = args[1]
var dmg = args[2]
var img = NSImage(byReferencingFile: icns)!
if NSWorkspace.shared.setIcon(img, forFile: dmg) {
print("Set \(dmg) icon to \(icns) [\(img.size)]")
} else {
print("Setting icon failed, don't know why")
exit(2)
}

@ -1,10 +1,74 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
codesign --verbose=4 --force -s "@CODESIGN_APPEX@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet-extension.entitlements.plist" \ if [ "@CODESIGN@" != "ON" ]; then
--deep --strict --timestamp --options=runtime "@SIGN_TARGET@/Contents/PlugIns/lokinet-extension.appex" echo "Cannot codesign: this build was not configured with codesigning" >&2
for file in "@SIGN_TARGET@/Contents/MacOS/lokinet" "@SIGN_TARGET@" ; do exit 1
codesign --verbose=4 --force -s "@CODESIGN_APP@" \ fi
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet.entitlements.plist" \
--deep --strict --timestamp --options=runtime "$file" signit() {
target="$1"
entitlements="$2"
echo -e "\n\e[33;1mSigning ${target/*\/Lokinet.app/Lokinet.app}...\e[0m" >&2
codesign \
--verbose=4 \
--force \
-s "@CODESIGN_ID@" \
--entitlements "$entitlements" \
--strict \
--timestamp \
--options=runtime \
"$target"
}
gui_entitlements="@PROJECT_SOURCE_DIR@/gui/node_modules/app-builder-lib/templates/entitlements.mac.plist"
ext_entitlements="@PROJECT_SOURCE_DIR@/contrib/macos/lokinet-extension.@LOKINET_ENTITLEMENTS_TYPE@.entitlements.plist"
app_entitlements="@PROJECT_SOURCE_DIR@/contrib/macos/lokinet.@LOKINET_ENTITLEMENTS_TYPE@.entitlements.plist"
SIGN_TARGET="@PROJECT_BINARY_DIR@/Lokinet @PROJECT_VERSION@/Lokinet.app"
for ext in systemextension appex; do
netext="$SIGN_TARGET/@lokinet_ext_dir@/org.lokinet.network-extension.$ext"
if [ -e "$netext" ]; then
signit "$netext" "$ext_entitlements"
fi
done done
if [ "@BUILD_GUI@" == "ON" ]; then
gui_app="$SIGN_TARGET"/Contents/Helpers/Lokinet-GUI.app
gui_sign_targets=()
for bundle in \
"$gui_app"/Contents/Frameworks/*.framework \
"$gui_app"/Contents/Frameworks/*.app
do
if [ -d "$bundle/Libraries" ]; then
gui_sign_targets+=("$bundle"/Libraries/*.dylib)
fi
if [ -d "$bundle/Helpers" ]; then
gui_sign_targets+=("$bundle"/Helpers/*)
fi
if [ -d "$bundle/Resources" ]; then
for f in "$bundle/Resources"/*; do
if [[ -f "$f" && -x "$f" && "$(file -b "$f")" == Mach-O* ]]; then
gui_sign_targets+=("$f")
fi
done
fi
gui_sign_targets+=("$bundle")
done
gui_sign_targets+=("$gui_app")
for target in "${gui_sign_targets[@]}"; do
signit "$target" "$gui_entitlements"
done
signit "$SIGN_TARGET"/Contents/MacOS/Lokinet "$app_entitlements"
fi
signit "$SIGN_TARGET" "$app_entitlements"
touch "@PROJECT_BINARY_DIR@"/macos-signed.stamp

@ -1,85 +0,0 @@
cmake_minimum_required(VERSION 3.10) # bionic's cmake version
# Has to be set before `project()`, and ignored on non-macos:
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12 CACHE STRING "macOS deployment target (Apple clang only)")
find_program(CCACHE_PROGRAM ccache)
if(CCACHE_PROGRAM)
foreach(lang C CXX)
if(NOT DEFINED CMAKE_${lang}_COMPILER_LAUNCHER AND NOT CMAKE_${lang}_COMPILER MATCHES ".*/ccache")
message(STATUS "Enabling ccache for ${lang}")
set(CMAKE_${lang}_COMPILER_LAUNCHER ${CCACHE_PROGRAM} CACHE STRING "")
endif()
endforeach()
endif()
set(PROJECT_NAME lokinet-uninstaller)
project(${PROJECT_NAME}
VERSION 0.0.1
DESCRIPTION "lokinet uninstaller for macos"
LANGUAGES CXX)
add_executable(${PROJECT_NAME}
main.cpp)
find_package(Qt5 COMPONENTS Widgets REQUIRED)
target_link_libraries(${PROJECT_NAME} PRIVATE
"-framework Security"
Qt5::Core Qt5::Widgets)
set_target_properties(${PROJECT_NAME}
PROPERTIES
CXX_STANDARD 17
CXX_EXTENSIONS OFF
CXX_STANDARD_REQUIRED ON
)
set(MACOS_SIGN ""
CACHE STRING "enable codesigning -- use a 'Apple Distribution' key (or key description) from `security find-identity -v`")
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/lokinet-uninstall.icns
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/mk-icns.sh ${CMAKE_CURRENT_SOURCE_DIR}/icon.svg ${CMAKE_CURRENT_BINARY_DIR}/lokinet-uninstall.icns
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/icon.svg ${CMAKE_CURRENT_SOURCE_DIR}/mk-icns.sh)
target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/lokinet-uninstall.icns)
set_target_properties(${PROJECT_NAME}
PROPERTIES
MACOSX_BUNDLE TRUE
OUTPUT_NAME UninstallLokinet
RESOURCE "${CMAKE_CURRENT_BINARY_DIR}/lokinet-uninstall.icns")
set(MACOSX_BUNDLE_BUNDLE_NAME UninstallLokinet)
set(MACOSX_BUNDLE_GUI_IDENTIFIER org.lokinet.lokinet-uninstaller)
set(MACOSX_BUNDLE_INFO_STRING "Lokinet uninstaller")
set(MACOSX_BUNDLE_ICON_FILE lokinet-uninstall.icns)
set(MACOSX_BUNDLE_LONG_VERSION_STRING ${PROJECT_VERSION})
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION})
set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION})
set(MACOSX_BUNDLE_COPYRIGHT "© 2020, The Loki Project")
get_target_property(uic_location Qt5::uic IMPORTED_LOCATION)
get_filename_component(qt_dir ${uic_location} DIRECTORY)
if(MACOS_SIGN)
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND echo "Running qt magic macos deploy script"
COMMAND "${qt_dir}/macdeployqt" UninstallLokinet.app -always-overwrite
COMMAND echo "Signing app bundle and everything inside it"
COMMAND codesign -s "${MACOS_SIGN}" --deep --strict --options runtime --force -vvv UninstallLokinet.app
)
else()
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND echo "Running qt magic macos deploy script"
COMMAND "${qt_dir}/macdeployqt" UninstallLokinet.app -always-overwrite
)
endif()
install(TARGETS lokinet-uninstaller
RUNTIME DESTINATION bin
BUNDLE DESTINATION .
RESOURCE DESTINATION .)

@ -1,26 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg data-name="Layer 1" version="1.1" viewBox="0 0 1e3 1e3" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<metadata>
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title>lokinet icon</dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs>
<style type="text/css">.cls-1{fill:#fff;}.cls-2{fill:#6cbe45;}.cls-3{fill:none;stroke:#fff;stroke-linecap:round;stroke-miterlimit:10;stroke-width:9px;}.cls-4{fill:#1c1c1c;}</style>
</defs>
<title>lokinet icon</title>
<circle class="cls-1" cx="500" cy="500" r="500"/>
<path class="cls-2" transform="translate(.02 .11)" d="M502.6,560.44l8,7.34,14.5,13.09c.74.67,1.42,1.38,2.09,2.09L541,595.54a20.87,20.87,0,0,1,0,31l-16.8,15.2a38.3,38.3,0,0,0,4-3.08l94.71-85.73-55.92-50.64Z"/>
<path class="cls-2" transform="translate(.02 .11)" d="m754.35 415.61v-0.52a69.39 69.39 0 0 0-23.13-50.47l-187.92-167.57a20.88 20.88 0 0 1-3.77 26.39l-14.07 12.73c-0.09 0.08-0.16 0.17-0.25 0.25l-25.1 22.71 168.94 150.65-46.22 41.83 116.27 105.29a46.54 46.54 0 0 1 15.28 34.54c0 1.28-0.1 2.55-0.21 3.82a38.26 38.26 0 0 0 0.23-4v-174.88c0-0.26-0.04-0.52-0.05-0.77z"/>
<path class="cls-2" transform="translate(.02 .11)" d="m500 441.68-38.63-35a20.88 20.88 0 0 1 0-31l10.49-9.49 0.35-0.43a37.93 37.93 0 0 1 6.07-5.38 39 39 0 0 0-3.46 2.75l-95.23 86.2 56 50.57z"/>
<path class="cls-2" transform="translate(.02 .11)" d="m460.61 776.35 11.83-10.7a32.29 32.29 0 0 1 2.34-2.34l25-22.59-166.32-148.34 46.15-41.77-118.72-107.23a46.58 46.58 0 0 1-15.31-34.47c0-1.48 0.09-3 0.23-4.43a36.09 36.09 0 0 0-0.25 4.18v174.85a69.42 69.42 0 0 0 23.19 51.75l188.46 168.07a20.86 20.86 0 0 1 3.4-26.98z"/>
<path class="cls-3" transform="translate(.02 .11)" d="m525 422.75"/>
<path class="cls-4" transform="translate(.02 .11)" d="M754.38,591.44A46.54,46.54,0,0,0,739.1,556.9L525.19,363.21c-.24-.22-.51-.41-.76-.62l-10.26-9.29a20.86,20.86,0,0,0-28,0l-7.92,7.16a37.93,37.93,0,0,0-6.07,5.38l-.35.43-10.49,9.49a20.88,20.88,0,0,0,0,31l38.83,35.15,0,0,165.1,149.5-190.51,172a32.29,32.29,0,0,0-2.34,2.34l-11.83,10.7a20.87,20.87,0,0,0,0,30.95l24.82,22.48a20.87,20.87,0,0,0,28,0l41.08-37.17-.08-.08L739,625.91A46.58,46.58,0,0,0,754.38,591.44Z"/>
<path class="cls-4" transform="translate(.02 .11)" d="M541,595.54,527.21,583c-.67-.71-1.35-1.42-2.09-2.09l-14.5-13.09-8.68-7.95-.06,0-167.22-151L525.21,236.42c.09-.08.16-.17.25-.25l14.07-12.73a20.88,20.88,0,0,0,0-31L514.71,170a20.87,20.87,0,0,0-28,0l-41.08,37.18h0L260.85,374.39a46.56,46.56,0,0,0,0,69L445.27,609.91l-.06.05,42.89,39a20.87,20.87,0,0,0,28,0l24.83-22.47A20.87,20.87,0,0,0,541,595.54Z"/>
<path d="m173.66 173.36 646.24 642.31" fill="#f00" stroke="#f00" stroke-linecap="round" stroke-width="50"/>
<path d="m824.02 175.25-648.03 648.03" fill="none" stroke="#f00" stroke-linecap="round" stroke-width="50"/>
</svg>

Before

Width:  |  Height:  |  Size: 3.0 KiB

@ -1,45 +0,0 @@
#include <QApplication>
#include <QMessageBox>
#include <CoreFoundation/CoreFoundation.h>
#include <Security/Security.h>
int uninstall();
int main(int argc, char * argv[])
{
QApplication app{argc, argv};
if(QMessageBox::question(nullptr, "Lokinet Uninstaller", "Do You want to uninstall Lokinet?",
QMessageBox::Yes|QMessageBox::No)
== QMessageBox::Yes)
{
QMessageBox msgBox;
const auto retcode = uninstall();
if(retcode == 0)
{
msgBox.setText("Lokinet has been successfully uninstalled, you may now remove the uninstaller if you wish.");
}
else
{
msgBox.setText("Failed to uninstall lokinet");
}
msgBox.exec();
}
return 0;
}
int uninstall()
{
AuthorizationRef authorizationRef;
OSStatus status;
status = AuthorizationCreate(nullptr, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authorizationRef);
if(status != 0)
return status;
char* tool = "/bin/sh";
char* args[] = {"/opt/lokinet/bin/lokinet_uninstall.sh", nullptr};
FILE* pipe = stdout;
return AuthorizationExecuteWithPrivileges(authorizationRef, tool, kAuthorizationFlagDefaults, args, &pipe);
}

@ -0,0 +1,31 @@
#!/bin/bash
# Invoked from cmake as make-ico.sh /path/to/icon.svg /path/to/output.ico
svg="$1"
out="$2"
outdir="$out.d"
set -e
sizes=(16 24 32 40 48 64 96 192 256)
outs=""
mkdir -p "${outdir}"
for size in "${sizes[@]}"; do
outf="${outdir}/${size}x${size}.png"
if [ $size -lt 32 ]; then
# For 16x16 and 24x24 we crop the image to 2/3 of its regular size make it all white
# (instead of transparent) to zoom in on it a bit because if we resize the full icon to the
# target size it ends up a fuzzy mess, while the crop and resize lets us retain some detail
# of the logo.
rsvg-convert -b white \
--page-height $size --page-width $size \
-w $(($size*3/2)) -h $(($size*3/2)) --left " -$(($size/4))" --top " -$(($size/4))" \
"$svg" >"$outf"
else
rsvg-convert -b transparent -w $size -h $size "$svg" >"$outf"
fi
outs="-r $outf $outs"
done
icotool -c -b 32 -o "$out" $outs

@ -1,12 +0,0 @@
diff --git a/tests/testutil.hpp b/tests/testutil.hpp
index c6f5e4de..6a1c8bb8 100644
--- a/tests/testutil.hpp
+++ b/tests/testutil.hpp
@@ -102,7 +102,6 @@ const uint8_t zmtp_ready_sub[27] = {
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdexcept>
-#define close closesocket
typedef int socket_size_t;
inline const char *as_setsockopt_opt_t (const void *opt)
{

@ -0,0 +1,14 @@
diff --git a/tests/testutil.hpp b/tests/testutil.hpp
index c6f5e4de78..09b9fa77e5 100644
--- a/tests/testutil.hpp
+++ b/tests/testutil.hpp
@@ -41,6 +41,9 @@
// For AF_INET and IPPROTO_TCP
#if defined _WIN32
#include "../src/windows.hpp"
+#if defined(__MINGW32__)
+#include <unistd.h>
+#endif
#else
#include <arpa/inet.h>
#include <unistd.h>

@ -0,0 +1,33 @@
commit 56d816014d5e8a7eb055169c7e13a303dad5e50f
Author: Jason Rhinelander <jason@imaginary.ca>
Date: Mon Oct 31 22:07:03 2022 -0300
Set tube->ev_listen to NULL to prevent double unregister
On windows when using threaded mode (i.e. `ub_ctx_async(ctx, 1)`)
tube_remove_bg_listen gets called twice: once when the thread does its
own cleanup, then again in `tube_delete()`. Because `ev_listen` doesn't
get cleared, however, we end we calling ub_winsock_unregister_wsaevent
with a freed pointer.
This doesn't always manifest because, apparently, for various compilers
and settings that memory *might* be overwritten in which case the
additional check for ev->magic will prevent anything actually happening,
but in my case under mingw32 that doesn't happen and we end up
eventually crashing.
This fixes the crash by properly NULLing the pointer so that the second
ub_winsock_unregister_wsaevent(...) becomes a no-op.
diff --git a/util/tube.c b/util/tube.c
index 43455fee..a92dfa77 100644
--- a/util/tube.c
+++ b/util/tube.c
@@ -570,6 +570,7 @@ void tube_remove_bg_listen(struct tube* tube)
{
verbose(VERB_ALGO, "tube remove_bg_listen");
ub_winsock_unregister_wsaevent(tube->ev_listen);
+ tube->ev_listen = NULL;
}
void tube_remove_bg_write(struct tube* tube)

@ -1,8 +1,9 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# #
# create signed release tarball with submodules bundled # create signed release tarball with submodules bundled
# usage: ./contrib/tarball.sh [keyid]
# #
repo=$(readlink -e $(dirname $0)/..) repo=$(readlink -e $(dirname $0)/..)
branch=$(test -e $repo/.git/ && git rev-parse --abbrev-ref HEAD) branch=$(test -e $repo/.git/ && git rev-parse --abbrev-ref HEAD)
out="lokinet-$(git describe --exact-match --tags $(git log -n1 --pretty='%h') 2> /dev/null || ( echo -n $branch- && git rev-parse --short HEAD)).tar.xz" out="lokinet-$(git describe --exact-match --tags $(git log -n1 --pretty='%h') 2> /dev/null || ( echo -n $branch- && git rev-parse --short HEAD)).tar.xz"
git-archive-all -C $repo --force-submodules $out && rm -f $out.sig && (gpg --sign --detach $out &> /dev/null && gpg --verify $out.sig) git-archive-all -C $repo --force-submodules $out && rm -f $out.sig && (gpg -u ${1:-jeff@lokinet.io} --sign --detach $out &> /dev/null && gpg --verify $out.sig)

@ -0,0 +1,47 @@
#!/bin/bash
set -e
set -x
# Usage: windows-configure.sh [rootdir [builddir]] -DWHATEVER=BLAH ...
if [ $# -ge 1 ] && [[ "$1" != -* ]]; then
root="$1"
shift
else
root="$(dirname $0)"/..
fi
root="$(readlink -f "$root")"
if [ $# -ge 1 ] && [[ "$1" != -* ]]; then
build="$(readlink -f "$1")"
shift
else
build="$root/build/win32"
echo "Setting up build in $build"
fi
mkdir -p "$build"
cmake \
-S "$root" -B "$build" \
-G 'Unix Makefiles' \
-DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \
-DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \
-DCMAKE_TOOLCHAIN_FILE="$root/contrib/cross/mingw64.cmake" \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_STATIC_DEPS=ON \
-DBUILD_PACKAGE=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DFORCE_OXENC_SUBMODULE=ON \
-DFORCE_FMT_SUBMODULE=ON \
-DFORCE_SPDLOG_SUBMODULE=ON \
-DFORCE_NLOHMANN_SUBMODULE=ON \
-DWITH_LTO=OFF \
"$@"

@ -6,26 +6,8 @@
set -e set -e
set +x set +x
mkdir -p build-windows root="$(readlink -f $(dirname $0)/../)"
cd build-windows mkdir -p $root/build/win32
cmake \ $root/contrib/windows-configure.sh $root $root/build/win32 "$@"
-G 'Unix Makefiles' \ make package -j${JOBS:-$(nproc)} -C $root/build/win32
-DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \
-DCMAKE_CXX_FLAGS=-fdiagnostics-color=always\
-DCMAKE_TOOLCHAIN_FILE=../contrib/cross/mingw64.cmake\
-DBUILD_STATIC_DEPS=ON \
-DBUILD_PACKAGE=ON \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DFORCE_OXENC_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=OFF \
-DCMAKE_BUILD_TYPE=Release \
$@ ..
make package -j${JOBS:-$(nproc)}

@ -57,7 +57,6 @@ else()
endif() endif()
enable_lto(lokinet-cryptography) enable_lto(lokinet-cryptography)
add_log_tag(lokinet-cryptography)
if (WARNINGS_AS_ERRORS) if (WARNINGS_AS_ERRORS)
target_compile_options(lokinet-cryptography PUBLIC -Wall -Wextra -Werror) target_compile_options(lokinet-cryptography PUBLIC -Wall -Wextra -Werror)

@ -1,14 +1,18 @@
set(exetargets lokinet)
add_executable(lokinet-vpn lokinet-vpn.cpp)
if(APPLE) if(APPLE)
add_executable(lokinet lokinet.swift) add_executable(lokinet lokinet.swift)
enable_lto(lokinet) target_compile_options(lokinet BEFORE PRIVATE -target x86_64-apple-macos${CMAKE_OSX_DEPLOYMENT_TARGET})
else() else()
add_executable(lokinet lokinet.cpp) add_executable(lokinet lokinet.cpp)
enable_lto(lokinet lokinet-vpn)
endif() endif()
add_executable(lokinet-vpn lokinet-vpn.cpp)
enable_lto(lokinet lokinet-vpn)
list(APPEND exetargets lokinet-vpn)
if(WITH_BOOTSTRAP) if(WITH_BOOTSTRAP)
add_executable(lokinet-bootstrap lokinet-bootstrap.cpp) add_executable(lokinet-bootstrap lokinet-bootstrap.cpp)
list(APPEND exetargets lokinet-bootstrap)
enable_lto(lokinet-bootstrap) enable_lto(lokinet-bootstrap)
endif() endif()
@ -42,86 +46,45 @@ if(WITH_BOOTSTRAP)
endif() endif()
endif() endif()
set(exetargets lokinet lokinet-vpn) # cmake interface library for bunch of cmake hacks to fix final link order
if(WITH_BOOTSTRAP) add_library(hax_and_shims_for_cmake INTERFACE)
list(APPEND exetargets lokinet-bootstrap) if(WIN32)
target_link_libraries(hax_and_shims_for_cmake INTERFACE uvw oxenmq::oxenmq -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
endif() endif()
foreach(exe ${exetargets}) foreach(exe ${exetargets})
if(WIN32 AND NOT MSVC_VERSION) if(WIN32)
target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc) target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc)
target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00) target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00)
target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_link_directories(${exe} PRIVATE /usr/local/lib) target_link_directories(${exe} PRIVATE /usr/local/lib)
endif() endif()
target_link_libraries(${exe} PUBLIC liblokinet) target_link_libraries(${exe} PUBLIC lokinet-amalgum hax_and_shims_for_cmake)
if(STRIP_SYMBOLS)
add_custom_command(TARGET ${exe}
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} ARGS --only-keep-debug $<TARGET_FILE:${exe}> $<TARGET_FILE:${exe}>.debug
COMMAND ${CMAKE_STRIP} ARGS --strip-all $<TARGET_FILE:${exe}>)
endif()
target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}") target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}")
target_compile_definitions(${exe} PRIVATE -DVERSIONTAG=${GIT_VERSION_REAL})
add_log_tag(${exe})
if(should_install) if(should_install)
if(APPLE) if(APPLE)
install(TARGETS ${exe} BUNDLE DESTINATION "${PROJECT_BINARY_DIR}" COMPONENT lokinet) install(TARGETS ${exe}
BUNDLE DESTINATION "${PROJECT_BINARY_DIR}"
RUNTIME DESTINATION "."
COMPONENT lokinet)
else() else()
install(TARGETS ${exe} RUNTIME DESTINATION bin COMPONENT lokinet) install(TARGETS ${exe} RUNTIME DESTINATION bin COMPONENT lokinet)
endif() endif()
endif() endif()
endforeach() endforeach()
if(APPLE)
set(CODESIGN_APP "" CACHE STRING "codesign the macos app using this key identity")
set(CODESIGN_APPEX "${CODESIGN_APP}" CACHE STRING "codesign the internal extension using this key identity; defaults to CODESIGN_APP if empty")
set(mac_icon ${CMAKE_CURRENT_BINARY_DIR}/lokinet.icns)
add_custom_command(OUTPUT ${mac_icon}
COMMAND ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${mac_icon}
DEPENDS ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh)
add_custom_target(icons DEPENDS ${mac_icon})
add_dependencies(lokinet icons lokinet-extension)
add_custom_command(TARGET lokinet
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed
$<TARGET_BUNDLE_DIR:lokinet-extension>/Contents/Resources/bootstrap.signed
COMMAND mkdir -p $<TARGET_BUNDLE_DIR:lokinet>/Contents/PlugIns
COMMAND cp -a $<TARGET_BUNDLE_DIR:lokinet-extension> $<TARGET_BUNDLE_DIR:lokinet>/Contents/PlugIns/
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${PROJECT_SOURCE_DIR}/contrib/macos/lokinet.provisionprofile
$<TARGET_BUNDLE_DIR:lokinet>/Contents/embedded.provisionprofile
)
set_target_properties(lokinet
PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_INFO_STRING "Lokinet IP Packet Onion Router"
MACOSX_BUNDLE_BUNDLE_NAME "Lokinet"
MACOSX_BUNDLE_BUNDLE_VERSION "${lokinet_VERSION}"
MACOSX_BUNDLE_LONG_VERSION_STRING "${lokinet_VERSION}"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${lokinet_VERSION_MAJOR}.${lokinet_VERSION_MINOR}"
MACOSX_BUNDLE_GUI_IDENTIFIER "com.loki-project.lokinet"
MACOSX_BUNDLE_INFO_PLIST "${PROJECT_SOURCE_DIR}/contrib/macos/Info.plist.in"
MACOSX_BUNDLE_ICON_FILE "${mac_icon}"
MACOSX_BUNDLE_COPYRIGHT "© 2021, The Oxen Project")
if (CODESIGN_APP AND CODESIGN_APPEX)
message(STATUS "codesigning with ${CODESIGN_APP} (app) ${CODESIGN_APPEX} (appex)")
set(SIGN_TARGET "${CMAKE_CURRENT_BINARY_DIR}/lokinet.app")
configure_file(
"${PROJECT_SOURCE_DIR}/contrib/macos/sign.sh.in"
"${PROJECT_BINARY_DIR}/sign.sh"
@ONLY)
add_custom_target(
sign
DEPENDS "${PROJECT_BINARY_DIR}/sign.sh" lokinet lokinet-extension
COMMAND "${PROJECT_BINARY_DIR}/sign.sh"
)
else()
message(WARNING "Not codesigning: CODESIGN_APP (=${CODESIGN_APP}) and/or CODESIGN_APPEX (=${CODESIGN_APPEX}) are not set")
add_custom_target(
sign
DEPENDS lokinet lokinet-extension
COMMAND "true")
endif()
endif()
if(SETCAP) if(SETCAP)
install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)") install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)")
endif() endif()
if(STRIP_SYMBOLS)
add_custom_target(symbols ALL
COMMAND ${CMAKE_COMMAND} -E tar cJf ${CMAKE_CURRENT_BINARY_DIR}/debug-symbols.tar.xz $<TARGET_FILE:lokinet>.debug
DEPENDS lokinet)
endif()

@ -1,5 +1,6 @@
#include <oxenmq/oxenmq.h> #include <oxenmq/oxenmq.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <fmt/core.h>
#include <cxxopts.hpp> #include <cxxopts.hpp>
#include <future> #include <future>
#include <vector> #include <vector>
@ -16,11 +17,11 @@
#include <sys/wait.h> #include <sys/wait.h>
#endif #endif
/// do a oxenmq request on an lmq instance blocking style /// do a oxenmq request on an omq instance blocking style
/// returns a json object parsed from the result /// returns a json object parsed from the result
std::optional<nlohmann::json> std::optional<nlohmann::json>
LMQ_Request( OMQ_Request(
oxenmq::OxenMQ& lmq, oxenmq::OxenMQ& omq,
const oxenmq::ConnectionID& id, const oxenmq::ConnectionID& id,
std::string_view method, std::string_view method,
std::optional<nlohmann::json> args = std::nullopt) std::optional<nlohmann::json> args = std::nullopt)
@ -37,11 +38,11 @@ LMQ_Request(
}; };
if (args.has_value()) if (args.has_value())
{ {
lmq.request(id, method, handleRequest, args->dump()); omq.request(id, method, handleRequest, args->dump());
} }
else else
{ {
lmq.request(id, method, handleRequest); omq.request(id, method, handleRequest);
} }
auto ftr = result_promise.get_future(); auto ftr = result_promise.get_future();
const auto str = ftr.get(); const auto str = ftr.get();
@ -50,6 +51,50 @@ LMQ_Request(
return std::nullopt; return std::nullopt;
} }
namespace
{
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)
{
if (r.count(name))
{
if constexpr (is_optional<T>)
value = r[name].as<typename T::value_type>();
else
value = r[name].as<T>();
}
}
// Takes a code, prints a message, and returns the code. Intended use is:
// return exit_error(1, "blah: {}", 42);
// from within main().
template <typename... T>
[[nodiscard]] int
exit_error(int code, const std::string& format, T&&... args)
{
fmt::print(format, std::forward<T>(args)...);
fmt::print("\n");
return code;
}
// Same as above, but with code omitted (uses exit code 1)
template <typename... T>
[[nodiscard]] int
exit_error(const std::string& format, T&&... args)
{
return exit_error(1, format, std::forward<T>(args)...);
}
} // namespace
int int
main(int argc, char* argv[]) main(int argc, char* argv[])
{ {
@ -74,8 +119,8 @@ main(int argc, char* argv[])
oxenmq::address rpcURL("tcp://127.0.0.1:1190"); oxenmq::address rpcURL("tcp://127.0.0.1:1190");
std::string exitAddress; std::string exitAddress;
std::string endpoint = "default"; std::string endpoint = "default";
std::optional<std::string> token; std::string token;
std::string range = "::/0"; std::optional<std::string> range;
oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn; oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn;
bool goUp = false; bool goUp = false;
bool goDown = false; bool goDown = false;
@ -95,69 +140,48 @@ main(int argc, char* argv[])
{ {
logLevel = oxenmq::LogLevel::debug; logLevel = oxenmq::LogLevel::debug;
} }
if (result.count("rpc") > 0)
{
rpcURL = oxenmq::address(result["rpc"].as<std::string>());
}
if (result.count("exit") > 0)
{
exitAddress = result["exit"].as<std::string>();
}
goUp = result.count("up") > 0; goUp = result.count("up") > 0;
goDown = result.count("down") > 0; goDown = result.count("down") > 0;
printStatus = result.count("status") > 0; printStatus = result.count("status") > 0;
killDaemon = result.count("kill") > 0; killDaemon = result.count("kill") > 0;
if (result.count("endpoint") > 0) extract_option(result, "rpc", rpcURL);
{ extract_option(result, "exit", exitAddress);
endpoint = result["endpoint"].as<std::string>(); extract_option(result, "endpoint", endpoint);
} extract_option(result, "token", token);
if (result.count("token") > 0) extract_option(result, "auth", token);
{ extract_option(result, "range", range);
token = result["token"].as<std::string>();
}
if (result.count("auth") > 0)
{
token = result["auth"].as<std::string>();
}
if (result.count("range") > 0)
{
range = result["range"].as<std::string>();
}
} }
catch (const cxxopts::option_not_exists_exception& ex) catch (const cxxopts::option_not_exists_exception& ex)
{ {
std::cerr << ex.what(); return exit_error(2, "{}\n{}", ex.what(), opts.help());
std::cout << opts.help() << std::endl;
return 1;
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
std::cout << ex.what() << std::endl; return exit_error(2, "{}", ex.what());
return 1;
}
if ((not goUp) and (not goDown) and (not printStatus) and (not killDaemon))
{
std::cout << opts.help() << std::endl;
return 1;
} }
int num_commands = goUp + goDown + printStatus + killDaemon;
if (num_commands == 0)
return exit_error(3, "One of --up/--down/--status/--kill must be specified");
if (num_commands != 1)
return exit_error(3, "Only one of --up/--down/--status/--kill may be specified");
if (goUp and exitAddress.empty()) if (goUp and exitAddress.empty())
{ return exit_error("no exit address provided");
std::cout << "no exit address provided" << std::endl;
return 1;
}
oxenmq::OxenMQ lmq{ 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;
}, },
logLevel}; logLevel};
lmq.start(); omq.start();
std::promise<bool> connectPromise; std::promise<bool> connectPromise;
const auto connID = lmq.connect_remote( const auto connID = omq.connect_remote(
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) {
@ -173,23 +197,16 @@ main(int argc, char* argv[])
if (killDaemon) if (killDaemon)
{ {
const auto maybe = LMQ_Request(lmq, connID, "llarp.halt"); if (not OMQ_Request(omq, connID, "llarp.halt"))
if (not maybe.has_value()) return exit_error("call to llarp.halt failed");
{
std::cout << "call to llarp.admin.die failed" << std::endl;
return 1;
}
return 0; return 0;
} }
if (printStatus) if (printStatus)
{ {
const auto maybe_status = LMQ_Request(lmq, connID, "llarp.status"); const auto maybe_status = OMQ_Request(omq, connID, "llarp.status");
if (not maybe_status.has_value()) if (not maybe_status)
{ return exit_error("call to llarp.status failed");
std::cout << "call to llarp.status failed" << std::endl;
return 1;
}
try try
{ {
@ -209,43 +226,34 @@ main(int argc, char* argv[])
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
std::cout << "failed to parse result: " << ex.what() << std::endl; return exit_error("failed to parse result: {}", ex.what());
return 1;
} }
return 0; return 0;
} }
if (goUp) if (goUp)
{ {
std::optional<nlohmann::json> maybe_result; nlohmann::json opts{{"exit", exitAddress}, {"token", token}};
if (token.has_value()) if (range)
{ opts["range"] = *range;
maybe_result = LMQ_Request(
lmq,
connID,
"llarp.exit",
nlohmann::json{{"exit", exitAddress}, {"range", range}, {"token", *token}});
}
else
{
maybe_result = LMQ_Request(
lmq, connID, "llarp.exit", nlohmann::json{{"exit", exitAddress}, {"range", range}});
}
if (not maybe_result.has_value()) auto maybe_result = OMQ_Request(omq, connID, "llarp.exit", std::move(opts));
{
std::cout << "could not add exit" << std::endl; if (not maybe_result)
return 1; return exit_error("could not add exit");
}
if (maybe_result->contains("error") and maybe_result->at("error").is_string()) if (auto err_it = maybe_result->find("error");
err_it != maybe_result->end() and not err_it.value().is_null())
{ {
std::cout << maybe_result->at("error").get<std::string>() << std::endl; return exit_error("{}", err_it.value());
return 1;
} }
} }
if (goDown) if (goDown)
{ {
LMQ_Request(lmq, connID, "llarp.exit", nlohmann::json{{"range", range}, {"unmap", true}}); nlohmann::json opts{{"unmap", true}};
if (range)
opts["range"] = *range;
if (not OMQ_Request(omq, connID, "llarp.exit", std::move(opts)))
return exit_error("failed to unmap exit");
} }
return 0; return 0;

@ -2,13 +2,15 @@
#include <llarp/constants/version.hpp> #include <llarp/constants/version.hpp>
#include <llarp.hpp> #include <llarp.hpp>
#include <llarp/util/lokinet_init.h> #include <llarp/util/lokinet_init.h>
#include <llarp/util/exceptions.hpp>
#include <llarp/util/fs.hpp> #include <llarp/util/fs.hpp>
#include <llarp/util/logging/logger.hpp>
#include <llarp/util/logging/ostream_logger.hpp>
#include <llarp/util/str.hpp> #include <llarp/util/str.hpp>
#ifdef _WIN32 #ifdef _WIN32
#include <llarp/win32/service_manager.hpp>
#include <dbghelp.h> #include <dbghelp.h>
#else
#include <llarp/util/service_manager.hpp>
#endif #endif
#include <csignal> #include <csignal>
@ -22,25 +24,24 @@ int
lokinet_main(int, char**); lokinet_main(int, char**);
#ifdef _WIN32 #ifdef _WIN32
#include <strsafe.h>
extern "C" LONG FAR PASCAL extern "C" LONG FAR PASCAL
win32_signal_handler(EXCEPTION_POINTERS*); win32_signal_handler(EXCEPTION_POINTERS*);
extern "C" VOID FAR PASCAL extern "C" VOID FAR PASCAL
win32_daemon_entry(DWORD, LPTSTR*); win32_daemon_entry(DWORD, LPTSTR*);
BOOL ReportSvcStatus(DWORD, DWORD, DWORD);
VOID VOID
insert_description(); insert_description();
SERVICE_STATUS SvcStatus;
SERVICE_STATUS_HANDLE SvcStatusHandle;
bool start_as_daemon = false;
#endif #endif
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;
void void
handle_signal(int sig) handle_signal(int sig)
{ {
llarp::log::info(logcat, "Handling signal {}", sig);
if (ctx) if (ctx)
ctx->loop->call([sig] { ctx->HandleSignal(sig); }); ctx->loop->call([sig] { ctx->HandleSignal(sig); });
else else
@ -83,9 +84,6 @@ install_win32_daemon()
llarp::LogError("Cannot install service ", GetLastError()); llarp::LogError("Cannot install service ", GetLastError());
return; return;
} }
// just put the flag here. we eat it later on and specify the
// config path in the daemon entry point
StringCchCat(szPath.data(), 1024, " --win32-daemon");
// Get a handle to the SCM database. // Get a handle to the SCM database.
schSCManager = OpenSCManager( schSCManager = OpenSCManager(
@ -102,7 +100,7 @@ install_win32_daemon()
// Create the service // Create the service
schService = CreateService( schService = CreateService(
schSCManager, // SCM database schSCManager, // SCM database
"lokinet", // name of service strdup("lokinet"), // name of service
"Lokinet for Windows", // service name to display "Lokinet for Windows", // service name to display
SERVICE_ALL_ACCESS, // desired access SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_WIN32_OWN_PROCESS, // service type
@ -135,10 +133,10 @@ insert_description()
SC_HANDLE schSCManager; SC_HANDLE schSCManager;
SC_HANDLE schService; SC_HANDLE schService;
SERVICE_DESCRIPTION sd; SERVICE_DESCRIPTION sd;
LPTSTR szDesc = LPTSTR szDesc = strdup(
"LokiNET is a free, open source, private, " "LokiNET is a free, open source, private, "
"decentralized, \"market based sybil resistant\" " "decentralized, \"market based sybil resistant\" "
"and IP based onion routing network"; "and IP based onion routing network");
// Get a handle to the SCM database. // Get a handle to the SCM database.
schSCManager = OpenSCManager( schSCManager = OpenSCManager(
NULL, // local computer NULL, // local computer
@ -229,7 +227,7 @@ uninstall_win32_daemon()
static void static void
run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts) run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions opts)
{ {
llarp::LogTrace("start of run_main_context()"); llarp::LogInfo(fmt::format("starting up {} {}", llarp::VERSION_FULL, llarp::RELEASE_MOTTO));
try try
{ {
std::shared_ptr<llarp::Config> conf; std::shared_ptr<llarp::Config> conf;
@ -263,14 +261,18 @@ run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions o
{ {
ctx->Setup(opts); 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) catch (std::exception& ex)
{ {
llarp::LogError( llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what()));
"failed to set up lokinet: ", ex.what(), ", is lokinet already running? 🤔");
exit_code.set_value(1); exit_code.set_value(1);
return; return;
} }
llarp::util::SetThreadName("llarp-mainloop"); llarp::util::SetThreadName("llarp-mainloop");
auto result = ctx->Run(opts); auto result = ctx->Run(opts);
@ -289,46 +291,14 @@ run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions o
} }
#ifdef _WIN32 #ifdef _WIN32
void
TellWindowsServiceStopped()
{
::WSACleanup();
if (not start_as_daemon)
return;
llarp::LogInfo("Telling Windows the service has stopped.");
if (not ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0))
{
auto error_code = GetLastError();
if (error_code == ERROR_INVALID_DATA)
llarp::LogError(
"SetServiceStatus failed: \"The specified service status structure is invalid.\"");
else if (error_code == ERROR_INVALID_HANDLE)
llarp::LogError("SetServiceStatus failed: \"The specified handle is invalid.\"");
else
llarp::LogError("SetServiceStatus failed with an unknown error.");
}
llarp::LogContext::Instance().ImmediateFlush();
}
class WindowsServiceStopped
{
public:
WindowsServiceStopped() = default;
~WindowsServiceStopped()
{
TellWindowsServiceStopped();
}
};
/// 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
LONG LONG
GenerateDump(EXCEPTION_POINTERS* pExceptionPointers) GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{ {
const DWORD flags = MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData const auto flags =
| MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo; (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo);
std::stringstream ss; std::stringstream ss;
ss << "C:\\ProgramData\\lokinet\\crash-" << llarp::time_now_ms().count() << ".dmp"; ss << "C:\\ProgramData\\lokinet\\crash-" << llarp::time_now_ms().count() << ".dmp";
@ -361,39 +331,57 @@ GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
int int
main(int argc, char* argv[]) main(int argc, char* argv[])
{ {
// Set up a default, stderr logging for very early logging; we'll replace this later once we read
// the desired log info from config.
llarp::log::add_sink(llarp::log::Type::Print, "stderr");
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 #ifndef _WIN32
return lokinet_main(argc, argv); return lokinet_main(argc, argv);
#else #else
SERVICE_TABLE_ENTRY DispatchTable[] = { SERVICE_TABLE_ENTRY DispatchTable[] = {
{"lokinet", (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
if (lstrcmpi(argv[1], "--win32-daemon") == 0)
// 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 (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
{ {
start_as_daemon = true; llarp::sys::service_manager->disable();
StartServiceCtrlDispatcher(DispatchTable); return lokinet_main(argc, argv);
} }
else else
return lokinet_main(argc, argv); {
llarp::log::critical(
logcat, "Error launching service: {}", std::system_category().message(error));
return 1;
}
#endif #endif
} }
int int
lokinet_main(int argc, char* argv[]) lokinet_main(int argc, char** argv)
{ {
auto result = Lokinet_INIT(); if (auto result = Lokinet_INIT())
if (result)
{
return result; return result;
}
llarp::RuntimeOptions opts; llarp::RuntimeOptions opts;
opts.showBanner = false;
#ifdef _WIN32 #ifdef _WIN32
WindowsServiceStopped stopped_raii;
if (startWinsock()) if (startWinsock())
return -1; return -1;
SetConsoleCtrlHandler(handle_signal_win32, TRUE); SetConsoleCtrlHandler(handle_signal_win32, TRUE);
// SetUnhandledExceptionFilter(win32_signal_handler);
#endif #endif
cxxopts::Options options( cxxopts::Options options(
"lokinet", "lokinet",
"LokiNET is a free, open source, private, " "LokiNET is a free, open source, private, "
@ -410,7 +398,6 @@ lokinet_main(int argc, char* argv[])
("g,generate", "generate default configuration and exit", cxxopts::value<bool>()) ("g,generate", "generate default configuration and exit", cxxopts::value<bool>())
("r,router", "run in routing mode instead of client only mode", cxxopts::value<bool>()) ("r,router", "run in routing mode instead of client only mode", cxxopts::value<bool>())
("f,force", "force writing config even if it already exists", cxxopts::value<bool>()) ("f,force", "force writing config even if it already exists", cxxopts::value<bool>())
("c,colour", "colour output", cxxopts::value<bool>()->default_value("true"))
("config", "path to lokinet.ini configuration file", cxxopts::value<std::string>()) ("config", "path to lokinet.ini configuration file", cxxopts::value<std::string>())
; ;
// clang-format on // clang-format on
@ -424,18 +411,11 @@ lokinet_main(int argc, char* argv[])
{ {
auto result = options.parse(argc, argv); auto result = options.parse(argc, argv);
if (!result["colour"].as<bool>())
{
llarp::LogContext::Instance().logStream =
std::make_unique<llarp::OStreamLogStream>(false, std::cerr);
}
if (result.count("help")) if (result.count("help"))
{ {
std::cout << options.help() << std::endl; std::cout << options.help() << std::endl;
return 0; return 0;
} }
if (result.count("version")) if (result.count("version"))
{ {
std::cout << llarp::VERSION_FULL << std::endl; std::cout << llarp::VERSION_FULL << std::endl;
@ -542,18 +522,15 @@ lokinet_main(int argc, char* argv[])
SetUnhandledExceptionFilter(&GenerateDump); SetUnhandledExceptionFilter(&GenerateDump);
#endif #endif
std::thread main_thread{[&] { run_main_context(configFile, opts); }}; std::thread main_thread{[configFile, opts] { run_main_context(configFile, opts); }};
auto ftr = exit_code.get_future(); auto ftr = exit_code.get_future();
#ifdef _WIN32
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
#endif
do do
{ {
// do periodic non lokinet related tasks here // do periodic non lokinet related tasks here
if (ctx and ctx->IsUp() and not ctx->LooksAlive()) if (ctx and ctx->IsUp() and not ctx->LooksAlive())
{ {
auto deadlock_cat = llarp::log::Cat("deadlock");
for (const auto& wtf : for (const auto& wtf :
{"you have been visited by the mascott of the deadlocked router.", {"you have been visited by the mascott of the deadlocked router.",
"⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠄⠄⠄⠄", "⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⠄⣀⣴⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣄⠄⠄⠄⠄",
@ -575,12 +552,10 @@ lokinet_main(int argc, char* argv[])
"file a bug report now or be cursed with this " "file a bug report now or be cursed with this "
"annoying image in your syslog for all time."}) "annoying image in your syslog for all time."})
{ {
llarp::LogError{wtf}; llarp::log::critical(deadlock_cat, wtf);
llarp::LogContext::Instance().ImmediateFlush(); llarp::log::flush();
} }
#ifdef _WIN32 llarp::sys::service_manager->failed();
TellWindowsServiceStopped();
#endif
std::abort(); std::abort();
} }
} while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready); } while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready);
@ -604,7 +579,8 @@ lokinet_main(int argc, char* argv[])
code = 2; code = 2;
} }
llarp::LogContext::Instance().ImmediateFlush(); llarp::log::flush();
llarp::sys::service_manager->stopped();
if (ctx) if (ctx)
{ {
ctx.reset(); ctx.reset();
@ -613,29 +589,6 @@ lokinet_main(int argc, char* argv[])
} }
#ifdef _WIN32 #ifdef _WIN32
BOOL
ReportSvcStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint)
{
static DWORD dwCheckPoint = 1;
// Fill in the SERVICE_STATUS structure.
SvcStatus.dwCurrentState = dwCurrentState;
SvcStatus.dwWin32ExitCode = dwWin32ExitCode;
SvcStatus.dwWaitHint = dwWaitHint;
if (dwCurrentState == SERVICE_START_PENDING)
SvcStatus.dwControlsAccepted = 0;
else
SvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
if ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED))
SvcStatus.dwCheckPoint = 0;
else
SvcStatus.dwCheckPoint = dwCheckPoint++;
// Report the status of the service to the SCM.
return SetServiceStatus(SvcStatusHandle, &SvcStatus);
}
VOID FAR PASCAL VOID FAR PASCAL
SvcCtrlHandler(DWORD dwCtrl) SvcCtrlHandler(DWORD dwCtrl)
@ -645,44 +598,45 @@ SvcCtrlHandler(DWORD dwCtrl)
switch (dwCtrl) switch (dwCtrl)
{ {
case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0); // tell service we are stopping
// Signal the service to stop. 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); handle_signal(SIGINT);
return; return;
case SERVICE_CONTROL_INTERROGATE: case SERVICE_CONTROL_INTERROGATE:
break; // report status
llarp::log::debug(logcat, "Got win32 service interrogate signal");
llarp::sys::service_manager->report_changed_state();
return;
default: default:
llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl);
break; break;
} }
} }
// The win32 daemon entry point is just a trampoline that returns control // The win32 daemon entry point is where we go when invoked as a windows service; we do the required
// to the original lokinet entry // service dance and then pretend we were invoked via main().
// and only gets called if we get --win32-daemon in the command line
VOID FAR PASCAL VOID FAR PASCAL
win32_daemon_entry(DWORD argc, LPTSTR* argv) win32_daemon_entry(DWORD, LPTSTR* argv)
{ {
// Register the handler function for the service // Register the handler function for the service
SvcStatusHandle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler); auto* svc = dynamic_cast<llarp::sys::SVC_Manager*>(llarp::sys::service_manager);
svc->handle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler);
if (!SvcStatusHandle) if (svc->handle == nullptr)
{ {
llarp::LogError("failed to register daemon control handler"); llarp::LogError("failed to register daemon control handler");
return; return;
} }
// These SERVICE_STATUS members remain as set here // we hard code the args to lokinet_main.
SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // we yoink argv[0] (lokinet.exe path) and pass in the new args.
SvcStatus.dwServiceSpecificExitCode = 0; std::array args = {
reinterpret_cast<char*>(argv[0]),
// Report initial status to the SCM reinterpret_cast<char*>(strdup("c:\\programdata\\lokinet\\lokinet.ini")),
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000); reinterpret_cast<char*>(0)};
// SCM clobbers startup args, regenerate them here lokinet_main(args.size() - 1, args.data());
argc = 2;
argv[1] = "c:/programdata/lokinet/lokinet.ini";
argv[2] = nullptr;
lokinet_main(argc, argv);
} }
#endif #endif

@ -1,34 +1,83 @@
import AppKit import AppKit
import Foundation import Foundation
import NetworkExtension import NetworkExtension
import SystemExtensions
let app = NSApplication.shared let app = NSApplication.shared
let START = "--start"
let STOP = "--stop"
let HELP_STRING = "usage: lokinet {--start|--stop}"
class LokinetMain: NSObject, NSApplicationDelegate { class LokinetMain: NSObject, NSApplicationDelegate {
var vpnManager = NETunnelProviderManager() var vpnManager = NETunnelProviderManager()
let lokinetComponent = "com.loki-project.lokinet.network-extension" var mode = START
let netextBundleId = "org.lokinet.network-extension"
func applicationDidFinishLaunching(_: Notification) { func applicationDidFinishLaunching(_: Notification) {
setupVPNJizz() if mode == START {
startNetworkExtension()
} else if mode == STOP {
tearDownVPNTunnel()
} else {
result(msg: HELP_STRING)
}
} }
func bail() { func bail() {
app.terminate(self) app.terminate(self)
} }
func setupVPNJizz() { func result(msg: String) {
NSLog("Starting up lokinet") NSLog(msg)
// TODO: does lokinet continue after this?
bail()
}
func tearDownVPNTunnel() {
NSLog("Stopping Lokinet")
NETunnelProviderManager.loadAllFromPreferences { [self] (savedManagers: [NETunnelProviderManager]?, error: Error?) in NETunnelProviderManager.loadAllFromPreferences { [self] (savedManagers: [NETunnelProviderManager]?, error: Error?) in
if let error = error { if let error = error {
NSLog(error.localizedDescription) self.result(msg: error.localizedDescription)
bail()
return return
} }
if let savedManagers = savedManagers { if let savedManagers = savedManagers {
for manager in savedManagers { for manager in savedManagers {
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.lokinetComponent { if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.netextBundleId {
NSLog("%@", manager) manager.connection.stopVPNTunnel()
self.result(msg: "Lokinet Down")
}
}
}
self.result(msg: "Lokinet is not up")
}
}
func startNetworkExtension() {
#if MACOS_SYSTEM_EXTENSION
NSLog("Loading Lokinet network extension")
// Start by activating the system extension
let activationRequest = OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: netextBundleId, queue: .main)
activationRequest.delegate = self
OSSystemExtensionManager.shared.submitRequest(activationRequest)
#else
setupVPNTunnel()
#endif
}
func setupVPNTunnel() {
NSLog("Starting up Lokinet tunnel")
NETunnelProviderManager.loadAllFromPreferences { [self] (savedManagers: [NETunnelProviderManager]?, error: Error?) in
if let error = error {
self.result(msg: error.localizedDescription)
return
}
if let savedManagers = savedManagers {
for manager in savedManagers {
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.netextBundleId {
NSLog("Found saved VPN Manager") NSLog("Found saved VPN Manager")
self.vpnManager = manager self.vpnManager = manager
} }
@ -37,8 +86,10 @@ class LokinetMain: NSObject, NSApplicationDelegate {
let providerProtocol = NETunnelProviderProtocol() let providerProtocol = NETunnelProviderProtocol()
providerProtocol.serverAddress = "loki.loki" // Needs to be set to some non-null dummy value providerProtocol.serverAddress = "loki.loki" // Needs to be set to some non-null dummy value
providerProtocol.username = "anonymous" providerProtocol.username = "anonymous"
providerProtocol.providerBundleIdentifier = self.lokinetComponent providerProtocol.providerBundleIdentifier = self.netextBundleId
providerProtocol.enforceRoutes = true if #available(macOS 11, *) {
providerProtocol.enforceRoutes = true
}
// macos seems to have trouble when this is true, and reports are that this breaks and // macos seems to have trouble when this is true, and reports are that this breaks and
// doesn't do what it says on the tin in the first place. Needs more testing. // doesn't do what it says on the tin in the first place. Needs more testing.
providerProtocol.includeAllNetworks = false providerProtocol.includeAllNetworks = false
@ -46,28 +97,30 @@ class LokinetMain: NSObject, NSApplicationDelegate {
self.vpnManager.isEnabled = true self.vpnManager.isEnabled = true
// self.vpnManager.isOnDemandEnabled = true // self.vpnManager.isOnDemandEnabled = true
self.vpnManager.localizedDescription = "lokinet" self.vpnManager.localizedDescription = "lokinet"
self.vpnManager.saveToPreferences(completionHandler: { error -> Void in self.vpnManager.saveToPreferences(completionHandler: { [self] error -> Void in
if error != nil { if error != nil {
NSLog("Error saving to preferences") NSLog("Error saving to preferences")
NSLog(error!.localizedDescription) self.result(msg: error!.localizedDescription)
bail()
} else { } else {
self.vpnManager.loadFromPreferences(completionHandler: { error in self.vpnManager.loadFromPreferences(completionHandler: { error in
if error != nil { if error != nil {
NSLog("Error loading from preferences") NSLog("Error loading from preferences")
NSLog(error!.localizedDescription) self.result(msg: error!.localizedDescription)
bail()
} else { } else {
do { do {
NSLog("Trying to start") NSLog("Trying to start")
self.initializeConnectionObserver() self.initializeConnectionObserver()
try self.vpnManager.connection.startVPNTunnel() try self.vpnManager.connection.startVPNTunnel()
} catch let error as NSError { } catch let error as NSError {
NSLog(error.localizedDescription) self.result(msg: error.localizedDescription)
bail()
} catch { } catch {
NSLog("There was a fatal error") self.result(msg: "There was a fatal error")
bail() }
// Check if we are already connected because, if so, we won't get a
// status change and will just hang waiting for one.
if self.vpnManager.connection.status == .connected {
self.result(msg: "VPN already connected");
} }
} }
}) })
@ -77,11 +130,11 @@ class LokinetMain: NSObject, NSApplicationDelegate {
} }
func initializeConnectionObserver() { func initializeConnectionObserver() {
NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: vpnManager.connection, queue: OperationQueue.main) { _ -> Void in NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: vpnManager.connection, queue: OperationQueue.main) { [self] _ -> Void in
if self.vpnManager.connection.status == .invalid { if self.vpnManager.connection.status == .invalid {
NSLog("VPN configuration is invalid") self.result(msg: "VPN configuration is invalid")
} else if self.vpnManager.connection.status == .disconnected { } else if self.vpnManager.connection.status == .disconnected {
NSLog("VPN is disconnected.") self.result(msg: "VPN is disconnected.")
} else if self.vpnManager.connection.status == .connecting { } else if self.vpnManager.connection.status == .connecting {
NSLog("VPN is connecting...") NSLog("VPN is connecting...")
} else if self.vpnManager.connection.status == .reasserting { } else if self.vpnManager.connection.status == .reasserting {
@ -89,12 +142,102 @@ class LokinetMain: NSObject, NSApplicationDelegate {
} else if self.vpnManager.connection.status == .disconnecting { } else if self.vpnManager.connection.status == .disconnecting {
NSLog("VPN is disconnecting...") NSLog("VPN is disconnecting...")
} else if self.vpnManager.connection.status == .connected { } else if self.vpnManager.connection.status == .connected {
NSLog("VPN Connected") self.result(msg: "VPN Connected")
} }
} }
} }
} }
let delegate = LokinetMain() #if MACOS_SYSTEM_EXTENSION
app.delegate = delegate
app.run() extension LokinetMain: OSSystemExtensionRequestDelegate {
func request(_: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
guard result == .completed else {
NSLog("Unexpected result %d for system extension request", result.rawValue)
return
}
NSLog("Lokinet system extension loaded")
setupVPNTunnel()
}
func request(_: OSSystemExtensionRequest, didFailWithError error: Error) {
NSLog("System extension request failed: %@", error.localizedDescription)
self.bail()
}
func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
NSLog("Extension %@ requires user approval", request.identifier)
}
func request(_ request: OSSystemExtensionRequest,
actionForReplacingExtension existing: OSSystemExtensionProperties,
withExtension extension: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction
{
NSLog("Replacing extension %@ version %@ with version %@", request.identifier, existing.bundleShortVersion, `extension`.bundleShortVersion)
return .replace
}
}
#endif
let args = CommandLine.arguments
// If we are invoked with no arguments then exec the gui. This is dumb, but there doesn't seem to
// be a nicer way to do this on Apple's half-baked platform because:
// - we have three "bundles" we need to manage: the GUI app, the system extension, and the Lokinet
// app (this file) which loads the system extension.
// - if we embed the system extension directly inside the GUI then it fails to launch because the
// electron GUI's requirements (needed for JIT) conflict with the ability to load a system
// extensions.
// - if we embed Lokinet.app inside Lokinet-GUI.app and then the system extension inside Lokinet.app
// then it works, but macos loses track of the system extension and doesn't remove it when you
// remove the application. (It breaks your system, leaving an impossible-to-remove system
// extension, in just the same way it breaks if you don't use Finder to remove the Application.
// Apple used to say (around 2 years ago as of writing) that they would fix this situation "soon",
// but hasn't, and has stopped saying anything about it.)
// - if we try to use multiple executables (one to launch the system extension, one simple shell
// script to execs the embedded GUI app) inside the Lokinet.app and make the GUI the default for
// the application then Lokinet gets killed by gatekeeper because code signing only applies the
// (required-for-system-extensions) provisioningprofile to the main binary in the app.
//
// So we are left needing *one* single binary that isn't the GUI but has to do double-duty for both
// exec'ing the binary and loading lokinet, depending on how it is called.
//
// But of course there is no way to specify command-line arguments to the default binary macOS runs,
// so we can't use a `--gui` flag or anything so abhorrent to macos purity, thus this nasty
// solution:
// - no args -- exec the GUI
// - `--start` -- load the system extension and start lokinet
// - `--stop` -- stop lokinet
//
// macOS: land of half-baked implementations and nasty hacks to make anything work.
if args.count == 1 {
let gui_path = Bundle.main.resourcePath! + "/../Helpers/Lokinet-GUI.app"
if !FileManager.default.fileExists(atPath: gui_path) {
NSLog("Could not find gui app at %@", gui_path)
exit(1)
}
let gui_url = URL(fileURLWithPath: gui_path, isDirectory: false)
let gui_app_conf = NSWorkspace.OpenConfiguration()
let group = DispatchGroup()
group.enter()
NSWorkspace.shared.openApplication(at: gui_url, configuration: gui_app_conf,
completionHandler: { (app, error) in
if error != nil {
NSLog("Error launching gui: %@", error!.localizedDescription)
} else {
NSLog("Lauched GUI");
}
group.leave()
})
group.wait()
} else if args.count == 2 {
let delegate = LokinetMain()
delegate.mode = args[1]
app.delegate = delegate
app.run()
} else {
NSLog(HELP_STRING)
}

@ -1,92 +0,0 @@
#include <llarp/config/config.hpp>
#include <llarp/router_contact.hpp>
#include <llarp/util/logging/logger.hpp>
#include <llarp/util/logging/ostream_logger.hpp>
#include <cxxopts.hpp>
#include <string>
#include <vector>
namespace
{
bool
dumpRc(const std::vector<std::string>& files)
{
nlohmann::json result;
for (const auto& file : files)
{
llarp::RouterContact rc;
const bool ret = rc.Read(file.c_str());
if (ret)
{
result[file] = rc.ToJson();
}
else
{
std::cerr << "file = " << file << " was not a valid rc file\n";
}
}
std::cout << result << "\n";
return true;
}
} // namespace
int
main(int argc, char* argv[])
{
cxxopts::Options options(
"lokinetctl",
"LokiNET is a free, open source, private, "
"decentralized, \"market based sybil resistant\" "
"and IP based onion routing network");
options.add_options()("v,verbose", "Verbose", cxxopts::value<bool>())(
"h,help", "help", cxxopts::value<bool>())(
"c,config",
"config file",
cxxopts::value<std::string>()->default_value(llarp::GetDefaultConfigPath().string()))(
"dump", "dump rc file", cxxopts::value<std::vector<std::string>>(), "FILE");
try
{
const auto result = options.parse(argc, argv);
if (result.count("verbose") > 0)
{
SetLogLevel(llarp::eLogDebug);
llarp::LogContext::Instance().logStream =
std::make_unique<llarp::OStreamLogStream>(true, std::cerr);
llarp::LogDebug("debug logging activated");
}
else
{
SetLogLevel(llarp::eLogError);
llarp::LogContext::Instance().logStream =
std::make_unique<llarp::OStreamLogStream>(true, std::cerr);
}
if (result.count("help") > 0)
{
std::cout << options.help() << std::endl;
return 0;
}
if (result.count("dump") > 0)
{
if (!dumpRc(result["dump"].as<std::vector<std::string>>()))
{
return 1;
}
}
}
catch (const cxxopts::OptionParseException& ex)
{
std::cerr << ex.what() << std::endl;
std::cout << options.help() << std::endl;
return 1;
}
return 0;
}

@ -0,0 +1,36 @@
# DNS in Lokinet
Lokinet uses dns are its primary interface for resolving, mapping and querying resources inside of lokinet.
This was done not because DNS is *good* protocol, but because there is almost no relevent userland applications that are incapable of interacting with DNS, across every platform.
Using DNS in lokinet allows for the most zero config setup possible with the current set of standard protocols.
Lokinet provides 2 internal gtld, `.loki` and `.snode`
## .snode
The `.snode` gtld is used to address a lokinet router in the form of `<zbase32 encoded public ed25519 identity key>.snode`.
Traffic bound to a `.snode` tld will have its source authenticatable only if it originates from another valid lokinet router.
Clients can also send traffic to and from addresses mapped to `.snode` addresses, but the source address on the service node side is ephemeral.
In both cases, ip traffic to addresses mapped to `.snode` addresses will have the destination ip rewritten by the lokinet router to be its local interface ip, this ensures traffic stays on the lokinet router' interface for snode traffic and preventing usage as an exit node.
## .loki
The `.loki` gtld is used to address anonymously published routes to lokinet clients on the network.
<!-- (todo: keyblinding info) -->
## What RR are provided?
All `.loki` domains by default have the following dns rr synthesized by lokinet:
* `A` record for initiating address mapping
* `MX` record pointing to the synthesizesd `A` record
* free wildcard entries for all of the above.
Wildard entries are currently only pointing
All `.snode` domains have by defult just an `A` record for initiating address mapping.
Additionally both `.loki` and `.snode` can optionally provide multiple `SRV` records to advertise existence of services on or off of the name.
<!-- (//todo: document and verify srv record limitations) -->

@ -0,0 +1,19 @@
## onion routing overview
<!-- todo: how is traffic transported (encryption, onion etc.) for somebody knowing nothing about LLARP) -->
<!-- todo: are there any techniques available to circumvent blocking of Lokinet traffic? (not at the moment) -->
<!-- todo: how does path multiplexing work? -->
## endpoint zmq api
<!-- todo: endpoint authentication (dns records) -->
## DNS
<!-- todo: how does LN handle DNS requests -->
<!-- todo: how are loki addresses looked up -->
<!-- todo: hoes does ONS work right now (info on lookup redundancy) -->

@ -1,3 +1,42 @@
# How Do I use lokinet? # What does Lokinet actually do?
`// TODO: this` Lokinet is an onion routed authenticated unicast IP network. It exposes an IP tunnel to the user and provides a dns resolver that maps `.loki` and `.snode` gtld onto a user defined ip range.
Lokinet allows users to tunnel arbitrary ip ranges to go to a `.loki` address to act as a tunnel broker via another network accessible via another lokinet client. This is commonly known as an "exit node" but the way lokinet does this is much more generic so that term is not very accurate given what it actually does.
The `.snode` gtld refers to a router on the network by its public ed25519 key.
The `.loki` gtld refers to clients that publish the existence anonymously to the network by their ed25519 public key. (`.loki` also has the ability to use short names resolved via external consensus method, like a blockchain).
# How Do I use Lokinet?
set system dns resolver to use the dns resolver provided by lokinet, make sure the upstream dns provider that lokinet uses for non lokinet gtlds is set as desired (see lokinet.ini `[dns]` section)
configure exit traffic provider if you want to tunnel ip traffic via lokinet, by default this is off as we cannot provide a sane defualt that makes everyone happy. to enable an exit node, see lokinet.ini `[network]` section, add multiple `exit-node=exitaddrgoeshere.loki` lines for each endpoint you want to use for exit traffic. each `exit-node` entry will be used to randomly stripe across per IP you are sending to.
note: per flow (ip+proto/port) isolation is trivial on a technical level but currently not implemented at this time.
# Can I run lokinet on a soho router
Yes and that is the best way to run it in practice.
## The "easy" way
We have a community maintained solution for ARM SBCs like rasperry pi: https://github.com/necro-nemesis/LabyrinthAP
## The "fun" way (DIY)
It is quite nice to DIY. if you choose to do so there is some assembly required:
on the lokinet side, make sure that the...
* ip ranges for `.loki` and `.snode` are statically set (see lokinet.ini `[network]` section `ifaddr=` option)
* network interace used by lokinet is statically set (see lokinet.ini `[network]` section `ifname=` option)
* dns socket is bound to an address the soho router's dns resolver can talk to, see `[dns]` section `bind=` option)
on the soho router side:
* route queries for `.loki` and `.snode` gtld to go to lokinet dns on soho router's dns resolver
* use dhcp options to set dns to use the soho router's dns resolver
* make sure that the ip ranges for lokinet are reachable via the LAN interface
* if you are tunneling over an exit ensure that LAN traffic will only forward to go over the lokinet vpn interface

@ -0,0 +1,174 @@
# Installing
If you are simply looking to install Lokinet and don't want to compile it yourself we provide several options for platforms to run on:
Tier 1:
* [Linux](#linux-install)
* [Windows](#windows-install)
* [MacOS](#macos-install)
Tier 2:
* [FreeBSD](#freebsd-install)
Currently Unsupported Platforms: (maintainers welcome)
* [Android](#apk-install)
* Apple iPhone
* Homebrew
* \[Insert Flavor of the Month windows package manager here\]
## Official Builds
### Windows / MacOS <span id="windows-install" /> <span id="macos-install" />
You can get the latest stable release for lokinet on windows or macos from https://lokinet.org/ or check the [releases page on github](https://github.com/oxen-io/lokinet/releases).
### Linux <span id="linux-install" />
You do not have to build from source if you do not wish to, we provide [apt](#deb-install) and [rpm](#rpm-install) repos.
#### APT repository <span id="deb-install" />
You can install debian packages from `deb.oxen.io` by adding the apt repo to your system.
$ sudo curl -so /etc/apt/trusted.gpg.d/oxen.gpg https://deb.oxen.io/pub.gpg
$ echo "deb https://deb.oxen.io $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/oxen.list
This apt repo is also available via lokinet at `http://deb.loki`
Once added you can install lokinet with:
$ sudo apt update
$ sudo apt install lokinet
When running from debian package the following steps are not needed as it is already running and ready to use. You can stop/start/restart it using `systemctl start lokinet`, `systemctl stop lokinet`, etc.
#### RPM <span id="rpm-install" />
We also provide an RPM repo, see `rpm.oxen.io`, also available on lokinet at `rpm.loki`
## Bleeding Edge dev builds <span id="ci-builds" />
automated builds from dev branches for the brave or impatient can be found from our CI pipeline [here](https://oxen.rocks/oxen-io/lokinet/). (warning: these nightly builds may or may not consume your first born child.)
## Building
Build requirements:
* Git
* CMake
* C++ 17 capable C++ compiler
* libuv >= 1.27.0
* libsodium >= 1.0.18
* libssl (for lokinet-bootstrap)
* libcurl (for lokinet-bootstrap)
* libunbound
* libzmq
* cppzmq
### Linux Compile
If you want to build from source: <span id="linux-compile" />
$ sudo apt install build-essential cmake git libcap-dev pkg-config automake libtool libuv1-dev libsodium-dev libzmq3-dev libcurl4-openssl-dev libevent-dev nettle-dev libunbound-dev libssl-dev nlohmann-json3-dev
$ git clone --recursive https://github.com/oxen-io/lokinet
$ cd lokinet
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF
$ make -j$(nproc)
$ sudo make install
set up the initial configs:
$ lokinet -g
$ lokinet-bootstrap
after you create default config, run it:
$ lokinet
This requires the binary to have the proper capabilities which is usually set by `make install` on the binary. If you have errors regarding permissions to open a new interface this can be resolved using:
$ sudo setcap cap_net_admin,cap_net_bind_service=+eip /usr/local/bin/lokinet
#### Arch Linux <span id="mom-cancel-my-meetings-arch-linux-broke-again" />
Due to [circumstances beyond our control](https://github.com/oxen-io/lokinet/discussions/1823) a working `PKGBUILD` can be found [here](https://raw.githubusercontent.com/oxen-io/lokinet/makepkg/contrib/archlinux/PKGBUILD).
#### Cross Compile For Linux <span id="linux-cross" />
current cross targets:
* aarch64-linux-gnu
* arm-linux-gnueabihf
* mips-linux-gnu
* mips64-linux-gnuabi64
* mipsel-linux-gnu
* powerpc64le-linux-gnu
install the toolchain (this one is for `aarch64-linux-gnu`, you can provide your own toolchain if you want)
$ sudo apt install g{cc,++}-aarch64-linux-gnu
build 1 or many cross targets:
$ ./contrib/cross.sh arch_1 arch_2 ... arch_n
### Building For Windows <span id="win32-cross" />
windows builds are cross compiled from debian/ubuntu linux
additional build requirements:
* nsis
* cpack
* rsvg-convert (`librsvg2-bin` package on Debian/Ubuntu)
setup:
$ sudo apt install build-essential cmake git pkg-config mingw-w64 nsis cpack automake libtool
$ sudo update-alternatives --set x86_64-w64-mingw32-gcc /usr/bin/x86_64-w64-mingw32-gcc-posix
$ sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw32-g++-posix
building:
$ git clone --recursive https://github.com/oxen-io/lokinet
$ cd lokinet
$ ./contrib/windows.sh
### Compiling for MacOS <span id="mac-compile" />
Source code compilation of Lokinet by end users is not supported or permitted by apple on their platforms, see [this](../contrib/macos/README.txt) for more information.
If you find this disagreeable consider using a platform that permits compiling from source.
### FreeBSD <span id="freebsd-install" />
Currently has no VPN Platform code, see issue `#1513`
build:
$ pkg install cmake git pkgconf
$ git clone --recursive https://github.com/oxen-io/lokinet
$ cd lokinet
$ mkdir build
$ cd build
$ cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON -DBUILD_STATIC_DEPS=ON ..
$ make
install (root):
# make install
### Android <span id="apk-install" />
We have an Android APK for lokinet VPN via android VPN API.
Coming to F-Droid whenever that happens. [[issue]](https://github.com/oxen-io/lokinet-flutter-app/issues/8)
* [source code](https://github.com/oxen-io/lokinet-flutter-app)

@ -1,73 +1,123 @@
Codesigning and notarization on macOS If you are reading this to try to build Lokinet for yourself for an Apple operating system and
simultaneously care about open source, privacy, or freedom then you, my friend, are a walking
contradiction: you are trying to get Lokinet to work on a platform that actively despises open
source, privacy, and freedom. Even Windows is a better choice in all of these categories than
Apple.
This is painful. Thankfully most of the pain is now in CMake and a python script. This directory contains the magical incantations and random voodoo symbols needed to coax an Apple
build. There's no reason builds have to be this stupid, except that Apple wants to funnel everyone
into the no-CI, no-help, undocumented, non-toy-apps-need-not-apply modern Apple culture.
To build, codesign, and notarized and installer package, CMake needs to be invoked with: This is disgusting.
cd build But it gets worse.
rm -rf * # optional but recommended
cmake .. -DBUILD_PACKAGE=ON -DDOWNLOAD_SODIUM=ON -DMACOS_SIGN_APP=ABC123... -DMACOS_SIGN_PKG=DEF456...
where the ABC123... key is a "Developer ID Installer" key and PKG key is a "Developer ID The following two files, in particular, are the very worst manifestations of this already toxic
Application" key. You have to go through a bunch of pain, pay Apple money, and then read a bunch of Apple cancer: they are required for proper permissions to run on macOS, are undocumented, and can
poorly written documentation that doesn't help very much to create these and get them working. But once you have them only be regenerated through the entirely closed source Apple Developer backend, for which you have
set up in Keychain, you should be able to list your keys with: to pay money first to get a team account (a personal account will not work), and they lock the
resulting binaries to only run on individually selected Apple computers selected at the time the
profile is provisioned (with no ability to allow it to run anywhere).
security find-identity -v lokinet.dev.provisionprofile
lokinet-extension.dev.provisionprofile
and you should see (at least) one "Developer ID Installer: ..." and one "Developer ID Application: This is actively hostile to open source development, but that is nothing new for Apple.
...". You need both for reasons that only Apple knows. The former is used to sign the installer
.pkg, and the latter is used to sign everything *inside* the .pkg, and you can't use the same key
for both because Apple designed code signing by marketing committee rather than ask any actual
competent software developers how code signing should work.
Either way, these two values can be specified either by hex value or description string that There are also release provisioning profiles
`security find-identity -v` spits out.
You also need to set up the notarization parameters; these can either be specified directly on the lokinet.release.provisionprofile
cmake command line by adding: lokinet-extension.release.provisionprofile
-DMACOS_NOTARIZE_ASC=XYZ123 -DMACOS_NOTARIZE_USER=me@example.com -DMACOS_NOTARIZE_PASS=@keychain:codesigning-password These ones allow distribution of the app, but only if notarized, and again require notarization plus
signing by a (paid) Apple developer account.
or, more simply, by putting them inside a `~/.notarization.cmake` file that will be included if it In order to make things work, you'll have to replace these provisioning profiles with your own
exists (and the MACOS_SIGN_* variables are set) -- see below. (after paying Apple for the privilege of developing on their platform, of course) and change all the
team/application/bundle IDs to reference your own team, matching the provisioning profiles. The dev
provisioning profiles must be a "macOS Development" provisioning profile, and must include the
signing keys and the authorized devices on which you want to run it. (The profiles bundled in this
repository contains the lokinet team's "Apple Development" keys associated with the Oxen project,
and mac dev boxes. This is *useless* for anyone else).
These three values here are: For release builds, you still need a provisioning profile, but it must be a "Distribution: Developer
ID" provisioning profile, and are tied to a (paid) Developer ID. The ones in the repository are
attached to the Oxen Project Developer ID and are useless to anyone else.
MACOS_NOTARIZE_ASC: Once you have that in place, you need to build and sign the package using a certificate matching
your provisioning profile before your Apple system will allow it to run. (That's right, your $2000
box won't let you run programs you build from source on it unless you also subscribe to a $100/year
Apple developer account).
Organization-specific unique value; this is printed inside (brackets) when you run: `security Okay, so now that you have paid Apple more money for the privilege of using your own computer,
find-identity -v`: here's how you make a signed lokinet app:
1) 1C75DDBF884DEF3D5927C3F29BB7FC5ADAE2E1B3 "Apple Development: me@example.com (ABC123XYZ9)" 1) Decide which type of build you are doing: a lokinet system extension, or an app extension. The
former must be signed and notarized and will only work when placed in the /Applications folder,
but will not work as a dev build and cannot be distributed outside the Mac App Store. The latter
is usable as a dev build, but still requires a signature and Apple-provided provisioningprofile
listing the limited number of devices on which it is allowed to run.
MACOS_NOTARIZE_USER: For system extension builds you want to add the -DMACOS_SYSTEM_EXTENSION=ON flag to cmake.
Your Apple Developer login. 2) Figure out the certificate to use for signing and make sure you have it installed. For a
distributable system extension build you need a "Developer ID Application" key and certificate,
issued by your paid developer.apple.com account. For dev builds you need a "Apple Development"
certificate.
MACOS_NOTARIZE_PASS: In most cases you don't need to specify these; the default cmake script will figure them out.
(If it can't, e.g. because you have multiple of the right type installed, it will error with the
keys it found).
This should be an app-specific password created for signing on the Apple Developer website. You To be explicit, use `security find-identity -v` to list your keys, then list the key identity
*can* specify it directly, but it is much better to use the magic `@keychain:blah` value, where with -DCODESIGN_ID=.....
'blah' is a password name recorded in Keychain. To get that in place you run:
export HISTFILE='' # for bash: you don't want to store this in your history 3) If you are doing a system extension build you will need to provide notarization login information by adding:
xcrun altool --store-password-in-keychain-item "NOTARIZE_PASSWORD" -u "user" -p "password"
where NOTARIZE_PASSWORD is just some name for the password (I called it 'blah' or -DMACOS_NOTARIZE_ASC=XYZ123 -DMACOS_NOTARIZE_USER=me@example.com -DMACOS_NOTARIZE_PASS=@keychain:codesigning-password
'codesigning-password' above), and the "user" and "password" are replaced with your actual Apple
Developer account device-specific login credentials.
Optionally, put these last three inside a `~/.notarization.cmake` file: a) The first value (XYZ123) needs to be the organization-specific unique value, and is printed in
brackets in the certificate description. For example:
set(MACOS_NOTARIZE_USER "jagerman@jagerman.com") 15095CD1E6AF441ABC69BDC52EE186A18200A49F "Developer ID Application: Some Developer (ABC123XYZ9)"
set(MACOS_NOTARIZE_PASS "@keychain:codesigning-password")
set(MACOS_NOTARIZE_ASC "SUQ8J2PCT7")
Then, finally, you can build the package from the build directory with: would require ABC123XYZ9 for this field.
make package -j4 # or whatever -j makes you happy b) The USER field is your Apple Developer login e-mail address.
make notarize
The former builds and signs the package, the latter submits it for notarization. This can take a c) The PASS field is a keychain reference holding your "Application-Specific Password". To set
few minutes; the script polls Apple's server until it is finished passing or failing notarization. up such a password for your account, consult Apple documentation. Once you have it, load it
into your keychain via:
export HISTFILE='' # Don't want to store this in the shell history
xcrun altool --store-password-in-keychain-item "codesigning-password" -u "user" -p "password"
You can change "codesigning-password" to whatever you want (just make sure it agrees with the
-DMACOS_NOTARIZE_PASS option you build with). "user" and "password" should be your developer
account device-specific login credentials provided by Apple.
To make your life easier, stash these settings into a `~/.notarization.cmake` file inside your
home directory; if you have not specified them in the build, and this file exists, lokinet's
cmake will load it:
set(MACOS_NOTARIZE_USER "me@example.com")
set(MACOS_NOTARIZE_PASS "@keychain:codesigning-password")
set(MACOS_NOTARIZE_ASC "ABC123XYZ9")
4) Build and sign the package; there is a script `contrib/mac.sh` that can help (extra cmake options
you need can be appended to the end), or you can build yourself in a build directory. See the
script for the other cmake options that are typically needed. Note that `-G Ninja` (as well as a
working ninja builder) are required.
If you get an error `errSecInternalComponent` this is Apple's highly descriptive way of telling
you that you need to unlock your keychain, which you can do by running `security unlock`.
If doing it yourself, `ninja sign` will build and then sign the app.
If you need to also notarize (e.g. for a system extension build) run `./notarize.py` from the
build directory (or alternatively `ninja notarize`, but the former gives you status output while
it runs).
5) Packaging the app: you want to use `-DBUILD_PACKAGE=ON` when configuring with cmake and then,
once all signing and notarization is complete, run `cpack` which will give you a .dmg and a .zip
containing the release.

@ -0,0 +1,110 @@
# Lokinet project structure
this codebase is a bit large. this is a high level map of the current code structure.
## lokinet executable main functions `(/daemon)`
* `lokinet.cpp`: lokinet daemon executable
* `lokinet.swift`: macos sysex/appex executable
* `lokinet-vpn.cpp`: lokinet rpc tool for controlling exit node usage
* `lokinet-bootstrap.cpp`: legacy util for windows, downloads a bootstrap file via https
## lokinet public headers `(/include)`
`lokinet.h and lokinet/*.h`: C headers for embedded lokinet
`llarp.hpp`: semi-internal C++ header for lokinet executables
## lokinet core library `(/llarp)`
* `/llarp`: contains a few straggling compilation units
* `/llarp/android`: android platform compat shims
* `/llarp/apple`: all apple platform specific code
* `/llarp/config`: configuration structs, generation/parsing/validating of config files
* `/llarp/consensus`: network consenus and inter relay testing
* `/llarp/constants`: contains all compile time constants
* `/llarp/crypto`: cryptography interface and implementation, includes various secure helpers
* `/llarp/dht`: dht message structs, parsing, validation and handlers of dht related parts of the protocol
* `/llarp/dns`: dns subsytem, dns udp wire parsers, resolver, server, rewriter/interceptor, the works
* `/llarp/ev`: event loop interfaces and implementations
* `/llarp/exit`: `.snode` endpoint "backend"
* `/llarp/handlers`: packet endpoint "frontends"
* `/llarp/iwp`: "internet wire protocol", hacky homegrown durable udp wire protocol used in lokinet
* `/llarp/link`: linklayer (node to node) communcation subsystem
* `/llarp/messages`: linklayer message parsing and handling
* `/llarp/net`: wrappers and helpers for ip addresses / ip ranges / sockaddrs, hides platform specific implemenation details
* `/llarp/path`: onion routing path logic, both client and relay side, path selection algorithms.
* `/llarp/peerstats`: deprecated
* `/llarp/quic`: plainquic shims for quic protocol inside lokinet
* `/llarp/router`: the relm of the god objects
* `/llarp/routing`: routing messages (onion routed messages sent over paths), parsing, validation and handler interfaces.
* `/llarp/rpc`: lokinet zmq rpc server and zmq client for externalizing logic (like with blockchain state and custom `.loki` endpoint orchestration)
* `/llarp/service`: `.loki` endpoint "backend"
* `/llarp/simulation`: network simulation shims
* `/llarp/tooling`: network simulation tooling
* `/llarp/util`: utility function dumping ground
* `/llarp/vpn`: vpn tunnel implemenation for each supported platform
* `/llarp/win32`: windows specific code
## component relations
### `/llarp/service` / `/llarp/handlers` / `/llarp/exit`
for all codepaths for traffic over lokinet, there is 2 parts, the "frontend" and the "backend".
the "backend" is responsible for sending and recieving data inside lokinet using our internal formats via paths, it handles flow management, lookups, timeouts, handover, and all state we have inside lokinet.
the "fontend", is a translation layer that takes in IP Packets from the OS, and send it to the backend to go where ever it wants to go, and recieves data from the "backend" and sends it to the OS as an IP Packet.
there are 2 'backends': `.snode` and `.loki`
there are 2 'frontends': "tun" (generic OS vpn interface) and "null" (does nothing)
* `//TODO: the backends need to be split up into multiple sub components as they are a kitchen sink.`
* `//TODO: the frontends blend into the backend too much and need to have their boundery clearer.`
### `/llarp/ev` / `/llarp/net` / `/llarp/vpn`
these contain most of the os/platform specific bits
* `//TODO: untangle these`
### `/llarp/link` / `/llarp/iwp`
node to node traffic logic and wire protocol dialects
* `//TODO: make better definitions of interfaces`
* `//TODO: separte implementation details from interfaces`
## platform contrib code `(/contrib)`
grab bag directory for non core related platform specific non source code
* `/contrib/format.sh`: clang-format / jsonnetfmt / swiftformat helper, will check or correct code style.
system layer and packaging related:
* `/contrib/NetworkManager`
* `/contrib/apparmor`
* `/contrib/systemd-resolved`
* `/contrib/lokinet-resolvconf`
* `/contrib/bootstrap`
build shims / ci helpers
* `/contrib/ci`
* `/contrib/patches`
* `/contrib/cross`
* `/contrib/android.sh`
* `/contrib/android-configure.sh`
* `/contrib/windows.sh`
* `/contrib/windows-configure.sh`
* `/contrib/mac.sh`
* `/contrib/ios.sh`
* `/contrib/cross.sh`

@ -2,14 +2,26 @@
This is where Lokinet documentation lives. This is where Lokinet documentation lives.
[How Do I install Lokinet?](install.md)
[How Do I use Lokinet?](ideal-ux.md)
## High level ## High level
[How is Lokinet different to \[insert network technology name here\] ?](net-comparisons.md) [How is Lokinet different to \[insert network technology name here\] ?](net-comparisons.md)
[How Do I use Lokinet?](ideal-ux.md) <!-- [How does Lokinet work?](high-level-overview.md) -->
[Lokinet and DNS](dns-overview.md)
[What Lokinet can't do](we-cannot-make-sandwiches.md) [What Lokinet can't do](we-cannot-make-sandwiches.md)
## Lokinet Internals
[High level layout of the git repo](project-structure.md)
[Build Doxygen Docs for internals](doxygen.md)
## Lokinet (SN)Application Developer Portal ## Lokinet (SN)Application Developer Portal
@ -19,7 +31,4 @@ This is where Lokinet documentation lives.
[How do I embed lokinet into my application?](liblokinet-dev-guide.md) [How do I embed lokinet into my application?](liblokinet-dev-guide.md)
## Lokinet Internals
[Build Doxygen Docs for internals](doxygen.md)

@ -12,13 +12,23 @@ if(SUBMODULE_CHECK)
else() else()
message(FATAL_ERROR "Submodule 'external/${relative_path}' is not up-to-date. Please update with\ngit submodule update --init --recursive\nor run cmake with -DSUBMODULE_CHECK=OFF") message(FATAL_ERROR "Submodule 'external/${relative_path}' is not up-to-date. Please update with\ngit submodule update --init --recursive\nor run cmake with -DSUBMODULE_CHECK=OFF")
endif() endif()
# Extra arguments check nested submodules
foreach(submod ${ARGN})
execute_process(COMMAND git rev-parse "HEAD" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${relative_path}/${submod} OUTPUT_VARIABLE localHead)
execute_process(COMMAND git rev-parse "HEAD:${submod}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${relative_path} OUTPUT_VARIABLE checkedHead)
string(COMPARE EQUAL "${localHead}" "${checkedHead}" upToDate)
if (NOT upToDate)
message(FATAL_ERROR "Nested submodule '${relative_path}/${submod}' is not up-to-date. Please update with\ngit submodule update --init --recursive\nor run cmake with -DSUBMODULE_CHECK=OFF")
endif()
endforeach()
endfunction () endfunction ()
message(STATUS "Checking submodules") message(STATUS "Checking submodules")
check_submodule(nlohmann) check_submodule(nlohmann)
check_submodule(cxxopts) check_submodule(cxxopts)
check_submodule(ghc-filesystem) check_submodule(ghc-filesystem)
check_submodule(date) check_submodule(oxen-logging fmt spdlog)
check_submodule(pybind11) check_submodule(pybind11)
check_submodule(sqlite_orm) check_submodule(sqlite_orm)
check_submodule(oxen-mq) check_submodule(oxen-mq)
@ -29,23 +39,57 @@ if(SUBMODULE_CHECK)
endif() endif()
endif() endif()
macro(system_or_submodule BIGNAME smallname pkgconf subdir)
option(FORCE_${BIGNAME}_SUBMODULE "force using ${smallname} submodule" OFF)
if(NOT BUILD_STATIC_DEPS AND NOT FORCE_${BIGNAME}_SUBMODULE AND NOT FORCE_ALL_SUBMODULES)
pkg_check_modules(${BIGNAME} ${pkgconf} IMPORTED_TARGET)
endif()
if(${BIGNAME}_FOUND)
add_library(${smallname} INTERFACE)
if(NOT TARGET PkgConfig::${BIGNAME} AND CMAKE_VERSION VERSION_LESS "3.21")
# Work around cmake bug 22180 (PkgConfig::THING not set if no flags needed)
else()
target_link_libraries(${smallname} INTERFACE PkgConfig::${BIGNAME})
endif()
message(STATUS "Found system ${smallname} ${${BIGNAME}_VERSION}")
else()
message(STATUS "using ${smallname} submodule")
add_subdirectory(${subdir})
endif()
if(NOT TARGET ${smallname}::${smallname})
add_library(${smallname}::${smallname} ALIAS ${smallname})
endif()
endmacro()
system_or_submodule(OXENC oxenc liboxenc>=1.0.4 oxen-encoding)
system_or_submodule(OXENMQ oxenmq liboxenmq>=1.2.14 oxen-mq)
set(JSON_BuildTests OFF CACHE INTERNAL "")
set(JSON_Install OFF CACHE INTERNAL "")
system_or_submodule(NLOHMANN nlohmann_json nlohmann_json>=3.7.0 nlohmann)
if (STATIC OR FORCE_SPDLOG_SUBMODULE OR FORCE_FMT_SUBMODULE)
set(OXEN_LOGGING_FORCE_SUBMODULES ON CACHE INTERNAL "")
endif()
set(OXEN_LOGGING_SOURCE_ROOT "${PROJECT_SOURCE_DIR}" CACHE INTERNAL "")
add_subdirectory(oxen-logging)
if(WITH_HIVE) if(WITH_HIVE)
add_subdirectory(pybind11 EXCLUDE_FROM_ALL) add_subdirectory(pybind11 EXCLUDE_FROM_ALL)
endif() endif()
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann EXCLUDE_FROM_ALL)
add_subdirectory(cxxopts EXCLUDE_FROM_ALL) add_subdirectory(cxxopts EXCLUDE_FROM_ALL)
add_subdirectory(date EXCLUDE_FROM_ALL)
if(WITH_PEERSTATS)
add_library(sqlite_orm INTERFACE) add_library(sqlite_orm INTERFACE)
target_include_directories(sqlite_orm SYSTEM INTERFACE sqlite_orm/include) target_include_directories(sqlite_orm SYSTEM INTERFACE sqlite_orm/include)
if(NOT TARGET sqlite3) if(NOT TARGET sqlite3)
add_library(sqlite3 INTERFACE) add_library(sqlite3 INTERFACE)
pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3) pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3)
target_link_libraries(sqlite3 INTERFACE PkgConfig::SQLITE3) target_link_libraries(sqlite3 INTERFACE PkgConfig::SQLITE3)
endif()
target_link_libraries(sqlite_orm INTERFACE sqlite3)
endif() endif()
target_link_libraries(sqlite_orm INTERFACE sqlite3)
add_library(uvw INTERFACE) add_library(uvw INTERFACE)
target_include_directories(uvw INTERFACE uvw/src) target_include_directories(uvw INTERFACE uvw/src)
@ -80,4 +124,19 @@ if(WITH_BOOTSTRAP)
target_include_directories(cpr PUBLIC cpr/include) target_include_directories(cpr PUBLIC cpr/include)
target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL) target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL)
add_library(cpr::cpr ALIAS cpr) add_library(cpr::cpr ALIAS cpr)
file(READ cpr/CMakeLists.txt cpr_cmake_head LIMIT 1000)
if(cpr_cmake_head MATCHES "project\\(cpr VERSION ([0-9]+)\.([0-9]+)\.([0-9]+) LANGUAGES CXX\\)")
set(cpr_VERSION_MAJOR ${CMAKE_MATCH_1})
set(cpr_VERSION_MINOR ${CMAKE_MATCH_2})
set(cpr_VERSION_PATCH ${CMAKE_MATCH_3})
set(cpr_VERSION "${cpr_VERSION_MAJOR}.${cpr_VERSION_MINOR}.${cpr_VERSION_PATCH}")
set(cpr_VERSION_NUM "(${cpr_VERSION_MAJOR} * 0x10000 + ${cpr_VERSION_MINOR} * 0x100 + ${cpr_VERSION_PATCH})")
configure_file(cpr/cmake/cprver.h.in "${CMAKE_CURRENT_BINARY_DIR}/cpr_generated_includes/cpr/cprver.h")
target_include_directories(cpr PUBLIC "${CMAKE_CURRENT_BINARY_DIR}/cpr_generated_includes")
else()
message(FATAL_ERROR "Could not identify cpr submodule version!")
endif()
endif() endif()

2
external/cpr vendored

@ -1 +1 @@
Subproject commit aac5058a15e9ad5ad393973dc6fe44d7614a7f55 Subproject commit f88fd7737de3e640c61703eb57a0fa0ce00c60cd

2
external/cxxopts vendored

@ -1 +1 @@
Subproject commit 6fa46a748838d5544ff8e9ab058906ba2c4bc0f3 Subproject commit c74846a891b3cc3bfa992d588b1295f528d43039

1
external/date vendored

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

@ -1 +1 @@
Subproject commit 2a8b380f8d4e77b389c42a194ab9c70d8e3a0f1e Subproject commit cd6805e94dd5d6346be1b75a54cdc27787319dd2

2
external/nlohmann vendored

@ -1 +1 @@
Subproject commit db78ac1d7716f56fc9f1b030b715f872f93964e4 Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d

@ -1 +1 @@
Subproject commit 79193e58fb26624d40cd2e95156f78160f2b9b3e Subproject commit a869ae2b0152ad70855e3774a425c39a25ae1ca6

@ -0,0 +1 @@
Subproject commit 9f2323a2db5fc54fe8394892769eff859967f735

2
external/oxen-mq vendored

@ -1 +1 @@
Subproject commit eadb37c7654150bef18497773718f15ef843734a Subproject commit ac6ef82ff6fd20437b7d073466dbef82a95a2173

2
external/pybind11 vendored

@ -1 +1 @@
Subproject commit 8de7772cc72daca8e947b79b83fea46214931604 Subproject commit aa304c9c7d725ffb9d10af08a3b34cb372307020

13
external/readme.md vendored

@ -0,0 +1,13 @@
directory for git submodules
* 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)
* ngtcp2: quic implementation
* nlohmann: json parser
* oxen-encoding: [bencode](https://www.bittorrent.org/beps/bep_0003.html#bencoding)/endian header-only library
* oxen-logging: spdlog wrapper library
* oxen-mq: zmq wrapper library for threadpool and rpc
* pybind11: for pybind modules
* sqlite_orm: for peer stats db
* uvw: libuv header only library for main event loop

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

Loading…
Cancel
Save