diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f982142c..9f9b1a4a0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,14 @@ 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(LANGS ASM C CXX) +if(APPLE) + set(LANGS ${LANGS} OBJC Swift) +endif() + find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) - foreach(lang C CXX) + foreach(lang ${LANGS}) 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 "") @@ -15,10 +20,11 @@ if(CCACHE_PROGRAM) endforeach() endif() + project(lokinet VERSION 0.9.5 DESCRIPTION "lokinet - IP packet onion router" - LANGUAGES C CXX) + LANGUAGES ${LANGS}) set(RELEASE_MOTTO "A Series of Tubes" CACHE STRING "Release motto") @@ -32,7 +38,6 @@ endif() list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake") - # Core options option(USE_AVX2 "enable avx2 code" OFF) option(USE_NETNS "enable networking namespace support. Linux only" OFF) @@ -94,14 +99,6 @@ endif() add_definitions(-D${CMAKE_SYSTEM_NAME}) -if(MSVC_VERSION) - enable_language(ASM_MASM) - list(APPEND CMAKE_ASM_MASM_SOURCE_FILE_EXTENSIONS s) - add_definitions(-D_WIN32_WINNT=0x0600 -DNOMINMAX -DSODIUM_STATIC) -else() - enable_language(ASM) -endif() - include(cmake/solaris.cmake) include(cmake/win32.cmake) @@ -184,8 +181,12 @@ else() endif() -# this is messing with release builds -add_compile_options(-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=0) +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" ) @@ -213,17 +214,6 @@ if(TRACY_ROOT) endif() -if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") - add_compile_options(-Wno-unknown-warning-option) -endif() - -if (NOT MSVC_VERSION) - add_compile_options(-Wall -Wextra -Wno-unknown-pragmas) - # vla are evil - add_compile_options(-Werror=vla) - add_compile_options(-Wno-unused-function -Wno-deprecated-declarations) -endif() - include(cmake/coverage.cmake) # these vars are set by the cmake toolchain spec @@ -231,18 +221,20 @@ if (WOW64_CROSS_COMPILE OR WIN64_CROSS_COMPILE) include(cmake/cross_compile.cmake) endif() -if(NATIVE_BUILD) - if(CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le) - add_compile_options(-mcpu=native -mtune=native) - else() - add_compile_options(-march=native -mtune=native) - endif() -elseif(NOT NON_PC_TARGET) - if (USE_AVX2) - add_compile_options(-march=haswell -mtune=haswell -mfpmath=sse) - else() - # Public binary releases - add_compile_options(-march=nocona -mtune=haswell -mfpmath=sse) +if(NOT APPLE) + if(NATIVE_BUILD) + if(CMAKE_SYSTEM_PROCESSOR STREQUAL ppc64le) + add_compile_options(-mcpu=native -mtune=native) + else() + add_compile_options(-march=native -mtune=native) + endif() + elseif(NOT NON_PC_TARGET) + if (USE_AVX2) + add_compile_options(-march=haswell -mtune=haswell -mfpmath=sse) + else() + # Public binary releases + add_compile_options(-march=nocona -mtune=haswell -mfpmath=sse) + endif() endif() endif() @@ -332,12 +324,11 @@ endif() add_subdirectory(crypto) add_subdirectory(llarp) add_subdirectory(daemon) + + if(WITH_HIVE) add_subdirectory(pybind) endif() - - - if (NOT SHADOW) if(WITH_TESTS OR WITH_HIVE) add_subdirectory(test) diff --git a/cmake/installer.cmake b/cmake/installer.cmake index 789a88ee4..b08a9d7b4 100644 --- a/cmake/installer.cmake +++ b/cmake/installer.cmake @@ -14,3 +14,4 @@ endif() # This must always be last! include(CPack) + diff --git a/cmake/macos_installer_deps.cmake b/cmake/macos_installer_deps.cmake index a56995a73..b315b389b 100644 --- a/cmake/macos_installer_deps.cmake +++ b/cmake/macos_installer_deps.cmake @@ -1,8 +1,9 @@ # macos specific cpack stuff goes here +return() + # Here we build lokinet-network-control-panel into 'lokinet-gui.app' in "extra/" where a postinstall # script will then move it to /Applications/. - set(LOKINET_GUI_REPO "https://github.com/oxen-io/loki-network-control-panel.git" CACHE STRING "Can be set to override the default lokinet-gui git repository") set(LOKINET_GUI_CHECKOUT "origin/master" @@ -18,83 +19,25 @@ set(MACOS_NOTARIZE_PASS "" set(MACOS_NOTARIZE_ASC "" CACHE STRING "set macos notarization asc provider; can also set it in ~/.notarization.cmake") -include(ExternalProject) - -message(STATUS "Building UninstallLokinet.app") - -ExternalProject_Add(lokinet-uninstaller - SOURCE_DIR ${CMAKE_SOURCE_DIR}/contrib/macos/uninstaller - CMAKE_ARGS -DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR} -DMACOS_SIGN=${MACOS_SIGN_APP} - -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -) - -message(STATUS "Building LokinetGUI.app from ${LOKINET_GUI_REPO} @ ${LOKINET_GUI_CHECKOUT}") - if(NOT BUILD_STATIC_DEPS) message(FATAL_ERROR "Building an installer on macos requires -DBUILD_STATIC_DEPS=ON") endif() - - -ExternalProject_Add(lokinet-gui - DEPENDS oxenmq::oxenmq - GIT_REPOSITORY "${LOKINET_GUI_REPO}" - GIT_TAG "${LOKINET_GUI_CHECKOUT}" - CMAKE_ARGS -DMACOS_APP=ON -DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR} -DMACOS_SIGN=${MACOS_SIGN_APP} - -DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH} -DBUILD_SHARED_LIBS=OFF - "-DOXENMQ_LIBRARIES=$$$$$" - "-DOXENMQ_INCLUDE_DIRS=$" - ) - -install(PROGRAMS ${CMAKE_SOURCE_DIR}/contrib/macos/lokinet_uninstall.sh - DESTINATION "bin/" - COMPONENT lokinet) - -install(DIRECTORY ${PROJECT_BINARY_DIR}/LokinetGUI.app - DESTINATION "../../Applications/Lokinet" - USE_SOURCE_PERMISSIONS - COMPONENT gui - PATTERN "*" - ) - -install(DIRECTORY ${PROJECT_BINARY_DIR}/UninstallLokinet.app - DESTINATION "../../Applications/Lokinet" - USE_SOURCE_PERMISSIONS - COMPONENT gui - PATTERN "*" - ) - -# copy files that will be later moved by the postinstall script to proper locations -install(FILES ${CMAKE_SOURCE_DIR}/contrib/macos/lokinet_macos_daemon_script.sh - ${CMAKE_SOURCE_DIR}/contrib/macos/network.loki.lokinet.daemon.plist - ${CMAKE_SOURCE_DIR}/contrib/macos/lokinet-newsyslog.conf - DESTINATION "extra/" - COMPONENT lokinet) - -set(CPACK_COMPONENTS_ALL lokinet gui) - -set(CPACK_COMPONENT_LOKINET_DISPLAY_NAME "Lokinet Service") -set(CPACK_COMPONENT_LOKINET_DESCRIPTION "Main Lokinet runtime service, managed by Launchd") - -set(CPACK_COMPONENT_GUI_DISPLAY_NAME "Lokinet GUI") -set(CPACK_COMPONENT_GUI_DESCRIPTION "Small GUI which provides stats and limited runtime control of the Lokinet service. Resides in the system tray.") - -set(CPACK_GENERATOR "productbuild") -set(CPACK_PACKAGING_INSTALL_PREFIX "/opt/lokinet") -set(CPACK_PREINSTALL_LOKINET_SCRIPT ${CMAKE_SOURCE_DIR}/contrib/macos/preinstall) -set(CPACK_POSTFLIGHT_LOKINET_SCRIPT ${CMAKE_SOURCE_DIR}/contrib/macos/postinstall) - -set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE.txt") - -set(CPACK_PRODUCTBUILD_IDENTITY_NAME "${MACOS_SIGN_PKG}") - -if(MACOS_SIGN_APP) - add_custom_target(sign ALL - echo "Signing lokinet and lokinet-vpn binaries" - COMMAND codesign -s "${MACOS_SIGN_APP}" --strict --options runtime --force -vvv $ $ - DEPENDS lokinet lokinet-vpn - ) -endif() +#set(CPACK_GENERATOR "Bundle") + +#set(MACOSX_BUNDLE_BUNDLE_NAME Lokinet) +#set(CPACK_BUNDLE_NAME Lokinet) +#set(CPACK_BUNDLE_PLIST ${CMAKE_SOURCE_DIR}/contrib/macos/Info.plist) +#set(CPACK_BUNDLE_ICON "${CMAKE_CURRENT_BINARY_DIR}/lokinet.icns") +#set(CPACK_BUNDLE_STARTUP_COMMAND ${CMAKE_BINARY_DIR}/daemon/lokinet) +#set(MACOSX_BUNDLE_GUI_IDENTIFIER org.lokinet.lokinet) +#set(MACOSX_BUNDLE_INFO_STRING "Lokinet IP Packet Onion Router") +#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 "© 2021, The Loki Project") +#set(CPACK_BUNDLE_APPLE_ENTITLEMENTS ${CMAKE_SOURCE_DIR}/contrib/macos/lokinet.entitlements) +#set(CPACK_BUNDLE_APPLE_CERT_APP "${MACOS_SIGN_APP}") if(MACOS_SIGN_APP AND MACOS_SIGN_PKG) if(NOT MACOS_NOTARIZE_USER) diff --git a/contrib/format.sh b/contrib/format.sh index f83c27436..26ed77d3c 100755 --- a/contrib/format.sh +++ b/contrib/format.sh @@ -18,7 +18,23 @@ fi cd "$(dirname $0)/../" if [ "$1" = "verify" ] ; then - exit $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.[hc](pp)?$' | grep -v '\#') | grep '' | wc -l) + if [ $($binary --output-replacements-xml $(find jni daemon llarp include pybind | grep -E '\.[hc](pp)?$' | grep -v '\#') | grep '' | wc -l) -ne 0 ] ; then + exit 1 + fi else $binary -i $(find jni daemon llarp include pybind | grep -E '\.[hc](pp)?$' | grep -v '\#') &> /dev/null fi + +swift_format=$(which swiftformat 2>/dev/null) +if [ $? -eq 0 ]; then + if [ "$1" = "verify" ] ; then + 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 1 + fi + done + else + $swift_format --quiet $(find daemon | grep -E '\.swift$' | grep -v '\#') + fi + +fi diff --git a/contrib/lokinet.svg b/contrib/lokinet.svg new file mode 100644 index 000000000..f8e760248 --- /dev/null +++ b/contrib/lokinet.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + diff --git a/contrib/mac.sh b/contrib/mac.sh new file mode 100755 index 000000000..62e14802e --- /dev/null +++ b/contrib/mac.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# build the shit on mac +# t. jeff +# + +set -e +set +x +mkdir -p build-mac +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=OFF \ + -DCMAKE_INSTALL_PREFIX=$(pwd) \ + -DCMAKE_BUILD_TYPE=Release \ + $@ .. +ninja install && ninja sign diff --git a/contrib/macos/Info.plist b/contrib/macos/Info.plist new file mode 100644 index 000000000..de014151e --- /dev/null +++ b/contrib/macos/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Lokinet + CFBundleExecutable + MacOS/lokinet + CFBundleIdentifier + org.lokinet.Daemon + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + lokinet + CFBundlePackageType + XPC! + CFBundleShortVersionString + 0.1 + CFBundleVersion + 0.1 + + diff --git a/contrib/macos/LokinetExtension.Info.plist b/contrib/macos/LokinetExtension.Info.plist new file mode 100644 index 000000000..bc6b357af --- /dev/null +++ b/contrib/macos/LokinetExtension.Info.plist @@ -0,0 +1,31 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + Lokinet + CFBundleExecutable + lokinet-extension + CFBundleIdentifier + org.lokinet.NetworkExtension + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + Lokinet + CFBundlePackageType + XPC! + CFBundleShortVersionString + 0.1 + CFBundleVersion + 0.1 + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + Lokinet.LLARPPacketTunnel + + + diff --git a/contrib/macos/lokinet.entitlements.plist b/contrib/macos/lokinet.entitlements.plist new file mode 100644 index 000000000..589ce8b28 --- /dev/null +++ b/contrib/macos/lokinet.entitlements.plist @@ -0,0 +1,10 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + + diff --git a/contrib/macos/uninstaller/mk-icns.sh b/contrib/macos/mk-icns.sh similarity index 100% rename from contrib/macos/uninstaller/mk-icns.sh rename to contrib/macos/mk-icns.sh diff --git a/contrib/macos/sign.sh.in b/contrib/macos/sign.sh.in new file mode 100755 index 000000000..fc1136d9b --- /dev/null +++ b/contrib/macos/sign.sh.in @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -x +set -e +for file in "${SIGN_TARGET}/Contents/Frameworks/lokinet-extension.framework" "${SIGN_TARGET}/Contents/MacOS/Lokinet" "${SIGN_TARGET}" ; do + codesign -vvvv --force -s "${CODESIGN_KEY}" --entitlements "${SIGN_ENTITLEMENTS}" --deep --timestamp --options=runtime "$file" +done + +codesign --verify "${SIGN_TARGET}" diff --git a/contrib/windows.sh b/contrib/windows.sh index c1224afe1..5d6bf399c 100755 --- a/contrib/windows.sh +++ b/contrib/windows.sh @@ -1,4 +1,9 @@ #!/bin/bash +# +# helper script for me for when i cross compile for windows +# t. jeff +# + set -e set +x mkdir -p build-windows diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 7c6e8a45b..257daebed 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -1,4 +1,16 @@ -add_executable(lokinet lokinet.cpp) +if(APPLE) + option(WITH_SWIFT "use swift" ON) + if(WITH_SWIFT) + add_executable(lokinet lokinet.swift) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Lokinet.modulemap.in ${CMAKE_CURRENT_BINARY_DIR}/swift/LokinetExtension/module.modulemap ESCAPE_QUOTES @ONLY) + target_include_directories(lokinet PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/swift) + else() + add_executable(lokinet lokinet.mm) + endif() + target_link_libraries(lokinet PUBLIC lokinet-extension) +else() + add_executable(lokinet lokinet.cpp) +endif() add_executable(lokinet-vpn lokinet-vpn.cpp) add_executable(lokinet-bootstrap lokinet-bootstrap.cpp) enable_lto(lokinet lokinet-vpn lokinet-bootstrap) @@ -30,7 +42,12 @@ if(NOT WIN32) target_link_libraries(lokinet-bootstrap PUBLIC OpenSSL::SSL OpenSSL::Crypto) endif() -foreach(exe lokinet lokinet-vpn lokinet-bootstrap) +set(exetargets lokinet-vpn lokinet-bootstrap) +if(NOT APPLE) + set(exetargets lokinet ${exes}) +endif() + +foreach(exe ${exetargets}) if(WIN32 AND NOT MSVC_VERSION) target_sources(${exe} PRIVATE ../llarp/win32/version.rc) target_link_libraries(${exe} PRIVATE -static-libstdc++ -static-libgcc --static -Wl,--pic-executable,-e,mainCRTStartup,--subsystem,console:5.00) @@ -46,10 +63,55 @@ foreach(exe lokinet lokinet-vpn lokinet-bootstrap) target_compile_definitions(${exe} PRIVATE -DVERSIONTAG=${GIT_VERSION_REAL}) add_log_tag(${exe}) if(should_install) - install(TARGETS ${exe} RUNTIME DESTINATION bin COMPONENT lokinet) + if(APPLE) + install(TARGETS ${exe} BUNDLE DESTINATION "${CMAKE_BINARY_DIR}" COMPONENT lokinet) + else() + install(TARGETS ${exe} RUNTIME DESTINATION bin COMPONENT lokinet) + endif() endif() endforeach() +if(APPLE) +# add_custom_command(TARGET lokinet +# POST_BUILD +# COMMAND ${CMAKE_COMMAND} -E echo "setting rpath" +# COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks/" $ +# ) + add_custom_target(icons ALL + COMMAND ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${CMAKE_CURRENT_BINARY_DIR}/lokinet.icns + DEPENDS ${PROJECT_SOURCE_DIR}/contrib/lokinet.svg ${PROJECT_SOURCE_DIR}/contrib/macos/mk-icns.sh) + add_dependencies(lokinet icons lokinet-extension) + install(TARGETS lokinet-extension FRAMEWORK DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/Lokinet.app/Contents/Frameworks" COMPONENT lokinet) + 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_MAJOR}.${lokinet_VERSION_MINOR}.${lokinet_VERSION_PATCH}" + MACOSX_BUNDLE_LONG_VERSION_STRING "${lokinet_VERSION}.$lokinet_VERSION_MINOR}" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${lokinet_VERSION_MAJOR}.${lokinet_VERSION_MINOR}" + MACOSX_BUNDLE_GUI_IDENTIFIER "org.lokinet.lokinet" + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_SOURCE_DIR}/contrib/macos/Info.plist" + MACOSX_BUNDLE_ICON_FILE "${CMAKE_CURRENT_BINARY_DIR}/lokinet.icns" + MACOSX_BUNDLE_COPYRIGHT "© 2021, The Loki Project") + option(CODESIGN_KEY "codesign all the shit with this key" OFF) + if (CODESIGN_KEY) + message(STATUS "codesigning with ${CODESIGN_KEY}") + set(SIGN_TARGET "${CMAKE_CURRENT_BINARY_DIR}/Lokinet.app") + set(SIGN_ENTITLEMENTS "${CMAKE_SOURCE_DIR}/contrib/macos/lokinet.entitlements.plist") + configure_file( + "${PROJECT_SOURCE_DIR}/contrib/macos/sign.sh.in" + "${CMAKE_BINARY_DIR}/sign.sh") + add_custom_target( + sign + DEPENDS "${CMAKE_BINARY_DIR}/sign.sh" lokinet lokinet-extension + COMMAND "${CMAKE_BINARY_DIR}/sign.sh" + ) + else() + message(STATUS "will not codesign") + endif() +endif() + if(SETCAP) install(CODE "execute_process(COMMAND ${SETCAP} cap_net_admin,cap_net_bind_service=+eip ${CMAKE_INSTALL_PREFIX}/bin/lokinet)") endif() diff --git a/daemon/Lokinet.modulemap.in b/daemon/Lokinet.modulemap.in new file mode 100644 index 000000000..607823e38 --- /dev/null +++ b/daemon/Lokinet.modulemap.in @@ -0,0 +1,4 @@ + +module LokinetExtension { + header "@CMAKE_SOURCE_DIR@/include/lokinet-extension.hpp" +} \ No newline at end of file diff --git a/daemon/lokinet.mm b/daemon/lokinet.mm new file mode 100644 index 000000000..1baa58379 --- /dev/null +++ b/daemon/lokinet.mm @@ -0,0 +1,9 @@ +#import +#include + + +int main (int argc, const char * argv[]) +{ + + return 0; +} diff --git a/daemon/lokinet.swift b/daemon/lokinet.swift new file mode 100644 index 000000000..28eefe06c --- /dev/null +++ b/daemon/lokinet.swift @@ -0,0 +1,77 @@ +// AppDelegateExtension.swift + +import Foundation +import LokinetExtension +import NetworkExtension + +class LokinetMain: NSObject { + var vpnManager = NETunnelProviderManager() + + let lokinetComponent = "org.lokinet.NetworkExtension" + var lokinetAdminTimer: DispatchSourceTimer? + + func runMain() { + print("Starting up lokinet") + NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in + if let error = error { + print(error) + } + + if let savedManagers = savedManagers { + for manager in savedManagers { + if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.lokinetComponent { + print("Found saved VPN Manager") + self.vpnManager = manager + } + } + } + let providerProtocol = NETunnelProviderProtocol() + providerProtocol.serverAddress = "lokinet" + providerProtocol.providerBundleIdentifier = self.lokinetComponent + self.vpnManager.protocolConfiguration = providerProtocol + self.vpnManager.isEnabled = true + self.vpnManager.saveToPreferences(completionHandler: { error -> Void in + if error != nil { + print("Error saving to preferences") + } else { + print("saved...") + self.vpnManager.loadFromPreferences(completionHandler: { error in + if error != nil { + print("Error loading from preferences") + } else { + do { + print("Trying to start") + self.initializeConnectionObserver() + try self.vpnManager.connection.startVPNTunnel() + } catch let error as NSError { + print(error) + } catch { + print("There was a fatal error") + } + } + }) + } + }) + } + } + + func initializeConnectionObserver() { + NotificationCenter.default.addObserver(forName: NSNotification.Name.NEVPNStatusDidChange, object: vpnManager.connection, queue: OperationQueue.main) { _ -> Void in + + if self.vpnManager.connection.status == .invalid { + print("VPN configuration is invalid") + } else if self.vpnManager.connection.status == .disconnected { + print("VPN is disconnected.") + } else if self.vpnManager.connection.status == .connecting { + print("VPN is connecting...") + } else if self.vpnManager.connection.status == .reasserting { + print("VPN is reasserting...") + } else if self.vpnManager.connection.status == .disconnecting { + print("VPN is disconnecting...") + } + } + } +} + +let lokinet = LokinetMain() +lokinet.runMain() diff --git a/daemon/swift/Lokinet/module.modulemap b/daemon/swift/Lokinet/module.modulemap new file mode 100644 index 000000000..fda10e4b7 --- /dev/null +++ b/daemon/swift/Lokinet/module.modulemap @@ -0,0 +1,4 @@ + +module Lokinet { + header "lokinet-extension.hpp" +} \ No newline at end of file diff --git a/daemon/swift/LokinetMain.swift b/daemon/swift/LokinetMain.swift new file mode 100644 index 000000000..561df3e7f --- /dev/null +++ b/daemon/swift/LokinetMain.swift @@ -0,0 +1,43 @@ +// AppDelegateExtension.swift +// lifed from yggdrasil network ios port +// + +import Foundation +import Lokinet +import NetworkExtension + +class LokinetMain: PlatformAppDelegate { + var vpnManager = NETunnelProviderManager() + var app = NSApplication.shared() + let lokinetComponent = "org.lokinet.NetworkExtension" + var lokinetAdminTimer: DispatchSourceTimer? + + func runMain() { + print("Starting up lokinet") + NETunnelProviderManager.loadAllFromPreferences { (savedManagers: [NETunnelProviderManager]?, error: Error?) in + if let error = error { + print(error) + } + + if let savedManagers = savedManagers { + for manager in savedManagers { + if (manager.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.lokinetComponent { + print("Found saved VPN Manager") + self.vpnManager = manager + } + } + } + + self.vpnManager.loadFromPreferences(completionHandler: { (error: Error?) in + if let error = error { + print(error) + } + self.vpnManager.localizedDescription = "Lokinet" + self.vpnManager.isEnabled = true + }) + } + app.finishLaunching() + app.run() + print("end") + } +} diff --git a/external/CMakeLists.txt b/external/CMakeLists.txt index 9634f0036..f574f4193 100644 --- a/external/CMakeLists.txt +++ b/external/CMakeLists.txt @@ -50,6 +50,16 @@ add_library(uvw INTERFACE) target_include_directories(uvw INTERFACE uvw/src) target_link_libraries(uvw INTERFACE libuv) + + +# We don't need any of these as we don't use the ssl crypto helper code at all: +set(ENABLE_GNUTLS OFF CACHE BOOL "Disable gnutls for ngtcp2") +set(ENABLE_OPENSSL OFF CACHE BOOL "Disable openssl for ngtcp2") +set(ENABLE_BORINGSSL OFF CACHE BOOL "Disable boringssl for ngtcp2") + +add_definitions(-D_GNU_SOURCE) +add_subdirectory(ngtcp2 EXCLUDE_FROM_ALL) + # cpr configuration. Ideally we'd just do this via add_subdirectory, but cpr's cmake requires # 3.15+, and we target lower than that (and this is fairly simple to build). @@ -75,11 +85,3 @@ target_link_libraries(cpr PUBLIC CURL::libcurl) target_include_directories(cpr PUBLIC cpr/include) target_compile_definitions(cpr PUBLIC CPR_CURL_NOSIGNAL) add_library(cpr::cpr ALIAS cpr) - -# We don't need any of these as we don't use the ssl crypto helper code at all: -set(ENABLE_GNUTLS OFF CACHE BOOL "Disable gnutls for ngtcp2") -set(ENABLE_OPENSSL OFF CACHE BOOL "Disable openssl for ngtcp2") -set(ENABLE_BORINGSSL OFF CACHE BOOL "Disable boringssl for ngtcp2") - -add_definitions(-D_GNU_SOURCE) -add_subdirectory(ngtcp2 EXCLUDE_FROM_ALL) diff --git a/include/llarp.hpp b/include/llarp.hpp index e333e2495..5838d3569 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -120,7 +120,6 @@ namespace llarp std::unique_ptr> closeWaiter; }; - } // namespace llarp #endif diff --git a/include/lokinet-extension.hpp b/include/lokinet-extension.hpp new file mode 100644 index 000000000..5bbae9946 --- /dev/null +++ b/include/lokinet-extension.hpp @@ -0,0 +1,18 @@ +#pragma once +#include +#include + +struct ContextWrapper; + +@interface LLARPPacketTunnel : NEPacketTunnelProvider +{ + @private + struct ContextWrapper* m_Context; +} +- (void)startTunnelWithOptions:(NSDictionary*)options + completionHandler:(void (^)(NSError* error))completionHandler; + +- (void)stopTunnelWithReason:(NEProviderStopReason)reason + completionHandler:(void (^)(void))completionHandler; + +@end diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 3ae31ae4e..aaf388457 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -262,6 +262,33 @@ if(BUILD_LIBLOKINET) add_log_tag(lokinet-shared) endif() +if(APPLE) + find_library(NETEXT NetworkExtension REQUIRED) + find_library(COREFOUNDATION CoreFoundation REQUIRED) + + add_library(lokinet-extension SHARED + framework.mm + ${CMAKE_SOURCE_DIR}/include/lokinet-extension.hpp) + target_include_directories(lokinet-extension PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}) + target_link_libraries(lokinet-extension PUBLIC + liblokinet + ${COREFOUNDATION} + ${NETEXT}) + + set_target_properties(lokinet-extension PROPERTIES + FRAMEWORK TRUE + FRAMEWORK_VERSION CXX + MACOSX_FRAMEWORK_IDENTIFIER org.lokinet.NetworkExtension + MACOSX_FRAMEWORK_INFO_PLIST ${CMAKE_SOURCE_DIR}/contrib/macos/LokinetExtension.Info.plist + # "current version" in semantic format in Mach-O binary file + VERSION 16.4.0 + # "compatibility version" in semantic format in Mach-O binary file + SOVERSION 1.0.0 + PUBLIC_HEADER ${CMAKE_SOURCE_DIR}/include/lokinet-extension.hpp) + +endif() + foreach(lokinet_lib liblokinet lokinet-platform lokinet-util lokinet-cryptography) add_log_tag(${lokinet_lib}) endforeach() diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index 4e1b4c80e..3542e9326 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -1446,4 +1446,18 @@ namespace llarp return config; } +#ifdef __APPLE__ + std::shared_ptr + Config::NetworkExtensionConfig() + { + auto config = std::make_shared(fs::path{}); + config->Load(); + config->logging.m_logLevel = eLogInfo; + config->api.m_enableRPCServer = false; + config->network.m_saveProfiles = false; + config->bootstrap.files.clear(); + return config; + } +#endif + } // namespace llarp diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 03f7a000c..35360aeb8 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -260,6 +260,11 @@ namespace llarp static std::shared_ptr EmbeddedConfig(); +#ifdef __APPLE__ + static std::shared_ptr + NetworkExtensionConfig(); +#endif + private: /// Load (initialize) a default config. /// diff --git a/llarp/framework.mm b/llarp/framework.mm new file mode 100644 index 000000000..a5bd7d93f --- /dev/null +++ b/llarp/framework.mm @@ -0,0 +1,202 @@ +#include +#include + +#include +#include +#include + +#include + +namespace llarp::apple +{ + struct FrameworkContext : public llarp::Context + { + + explicit FrameworkContext(NEPacketTunnelProvider * tunnel); + + ~FrameworkContext() {} + + std::shared_ptr + makeVPNPlatform() override; + + void + Start(); + + private: + NEPacketTunnelProvider * m_Tunnel; + std::unique_ptr m_Runner; + }; + + class VPNInterface final : public vpn::NetworkInterface + { + NEPacketTunnelProvider * m_Tunnel; + + static inline constexpr auto PacketQueueSize = 1024; + + thread::Queue m_ReadQueue; + + void + OfferReadPacket(NSData * data) + { + llarp::net::IPPacket pkt; + const llarp_buffer_t buf{static_cast(data.bytes), data.length}; + if(pkt.Load(buf)) + m_ReadQueue.tryPushBack(std::move(pkt)); + } + + public: + explicit VPNInterface(NEPacketTunnelProvider * tunnel) + : m_Tunnel{tunnel}, + m_ReadQueue{PacketQueueSize} + { + auto handler = + [this](NSArray * packets, NSArray *) + { + NSUInteger num = [packets count]; + for(NSUInteger idx = 0; idx < num ; ++idx) + { + NSData * pkt = [packets objectAtIndex:idx]; + OfferReadPacket(pkt); + } + }; + [m_Tunnel.packetFlow readPacketsWithCompletionHandler:handler]; + } + + int + PollFD() const override + { + return -1; + } + + std::string + IfName() const override + { + return ""; + } + + net::IPPacket + ReadNextPacket() override + { + net::IPPacket pkt{}; + if(not m_ReadQueue.empty()) + pkt = m_ReadQueue.popFront(); + return pkt; + } + + bool + WritePacket(net::IPPacket pkt) override + { + const sa_family_t fam = pkt.IsV6() ? AF_INET6 : AF_INET; + const uint8_t * pktbuf = pkt.buf; + const size_t pktsz = pkt.sz; + NSData * datapkt = [NSData dataWithBytes:pktbuf length:pktsz]; + NEPacket * npkt = [[NEPacket alloc] initWithData:datapkt protocolFamily:fam]; + NSArray * pkts = @[npkt]; + return [m_Tunnel.packetFlow writePacketObjects:pkts]; + } + + }; + + class VPNPlatform final : public vpn::Platform + { + NEPacketTunnelProvider * m_Tunnel; + public: + explicit VPNPlatform(NEPacketTunnelProvider * tunnel) + : m_Tunnel{tunnel} + { + } + + std::shared_ptr + ObtainInterface(vpn::InterfaceInfo) override + { + return std::make_shared(m_Tunnel); + } + }; + + + FrameworkContext::FrameworkContext(NEPacketTunnelProvider * tunnel) : + llarp::Context{}, + m_Tunnel{tunnel} + { + } + + void + FrameworkContext::Start() + { + std::promise result; + + m_Runner = std::make_unique( + [&result, this]() + { + const RuntimeOptions opts{}; + try + { + Setup(opts); + Configure(llarp::Config::NetworkExtensionConfig()); + } + catch(std::exception & ) + { + result.set_exception(std::current_exception()); + return; + } + result.set_value(); + Run(opts); + }); + + auto ftr = result.get_future(); + ftr.get(); + } + + std::shared_ptr + FrameworkContext::makeVPNPlatform() + { + return std::make_shared(m_Tunnel); + } +} + + +struct ContextWrapper +{ + std::shared_ptr m_Context; +public: + explicit ContextWrapper(NEPacketTunnelProvider * tunnel) : + m_Context{std::make_shared(tunnel)} + {} + + void + Start() + { + m_Context->Start(); + } + + void + Stop() + { + m_Context->CloseAsync(); + m_Context->Wait(); + } +}; + + + +@implementation LLARPPacketTunnel + +- (void)startTunnelWithOptions:(NSDictionary *)options completionHandler:(void (^)(NSError *error))completionHandler { + m_Context = new ContextWrapper{self}; + m_Context->Start(); + completionHandler(nullptr); +} + +- (void)stopTunnelWithReason:(NEProviderStopReason)reason +completionHandler:(void (^)(void))completionHandler { + if(m_Context) + { + m_Context->Stop(); + delete m_Context; + m_Context = nullptr; + } + completionHandler(); +} + + +@end diff --git a/llarp/vpn/apple.hpp b/llarp/vpn/apple.hpp index f20c50ce3..e69de29bb 100644 --- a/llarp/vpn/apple.hpp +++ b/llarp/vpn/apple.hpp @@ -1,173 +0,0 @@ -#pragma once - -#include -#include "common.hpp" - -#include -#include -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -namespace llarp::vpn -{ - class AppleInterface : public NetworkInterface - { - const int m_FD; - const InterfaceInfo m_Info; - std::string m_IfName; - - static void - Exec(std::string cmd) - { - LogDebug(cmd); - system(cmd.c_str()); - } - - public: - AppleInterface(InterfaceInfo info) - : m_FD{::socket(PF_SYSTEM, SOCK_DGRAM, SYSPROTO_CONTROL)}, m_Info{std::move(info)} - { - if (m_FD == -1) - throw std::invalid_argument{"cannot open control socket: " + std::string{strerror(errno)}}; - - ctl_info cinfo{}; - const std::string apple_utun = "com.apple.net.utun_control"; - std::copy_n(apple_utun.c_str(), apple_utun.size(), cinfo.ctl_name); - if (::ioctl(m_FD, CTLIOCGINFO, &cinfo) < 0) - { - ::close(m_FD); - throw std::runtime_error{"ioctl CTLIOCGINFO call failed: " + std::string{strerror(errno)}}; - } - sockaddr_ctl addr{}; - addr.sc_id = cinfo.ctl_id; - - addr.sc_len = sizeof(addr); - addr.sc_family = AF_SYSTEM; - addr.ss_sysaddr = AF_SYS_CONTROL; - addr.sc_unit = 0; - - if (connect(m_FD, (sockaddr*)&addr, sizeof(addr)) < 0) - { - ::close(m_FD); - throw std::runtime_error{ - "cannot connect to control socket address: " + std::string{strerror(errno)}}; - } - uint32_t namesz = IFNAMSIZ; - char name[IFNAMSIZ + 1]{}; - if (getsockopt(m_FD, SYSPROTO_CONTROL, 2, name, &namesz) < 0) - { - ::close(m_FD); - throw std::runtime_error{ - "cannot query for interface name: " + std::string{strerror(errno)}}; - } - m_IfName = name; - for (const auto& ifaddr : m_Info.addrs) - { - if (ifaddr.fam == AF_INET) - { - const huint32_t addr = net::TruncateV6(ifaddr.range.addr); - const huint32_t netmask = net::TruncateV6(ifaddr.range.netmask_bits); - const huint32_t daddr = addr & netmask; - Exec( - "/sbin/ifconfig " + m_IfName + " " + addr.ToString() + " " + daddr.ToString() - + " mtu 1500 netmask 255.255.255.255 up"); - Exec( - "/sbin/route add " + daddr.ToString() + " -netmask " + netmask.ToString() - + " -interface " + m_IfName); - Exec("/sbin/route add " + addr.ToString() + " -interface lo0"); - } - else if (ifaddr.fam == AF_INET6) - { - Exec("/sbin/ifconfig " + m_IfName + " inet6 " + ifaddr.range.ToString()); - } - } - } - - ~AppleInterface() - { - ::close(m_FD); - } - - std::string - IfName() const override - { - return m_IfName; - } - - int - PollFD() const override - { - return m_FD; - } - - net::IPPacket - ReadNextPacket() override - { - constexpr int uintsize = sizeof(unsigned int); - net::IPPacket pkt{}; - unsigned int pktinfo = 0; - const struct iovec vecs[2] = { - {.iov_base = &pktinfo, .iov_len = uintsize}, - {.iov_base = pkt.buf, .iov_len = sizeof(pkt.buf)}}; - int sz = readv(m_FD, vecs, 2); - if (sz >= uintsize) - pkt.sz = sz - uintsize; - else if (sz >= 0 || errno == EAGAIN || errno == EWOULDBLOCK) - pkt.sz = 0; - else - throw std::error_code{errno, std::system_category()}; - return pkt; - } - - bool - WritePacket(net::IPPacket pkt) override - { - static unsigned int af4 = htonl(AF_INET); - static unsigned int af6 = htonl(AF_INET6); - - const struct iovec vecs[2] = { - {.iov_base = pkt.IsV6() ? &af6 : &af4, .iov_len = sizeof(unsigned int)}, - {.iov_base = pkt.buf, .iov_len = pkt.sz}}; - - ssize_t n = writev(m_FD, vecs, 2); - if (n >= (int)sizeof(unsigned int)) - { - n -= sizeof(unsigned int); - return static_cast(n) == pkt.sz; - } - return false; - } - }; - - class ApplePlatform : public Platform - { - public: - std::shared_ptr - ObtainInterface(InterfaceInfo info) override - { - return std::make_shared(std::move(info)); - } - }; -} // namespace llarp::vpn diff --git a/llarp/vpn/platform.cpp b/llarp/vpn/platform.cpp index 0784ba110..341e28fcb 100644 --- a/llarp/vpn/platform.cpp +++ b/llarp/vpn/platform.cpp @@ -1,3 +1,6 @@ + +#include + #ifdef _WIN32 #include "win32.hpp" #endif @@ -8,9 +11,8 @@ #include "linux.hpp" #endif #endif -#ifdef __APPLE__ -#include "apple.hpp" -#endif + +#include namespace llarp::vpn { @@ -30,7 +32,7 @@ namespace llarp::vpn #endif #endif #ifdef __APPLE__ - plat = std::make_shared(); + throw std::runtime_error{"not supported"}; #endif return plat; } diff --git a/readme.md b/readme.md index b0b08e446..9122baf13 100644 --- a/readme.md +++ b/readme.md @@ -62,14 +62,7 @@ alternatively you can build from source, make sure you have cmake, libuv and xco $ git clone --recursive https://github.com/oxen-io/lokinet $ cd lokinet - $ mkdir build - $ cd build - $ cmake .. -DBUILD_STATIC_DEPS=ON -DBUILD_SHARED_LIBS=OFF -DSTATIC_LINK=ON - $ make -j$(sysctl -n hw.ncpu) - -install: - - $ sudo make install + $ ./contrib/mac.sh -DCODESIGN_KEY='insert your key identity here' ### Windows