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_name = 'Fedora 35';
local distro_docker = 'fedora:35';
local distro = 'fedora-36';
local distro_name = 'Fedora 36';
local distro_docker = 'fedora:36';
local submodules = {
name: 'submodules',
image: 'drone/git',
commands: ['git fetch --tags', 'git submodule update --init --recursive --depth=1']
name: 'submodules',
image: 'drone/git',
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) = {
kind: 'pipeline',
type: 'docker',
name: distro_name + ' (' + rpmarch + ')',
platform: { arch: buildarch },
steps: [
submodules,
{
name: 'build',
image: image,
environment: {
SSH_KEY: { from_secret: "SSH_KEY" },
RPM_BUILD_NCPUS: jobs
},
commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"',
dnf(rpmarch) + 'distro-sync',
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',
'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',
dnf(rpmarch) + 'builddep --spec SPECS/lokinet.spec',
'if [ -n "$CCACHE_DIR" ]; then mkdir -pv ~/.cache; ln -sv "$CCACHE_DIR" ~/.cache/ccache; fi',
'rpmbuild --define "_topdir $(pwd)" -bb SPECS/lokinet.spec',
'./SPECS/ci-upload.sh ' + distro + ' ' + rpmarch,
],
}
]
kind: 'pipeline',
type: 'docker',
name: distro_name + ' (' + rpmarch + ')',
platform: { arch: buildarch },
steps: [
submodules,
{
name: 'build',
image: image,
environment: {
SSH_KEY: { from_secret: 'SSH_KEY' },
RPM_BUILD_NCPUS: jobs,
},
commands: [
'echo "Building on ${DRONE_STAGE_MACHINE}"',
dnf(rpmarch) + 'distro-sync',
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',
'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',
dnf(rpmarch) + 'builddep --spec SPECS/lokinet.spec',
'if [ -n "$CCACHE_DIR" ]; then mkdir -pv ~/.cache; ln -sv "$CCACHE_DIR" ~/.cache/ccache; fi',
'rpmbuild --define "_topdir $(pwd)" -bb SPECS/lokinet.spec',
'./SPECS/ci-upload.sh ' + distro + ' ' + rpmarch,
],
},
],
};
[
rpm_pipeline(distro_docker),
rpm_pipeline("arm64v8/" + distro_docker, buildarch='arm64', rpmarch="aarch64", jobs=4)
rpm_pipeline(distro_docker),
rpm_pipeline('arm64v8/' + distro_docker, buildarch='arm64', rpmarch='aarch64', jobs=3),
]

9
.gitmodules vendored

@ -10,9 +10,6 @@
[submodule "test/Catch2"]
path = test/Catch2
url = https://github.com/catchorg/Catch2
[submodule "external/date"]
path = external/date
url = https://github.com/HowardHinnant/date.git
[submodule "external/pybind11"]
path = external/pybind11
url = https://github.com/pybind/pybind11
@ -36,3 +33,9 @@
[submodule "external/oxen-encoding"]
path = external/oxen-encoding
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)
# 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)
@ -25,17 +25,17 @@ endif()
project(lokinet
VERSION 0.9.9
VERSION 0.9.11
DESCRIPTION "lokinet - IP packet onion router"
LANGUAGES ${LANGS})
if(APPLE)
# 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.
set(LOKINET_APPLE_BUILD 0)
set(LOKINET_APPLE_BUILD 6)
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")
@ -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(EMBEDDED_CFG "optimise for older hardware or embedded systems" OFF)
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(USE_JEMALLOC "Link to jemalloc for memory allocations, if found" ON)
option(TESTNET "testnet build" 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(TRACY_ROOT "include tracy profiler source" OFF)
option(WITH_TESTS "build unit tests" OFF)
option(WITH_HIVE "build simulation stubs" OFF)
option(BUILD_PACKAGE "builds extra components for making an installer (with 'make package')" OFF)
option(WITH_BOOTSTRAP "build lokinet-bootstrap tool" ${DEFAULT_WITH_BOOTSTRAP})
option(WITH_PEERSTATS "build with experimental peerstats db support" OFF)
option(STRIP_SYMBOLS "strip off all debug symbols into an external archive for all executables built" OFF)
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)
@ -83,9 +85,17 @@ if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE RelWithDebInfo)
endif()
set(debug OFF)
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
set(debug ON)
add_definitions(-DLOKINET_DEBUG)
endif()
option(WARN_DEPRECATED "show deprecation warnings" ${debug})
if(BUILD_STATIC_DEPS AND STATIC_LINK)
message(STATUS "we are building static deps so we won't build shared libs")
set(BUILD_SHARED_LIBS OFF)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "")
endif()
include(CheckCXXSourceCompiles)
@ -99,7 +109,6 @@ set(CMAKE_C_EXTENSIONS OFF)
include(cmake/target_link_libraries_system.cmake)
include(cmake/add_import_library.cmake)
include(cmake/add_log_tag.cmake)
include(cmake/libatomic.cmake)
if (STATIC_LINK)
@ -107,10 +116,11 @@ if (STATIC_LINK)
message(STATUS "setting static library suffix search")
endif()
add_definitions(-D${CMAKE_SYSTEM_NAME})
include(cmake/gui-option.cmake)
include(cmake/solaris.cmake)
include(cmake/win32.cmake)
include(cmake/macos.cmake)
# No in-source building
include(MacroEnsureOutOfSourceBuild)
@ -175,49 +185,19 @@ if(NOT TARGET sodium)
export(TARGETS sodium NAMESPACE sodium:: FILE sodium-exports.cmake)
endif()
option(FORCE_OXENC_SUBMODULE "force using oxen-encoding submodule" OFF)
if(NOT FORCE_OXENC_SUBMODULE)
pkg_check_modules(OXENC liboxenc>=1.0.3 IMPORTED_TARGET)
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)
set(warning_flags -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Werror=vla)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND warning_flags -Wno-unknown-warning-option)
endif()
if(OXENMQ_FOUND)
add_library(oxenmq::oxenmq ALIAS PkgConfig::OXENMQ)
message(STATUS "Found system liboxenmq ${OXENMQ_VERSION}")
if(WARN_DEPRECATED)
list(APPEND warning_flags -Wdeprecated-declarations)
else()
message(STATUS "using oxenmq submodule")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/external/oxen-mq)
list(APPEND warning_flags -Wno-deprecated-declarations)
endif()
if(NOT APPLE)
add_compile_options(-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0 -Wall -Wextra -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations -Werror=vla)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wno-unknown-warning-option)
endif()
endif()
if (NOT CMAKE_SYSTEM_NAME MATCHES "Linux" AND SHADOW)
message( FATAL_ERROR "shadow-framework is Linux only" )
endif()
# If we blindly add these directly as compile_options then they get passed to swiftc on Apple and
# break, so we use a generate expression to set them only for C++/C/ObjC
add_compile_options("$<$<OR:$<COMPILE_LANGUAGE:CXX>,$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:OBJC>>:${warning_flags}>")
if(XSAN)
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fsanitize=${XSAN} -fno-omit-frame-pointer -fno-sanitize-recover")
@ -227,20 +207,6 @@ if(XSAN)
message(STATUS "Doing a ${XSAN} sanitizer build")
endif()
if(CMAKE_BUILD_TYPE MATCHES "[Dd][Ee][Bb][Uu][Gg]")
add_definitions(-DLOKINET_DEBUG=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)
# 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)
find_package(Threads REQUIRED)
if(USE_NETNS)
add_definitions(-DNETNS=1)
else()
add_definitions(-DNETNS=0)
endif()
if(TESTNET)
add_definitions(-DTESTNET=1)
add_definitions(-DTESTNET)
# 5 times slower than realtime
# add_definitions(-DTESTNET_SPEED=5)
endif()
if(SHADOW)
include(cmake/shadow.cmake)
endif()
unset(GIT_VERSION)
unset(GIT_VERSION_REAL)
@ -319,7 +275,6 @@ if(WITH_SYSTEMD AND (NOT ANDROID))
endif()
add_subdirectory(external)
include_directories(SYSTEM external/sqlite_orm/include)
if(USE_JEMALLOC AND NOT STATIC_LINK)
pkg_check_modules(JEMALLOC jemalloc IMPORTED_TARGET)
@ -339,12 +294,8 @@ if(ANDROID)
set(ANDROID_PLATFORM_SRC android/ifaddrs.c)
endif()
if(TRACY_ROOT)
target_link_libraries(base_libs INTERFACE dl)
endif()
if(WITH_HIVE)
add_definitions(-DLOKINET_HIVE=1)
add_definitions(-DLOKINET_HIVE)
endif()
add_subdirectory(crypto)
@ -356,17 +307,21 @@ endif()
if(WITH_HIVE)
add_subdirectory(pybind)
endif()
if (NOT SHADOW)
if(WITH_TESTS OR WITH_HIVE)
add_subdirectory(test)
endif()
if(ANDROID)
add_subdirectory(jni)
endif()
if(WITH_TESTS OR WITH_HIVE)
add_subdirectory(test)
endif()
if(ANDROID)
add_subdirectory(jni)
endif()
add_subdirectory(docs)
include(cmake/gui.cmake)
if(APPLE)
macos_target_setup()
endif()
# uninstall target
if(NOT TARGET uninstall)
configure_file(
@ -378,7 +333,10 @@ if(NOT TARGET uninstall)
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake)
endif()
if(BUILD_PACKAGE AND NOT APPLE)
include(cmake/installer.cmake)
include(cmake/installer.cmake)
endif()
if(TARGET package)
add_dependencies(package assemble_gui)
endif()

@ -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.
* RUN `make format` BEFORE COMMITING ALWAYS.
* RUN `./contrib/format.sh` BEFORE COMMITING ALWAYS.
# 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]
Description=LokiNET: Anonymous Network layer thingydoo, client
AssertFileNotEmpty=/var/lib/lokinet/bootstrap.signed
Wants=network-online.target
After=network-online.target

@ -1,5 +1,5 @@
Name: lokinet
Version: 0.9.9
Version: 0.9.11
Release: 1%{?dist}
Summary: Lokinet anonymous, decentralized overlay network
@ -19,11 +19,6 @@ BuildRequires: systemd-devel
BuildRequires: systemd-rpm-macros
BuildRequires: libcurl-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}
%{?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 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/bootstrap.signed $RPM_BUILD_ROOT%{_sharedstatedir}/lokinet/bootstrap.signed
install -Dm644 contrib/bootstrap/mainnet.signed $RPM_BUILD_ROOT%{_sharedstatedir}/lokinet/bootstrap.signed
%files
@ -122,7 +117,7 @@ if ! getent group _loki >/dev/null; then
groupadd --system _loki
fi
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
# 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
%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
- bump version
- cmake flags for no system library search

@ -5,31 +5,31 @@
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_SOURCE openssl-${OPENSSL_VERSION}.tar.gz)
set(OPENSSL_HASH SHA256=9384a2b0570dd80358841464677115df785edb941c71211f75076d72fe6b438f
set(OPENSSL_HASH SHA256=83049d042a260e696f62406ac5c08bf706fd84383f945cf21bd61e9ed95c396e
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}")
set(EXPAT_MIRROR ${LOCAL_MIRROR} https://github.com/libexpat/libexpat/releases/download/${EXPAT_TAG}
CACHE STRING "expat download mirror(s)")
set(EXPAT_SOURCE expat-${EXPAT_VERSION}.tar.xz)
set(EXPAT_HASH SHA256=f79b8f904b749e3e0d20afeadecf8249c55b2e32d4ebb089ae378df479dcaf25
set(EXPAT_HASH SHA512=2da73b991b7c0c54440485c787e5edeb3567230204e31b3cac1c3a6713ec6f9f1554d3afffc0f8336168dfd5df02db4a69bcf21b4d959723d14162d13ab87516
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_SOURCE unbound-${UNBOUND_VERSION}.tar.gz)
set(UNBOUND_HASH SHA256=a480dc6c8937447b98d161fe911ffc76cfaffa2da18788781314e81339f1126f
set(UNBOUND_HASH SHA512=f6b9f279330fb19b5feca09524959940aad8c4e064528aa82b369c726d77e9e8e5ca23f366f6e9edcf2c061b96f482ed7a2c26ac70fc15ae5762b3d7e36a5284
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
CACHE STRING "sqlite3 download mirror(s)")
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")
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
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}
CACHE STRING "libuv mirror(s)")
set(LIBUV_SOURCE libuv-v${LIBUV_VERSION}.tar.gz)
set(LIBUV_HASH SHA512=b4f8944e2c79e3a6a31ded6cccbe4c0eeada50db6bc8a448d7015642795012a4b80ffeef7ca455bb093c59a8950d0e1430566c3c2fa87b73f82699098162d834
set(LIBUV_HASH SHA512=91197ff9303112567bbb915bbb88058050e2ad1c048815a3b57c054635d5dc7df458b956089d785475290132236cb0edcfae830f5d749de29a9a3213eeaf0b20
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
CACHE STRING "zlib mirror(s)")
set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.gz)
set(ZLIB_HASH SHA256=91844808532e5ce316b3c010929493c0244f3d37593afd6de04f71821d5136d9
set(ZLIB_SOURCE zlib-${ZLIB_VERSION}.tar.xz)
set(ZLIB_HASH SHA256=d14c38e313afc35a9a8760dadf26042f51ea0f5d154b0630a31da0540107fb98
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
CACHE STRING "curl mirror(s)")
set(CURL_SOURCE curl-${CURL_VERSION}.tar.xz)
set(CURL_HASH SHA256=2cb9c2356e7263a1272fd1435ef7cdebf2cd21400ec287b068396deb705c22c4
set(CURL_HASH SHA512=18e03a3c00f22125e07bddb18becbf5acdca22baeb7b29f45ef189a5c56f95b2d51247813f7a9a90f04eb051739e9aa7d3a1c5be397bae75d763a2b918d1b656
CACHE STRING "curl source hash")
include(ExternalProject)
@ -125,21 +125,21 @@ if(ANDROID)
set(android_toolchain_prefix x86_64)
set(android_toolchain_suffix linux-android)
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES x86)
set(android_machine i686)
set(android_machine x86)
set(cross_host "--host=i686-linux-android")
set(android_compiler_prefix i686)
set(android_compiler_suffix linux-android23)
set(android_toolchain_prefix i686)
set(android_toolchain_suffix linux-android)
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES armeabi-v7a)
set(android_machine armv7)
set(android_machine arm)
set(cross_host "--host=armv7a-linux-androideabi")
set(android_compiler_prefix armv7a)
set(android_compiler_suffix linux-androideabi23)
set(android_toolchain_prefix arm)
set(android_toolchain_suffix linux-androideabi)
elseif(CMAKE_ANDROID_ARCH_ABI MATCHES arm64-v8a)
set(android_machine aarch64)
set(android_machine arm64)
set(cross_host "--host=aarch64-linux-android")
set(android_compiler_prefix aarch64)
set(android_compiler_suffix linux-android23)
@ -167,6 +167,11 @@ if(APPLE)
set(deps_CXXFLAGS "${deps_CXXFLAGS} -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET}")
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")
set(_make $(MAKE))
@ -223,7 +228,7 @@ build_external(libuv
add_static_target(libuv libuv_external libuv.a)
target_link_libraries(libuv INTERFACE ${CMAKE_DL_LIBS})
build_external(zlib
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env "CC=${deps_cc}" "CFLAGS=${deps_CFLAGS} -fPIC" ${cross_extra} ./configure --prefix=${DEPS_DESTDIR} --static
BUILD_BYPRODUCTS
@ -234,43 +239,51 @@ add_static_target(zlib zlib_external libz.a)
set(openssl_system_env "")
set(openssl_arch "")
set(openssl_configure_command ./config)
set(openssl_flags "CFLAGS=${deps_CFLAGS}")
if(CMAKE_CROSSCOMPILING)
if(ARCH_TRIPLET STREQUAL x86_64-w64-mingw32)
set(openssl_system_env SYSTEM=MINGW64 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
set(openssl_arch mingw64)
set(openssl_system_env RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
elseif(ARCH_TRIPLET STREQUAL i686-w64-mingw32)
set(openssl_system_env SYSTEM=MINGW32 RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
set(openssl_arch mingw)
set(openssl_system_env RC=${CMAKE_RC_COMPILER} AR=${ARCH_TRIPLET}-ar RANLIB=${ARCH_TRIPLET}-ranlib)
elseif(ANDROID)
set(openssl_system_env SYSTEM=Linux MACHINE=${android_machine} LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar})
set(openssl_arch android-${android_machine})
set(openssl_system_env LD=${deps_ld} RANLIB=${deps_ranlib} AR=${deps_ar} ANDROID_NDK_ROOT=${CMAKE_ANDROID_NDK} "PATH=${CMAKE_ANDROID_NDK}/toolchains/llvm/prebuilt/linux-x86_64/bin:$ENV{PATH}")
list(APPEND openssl_flags "CPPFLAGS=-D__ANDROID_API__=${ANDROID_API}")
set(openssl_extra_opts no-asm)
elseif(ARCH_TRIPLET STREQUAL mips64-linux-gnuabi64)
set(openssl_system_env SYSTEM=Linux MACHINE=mips64)
set(openssl_configure_command ./Configure linux64-mips64)
set(openssl_arch linux-mips64)
elseif(ARCH_TRIPLET STREQUAL mips-linux-gnu)
set(openssl_system_env SYSTEM=Linux MACHINE=mips)
set(openssl_arch linux-mips32)
elseif(ARCH_TRIPLET STREQUAL mipsel-linux-gnu)
set(openssl_system_env SYSTEM=Linux MACHINE=mipsel)
set(openssl_arch linux-mips)
elseif(ARCH_TRIPLET STREQUAL aarch64-linux-gnu)
# cross compile arm64
set(openssl_system_env SYSTEM=Linux MACHINE=aarch64)
set(openssl_arch linux-aarch64)
elseif(ARCH_TRIPLET MATCHES arm-linux)
# cross compile armhf
set(openssl_system_env SYSTEM=Linux MACHINE=armv4)
set(openssl_arch linux-armv4)
elseif(ARCH_TRIPLET MATCHES powerpc64le)
# cross compile ppc64le
set(openssl_system_env SYSTEM=Linux MACHINE=ppc64le)
set(openssl_arch linux-ppc64le)
endif()
elseif(CMAKE_C_FLAGS MATCHES "-march=armv7")
# Help openssl figure out that we're building from armv7 even if on armv8 hardware:
set(openssl_system_env SYSTEM=Linux MACHINE=armv7)
set(openssl_arch linux-armv4)
endif()
build_external(openssl
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env CC=${deps_cc} ${openssl_system_env} ${openssl_configure_command}
--prefix=${DEPS_DESTDIR} ${openssl_extra_opts} no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost
no-heartbeats no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl2 no-ssl3
no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic "CFLAGS=${deps_CFLAGS}"
--prefix=${DEPS_DESTDIR} --libdir=lib ${openssl_extra_opts}
no-shared no-capieng no-dso no-dtls1 no-ec_nistp_64_gcc_128 no-gost
no-md2 no-rc5 no-rdrand no-rfc3779 no-sctp no-ssl-trace no-ssl3
no-static-engine no-tests no-weak-ssl-ciphers no-zlib no-zlib-dynamic ${openssl_flags}
${openssl_arch}
BUILD_COMMAND ${CMAKE_COMMAND} -E env ${openssl_system_env} ${_make}
INSTALL_COMMAND ${_make} install_sw
BUILD_BYPRODUCTS
${DEPS_DESTDIR}/lib/libssl.a ${DEPS_DESTDIR}/lib/libcrypto.a
@ -284,7 +297,6 @@ endif()
set(OPENSSL_INCLUDE_DIR ${DEPS_DESTDIR}/include)
set(OPENSSL_CRYPTO_LIBRARY ${DEPS_DESTDIR}/lib/libcrypto.a ${DEPS_DESTDIR}/lib/libssl.a)
set(OPENSSL_VERSION 1.1.1)
set(OPENSSL_ROOT_DIR ${DEPS_DESTDIR})
build_external(expat
@ -294,8 +306,15 @@ build_external(expat
)
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
DEPENDS openssl_external expat_external
${unbound_patch}
CONFIGURE_COMMAND ./configure ${cross_host} ${cross_rc} --prefix=${DEPS_DESTDIR} --disable-shared
--enable-static --with-libunbound-only --with-pic
--$<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}")
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)
@ -328,7 +350,9 @@ endif()
if(CMAKE_CROSSCOMPILING AND ARCH_TRIPLET MATCHES mingw)
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()
build_external(zmq
@ -351,6 +375,15 @@ set_target_properties(libzmq PROPERTIES
INTERFACE_LINK_LIBRARIES "${libzmq_link_libs}"
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)
return()
endif()
@ -420,7 +453,7 @@ foreach(curl_arch ${curl_arches})
list(APPEND curl_lib_outputs ${curl_prefix}/lib/libcurl.a)
endforeach()
message(STATUS "TARGETS: ${curl_lib_targets}")
if(IOS AND num_arches GREATER 1)
# 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.
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()
if(LOKINET_VERSIONTAG)
set(VERSIONTAG "${LOKINET_VERSIONTAG}")
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()

@ -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()
# Probably broken AF macos
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)
target_link_libraries(filesystem INTERFACE ghc_filesystem)
target_compile_definitions(filesystem INTERFACE USE_GHC_FILESYSTEM)

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

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

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

@ -1,34 +1,29 @@
if(NOT GUI_ZIP_URL)
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)
install(DIRECTORY ${CMAKE_BINARY_DIR}/gui DESTINATION share COMPONENT gui)
if(WITH_WINDOWS_32)
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/x86/wintun.dll DESTINATION bin COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.sys DESTINATION lib COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x86/WinDivert.dll DESTINATION bin COMPONENT lokinet)
else()
install(FILES ${CMAKE_BINARY_DIR}/wintun/bin/amd64/wintun.dll DESTINATION bin COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert64.sys DESTINATION lib COMPONENT lokinet)
install(FILES ${CMAKE_BINARY_DIR}/WinDivert-${WINDIVERT_VERSION}/x64/WinDivert.dll DESTINATION bin COMPONENT lokinet)
endif()
set(TUNTAP_URL "https://build.openvpn.net/downloads/releases/latest/tap-windows-latest-stable.exe")
set(TUNTAP_EXE "${CMAKE_BINARY_DIR}/tuntap-install.exe")
set(BOOTSTRAP_FILE "${PROJECT_SOURCE_DIR}/contrib/bootstrap/mainnet.signed")
file(DOWNLOAD
${TUNTAP_URL}
${TUNTAP_EXE})
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)
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_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_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
function(read_nsis_file filename outvar)
file(STRINGS "${filename}" _outvar)
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_INSTALL_COMMANDS "${_extra_install}")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS "${_extra_uninstall}")
set(CPACK_NSIS_CREATE_ICONS_EXTRA "${_extra_create_icons}")
set(CPACK_NSIS_DELETE_ICONS_EXTRA "${_extra_delete_icons}")
get_cmake_property(CPACK_COMPONENTS_ALL COMPONENTS)
list(REMOVE_ITEM CPACK_COMPONENTS_ALL "Unspecified")
set(CPACK_NSIS_COMPRESSOR "/SOLID lzma")

@ -1,17 +1,18 @@
#!/bin/bash
set -e
set +x
default_abis="armeabi-v7a arm64-v8a x86_64"
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
echo "building abis: $build_abis"
root="$(readlink -f $(dirname $0)/../)"
build=$root/build-android
root=$(readlink -f "$1")
shift
build=$(readlink -f "$1")
shift
mkdir -p $build
cd $build
@ -19,11 +20,13 @@ for abi in $build_abis; do
mkdir -p build-$abi
cd build-$abi
cmake \
-S "$root" -B . \
-G 'Unix Makefiles' \
-DANDROID=ON \
-DANDROID_ABI=$abi \
-DANDROID_ARM_MODE=arm \
-DANDROID_PLATFORM=android-23 \
-DANDROID_API=23 \
-DANDROID_STL=c++_static \
-DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
-DBUILD_STATIC_DEPS=ON \
@ -32,15 +35,19 @@ for abi in $build_abis; do
-DBUILD_TESTING=OFF \
-DBUILD_LIBLOKINET=OFF \
-DWITH_TESTS=OFF \
-DWITH_BOOTSTRAP=OFF \
-DNATIVE_BUILD=OFF \
-DSTATIC_LINK=ON \
-DWITH_SYSTEMD=OFF \
-DFORCE_OXENMQ_SUBMODULE=ON \
-DFORCE_OXENC_SUBMODULE=ON \
-DFORCE_FMT_SUBMODULE=ON \
-DFORCE_SPDLOG_SUBMODULE=ON \
-DFORCE_NLOHMANN_SUBMODULE=ON \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=OFF \
-DCMAKE_BUILD_TYPE=Release \
$@ $root
"$@"
cd -
done
rm -f $build/Makefile
@ -51,4 +58,11 @@ for abi in $build_abis; do
echo -ne '$(MAKE) -C ' >> $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 "clean-$abi:\n\t" >> $build/Makefile
echo -ne '$(MAKE) -C ' >> $build/Makefile
echo "build-$abi clean" >> $build/Makefile
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 +x
test x$NDK = x && echo "NDK env var not set"
test x$NDK = x && exit 1
root="$(readlink -f $(dirname $0)/../)"
cd "$root"
./contrib/android-configure.sh $@
./contrib/android-configure.sh . build-android "$@"
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=
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
fi
elif [ "$DRONE_STAGE_OS" == "linux" ]; then

@ -1,6 +1,9 @@
#!/usr/bin/env bash
test "x$IGNORE" != "x" && exit 0
. $(dirname $0)/../format-version.sh
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
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
os="${UPLOAD_OS:-$DRONE_STAGE_OS-$DRONE_STAGE_ARCH}"
if [ -n "$WINDOWS_BUILD_NAME" ]; then
os="windows-$WINDOWS_BUILD_NAME"
os="$UPLOAD_OS"
if [ -z "$os" ]; then
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
if [ -n "$DRONE_TAG" ]; then
@ -34,8 +40,11 @@ else
fi
mkdir -v "$base"
if [ -e build-windows ]; then
cp -av build-windows/lokinet-*.exe "$base"
if [ -e build/win32 ]; then
# save debug symbols
cp -av build/win32/daemon/debug-symbols.tar.xz "$base-debug-symbols.tar.xz"
# save installer
cp -av build/win32/*.exe "$base"
# zipit up yo
archive="$base.zip"
zip -r "$archive" "$base"
@ -47,9 +56,13 @@ elif [ -e build-docs ]; then
archive="$base.tar.xz"
cp -av build-docs/docs/mkdocs.yml build-docs/docs/markdown "$base"
tar cJvf "$archive" "$base"
elif [ -e build-mac ]; then
archive="$base.tar.xz"
mv build-mac/Lokinet*/ "$base"
tar cJvf "$archive" "$base"
else
cp -av daemon/lokinet daemon/lokinet-vpn "$base"
cp -av ../contrib/bootstrap/mainnet.signed "$base/bootstrap.signed"
cp -av build/daemon/lokinet{,-vpn} "$base"
cp -av contrib/bootstrap/mainnet.signed "$base/bootstrap.signed"
# tar dat shiz up yo
archive="$base.tar.xz"
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
# without error.
upload_dirs=(${upload_to//\// })
put_debug=
mkdirs=
dir_tmp=""
for p in "${upload_dirs[@]}"; do
@ -68,10 +82,13 @@ for p in "${upload_dirs[@]}"; do
mkdirs="$mkdirs
-mkdir $dir_tmp"
done
if [ -e "$base-debug-symbols.tar.xz" ] ; then
put_debug="put $base-debug-symbols.tar.xz $upload_to"
fi
sftp -i ssh_key -b - -o StrictHostKeyChecking=off drone@oxen.rocks <<SFTP
$mkdirs
put $archive $upload_to
$put_debug
SFTP
set +o xtrace

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

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

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

@ -15,27 +15,13 @@ root="$(readlink -e $(dirname $0)/../)"
cd $root
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=()
cmake_extra=()
while [ "$#" -gt 0 ]; do
if [ "$1" = "--" ]; then
shift
cmake_opts=$@
cmake_extra=("$@")
break
fi
targets+=("$1")
@ -55,7 +41,21 @@ for arch in $archs ; do
-DCMAKE_EXE_LINKER_FLAGS=-fstack-protector \
-DCMAKE_CXX_FLAGS=-fdiagnostics-color=always \
-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
cd $root/build-cross
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
# 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
CLANG_FORMAT_DESIRED_VERSION=11
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
. $(dirname $0)/format-version.sh
cd "$(dirname $0)/../"
if [ "$1" = "verify" ] ; then
if [ $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.([hc](pp)?|mm?)$' | grep -v '\#') | grep '</replacement>' | wc -l) -ne 0 ] ; then
if [ $($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
fi
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
swift_format=$(command -v swiftformat 2>/dev/null)
if [ $? -eq 0 ]; then
if [ "$1" = "verify" ] ; then
for f in $(find daemon | grep -E '\.swift$' | grep -v '\#') ; do
for f in $(find daemon | grep -E '\.swift$' | grep -v '#') ; do
if [ $($swift_format --quiet --dryrun < "$f" | diff "$f" - | wc -l) -ne 0 ] ; then
exit 3
fi
done
else
$swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '\#')
$swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '#')
fi
fi

@ -0,0 +1,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"?>
<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"
viewBox="0 0 189.4 189.4" style="enable-background:new 0 0 189.4 189.4;" xml:space="preserve">
<!-- 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">
.st0{fill:#FFFFFF;}
.bg{fill:#FFFFFF;}
</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 "/>
<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 "/>
<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 "/>
<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"/>
<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"/>
<!--
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(512)">
<circle r="1" class="bg"/>
</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>

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 +x
set -x
if ! [ -f LICENSE ] || ! [ -d llarp ]; then
echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory"
echo "You need to run this as ./contrib/mac.sh from the top-level lokinet project directory" >&2
exit 1
fi
mkdir -p build-mac
./contrib/mac-configure.sh "$@"
cd build-mac
cmake \
-G Ninja \
-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 \
-DSUBMODULE_CHECK=OFF \
-DWITH_LTO=ON \
-DCMAKE_BUILD_TYPE=Release \
"$@" \
..
ninja sign
rm -rf Lokinet\ *
ninja -j${JOBS:-1} package
cd ..
echo -e "Build complete, your app is here:\n"
ls -lad $(pwd)/daemon/lokinet.app
ls -lad $(pwd)/build-mac/Lokinet\ *
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">
<dict>
<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>
<array>
<string>packet-tunnel-provider</string>
<string>dns-proxy</string>
</array>
<key>com.apple.developer.team-identifier</key>
@ -16,9 +17,6 @@
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
<key>com.apple.security.network.client</key>
<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">
<dict>
<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>
<array>
<string>packet-tunnel-provider</string>
<string>dns-proxy</string>
<string>dns-settings</string>
<string>dns-proxy</string>
<string>dns-settings</string>
</array>
<key>com.apple.developer.team-identifier</key>
@ -18,13 +18,10 @@
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.network.server</key>
<key>com.apple.security.network.server</key>
<true/>
</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
# 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}"
for size in 16 32 64 128 256 512 1024; do
convert -background none -resize "${size}x${size}" "$svg" -strip "png32:${outdir}/icon_${size}x${size}.png"
for size in 32 64 128 256 512 1024; do
# 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
# 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"
for size in 16 32 128 256; do
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
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 subprocess
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@")
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()
result = subprocess.run([
command = [
'xcrun', 'altool',
'--notarize-app',
'--primary-bundle-id', 'org.lokinet.lokinet.pkg.@PROJECT_VERSION@',
'--primary-bundle-id', 'org.lokinet.@PROJECT_VERSION@',
*userpass,
'--asc-provider', "@MACOS_NOTARIZE_ASC@",
'--file', pkg,
'--file', zipfile,
'--output-format', 'xml'
], stdout=subprocess.PIPE)
]
print(command)
result = subprocess.run(command, stdout=subprocess.PIPE)
data = plistlib.loads(result.stdout)
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,
*userpass,
'--output-format', 'xml'
], stdout=subprocess.PIPE)
result.check_returncode()
data = plistlib.loads(result.stdout)
if 'notarization-info' not in data or 'Status' not in data['notarization-info']:
status = 'Request failed'
], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
if result.returncode == 1 and b'Gateway Timeout' in result.stderr:
status = "Apple's servers are trash (aka Gateway Timeout)"
else:
status = data['notarization-info']['Status Message'] if 'Status Message' in data['notarization-info'] else ''
st = data['notarization-info']['Status']
if st == 'success':
success = True
done = True
elif st == 'invalid':
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'])
result.check_returncode()
data = plistlib.loads(result.stdout)
if 'notarization-info' not in data or 'Status' not in data['notarization-info']:
status = 'Request failed'
else:
status = data['notarization-info']['Status Message'] if 'Status Message' in data['notarization-info'] else ''
st = data['notarization-info']['Status']
if st == 'success':
success = True
done = True
elif st == 'invalid':
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
mins, secs = int(elapsed // 60), int(elapsed % 60)
@ -70,7 +99,15 @@ print("\n")
if not success:
sys.exit(42)
print("Stapling {}".format(pkg))
result = subprocess.run(['xcrun', 'stapler', 'staple', pkg])
if os.path.exists(zipfile):
os.remove(zipfile)
print("Stapling {}...".format(app), end='')
result = subprocess.run(['xcrun', 'stapler', 'staple', app])
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
set -e
codesign --verbose=4 --force -s "@CODESIGN_APPEX@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet-extension.entitlements.plist" \
--deep --strict --timestamp --options=runtime "@SIGN_TARGET@/Contents/PlugIns/lokinet-extension.appex"
for file in "@SIGN_TARGET@/Contents/MacOS/lokinet" "@SIGN_TARGET@" ; do
codesign --verbose=4 --force -s "@CODESIGN_APP@" \
--entitlements "@PROJECT_SOURCE_DIR@/contrib/macos/lokinet.entitlements.plist" \
--deep --strict --timestamp --options=runtime "$file"
if [ "@CODESIGN@" != "ON" ]; then
echo "Cannot codesign: this build was not configured with codesigning" >&2
exit 1
fi
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
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
#
# create signed release tarball with submodules bundled
# usage: ./contrib/tarball.sh [keyid]
#
repo=$(readlink -e $(dirname $0)/..)
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"
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 +x
mkdir -p build-windows
cd build-windows
cmake \
-G 'Unix Makefiles' \
-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)}
root="$(readlink -f $(dirname $0)/../)"
mkdir -p $root/build/win32
$root/contrib/windows-configure.sh $root $root/build/win32 "$@"
make package -j${JOBS:-$(nproc)} -C $root/build/win32

@ -57,7 +57,6 @@ else()
endif()
enable_lto(lokinet-cryptography)
add_log_tag(lokinet-cryptography)
if (WARNINGS_AS_ERRORS)
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)
add_executable(lokinet lokinet.swift)
enable_lto(lokinet)
target_compile_options(lokinet BEFORE PRIVATE -target x86_64-apple-macos${CMAKE_OSX_DEPLOYMENT_TARGET})
else()
add_executable(lokinet lokinet.cpp)
enable_lto(lokinet lokinet-vpn)
endif()
add_executable(lokinet-vpn lokinet-vpn.cpp)
enable_lto(lokinet lokinet-vpn)
list(APPEND exetargets lokinet-vpn)
if(WITH_BOOTSTRAP)
add_executable(lokinet-bootstrap lokinet-bootstrap.cpp)
list(APPEND exetargets lokinet-bootstrap)
enable_lto(lokinet-bootstrap)
endif()
@ -42,86 +46,45 @@ if(WITH_BOOTSTRAP)
endif()
endif()
set(exetargets lokinet lokinet-vpn)
if(WITH_BOOTSTRAP)
list(APPEND exetargets lokinet-bootstrap)
# cmake interface library for bunch of cmake hacks to fix final link order
add_library(hax_and_shims_for_cmake INTERFACE)
if(WIN32)
target_link_libraries(hax_and_shims_for_cmake INTERFACE uvw oxenmq::oxenmq -lws2_32 -lshlwapi -ldbghelp -luser32 -liphlpapi -lpsapi -luserenv)
endif()
foreach(exe ${exetargets})
if(WIN32 AND NOT MSVC_VERSION)
if(WIN32)
target_sources(${exe} PRIVATE ${CMAKE_BINARY_DIR}/${exe}.rc)
target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00)
target_link_libraries(${exe} PRIVATE ws2_32 iphlpapi)
elseif(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_link_directories(${exe} PRIVATE /usr/local/lib)
endif()
target_link_libraries(${exe} PUBLIC liblokinet)
target_link_libraries(${exe} PUBLIC lokinet-amalgum hax_and_shims_for_cmake)
if(STRIP_SYMBOLS)
add_custom_command(TARGET ${exe}
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} ARGS --only-keep-debug $<TARGET_FILE:${exe}> $<TARGET_FILE:${exe}>.debug
COMMAND ${CMAKE_STRIP} ARGS --strip-all $<TARGET_FILE:${exe}>)
endif()
target_include_directories(${exe} PUBLIC "${PROJECT_SOURCE_DIR}")
target_compile_definitions(${exe} PRIVATE -DVERSIONTAG=${GIT_VERSION_REAL})
add_log_tag(${exe})
if(should_install)
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()
install(TARGETS ${exe} RUNTIME DESTINATION bin COMPONENT lokinet)
endif()
endif()
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)
install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)")
endif()
if(STRIP_SYMBOLS)
add_custom_target(symbols ALL
COMMAND ${CMAKE_COMMAND} -E tar cJf ${CMAKE_CURRENT_BINARY_DIR}/debug-symbols.tar.xz $<TARGET_FILE:lokinet>.debug
DEPENDS lokinet)
endif()

@ -1,5 +1,6 @@
#include <oxenmq/oxenmq.h>
#include <nlohmann/json.hpp>
#include <fmt/core.h>
#include <cxxopts.hpp>
#include <future>
#include <vector>
@ -16,11 +17,11 @@
#include <sys/wait.h>
#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
std::optional<nlohmann::json>
LMQ_Request(
oxenmq::OxenMQ& lmq,
OMQ_Request(
oxenmq::OxenMQ& omq,
const oxenmq::ConnectionID& id,
std::string_view method,
std::optional<nlohmann::json> args = std::nullopt)
@ -37,11 +38,11 @@ LMQ_Request(
};
if (args.has_value())
{
lmq.request(id, method, handleRequest, args->dump());
omq.request(id, method, handleRequest, args->dump());
}
else
{
lmq.request(id, method, handleRequest);
omq.request(id, method, handleRequest);
}
auto ftr = result_promise.get_future();
const auto str = ftr.get();
@ -50,6 +51,50 @@ LMQ_Request(
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
main(int argc, char* argv[])
{
@ -74,8 +119,8 @@ main(int argc, char* argv[])
oxenmq::address rpcURL("tcp://127.0.0.1:1190");
std::string exitAddress;
std::string endpoint = "default";
std::optional<std::string> token;
std::string range = "::/0";
std::string token;
std::optional<std::string> range;
oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn;
bool goUp = false;
bool goDown = false;
@ -95,69 +140,48 @@ main(int argc, char* argv[])
{
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;
goDown = result.count("down") > 0;
printStatus = result.count("status") > 0;
killDaemon = result.count("kill") > 0;
if (result.count("endpoint") > 0)
{
endpoint = result["endpoint"].as<std::string>();
}
if (result.count("token") > 0)
{
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>();
}
extract_option(result, "rpc", rpcURL);
extract_option(result, "exit", exitAddress);
extract_option(result, "endpoint", endpoint);
extract_option(result, "token", token);
extract_option(result, "auth", token);
extract_option(result, "range", range);
}
catch (const cxxopts::option_not_exists_exception& ex)
{
std::cerr << ex.what();
std::cout << opts.help() << std::endl;
return 1;
return exit_error(2, "{}\n{}", ex.what(), opts.help());
}
catch (std::exception& ex)
{
std::cout << ex.what() << std::endl;
return 1;
}
if ((not goUp) and (not goDown) and (not printStatus) and (not killDaemon))
{
std::cout << opts.help() << std::endl;
return 1;
return exit_error(2, "{}", ex.what());
}
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())
{
std::cout << "no exit address provided" << std::endl;
return 1;
}
return exit_error("no exit address provided");
oxenmq::OxenMQ lmq{
oxenmq::OxenMQ omq{
[](oxenmq::LogLevel lvl, const char* file, int line, std::string msg) {
std::cout << lvl << " [" << file << ":" << line << "] " << msg << std::endl;
},
logLevel};
lmq.start();
omq.start();
std::promise<bool> connectPromise;
const auto connID = lmq.connect_remote(
const auto connID = omq.connect_remote(
rpcURL,
[&connectPromise](auto) { connectPromise.set_value(true); },
[&connectPromise](auto, std::string_view msg) {
@ -173,23 +197,16 @@ main(int argc, char* argv[])
if (killDaemon)
{
const auto maybe = LMQ_Request(lmq, connID, "llarp.halt");
if (not maybe.has_value())
{
std::cout << "call to llarp.admin.die failed" << std::endl;
return 1;
}
if (not OMQ_Request(omq, connID, "llarp.halt"))
return exit_error("call to llarp.halt failed");
return 0;
}
if (printStatus)
{
const auto maybe_status = LMQ_Request(lmq, connID, "llarp.status");
if (not maybe_status.has_value())
{
std::cout << "call to llarp.status failed" << std::endl;
return 1;
}
const auto maybe_status = OMQ_Request(omq, connID, "llarp.status");
if (not maybe_status)
return exit_error("call to llarp.status failed");
try
{
@ -209,43 +226,34 @@ main(int argc, char* argv[])
}
catch (std::exception& ex)
{
std::cout << "failed to parse result: " << ex.what() << std::endl;
return 1;
return exit_error("failed to parse result: {}", ex.what());
}
return 0;
}
if (goUp)
{
std::optional<nlohmann::json> maybe_result;
if (token.has_value())
{
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}});
}
nlohmann::json opts{{"exit", exitAddress}, {"token", token}};
if (range)
opts["range"] = *range;
if (not maybe_result.has_value())
{
std::cout << "could not add exit" << std::endl;
return 1;
}
auto maybe_result = OMQ_Request(omq, connID, "llarp.exit", std::move(opts));
if (not maybe_result)
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 1;
return exit_error("{}", err_it.value());
}
}
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;

@ -2,13 +2,15 @@
#include <llarp/constants/version.hpp>
#include <llarp.hpp>
#include <llarp/util/lokinet_init.h>
#include <llarp/util/exceptions.hpp>
#include <llarp/util/fs.hpp>
#include <llarp/util/logging/logger.hpp>
#include <llarp/util/logging/ostream_logger.hpp>
#include <llarp/util/str.hpp>
#ifdef _WIN32
#include <llarp/win32/service_manager.hpp>
#include <dbghelp.h>
#else
#include <llarp/util/service_manager.hpp>
#endif
#include <csignal>
@ -22,25 +24,24 @@ int
lokinet_main(int, char**);
#ifdef _WIN32
#include <strsafe.h>
extern "C" LONG FAR PASCAL
win32_signal_handler(EXCEPTION_POINTERS*);
extern "C" VOID FAR PASCAL
win32_daemon_entry(DWORD, LPTSTR*);
BOOL ReportSvcStatus(DWORD, DWORD, DWORD);
VOID
insert_description();
SERVICE_STATUS SvcStatus;
SERVICE_STATUS_HANDLE SvcStatusHandle;
bool start_as_daemon = false;
#endif
static auto logcat = llarp::log::Cat("main");
std::shared_ptr<llarp::Context> ctx;
std::promise<int> exit_code;
void
handle_signal(int sig)
{
llarp::log::info(logcat, "Handling signal {}", sig);
if (ctx)
ctx->loop->call([sig] { ctx->HandleSignal(sig); });
else
@ -83,9 +84,6 @@ install_win32_daemon()
llarp::LogError("Cannot install service ", GetLastError());
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.
schSCManager = OpenSCManager(
@ -102,7 +100,7 @@ install_win32_daemon()
// Create the service
schService = CreateService(
schSCManager, // SCM database
"lokinet", // name of service
strdup("lokinet"), // name of service
"Lokinet for Windows", // service name to display
SERVICE_ALL_ACCESS, // desired access
SERVICE_WIN32_OWN_PROCESS, // service type
@ -135,10 +133,10 @@ insert_description()
SC_HANDLE schSCManager;
SC_HANDLE schService;
SERVICE_DESCRIPTION sd;
LPTSTR szDesc =
LPTSTR szDesc = strdup(
"LokiNET is a free, open source, private, "
"decentralized, \"market based sybil resistant\" "
"and IP based onion routing network";
"and IP based onion routing network");
// Get a handle to the SCM database.
schSCManager = OpenSCManager(
NULL, // local computer
@ -229,7 +227,7 @@ uninstall_win32_daemon()
static void
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
{
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);
}
catch (llarp::util::bind_socket_error& ex)
{
llarp::LogError(fmt::format("{}, is lokinet already running? 🤔", ex.what()));
exit_code.set_value(1);
return;
}
catch (std::exception& ex)
{
llarp::LogError(
"failed to set up lokinet: ", ex.what(), ", is lokinet already running? 🤔");
llarp::LogError(fmt::format("failed to start up lokinet: {}", ex.what()));
exit_code.set_value(1);
return;
}
llarp::util::SetThreadName("llarp-mainloop");
auto result = ctx->Run(opts);
@ -289,46 +291,14 @@ run_main_context(std::optional<fs::path> confFile, const llarp::RuntimeOptions o
}
#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
/// will make a coredump when there is an unhandled exception
LONG
GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
{
const DWORD flags = MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData
| MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo;
const auto flags =
(MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo);
std::stringstream ss;
ss << "C:\\ProgramData\\lokinet\\crash-" << llarp::time_now_ms().count() << ".dmp";
@ -361,39 +331,57 @@ GenerateDump(EXCEPTION_POINTERS* pExceptionPointers)
int
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
return lokinet_main(argc, argv);
#else
SERVICE_TABLE_ENTRY DispatchTable[] = {
{"lokinet", (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
if (lstrcmpi(argv[1], "--win32-daemon") == 0)
{strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}};
// Try first to run as a service; if this works it fires off to win32_daemon_entry and doesn't
// return until the service enters STOPPED state.
if (StartServiceCtrlDispatcher(DispatchTable))
return 0;
auto error = GetLastError();
// We'll get this error if not invoked as a service, which is fine: we can just run directly
if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
{
start_as_daemon = true;
StartServiceCtrlDispatcher(DispatchTable);
llarp::sys::service_manager->disable();
return lokinet_main(argc, argv);
}
else
return lokinet_main(argc, argv);
{
llarp::log::critical(
logcat, "Error launching service: {}", std::system_category().message(error));
return 1;
}
#endif
}
int
lokinet_main(int argc, char* argv[])
lokinet_main(int argc, char** argv)
{
auto result = Lokinet_INIT();
if (result)
{
if (auto result = Lokinet_INIT())
return result;
}
llarp::RuntimeOptions opts;
opts.showBanner = false;
#ifdef _WIN32
WindowsServiceStopped stopped_raii;
if (startWinsock())
return -1;
SetConsoleCtrlHandler(handle_signal_win32, TRUE);
// SetUnhandledExceptionFilter(win32_signal_handler);
#endif
cxxopts::Options options(
"lokinet",
"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>())
("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>())
("c,colour", "colour output", cxxopts::value<bool>()->default_value("true"))
("config", "path to lokinet.ini configuration file", cxxopts::value<std::string>())
;
// clang-format on
@ -424,18 +411,11 @@ lokinet_main(int argc, char* 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"))
{
std::cout << options.help() << std::endl;
return 0;
}
if (result.count("version"))
{
std::cout << llarp::VERSION_FULL << std::endl;
@ -542,18 +522,15 @@ lokinet_main(int argc, char* argv[])
SetUnhandledExceptionFilter(&GenerateDump);
#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();
#ifdef _WIN32
ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);
#endif
do
{
// do periodic non lokinet related tasks here
if (ctx and ctx->IsUp() and not ctx->LooksAlive())
{
auto deadlock_cat = llarp::log::Cat("deadlock");
for (const auto& wtf :
{"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 "
"annoying image in your syslog for all time."})
{
llarp::LogError{wtf};
llarp::LogContext::Instance().ImmediateFlush();
llarp::log::critical(deadlock_cat, wtf);
llarp::log::flush();
}
#ifdef _WIN32
TellWindowsServiceStopped();
#endif
llarp::sys::service_manager->failed();
std::abort();
}
} while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready);
@ -604,7 +579,8 @@ lokinet_main(int argc, char* argv[])
code = 2;
}
llarp::LogContext::Instance().ImmediateFlush();
llarp::log::flush();
llarp::sys::service_manager->stopped();
if (ctx)
{
ctx.reset();
@ -613,29 +589,6 @@ lokinet_main(int argc, char* argv[])
}
#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
SvcCtrlHandler(DWORD dwCtrl)
@ -645,44 +598,45 @@ SvcCtrlHandler(DWORD dwCtrl)
switch (dwCtrl)
{
case SERVICE_CONTROL_STOP:
ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
// Signal the service to stop.
// tell service we are stopping
llarp::log::debug(logcat, "Windows service controller gave SERVICE_CONTROL_STOP");
llarp::sys::service_manager->system_changed_our_state(llarp::sys::ServiceState::Stopping);
handle_signal(SIGINT);
return;
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:
llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl);
break;
}
}
// The win32 daemon entry point is just a trampoline that returns control
// to the original lokinet entry
// and only gets called if we get --win32-daemon in the command line
// The win32 daemon entry point is where we go when invoked as a windows service; we do the required
// service dance and then pretend we were invoked via main().
VOID FAR PASCAL
win32_daemon_entry(DWORD argc, LPTSTR* argv)
win32_daemon_entry(DWORD, LPTSTR* argv)
{
// 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");
return;
}
// These SERVICE_STATUS members remain as set here
SvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
SvcStatus.dwServiceSpecificExitCode = 0;
// Report initial status to the SCM
ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);
// SCM clobbers startup args, regenerate them here
argc = 2;
argv[1] = "c:/programdata/lokinet/lokinet.ini";
argv[2] = nullptr;
lokinet_main(argc, argv);
// we hard code the args to lokinet_main.
// we yoink argv[0] (lokinet.exe path) and pass in the new args.
std::array args = {
reinterpret_cast<char*>(argv[0]),
reinterpret_cast<char*>(strdup("c:\\programdata\\lokinet\\lokinet.ini")),
reinterpret_cast<char*>(0)};
lokinet_main(args.size() - 1, args.data());
}
#endif

@ -1,34 +1,83 @@
import AppKit
import Foundation
import NetworkExtension
import SystemExtensions
let app = NSApplication.shared
let START = "--start"
let STOP = "--stop"
let HELP_STRING = "usage: lokinet {--start|--stop}"
class LokinetMain: NSObject, NSApplicationDelegate {
var vpnManager = NETunnelProviderManager()
let lokinetComponent = "com.loki-project.lokinet.network-extension"
var mode = START
let netextBundleId = "org.lokinet.network-extension"
func applicationDidFinishLaunching(_: Notification) {
setupVPNJizz()
if mode == START {
startNetworkExtension()
} else if mode == STOP {
tearDownVPNTunnel()
} else {
result(msg: HELP_STRING)
}
}
func bail() {
app.terminate(self)
}
func setupVPNJizz() {
NSLog("Starting up lokinet")
func result(msg: String) {
NSLog(msg)
// TODO: does lokinet continue after this?
bail()
}
func tearDownVPNTunnel() {
NSLog("Stopping Lokinet")
NETunnelProviderManager.loadAllFromPreferences { [self] (savedManagers: [NETunnelProviderManager]?, error: Error?) in
if let error = error {
NSLog(error.localizedDescription)
bail()
self.result(msg: error.localizedDescription)
return
}
if let savedManagers = savedManagers {
for manager in savedManagers {
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.lokinetComponent {
NSLog("%@", manager)
if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.netextBundleId {
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")
self.vpnManager = manager
}
@ -37,8 +86,10 @@ class LokinetMain: NSObject, NSApplicationDelegate {
let providerProtocol = NETunnelProviderProtocol()
providerProtocol.serverAddress = "loki.loki" // Needs to be set to some non-null dummy value
providerProtocol.username = "anonymous"
providerProtocol.providerBundleIdentifier = self.lokinetComponent
providerProtocol.enforceRoutes = true
providerProtocol.providerBundleIdentifier = self.netextBundleId
if #available(macOS 11, *) {
providerProtocol.enforceRoutes = true
}
// 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.
providerProtocol.includeAllNetworks = false
@ -46,28 +97,30 @@ class LokinetMain: NSObject, NSApplicationDelegate {
self.vpnManager.isEnabled = true
// self.vpnManager.isOnDemandEnabled = true
self.vpnManager.localizedDescription = "lokinet"
self.vpnManager.saveToPreferences(completionHandler: { error -> Void in
self.vpnManager.saveToPreferences(completionHandler: { [self] error -> Void in
if error != nil {
NSLog("Error saving to preferences")
NSLog(error!.localizedDescription)
bail()
self.result(msg: error!.localizedDescription)
} else {
self.vpnManager.loadFromPreferences(completionHandler: { error in
if error != nil {
NSLog("Error loading from preferences")
NSLog(error!.localizedDescription)
bail()
self.result(msg: error!.localizedDescription)
} else {
do {
NSLog("Trying to start")
self.initializeConnectionObserver()
try self.vpnManager.connection.startVPNTunnel()
} catch let error as NSError {
NSLog(error.localizedDescription)
bail()
self.result(msg: error.localizedDescription)
} catch {
NSLog("There was a fatal error")
bail()
self.result(msg: "There was a fatal error")
}
// 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() {
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 {
NSLog("VPN configuration is invalid")
self.result(msg: "VPN configuration is invalid")
} else if self.vpnManager.connection.status == .disconnected {
NSLog("VPN is disconnected.")
self.result(msg: "VPN is disconnected.")
} else if self.vpnManager.connection.status == .connecting {
NSLog("VPN is connecting...")
} else if self.vpnManager.connection.status == .reasserting {
@ -89,12 +142,102 @@ class LokinetMain: NSObject, NSApplicationDelegate {
} else if self.vpnManager.connection.status == .disconnecting {
NSLog("VPN is disconnecting...")
} else if self.vpnManager.connection.status == .connected {
NSLog("VPN Connected")
self.result(msg: "VPN Connected")
}
}
}
}
let delegate = LokinetMain()
app.delegate = delegate
app.run()
#if MACOS_SYSTEM_EXTENSION
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
rm -rf * # optional but recommended
cmake .. -DBUILD_PACKAGE=ON -DDOWNLOAD_SODIUM=ON -DMACOS_SIGN_APP=ABC123... -DMACOS_SIGN_PKG=DEF456...
But it gets worse.
where the ABC123... key is a "Developer ID Installer" key and PKG key is a "Developer ID
Application" key. You have to go through a bunch of pain, pay Apple money, and then read a bunch of
poorly written documentation that doesn't help very much to create these and get them working. But once you have them
set up in Keychain, you should be able to list your keys with:
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).
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:
...". 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.
This is actively hostile to open source development, but that is nothing new for Apple.
Either way, these two values can be specified either by hex value or description string that
`security find-identity -v` spits out.
There are also release provisioning profiles
You also need to set up the notarization parameters; these can either be specified directly on the
cmake command line by adding:
lokinet.release.provisionprofile
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
exists (and the MACOS_SIGN_* variables are set) -- see below.
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 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
find-identity -v`:
Okay, so now that you have paid Apple more money for the privilege of using your own computer,
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
*can* specify it directly, but it is much better to use the magic `@keychain:blah` value, where
'blah' is a password name recorded in Keychain. To get that in place you run:
To be explicit, use `security find-identity -v` to list your keys, then list the key identity
with -DCODESIGN_ID=.....
export HISTFILE='' # for bash: you don't want to store this in your history
xcrun altool --store-password-in-keychain-item "NOTARIZE_PASSWORD" -u "user" -p "password"
3) If you are doing a system extension build you will need to provide notarization login information by adding:
where NOTARIZE_PASSWORD is just some name for the password (I called it 'blah' or
'codesigning-password' above), and the "user" and "password" are replaced with your actual Apple
Developer account device-specific login credentials.
-DMACOS_NOTARIZE_ASC=XYZ123 -DMACOS_NOTARIZE_USER=me@example.com -DMACOS_NOTARIZE_PASS=@keychain:codesigning-password
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")
set(MACOS_NOTARIZE_PASS "@keychain:codesigning-password")
set(MACOS_NOTARIZE_ASC "SUQ8J2PCT7")
15095CD1E6AF441ABC69BDC52EE186A18200A49F "Developer ID Application: Some Developer (ABC123XYZ9)"
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
make notarize
b) The USER field is your Apple Developer login e-mail address.
The former builds and signs the package, the latter submits it for notarization. This can take a
few minutes; the script polls Apple's server until it is finished passing or failing notarization.
c) The PASS field is a keychain reference holding your "Application-Specific Password". To set
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.
[How Do I install Lokinet?](install.md)
[How Do I use Lokinet?](ideal-ux.md)
## High level
[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)
## Lokinet Internals
[High level layout of the git repo](project-structure.md)
[Build Doxygen Docs for internals](doxygen.md)
## 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)
## Lokinet Internals
[Build Doxygen Docs for internals](doxygen.md)

@ -12,13 +12,23 @@ if(SUBMODULE_CHECK)
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")
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 ()
message(STATUS "Checking submodules")
check_submodule(nlohmann)
check_submodule(cxxopts)
check_submodule(ghc-filesystem)
check_submodule(date)
check_submodule(oxen-logging fmt spdlog)
check_submodule(pybind11)
check_submodule(sqlite_orm)
check_submodule(oxen-mq)
@ -29,23 +39,57 @@ if(SUBMODULE_CHECK)
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)
add_subdirectory(pybind11 EXCLUDE_FROM_ALL)
endif()
set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann EXCLUDE_FROM_ALL)
add_subdirectory(cxxopts EXCLUDE_FROM_ALL)
add_subdirectory(date EXCLUDE_FROM_ALL)
add_library(sqlite_orm INTERFACE)
target_include_directories(sqlite_orm SYSTEM INTERFACE sqlite_orm/include)
if(NOT TARGET sqlite3)
add_library(sqlite3 INTERFACE)
pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3)
target_link_libraries(sqlite3 INTERFACE PkgConfig::SQLITE3)
if(WITH_PEERSTATS)
add_library(sqlite_orm INTERFACE)
target_include_directories(sqlite_orm SYSTEM INTERFACE sqlite_orm/include)
if(NOT TARGET sqlite3)
add_library(sqlite3 INTERFACE)
pkg_check_modules(SQLITE3 REQUIRED IMPORTED_TARGET sqlite3)
target_link_libraries(sqlite3 INTERFACE PkgConfig::SQLITE3)
endif()
target_link_libraries(sqlite_orm INTERFACE sqlite3)
endif()
target_link_libraries(sqlite_orm INTERFACE sqlite3)
add_library(uvw INTERFACE)
target_include_directories(uvw INTERFACE uvw/src)
@ -80,4 +124,19 @@ if(WITH_BOOTSTRAP)
target_include_directories(cpr PUBLIC cpr/include)
target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL)
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()

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