WireGuard for MacOS (#248)
* WireGuard for MacOS * Fix openvpn block-outside-dns
This commit is contained in:
parent
ed5dc7cdfd
commit
35ecb8499d
118 changed files with 5150 additions and 3486 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -58,3 +58,6 @@
|
|||
[submodule "client/3rd/mbedtls"]
|
||||
path = client/3rd/mbedtls
|
||||
url = https://github.com/Mbed-TLS/mbedtls.git
|
||||
[submodule "client/3rd/wireguard-go"]
|
||||
path = client/3rd/wireguard-go
|
||||
url = https://github.com/WireGuard/wireguard-go/
|
||||
|
|
|
@ -2,13 +2,31 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||
|
||||
set(PROJECT AmneziaVPN)
|
||||
|
||||
project(${PROJECT} VERSION 3.0.7.5
|
||||
project(${PROJECT} VERSION 3.0.8.0
|
||||
DESCRIPTION "AmneziaVPN"
|
||||
HOMEPAGE_URL "https://amnezia.org/"
|
||||
)
|
||||
set(RELEASE_DATE "2023-05-15")
|
||||
set(RELEASE_DATE "2023-07-15")
|
||||
set(APP_MAJOR_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(MZ_PLATFORM_NAME "linux")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
|
||||
set(MZ_PLATFORM_NAME "windows")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
|
||||
set(MZ_PLATFORM_NAME "macos")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Android")
|
||||
set(MZ_PLATFORM_NAME "android")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
|
||||
set(MZ_PLATFORM_NAME "ios")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Emscripten")
|
||||
set(MZ_PLATFORM_NAME "wasm")
|
||||
endif()
|
||||
|
||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(ANDROID)
|
||||
set(QT_ANDROID_BUILD_ALL_ABIS ON)
|
||||
endif()
|
||||
|
|
1
client/3rd/wireguard-go
vendored
Submodule
1
client/3rd/wireguard-go
vendored
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 052af4a8072bbbd3bfe7edf46fe3c1b350f71f08
|
|
@ -3,9 +3,6 @@ cmake_minimum_required(VERSION 3.25.0 FATAL_ERROR)
|
|||
set(PROJECT AmneziaVPN)
|
||||
project(${PROJECT})
|
||||
|
||||
set(QT_BUILD_TOOLS_WHEN_CROSS_COMPILING ON)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if(ANDROID)
|
||||
# For a some reason cmake do not applying GNU_SOURCE/BSD_SOURCE flags on Android platform.
|
||||
|
@ -19,14 +16,14 @@ set_property(GLOBAL PROPERTY AUTOMOC_TARGETS_FOLDER "Autogen")
|
|||
set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER "Autogen")
|
||||
|
||||
set(PACKAGES
|
||||
Widgets Core Gui Network Xml
|
||||
RemoteObjects Quick Svg QuickControls2
|
||||
Core5Compat Concurrent
|
||||
Widgets Core Gui Network Xml
|
||||
RemoteObjects Quick Svg QuickControls2
|
||||
Core5Compat Concurrent LinguistTools
|
||||
)
|
||||
if(IOS)
|
||||
set(PACKAGES
|
||||
${PACKAGES}
|
||||
Multimedia
|
||||
set(PACKAGES
|
||||
${PACKAGES}
|
||||
Multimedia
|
||||
)
|
||||
endif()
|
||||
|
||||
|
@ -39,13 +36,25 @@ set(LIBS ${LIBS}
|
|||
Qt6::Core5Compat Qt6::Concurrent
|
||||
)
|
||||
if(IOS)
|
||||
set(LIBS
|
||||
${LIBS}
|
||||
Qt6::Multimedia
|
||||
)
|
||||
set(LIBS
|
||||
${LIBS}
|
||||
Qt6::Multimedia
|
||||
)
|
||||
endif()
|
||||
|
||||
qt_standard_project_setup()
|
||||
qt_add_executable(${PROJECT} MANUAL_FINALIZATION)
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||
endif()
|
||||
|
||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||
|
||||
qt6_add_translations(${PROJECT} TS_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts
|
||||
)
|
||||
|
||||
if(IOS)
|
||||
#execute_process(COMMAND bash ${CMAKE_CURRENT_LIST_DIR}/scripts/run-build-cloak.sh)
|
||||
|
@ -62,6 +71,7 @@ if(IS_CI)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
|
||||
|
||||
include_directories(
|
||||
|
@ -82,8 +92,8 @@ set(HEADERS ${HEADERS}
|
|||
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/linux/leakdetector.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/qml_register_protocols.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/pages.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/property_helper.h
|
||||
|
@ -94,6 +104,19 @@ set(HEADERS ${HEADERS}
|
|||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.h
|
||||
)
|
||||
|
||||
# Mozilla headres
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/controllerimpl.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.h
|
||||
)
|
||||
|
||||
include_directories(mozilla)
|
||||
include_directories(mozilla/shared)
|
||||
include_directories(mozilla/models)
|
||||
|
||||
if(NOT IOS)
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.h
|
||||
|
@ -109,7 +132,6 @@ set(SOURCES ${SOURCES}
|
|||
${CMAKE_CURRENT_LIST_DIR}/core/scripts_registry.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/server_defs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/core/servercontroller.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/linux/leakdetector.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/protocols_defs.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/notificationhandler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ui/uilogic.cpp
|
||||
|
@ -118,6 +140,18 @@ set(SOURCES ${SOURCES}
|
|||
${CMAKE_CURRENT_LIST_DIR}/core/sshclient.cpp
|
||||
)
|
||||
|
||||
# Mozilla sources
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/models/server.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/ipaddress.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/shared/leakdetector.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mozilla/localsocketcontroller.cpp
|
||||
)
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_DEBUG")
|
||||
endif()
|
||||
|
||||
if(NOT IOS)
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.cpp
|
||||
|
@ -150,11 +184,7 @@ set(SOURCES ${SOURCES}
|
|||
${UI_MODELS_CPP}
|
||||
)
|
||||
|
||||
qt6_add_resources(QRC ${QRC} ${CMAKE_CURRENT_LIST_DIR}/resources.qrc)
|
||||
|
||||
if(WIN32)
|
||||
add_compile_definitions(MVPN_WINDOWS)
|
||||
|
||||
configure_file(
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/windows/amneziavpn.rc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/amneziavpn.rc
|
||||
|
@ -205,40 +235,9 @@ if(APPLE)
|
|||
set(CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM ${BUILD_VPN_DEVELOPMENT_TEAM})
|
||||
set(CMAKE_XCODE_ATTRIBUTE_GROUP_ID_IOS ${BUILD_IOS_GROUP_IDENTIFIER})
|
||||
|
||||
if(NOT IOS)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||
|
||||
message("MAC build")
|
||||
set(HEADERS ${HEADERS} ${CMAKE_CURRENT_LIST_DIR}/ui/macos_util.h)
|
||||
set(SOURCES ${SOURCES} ${CMAKE_CURRENT_LIST_DIR}/ui/macos_util.mm)
|
||||
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
add_compile_definitions(MVPN_MACOS)
|
||||
|
||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set(SOURCES ${SOURCES} ${ICON_FILE})
|
||||
|
||||
find_library(FW_COCOA Cocoa)
|
||||
find_library(FW_APPLICATIONSERVICES ApplicationServices)
|
||||
find_library(FW_CORESERVICES CoreServices)
|
||||
find_library(FW_FOUNDATION Foundation)
|
||||
find_library(FW_APPKIT AppKit)
|
||||
find_library(FW_SECURITY Security)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${FW_COCOA} ${FW_APPLICATIONSERVICES}
|
||||
${FW_FOUNDATION} ${FW_APPKIT}
|
||||
${FW_SECURITY} ${FW_CORESERVICES}
|
||||
${LIB_LIBCRYPTO}
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(LINUX AND NOT ANDROID)
|
||||
add_compile_definitions(MVPN_LINUX)
|
||||
|
||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/linux)
|
||||
endif()
|
||||
|
||||
|
@ -273,8 +272,6 @@ if(ANDROID)
|
|||
# As QAndroidBinder is not yet implemented with a public api
|
||||
set(LIBS ${LIBS} Qt6::CorePrivate)
|
||||
|
||||
add_compile_definitions(MVPN_ANDROID)
|
||||
|
||||
link_directories(${CMAKE_CURRENT_LIST_DIR}/platforms/android)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
|
@ -295,179 +292,12 @@ if(ANDROID)
|
|||
endif()
|
||||
|
||||
if(IOS)
|
||||
message("Client iOS build")
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
||||
set(LIBS ${LIBS} Qt6::ShaderTools)
|
||||
|
||||
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||
find_library(FW_UIKIT UIKit)
|
||||
find_library(FW_AVFOUNDATION AVFoundation)
|
||||
find_library(FW_FOUNDATION Foundation)
|
||||
find_library(FW_STOREKIT StoreKit)
|
||||
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${FW_AUTHENTICATIONSERVICES} ${FW_UIKIT}
|
||||
${FW_AVFOUNDATION} ${FW_FOUNDATION} ${FW_STOREKIT}
|
||||
${FW_USERNOTIFICATIONS}
|
||||
)
|
||||
|
||||
add_compile_definitions(MVPN_IOS)
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/ios_vpnprotocol.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosnotificationhandler.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/bigint.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/bigintipv6addr.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddress.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddressrange.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QtAppDelegate.h
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocols/ios_vpnprotocol.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosglue.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddress.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ipaddressrange.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/QtAppDelegate.mm
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/MobileUtils.mm
|
||||
)
|
||||
endif()
|
||||
|
||||
if(CMAKE_OSX_SYSROOT STREQUAL "iphoneos")
|
||||
message("Building for iPhone OS")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
|
||||
endif()
|
||||
|
||||
qt_add_executable(${PROJECT} ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC})
|
||||
qt_add_translations(${PROJECT} TS_FILES
|
||||
${CMAKE_CURRENT_LIST_DIR}/translations/amneziavpn_ru.ts)
|
||||
|
||||
if(APPLE AND NOT IOS)
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
endif()
|
||||
|
||||
if(IOS)
|
||||
enable_language(OBJC)
|
||||
enable_language(OBJCXX)
|
||||
enable_language(Swift)
|
||||
|
||||
include(cmake/ios.cmake)
|
||||
include(cmake/ios-arch-fixup.cmake)
|
||||
elseif(APPLE AND NOT IOS)
|
||||
include(cmake/golang.cmake)
|
||||
include(cmake/osxtools.cmake)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/ios/iphone/libcrypto.a
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/OpenSSL/lib/ios/iphone/libssl.a
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_LIST_DIR}/ios/app/Info.plist.in
|
||||
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_LIST_DIR}/ios/app/main.entitlements"
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
|
||||
XCODE_GENERATE_SCHEME TRUE
|
||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
|
||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||
|
||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_LIST_DIR}/platforms/ios/WireGuard-Bridging-Header.h"
|
||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||
)
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
|
||||
target_compile_options(${PROJECT} PRIVATE
|
||||
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/iosvpnprotocol.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/platforms/ios/ioslogger.swift
|
||||
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Keychain.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardApp/LocalizationHelper.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c
|
||||
${CMAKE_CURRENT_LIST_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/launch.png
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/Media.xcassets
|
||||
)
|
||||
set_source_files_properties(
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/launch.png
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_LIST_DIR}/ios/Media.xcassets
|
||||
PROPERTIES MACOSX_PACKAGE_LOCATION "Resources"
|
||||
)
|
||||
|
||||
|
||||
add_subdirectory(ios/networkextension)
|
||||
add_dependencies(${PROJECT} networkextension)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_LIST_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
||||
)
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_LIST_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_LIST_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
||||
|
||||
include(cmake/macos.cmake)
|
||||
endif()
|
||||
|
||||
if(ANDROID)
|
||||
|
@ -522,11 +352,7 @@ if(ANDROID)
|
|||
endif()
|
||||
|
||||
target_link_libraries(${PROJECT} PRIVATE ${LIBS})
|
||||
|
||||
if(WIN32 OR (APPLE AND NOT IOS) OR (LINUX AND NOT ANDROID))
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_interface.rep)
|
||||
qt_add_repc_replicas(${PROJECT} ${CMAKE_CURRENT_LIST_DIR}/../ipc/ipc_process_interface.rep)
|
||||
endif()
|
||||
target_compile_definitions(${PROJECT} PRIVATE "MZ_$<UPPER_CASE:${MZ_PLATFORM_NAME}>")
|
||||
|
||||
# deploy artifacts required to run the application to the debug build folder
|
||||
if(WIN32)
|
||||
|
@ -560,6 +386,7 @@ if(WIN32)
|
|||
COMMAND_EXPAND_LISTS
|
||||
)
|
||||
endif()
|
||||
if(IOS)
|
||||
include(cmake/ios-arch-fixup.cmake)
|
||||
endif()
|
||||
|
||||
|
||||
target_sources(${PROJECT} PRIVATE ${SOURCES} ${HEADERS} ${RESOURCES} ${QRC})
|
||||
qt_finalize_target(${PROJECT})
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
#include "ui/pages_logic/protocols/OpenVpnLogic.h"
|
||||
#include "ui/pages_logic/protocols/ShadowSocksLogic.h"
|
||||
|
||||
#include "protocols/qml_register_protocols.h"
|
||||
|
||||
#if defined(Q_OS_IOS)
|
||||
#include "platforms/ios/QtAppDelegate-C-Interface.h"
|
||||
#endif
|
||||
|
|
|
@ -137,8 +137,8 @@ android {
|
|||
resConfig "en"
|
||||
minSdkVersion = 24
|
||||
targetSdkVersion = 34
|
||||
versionCode 22 // Change to a higher number
|
||||
versionName "3.0.7" // Change to a higher number
|
||||
versionCode 25 // Change to a higher number
|
||||
versionName "3.0.8" // Change to a higher number
|
||||
|
||||
javaCompileOptions.annotationProcessorOptions.arguments = [
|
||||
"room.schemaLocation": "${qtAndroidDir}/schemas".toString()
|
||||
|
|
|
@ -93,7 +93,7 @@ object NotificationUtil {
|
|||
val prefs = Prefs.get(service)
|
||||
val message =
|
||||
"" + prefs.getString("fallbackNotificationMessage", "Running in the Background")
|
||||
val header = "" + prefs.getString("fallbackNotificationHeader", "Mozilla VPN")
|
||||
val header = "" + prefs.getString("fallbackNotificationHeader", "Amnezia VPN")
|
||||
|
||||
// Create the Intent that Should be Fired if the User Clicks the notification
|
||||
val mainActivityName = "org.amnezia.vpn.qt.VPNActivity"
|
||||
|
|
|
@ -11,14 +11,14 @@ import android.util.Log as nativeLog
|
|||
|
||||
/*
|
||||
* Drop in replacement for android.util.Log
|
||||
* Also stores a copy of all logs in tmp/mozilla_daemon_logs.txt
|
||||
* Also stores a copy of all logs in tmp/amnezia_daemon_logs.txt
|
||||
*/
|
||||
class Log {
|
||||
val LOG_MAX_FILE_SIZE = 204800
|
||||
private var file: File
|
||||
private constructor(context: Context) {
|
||||
val tempDIR = context.cacheDir
|
||||
file = File(tempDIR, "mozilla_daemon_logs.txt")
|
||||
file = File(tempDIR, "amnezia_daemon_logs.txt")
|
||||
if (file.length() > LOG_MAX_FILE_SIZE) {
|
||||
file.writeText("")
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ public class PackageManagerHelper {
|
|||
|
||||
// Returns List of all Packages that can classify themselves as browsers
|
||||
private static List<String> getBrowserIDs(PackageManager pm) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.mozilla.org/"));
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.amnezia.org/"));
|
||||
intent.addCategory(Intent.CATEGORY_BROWSABLE);
|
||||
// We've tried using PackageManager.MATCH_DEFAULT_ONLY flag and found that browsers that
|
||||
// are not set as the default browser won't be matched even if they had CATEGORY_DEFAULT set
|
||||
|
|
|
@ -16,7 +16,7 @@ function(build_go_archive OUTPUT_NAME MODULE_FILE)
|
|||
string(REGEX REPLACE "\\.[^/]*$" ".h" GOBUILD_HEADER_FILE ${OUTPUT_NAME})
|
||||
get_filename_component(GOBUILD_MODULE_ABS ${MODULE_FILE} ABSOLUTE)
|
||||
get_filename_component(GOBUILD_MODULE_DIR ${GOBUILD_MODULE_ABS} DIRECTORY)
|
||||
set(GOBUILD_ARGS -buildmode=c-archive -trimpath -v)
|
||||
set(GOBUILD_ARGS -buildmode=c-archive -buildvcs=false -trimpath -v)
|
||||
if(IS_DIRECTORY ${GOBUILD_MODULE_DIR}/vendor)
|
||||
list(APPEND GOBUILD_ARGS -mod vendor)
|
||||
endif()
|
||||
|
@ -48,6 +48,8 @@ function(build_go_archive OUTPUT_NAME MODULE_FILE)
|
|||
DEPENDS ${MODULE_FILE} ${GOBUILD_SOURCES}
|
||||
WORKING_DIRECTORY ${GOBUILD_MODULE_DIR}
|
||||
COMMAND ${CMAKE_COMMAND} -E env GOCACHE=${GOCACHE}
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
CXX=${CMAKE_CXX_COMPILER}
|
||||
CGO_ENABLED=1
|
||||
CGO_CFLAGS="${GOBUILD_CGO_CFLAGS}"
|
||||
CGO_LDFLAGS="${GOBUILD_CGO_LDFLAGS}"
|
||||
|
@ -61,64 +63,65 @@ endfunction(build_go_archive)
|
|||
function(add_go_library GOTARGET SOURCE)
|
||||
cmake_parse_arguments(GOLANG
|
||||
""
|
||||
"GOOS;GOARCH"
|
||||
""
|
||||
"CGO_CFLAGS;CGO_LDFLAGS"
|
||||
${ARGN})
|
||||
get_filename_component(SRC_NAME ${SOURCE} NAME)
|
||||
get_filename_component(DIR_NAME ${SOURCE} DIRECTORY)
|
||||
get_filename_component(DIR_ABSOLUTE ${DIR_NAME} ABSOLUTE)
|
||||
|
||||
file(GLOB_RECURSE SRC_DEPS ${DIR_NAME}/*.go)
|
||||
set(HEADER_NAME "${GOTARGET}.h")
|
||||
set(ARCHIVE_NAME "${GOTARGET}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
|
||||
set(GOCACHE ${CMAKE_BINARY_DIR}/go-cache)
|
||||
set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES ${CMAKE_BINARY_DIR}/go-cache)
|
||||
set(GOFLAGS -buildmode=c-archive -trimpath -v)
|
||||
if(IS_DIRECTORY ${DIR_NAME}/vendor)
|
||||
set(GOFLAGS ${GOFLAGS} -mod vendor)
|
||||
endif()
|
||||
|
||||
## Add extras to the CGO compiler and linker flags.
|
||||
execute_process(OUTPUT_VARIABLE DEFAULT_CGO_CFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env CGO_CFLAGS)
|
||||
execute_process(OUTPUT_VARIABLE DEFAULT_CGO_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env CGO_LDFLAGS)
|
||||
separate_arguments(DEFAULT_CGO_CFLAGS NATIVE_COMMAND ${DEFAULT_CGO_CFLAGS})
|
||||
separate_arguments(DEFAULT_CGO_LDFLAGS NATIVE_COMMAND ${DEFAULT_CGO_LDFLAGS})
|
||||
list(PREPEND GOLANG_CGO_CFLAGS ${DEFAULT_CGO_CFLAGS})
|
||||
list(PREPEND GOLANG_CGO_LDFLAGS ${DEFAULT_CGO_LDFLAGS})
|
||||
if(NOT GOLANG_GOOS)
|
||||
execute_process(OUTPUT_VARIABLE GOLANG_GOOS OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env GOOS)
|
||||
endif()
|
||||
if(NOT GOLANG_GOARCH)
|
||||
execute_process(OUTPUT_VARIABLE GOLANG_GOARCH OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND ${GOLANG_BUILD_TOOL} env GOARCH)
|
||||
endif()
|
||||
|
||||
if(APPLE AND CMAKE_OSX_SYSROOT)
|
||||
execute_process(OUTPUT_VARIABLE SDKROOT OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND xcrun --sdk ${CMAKE_OSX_SYSROOT} --show-sdk-path)
|
||||
list(APPEND GOLANG_CGO_CFLAGS -isysroot ${SDKROOT})
|
||||
list(APPEND GOLANG_CGO_LDFLAGS -isysroot ${SDKROOT})
|
||||
endif()
|
||||
|
||||
## The actual commands that do the building.
|
||||
add_custom_target(golang_${GOTARGET}
|
||||
BYPRODUCTS ${ARCHIVE_NAME} ${HEADER_NAME}
|
||||
WORKING_DIRECTORY ${DIR_ABSOLUTE}
|
||||
SOURCES ${SRC_DEPS} ${DIR_NAME}/go.mod
|
||||
COMMAND ${CMAKE_COMMAND} -E env GOCACHE=${GOCACHE}
|
||||
CGO_ENABLED=1
|
||||
CGO_CFLAGS="${GOLANG_CGO_CFLAGS}"
|
||||
CGO_LDFLAGS="${GOLANG_CGO_LDFLAGS}"
|
||||
GOOS="${GOLANG_GOOS}"
|
||||
GOARCH="${GOLANG_GOARCH}"
|
||||
${GOLANG_BUILD_TOOL} build ${GOFLAGS} -o ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME} ${SRC_NAME}
|
||||
if((CMAKE_SYSTEM_NAME STREQUAL "Darwin") AND CMAKE_OSX_ARCHITECTURES)
|
||||
foreach(OSXARCH ${CMAKE_OSX_ARCHITECTURES})
|
||||
string(REPLACE "x86_64" "amd64" GOARCH ${OSXARCH})
|
||||
build_go_archive(${CMAKE_CURRENT_BINARY_DIR}/${OSXARCH}/${ARCHIVE_NAME} ${DIR_NAME}/go.mod
|
||||
GOARCH ${GOARCH}
|
||||
CGO_CFLAGS ${DEFAULT_CGO_CFLAGS} ${GOLANG_CGO_CFLAGS} -arch ${OSXARCH}
|
||||
CGO_LDFLAGS ${DEFAULT_CGO_LDFLAGS} ${GOLANG_CGO_LDFLAGS} -arch ${OSXARCH}
|
||||
)
|
||||
list(APPEND ARCH_ARCHIVE_FILES ${CMAKE_CURRENT_BINARY_DIR}/${OSXARCH}/${ARCHIVE_NAME})
|
||||
list(APPEND ARCH_HEADER_FILES ${CMAKE_CURRENT_BINARY_DIR}/${OSXARCH}/${HEADER_NAME})
|
||||
endforeach()
|
||||
|
||||
list(GET ARCH_HEADER_FILES 0 FIRST_HEADER_FILE)
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME} ${CMAKE_CURRENT_BINARY_DIR}/${HEADER_NAME}
|
||||
DEPENDS ${ARCH_ARCHIVE_FILES} ${ARCH_HEADER_FILES}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
COMMAND lipo -create -output ${ARCHIVE_NAME} ${ARCH_ARCHIVE_FILES}
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${FIRST_HEADER_FILE} ${HEADER_NAME}
|
||||
)
|
||||
else()
|
||||
## Regular single architecture build
|
||||
build_go_archive(${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME} ${DIR_NAME}/go.mod
|
||||
CGO_CFLAGS ${DEFAULT_CGO_CFLAGS} ${GOLANG_CGO_CFLAGS}
|
||||
CGO_LDFLAGS ${DEFAULT_CGO_LDFLAGS} ${GOLANG_CGO_LDFLAGS}
|
||||
)
|
||||
endif()
|
||||
|
||||
set_source_files_properties({CMAKE_CURRENT_BINARY_DIR}/${HEADER_NAME} PROPERTIES
|
||||
GENERATED TRUE
|
||||
SKIP_AUTOGEN TRUE
|
||||
)
|
||||
set_target_properties(golang_${GOTARGET} PROPERTIES FOLDER "Libs")
|
||||
|
||||
## Wrap up the built library as an imported target.
|
||||
add_library(${GOTARGET} STATIC IMPORTED GLOBAL)
|
||||
add_dependencies(${GOTARGET} golang_${GOTARGET})
|
||||
set_target_properties(${GOTARGET} PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR}
|
||||
INTERFACE_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${HEADER_NAME}
|
||||
IMPORTED_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME})
|
||||
endfunction(add_go_library)
|
||||
|
||||
## Some dependency glue to ensure we actually build the library.
|
||||
add_custom_target(${GOTARGET}_builder DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ARCHIVE_NAME})
|
||||
add_dependencies(${GOTARGET} ${GOTARGET}_builder)
|
||||
set_target_properties(${GOTARGET}_builder PROPERTIES FOLDER "Libs")
|
||||
|
||||
endfunction(add_go_library)
|
||||
|
|
159
client/cmake/ios.cmake
Normal file
159
client/cmake/ios.cmake
Normal file
|
@ -0,0 +1,159 @@
|
|||
message("Client iOS build")
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 13.0)
|
||||
set(APPLE_PROJECT_VERSION ${CMAKE_PROJECT_VERSION_MAJOR}.${CMAKE_PROJECT_VERSION_MINOR}.${CMAKE_PROJECT_VERSION_PATCH})
|
||||
|
||||
|
||||
enable_language(OBJC)
|
||||
enable_language(OBJCXX)
|
||||
enable_language(Swift)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS ShaderTools)
|
||||
set(LIBS ${LIBS} Qt6::ShaderTools)
|
||||
|
||||
find_library(FW_AUTHENTICATIONSERVICES AuthenticationServices)
|
||||
find_library(FW_UIKIT UIKit)
|
||||
find_library(FW_AVFOUNDATION AVFoundation)
|
||||
find_library(FW_FOUNDATION Foundation)
|
||||
find_library(FW_STOREKIT StoreKit)
|
||||
find_library(FW_USERNOTIFICATIONS UserNotifications)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${FW_AUTHENTICATIONSERVICES} ${FW_UIKIT}
|
||||
${FW_AVFOUNDATION} ${FW_FOUNDATION} ${FW_STOREKIT}
|
||||
${FW_USERNOTIFICATIONS}
|
||||
)
|
||||
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/ios_vpnprotocol.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.h
|
||||
#${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/bigint.h
|
||||
#${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/bigintipv6addr.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/ipaddress.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ipaddressrange.h # TODO need refactor
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate-C-Interface.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/protocols/ios_vpnprotocol.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosnotificationhandler.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosglue.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mozilla/shared/ipaddress.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ipaddressrange.cpp # TODO need refactor
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QRCodeReaderBase.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/QtAppDelegate.mm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/MobileUtils.mm
|
||||
)
|
||||
|
||||
#set(SOURCES ${SOURCES}
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Keychain.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardApp/LocalizationHelper.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c
|
||||
# ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift
|
||||
#)
|
||||
|
||||
set(LIBS ${LIBS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenSSL/lib/ios/iphone/libcrypto.a
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenSSL/lib/ios/iphone/libssl.a
|
||||
)
|
||||
|
||||
target_include_directories(${PROJECT} PRIVATE ${Qt6Gui_PRIVATE_INCLUDE_DIRS})
|
||||
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Info.plist.in
|
||||
MACOSX_BUNDLE_ICON_FILE "AppIcon"
|
||||
MACOSX_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
MACOSX_BUNDLE_BUNDLE_NAME "AmneziaVPN"
|
||||
MACOSX_BUNDLE_GUI_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_LONG_VERSION_STRING "${APPLE_PROJECT_VERSION}-${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
MACOSX_BUNDLE_SHORT_VERSION_STRING "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${BUILD_IOS_APP_IDENTIFIER}"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_ENTITLEMENTS "${CMAKE_CURRENT_SOURCE_DIR}/ios/app/main.entitlements"
|
||||
XCODE_ATTRIBUTE_MARKETING_VERSION "${APPLE_PROJECT_VERSION}"
|
||||
XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${CMAKE_PROJECT_VERSION_TWEAK}"
|
||||
XCODE_ATTRIBUTE_PRODUCT_NAME "AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_BUNDLE_INFO_STRING "AmneziaVPN"
|
||||
XCODE_GENERATE_SCHEME TRUE
|
||||
XCODE_ATTRIBUTE_ENABLE_BITCODE "NO"
|
||||
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME "AppIcon"
|
||||
XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY "1,2"
|
||||
XCODE_EMBED_FRAMEWORKS_CODE_SIGN_ON_COPY ON
|
||||
XCODE_LINK_BUILD_PHASE_MODE KNOWN_LOCATION
|
||||
XCODE_ATTRIBUTE_LD_RUNPATH_SEARCH_PATHS "@executable_path/Frameworks"
|
||||
XCODE_EMBED_APP_EXTENSIONS networkextension
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "Apple Distribution"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY[variant=Debug] "Apple Development"
|
||||
XCODE_ATTRIBUTE_CODE_SIGN_STYLE Manual
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER "match AppStore org.amnezia.AmneziaVPN"
|
||||
XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER[variant=Debug] "match Development org.amnezia.AmneziaVPN"
|
||||
)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_SWIFT_VERSION "5.0"
|
||||
XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_BRIDGING_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/WireGuard-Bridging-Header.h"
|
||||
XCODE_ATTRIBUTE_SWIFT_PRECOMPILE_BRIDGING_HEADER "NO"
|
||||
XCODE_ATTRIBUTE_SWIFT_OBJC_INTERFACE_HEADER_NAME "AmneziaVPN-Swift.h"
|
||||
)
|
||||
set_target_properties(${PROJECT} PROPERTIES
|
||||
XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "X7UJ388FXK"
|
||||
)
|
||||
target_include_directories(${PROJECT} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
|
||||
target_compile_options(${PROJECT} PRIVATE
|
||||
-DGROUP_ID=\"${BUILD_IOS_GROUP_IDENTIFIER}\"
|
||||
-DVPN_NE_BUNDLEID=\"${BUILD_IOS_APP_IDENTIFIER}.network-extension\"
|
||||
)
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/iosvpnprotocol.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/platforms/ios/ioslogger.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Keychain.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardApp/LocalizationHelper.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift
|
||||
)
|
||||
|
||||
target_sources(${PROJECT} PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/launch.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/Media.xcassets
|
||||
)
|
||||
|
||||
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/launch.png
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ios/Media.xcassets
|
||||
)
|
||||
|
||||
add_subdirectory(ios/networkextension)
|
||||
add_dependencies(${PROJECT} networkextension)
|
||||
|
||||
set_property(TARGET ${PROJECT} PROPERTY XCODE_EMBED_FRAMEWORKS
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework"
|
||||
)
|
||||
|
||||
set(CMAKE_XCODE_ATTRIBUTE_FRAMEWORK_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos)
|
||||
target_link_libraries("networkextension" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/3rd/OpenVPNAdapter/build/Release-iphoneos/OpenVPNAdapter.framework")
|
||||
|
101
client/cmake/macos.cmake
Normal file
101
client/cmake/macos.cmake
Normal file
|
@ -0,0 +1,101 @@
|
|||
message("MAC build")
|
||||
|
||||
set_target_properties(${PROJECT} PROPERTIES MACOSX_BUNDLE TRUE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64" CACHE INTERNAL "" FORCE)
|
||||
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.15)
|
||||
|
||||
|
||||
set(HEADERS ${HEADERS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.h
|
||||
)
|
||||
|
||||
set(SOURCES ${SOURCES}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ui/macos_util.mm
|
||||
)
|
||||
|
||||
set(ICON_FILE ${CMAKE_CURRENT_SOURCE_DIR}/images/app.icns)
|
||||
set(MACOSX_BUNDLE_ICON_FILE app.icns)
|
||||
set_source_files_properties(${ICON_FILE} PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
|
||||
set(SOURCES ${SOURCES} ${ICON_FILE})
|
||||
|
||||
|
||||
|
||||
find_library(FW_SYSTEMCONFIG SystemConfiguration)
|
||||
find_library(FW_SERVICEMGMT ServiceManagement)
|
||||
find_library(FW_SECURITY Security)
|
||||
find_library(FW_COREWLAN CoreWLAN)
|
||||
find_library(FW_NETWORK Network)
|
||||
find_library(FW_USER_NOTIFICATIONS UserNotifications)
|
||||
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_SYSTEMCONFIG})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_SERVICEMGMT})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_SECURITY})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_COREWLAN})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_NETWORK})
|
||||
target_link_libraries(${PROJECT} PRIVATE ${FW_USER_NOTIFICATIONS})
|
||||
|
||||
# Get SDK path
|
||||
execute_process(
|
||||
COMMAND sh -c "xcrun --sdk macosx --show-sdk-path"
|
||||
OUTPUT_VARIABLE OSX_SDK_PATH
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
message("OSX_SDK_PATH is: ${OSX_SDK_PATH}")
|
||||
|
||||
# Build the Wireguard Go tunnel
|
||||
file(GLOB_RECURSE WIREGUARD_GO_DEPS ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/*.go)
|
||||
set(WIREGUARD_GO_ENV
|
||||
GOCACHE=${CMAKE_BINARY_DIR}/go-cache
|
||||
CC=${CMAKE_C_COMPILER}
|
||||
CXX=${CMAKE_CXX_COMPILER}
|
||||
GOOS=darwin
|
||||
CGO_ENABLED=1
|
||||
GO111MODULE=on
|
||||
CGO_CFLAGS='-g -O3 -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -isysroot ${OSX_SDK_PATH}'
|
||||
CGO_LDFLAGS='-g -O3 -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET} -isysroot ${OSX_SDK_PATH}'
|
||||
)
|
||||
|
||||
if(NOT CMAKE_OSX_ARCHITECTURES)
|
||||
foreach(OSXARCH ${CMAKE_OSX_ARCHITECTURES})
|
||||
message("Build wg for OSXARCH: ${OSXARCH}")
|
||||
|
||||
string(REPLACE "x86_64" "amd64" GOARCH ${OSXARCH})
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-${OSXARCH}
|
||||
COMMENT "Building wireguard-go for ${OSXARCH}"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go
|
||||
DEPENDS
|
||||
${WIREGUARD_GO_DEPS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.sum
|
||||
COMMAND ${CMAKE_COMMAND} -E env ${WIREGUARD_GO_ENV} GOARCH=${GOARCH}
|
||||
${GOLANG_BUILD_TOOL} build -buildmode exe -buildvcs=false -trimpath -v
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-${OSXARCH}
|
||||
)
|
||||
list(APPEND WG_GO_ARCH_BUILDS ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go-${OSXARCH}/wireguard)
|
||||
endforeach()
|
||||
|
||||
add_custom_target(build_wireguard_go
|
||||
COMMENT "Building wireguard-go"
|
||||
DEPENDS ${WG_GO_ARCH_BUILDS}
|
||||
COMMAND lipo -create -output ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go ${WG_GO_ARCH_BUILDS}
|
||||
)
|
||||
else()
|
||||
# This only builds for the host architecture.
|
||||
add_custom_target(build_wireguard_go
|
||||
COMMENT "Building wireguard-go"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go
|
||||
DEPENDS
|
||||
${WIREGUARD_GO_DEPS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/3rd/wireguard-go/go.sum
|
||||
COMMAND ${CMAKE_COMMAND} -E env ${WIREGUARD_GO_ENV}
|
||||
${GOLANG_BUILD_TOOL} build -buildmode exe -buildvcs=false -trimpath -v
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go
|
||||
)
|
||||
endif()
|
||||
add_dependencies(${PROJECT} build_wireguard_go)
|
||||
osx_bundle_files(${PROJECT}
|
||||
FILES ${CMAKE_CURRENT_BINARY_DIR}/wireguard-go
|
||||
DESTINATION MacOS/
|
||||
)
|
|
@ -95,7 +95,7 @@ QString OpenVpnConfigurator::genOpenVpnConfig(const ServerCredentials &credentia
|
|||
config.replace("</tls-auth>", "");
|
||||
}
|
||||
|
||||
#if defined Q_OS_MAC || defined(Q_OS_LINUX)
|
||||
#ifndef MZ_WINDOWS
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
|
@ -123,8 +123,11 @@ QString OpenVpnConfigurator::processConfigWithLocalSettings(QString jsonConfig)
|
|||
config.append("ifconfig-ipv6 fd15:53b6:dead::2/64 fd15:53b6:dead::1\n");
|
||||
config.append("redirect-gateway ipv6\n");
|
||||
|
||||
#if (defined Q_OS_MAC || defined(Q_OS_LINUX)) && !defined(Q_OS_ANDROID)
|
||||
#ifndef MZ_WINDOWS
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
#if (defined (MZ_MACOS) || defined(MZ_LINUX))
|
||||
QString dnsConf = QString(
|
||||
"\nscript-security 2\n"
|
||||
"up %1/update-resolv-conf.sh\n"
|
||||
|
@ -147,9 +150,8 @@ QString OpenVpnConfigurator::processConfigWithExportSettings(QString jsonConfig)
|
|||
config.append("redirect-gateway def1 bypass-dhcp\n");
|
||||
}
|
||||
|
||||
#if (defined Q_OS_MAC || defined(Q_OS_LINUX)) && !defined(Q_OS_ANDROID)
|
||||
// remove block-outside-dns for all exported configs
|
||||
config.replace("block-outside-dns", "");
|
||||
#endif
|
||||
|
||||
json[config_key::config] = config;
|
||||
return QJsonDocument(json).toJson();
|
||||
|
|
|
@ -63,6 +63,7 @@ WireguardConfigurator::ConnectionData WireguardConfigurator::prepareWireguardCon
|
|||
{
|
||||
WireguardConfigurator::ConnectionData connData = WireguardConfigurator::genClientKeys();
|
||||
connData.host = credentials.hostName;
|
||||
connData.port = containerConfig.value(config_key::port).toString(protocols::wireguard::defaultPort);
|
||||
|
||||
if (connData.clientPrivKey.isEmpty() || connData.clientPubKey.isEmpty()) {
|
||||
if (errorCode) *errorCode = ErrorCode::InternalError;
|
||||
|
@ -181,6 +182,7 @@ QString WireguardConfigurator::genWireguardConfig(const ServerCredentials &crede
|
|||
jConfig[config_key::config] = config;
|
||||
|
||||
jConfig[config_key::hostName] = connData.host;
|
||||
jConfig[config_key::port] = connData.port.toInt();
|
||||
jConfig[config_key::client_priv_key] = connData.clientPrivKey;
|
||||
jConfig[config_key::client_ip] = connData.clientIP;
|
||||
jConfig[config_key::client_pub_key] = connData.clientPubKey;
|
||||
|
|
|
@ -20,6 +20,7 @@ public:
|
|||
QString serverPubKey; // tls-auth key
|
||||
QString pskKey; // preshared key
|
||||
QString host; // host ip
|
||||
QString port;
|
||||
};
|
||||
|
||||
QString genWireguardConfig(const ServerCredentials &credentials, DockerContainer container,
|
||||
|
|
|
@ -5,107 +5,31 @@
|
|||
#ifndef CONSTANTS_H
|
||||
#define CONSTANTS_H
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <QObject>
|
||||
|
||||
namespace Constants {
|
||||
|
||||
// Returns true if we are in a production environment.
|
||||
bool inProduction();
|
||||
void setStaging();
|
||||
|
||||
// Number of msecs for the captive-portal block alert.
|
||||
constexpr uint32_t CAPTIVE_PORTAL_ALERT_MSEC = 4000;
|
||||
|
||||
// Number of msecs for the unsecured network alert.
|
||||
constexpr uint32_t UNSECURED_NETWORK_ALERT_MSEC = 4000;
|
||||
|
||||
// Number of recent connections to retain.
|
||||
constexpr int RECENT_CONNECTIONS_MAX_COUNT = 5;
|
||||
|
||||
#if defined(UNIT_TEST)
|
||||
# define CONSTEXPR(type, functionName, releaseValue, debugValue, \
|
||||
testingValue) \
|
||||
inline type functionName() { return testingValue; }
|
||||
#else
|
||||
# define CONSTEXPR(type, functionName, releaseValue, debugValue, \
|
||||
testingValue) \
|
||||
inline type functionName() { \
|
||||
return inProduction() ? releaseValue : debugValue; \
|
||||
}
|
||||
#endif
|
||||
|
||||
// Let's refresh the IP address any 10 minutes (in milliseconds).
|
||||
CONSTEXPR(uint32_t, ipAddressTimerMsec, 600000, 10000, 0)
|
||||
|
||||
// Let's check the connection status any second.
|
||||
CONSTEXPR(uint32_t, checkStatusTimerMsec, 1000, 1000, 0)
|
||||
|
||||
// Number of points for the charts.
|
||||
CONSTEXPR(int, chartsMaxPoints, 30, 30, 30);
|
||||
|
||||
// Any 6 hours, a new check
|
||||
CONSTEXPR(uint32_t, releaseMonitorMsec, 21600000, 4000, 0)
|
||||
|
||||
// in milliseconds, how often we should fetch the server list and the account.
|
||||
CONSTEXPR(uint32_t, scheduleAccountAndServersTimerMsec, 3600000, 4000, 0)
|
||||
|
||||
// how often we check the captive portal when the VPN is on.
|
||||
CONSTEXPR(uint32_t, captivePortalRequestTimeoutMsec, 10000, 4000, 0)
|
||||
|
||||
// How fast the animated icon should move
|
||||
CONSTEXPR(uint32_t, statusIconAnimationMsec, 200, 200, 0)
|
||||
|
||||
// How often glean pings are sent
|
||||
CONSTEXPR(uint32_t, gleanTimeoutMsec, 1200000, 1000, 0)
|
||||
|
||||
// How often we check the surveys to be executed (no network requests are done
|
||||
// for this check)
|
||||
CONSTEXPR(uint32_t, surveyTimerMsec, 300000, 4000, 0)
|
||||
|
||||
#undef CONSTEXPR
|
||||
|
||||
#define PRODBETAEXPR(type, functionName, prod, beta) \
|
||||
inline type functionName() { return inProduction() ? prod : beta; }
|
||||
|
||||
constexpr const char* API_PRODUCTION_URL = "https://vpn.mozilla.org";
|
||||
constexpr const char* API_STAGING_URL =
|
||||
"https://stage-vpn.guardian.nonprod.cloudops.mozgcp.net";
|
||||
|
||||
constexpr const char* LOGO_URL = ":/ui/resources/logo-dock.png";
|
||||
|
||||
PRODBETAEXPR(const char*, fxaUrl, "https://api.accounts.firefox.com",
|
||||
"https://api-accounts.stage.mozaws.net")
|
||||
PRODBETAEXPR(
|
||||
const char*, balrogUrl,
|
||||
"https://aus5.mozilla.org/json/1/FirefoxVPN/%1/%2/release/update.json",
|
||||
"https://stage.balrog.nonprod.cloudops.mozgcp.net/json/1/FirefoxVPN/%1/%2/"
|
||||
"release-cdntest/update.json");
|
||||
PRODBETAEXPR(
|
||||
const char*, balrogRootCertFingerprint,
|
||||
"97e8ba9cf12fb3de53cc42a4e6577ed64df493c247b414fea036818d3823560e",
|
||||
"3c01446abe9036cea9a09acaa3a520ac628f20a7ae32ce861cb2efb70fa0c745");
|
||||
|
||||
#undef PRODBETAEXPR
|
||||
inline QString versionString() { return QStringLiteral(APP_VERSION); }
|
||||
|
||||
constexpr const char* PLATFORM_NAME =
|
||||
#if defined(MVPN_IOS)
|
||||
#if defined(MZ_IOS)
|
||||
"ios"
|
||||
#elif defined(MVPN_MACOS)
|
||||
#elif defined(MZ_MACOS)
|
||||
"macos"
|
||||
#elif defined(MVPN_LINUX)
|
||||
#elif defined(MZ_LINUX)
|
||||
"linux"
|
||||
#elif defined(MVPN_ANDROID)
|
||||
#elif defined(MZ_ANDROID)
|
||||
"android"
|
||||
#elif defined(MVPN_WINDOWS)
|
||||
#elif defined(MZ_WINDOWS)
|
||||
"windows"
|
||||
#elif defined(UNIT_TEST) || defined(MVPN_DUMMY)
|
||||
#elif defined(UNIT_TEST) || defined(MZ_DUMMY)
|
||||
"dummy"
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
;
|
||||
|
||||
constexpr const char* PLACEHOLDER_USER_DNS = "127.0.0.1";
|
||||
|
||||
};
|
||||
}
|
||||
#endif // CONSTANTS_H
|
||||
|
|
|
@ -161,7 +161,7 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||
}
|
||||
#elif defined (Q_OS_MAC)
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return false;
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
default: return true;
|
||||
}
|
||||
|
@ -176,7 +176,11 @@ bool ContainerProps::isSupportedByCurrentPlatform(DockerContainer c)
|
|||
}
|
||||
|
||||
#elif defined (Q_OS_LINUX)
|
||||
return true;
|
||||
switch (c) {
|
||||
case DockerContainer::WireGuard: return true;
|
||||
case DockerContainer::Ipsec: return false;
|
||||
default: return true;
|
||||
}
|
||||
|
||||
#else
|
||||
return false;
|
||||
|
|
|
@ -54,7 +54,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
|||
|
||||
script.replace("\r", "");
|
||||
|
||||
qDebug() << "Run script";
|
||||
qDebug() << "ServerController::Run script";
|
||||
|
||||
QString totalLine;
|
||||
const QStringList &lines = script.split("\n", Qt::SkipEmptyParts);
|
||||
|
@ -79,17 +79,16 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
|
|||
continue;
|
||||
}
|
||||
|
||||
qDebug().noquote() << "EXEC" << lineToExec;
|
||||
qDebug().noquote() << lineToExec;
|
||||
Logger::appendSshLog("Run command:" + lineToExec);
|
||||
|
||||
|
||||
error = m_sshClient.executeCommand(lineToExec, cbReadStdOut, cbReadStdErr);
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
qDebug() << "ServerController::runScript finished\n";
|
||||
qDebug().noquote() << "ServerController::runScript finished\n";
|
||||
return ErrorCode::NoError;
|
||||
}
|
||||
|
||||
|
@ -194,12 +193,6 @@ QByteArray ServerController::getTextFileFromContainer(DockerContainer container,
|
|||
};
|
||||
|
||||
*errorCode = runScript(credentials, script, cbReadStdOut);
|
||||
|
||||
qDebug().noquote() << "Copy file from container stdout : \n" << stdOut;
|
||||
|
||||
|
||||
qDebug().noquote() << "Copy file from container END : \n" ;
|
||||
|
||||
return QByteArray::fromHex(stdOut.toUtf8());
|
||||
}
|
||||
|
||||
|
@ -216,8 +209,6 @@ ErrorCode ServerController::uploadFileToHost(const ServerCredentials &credential
|
|||
localFile.write(data);
|
||||
localFile.close();
|
||||
|
||||
qDebug() << "remotePath" << remotePath;
|
||||
|
||||
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
|
||||
if (error != ErrorCode::NoError) {
|
||||
return error;
|
||||
|
@ -242,7 +233,6 @@ ErrorCode ServerController::setupContainer(const ServerCredentials &credentials,
|
|||
QJsonObject &config, bool isUpdate)
|
||||
{
|
||||
qDebug().noquote() << "ServerController::setupContainer" << ContainerProps::containerToString(container);
|
||||
//qDebug().noquote() << QJsonDocument(config).toJson();
|
||||
ErrorCode e = ErrorCode::NoError;
|
||||
|
||||
e = isUserInSudo(credentials, container);
|
||||
|
@ -430,8 +420,6 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
|
|||
replaceVars(amnezia::scriptData(ProtocolScriptType::run_container, container),
|
||||
genVarsForScript(credentials, container, config)), cbReadStdOut);
|
||||
|
||||
qDebug() << "cbReadStdOut: " << stdOut;
|
||||
|
||||
|
||||
if (stdOut.contains("docker: Error response from daemon")) return ErrorCode::ServerDockerFailedError;
|
||||
|
||||
|
@ -491,7 +479,6 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential
|
|||
const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Proto::ShadowSocks)).toObject();
|
||||
const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Proto::WireGuard)).toObject();
|
||||
const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Proto::Sftp)).toObject();
|
||||
//
|
||||
|
||||
Vars vars;
|
||||
|
||||
|
@ -616,10 +603,8 @@ QString ServerController::replaceVars(const QString &script, const Vars &vars)
|
|||
{
|
||||
QString s = script;
|
||||
for (const QPair<QString, QString> &var : vars) {
|
||||
//qDebug() << "Replacing" << var.first << var.second;
|
||||
s.replace(var.first, var.second);
|
||||
}
|
||||
//qDebug().noquote() << script;
|
||||
return s;
|
||||
}
|
||||
|
||||
|
|
|
@ -143,8 +143,6 @@ namespace libssh {
|
|||
{
|
||||
output = std::string(buffer, bytesRead);
|
||||
if (!output.empty()) {
|
||||
qDebug().noquote() << (isStdErr ? "stdErr" : "stdOut") << QString(output.c_str());
|
||||
|
||||
if (cbReadStdOut && !isStdErr){
|
||||
auto error = cbReadStdOut(output.c_str(), *this);
|
||||
if (error != ErrorCode::NoError) {
|
||||
|
|
519
client/daemon/daemon.cpp
Normal file
519
client/daemon/daemon.cpp
Normal file
|
@ -0,0 +1,519 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "daemon.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QTimer>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr const char* JSON_ALLOWEDIPADDRESSRANGES = "allowedIPAddressRanges";
|
||||
constexpr int HANDSHAKE_POLL_MSEC = 250;
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger("Daemon");
|
||||
|
||||
Daemon* s_daemon = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
Daemon::Daemon(QObject* parent) : QObject(parent) {
|
||||
MZ_COUNT_CTOR(Daemon);
|
||||
|
||||
logger.debug() << "Daemon created";
|
||||
|
||||
Q_ASSERT(s_daemon == nullptr);
|
||||
s_daemon = this;
|
||||
|
||||
m_handshakeTimer.setSingleShot(true);
|
||||
connect(&m_handshakeTimer, &QTimer::timeout, this, &Daemon::checkHandshake);
|
||||
}
|
||||
|
||||
Daemon::~Daemon() {
|
||||
MZ_COUNT_DTOR(Daemon);
|
||||
|
||||
logger.debug() << "Daemon released";
|
||||
|
||||
Q_ASSERT(s_daemon == this);
|
||||
s_daemon = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
Daemon* Daemon::instance() {
|
||||
Q_ASSERT(s_daemon);
|
||||
return s_daemon;
|
||||
}
|
||||
|
||||
bool Daemon::activate(const InterfaceConfig& config) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
// There are 3 possible scenarios in which this method is called:
|
||||
//
|
||||
// 1. the VPN is off: the method tries to enable the VPN.
|
||||
// 2. the VPN is on and the platform doesn't support the server-switching:
|
||||
// this method calls deactivate() and then it continues as 1.
|
||||
// 3. the VPN is on and the platform supports the server-switching: this
|
||||
// method calls switchServer().
|
||||
//
|
||||
// At the end, if the activation succeds, the `connected` signal is emitted.
|
||||
logger.debug() << "Activating interface";
|
||||
|
||||
if (m_connections.contains(config.m_hopindex)) {
|
||||
if (supportServerSwitching(config)) {
|
||||
logger.debug() << "Already connected. Server switching supported.";
|
||||
|
||||
if (!switchServer(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool status = run(Switch, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
logger.warning() << "Already connected. Server switching not supported.";
|
||||
if (!deactivate(false)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_ASSERT(!m_connections.contains(config.m_hopindex));
|
||||
return activate(config);
|
||||
}
|
||||
|
||||
prepareActivation(config);
|
||||
|
||||
// Bring up the wireguard interface if not already done.
|
||||
if (!wgutils()->interfaceExists()) {
|
||||
if (!wgutils()->addInterface(config)) {
|
||||
logger.error() << "Interface creation failed.";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Configure routing for excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
if (m_excludedAddrSet.contains(address)) {
|
||||
m_excludedAddrSet[address]++;
|
||||
continue;
|
||||
}
|
||||
wgutils()->addExclusionRoute(address);
|
||||
m_excludedAddrSet[address] = 1;
|
||||
}
|
||||
|
||||
// Add the peer to this interface.
|
||||
if (!wgutils()->updatePeer(config)) {
|
||||
logger.error() << "Peer creation failed.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!maybeUpdateResolvers(config)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (supportIPUtils()) {
|
||||
if (!iputils()->addInterfaceIPs(config)) {
|
||||
return false;
|
||||
}
|
||||
if (!iputils()->setMTUAndUp(config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// set routing
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip, config.m_hopindex)) {
|
||||
logger.debug() << "Routing configuration failed for"
|
||||
<< logger.sensitive(ip.toString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool status = run(Up, config);
|
||||
logger.debug() << "Connection status:" << status;
|
||||
if (status) {
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool Daemon::maybeUpdateResolvers(const InterfaceConfig& config) {
|
||||
if ((config.m_hopindex == 0) && supportDnsUtils()) {
|
||||
QList<QHostAddress> resolvers;
|
||||
resolvers.append(QHostAddress(config.m_dnsServer));
|
||||
|
||||
// If the DNS is not the Gateway, it's a user defined DNS
|
||||
// thus, not add any other :)
|
||||
if (config.m_dnsServer == config.m_serverIpv4Gateway) {
|
||||
resolvers.append(QHostAddress(config.m_serverIpv6Gateway));
|
||||
}
|
||||
|
||||
if (!dnsutils()->updateResolvers(wgutils()->interfaceName(), resolvers)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Daemon::parseStringList(const QJsonObject& obj, const QString& name,
|
||||
QStringList& list) {
|
||||
if (obj.contains(name)) {
|
||||
QJsonValue value = obj.value(name);
|
||||
if (!value.isArray()) {
|
||||
logger.error() << name << "is not an array";
|
||||
return false;
|
||||
}
|
||||
QJsonArray array = value.toArray();
|
||||
for (const QJsonValue& i : array) {
|
||||
if (!i.isString()) {
|
||||
logger.error() << name << "must contain only strings";
|
||||
return false;
|
||||
}
|
||||
list.append(i.toString());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
bool Daemon::parseConfig(const QJsonObject& obj, InterfaceConfig& config) {
|
||||
#define GETVALUE(name, where, jsontype) \
|
||||
if (!obj.contains(name)) { \
|
||||
logger.debug() << name << " missing in the jsonConfig input"; \
|
||||
return false; \
|
||||
} else { \
|
||||
QJsonValue value = obj.value(name); \
|
||||
if (value.type() != QJsonValue::jsontype) { \
|
||||
logger.error() << name << " is not a " #jsontype; \
|
||||
return false; \
|
||||
} \
|
||||
where = value.to##jsontype(); \
|
||||
}
|
||||
|
||||
GETVALUE("privateKey", config.m_privateKey, String);
|
||||
GETVALUE("serverPublicKey", config.m_serverPublicKey, String);
|
||||
GETVALUE("serverPort", config.m_serverPort, Double);
|
||||
GETVALUE("serverPskKey", config.m_serverPskKey, String);
|
||||
|
||||
config.m_deviceIpv4Address = obj.value("deviceIpv4Address").toString();
|
||||
config.m_deviceIpv6Address = obj.value("deviceIpv6Address").toString();
|
||||
if (config.m_deviceIpv4Address.isNull() &&
|
||||
config.m_deviceIpv6Address.isNull()) {
|
||||
logger.warning() << "no device addresses found in jsonConfig input";
|
||||
return false;
|
||||
}
|
||||
config.m_serverIpv4AddrIn = obj.value("serverIpv4AddrIn").toString();
|
||||
config.m_serverIpv6AddrIn = obj.value("serverIpv6AddrIn").toString();
|
||||
if (config.m_serverIpv4AddrIn.isNull() &&
|
||||
config.m_serverIpv6AddrIn.isNull()) {
|
||||
logger.error() << "no server addresses found in jsonConfig input";
|
||||
return false;
|
||||
}
|
||||
config.m_serverIpv4Gateway = obj.value("serverIpv4Gateway").toString();
|
||||
config.m_serverIpv6Gateway = obj.value("serverIpv6Gateway").toString();
|
||||
|
||||
if (!obj.contains("dnsServer")) {
|
||||
config.m_dnsServer = QString();
|
||||
} else {
|
||||
QJsonValue value = obj.value("dnsServer");
|
||||
if (!value.isString()) {
|
||||
logger.error() << "dnsServer is not a string";
|
||||
return false;
|
||||
}
|
||||
config.m_dnsServer = value.toString();
|
||||
}
|
||||
|
||||
if (!obj.contains("hopindex")) {
|
||||
config.m_hopindex = 0;
|
||||
} else {
|
||||
QJsonValue value = obj.value("hopindex");
|
||||
if (!value.isDouble()) {
|
||||
logger.error() << "hopindex is not a number";
|
||||
return false;
|
||||
}
|
||||
config.m_hopindex = value.toInt();
|
||||
}
|
||||
|
||||
if (!obj.contains(JSON_ALLOWEDIPADDRESSRANGES)) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "missing in the jsonconfig input";
|
||||
return false;
|
||||
} else {
|
||||
QJsonValue value = obj.value(JSON_ALLOWEDIPADDRESSRANGES);
|
||||
if (!value.isArray()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES << "is not an array";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray array = value.toArray();
|
||||
for (const QJsonValue& i : array) {
|
||||
if (!i.isObject()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "must contain only objects";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonObject ipObj = i.toObject();
|
||||
|
||||
QJsonValue address = ipObj.value("address");
|
||||
if (!address.isString()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "objects must have a string address";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue range = ipObj.value("range");
|
||||
if (!range.isDouble()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "object must have a numberic range";
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue isIpv6 = ipObj.value("isIpv6");
|
||||
if (!isIpv6.isBool()) {
|
||||
logger.error() << JSON_ALLOWEDIPADDRESSRANGES
|
||||
<< "object must have a boolean isIpv6";
|
||||
return false;
|
||||
}
|
||||
|
||||
config.m_allowedIPAddressRanges.append(
|
||||
IPAddress(QHostAddress(address.toString()), range.toInt()));
|
||||
}
|
||||
|
||||
// Sort allowed IPs by decreasing prefix length.
|
||||
std::sort(config.m_allowedIPAddressRanges.begin(),
|
||||
config.m_allowedIPAddressRanges.end(),
|
||||
[&](const IPAddress& a, const IPAddress& b) -> bool {
|
||||
return a.prefixLength() > b.prefixLength();
|
||||
});
|
||||
}
|
||||
|
||||
if (!parseStringList(obj, "excludedAddresses", config.m_excludedAddresses)) {
|
||||
return false;
|
||||
}
|
||||
if (!parseStringList(obj, "vpnDisabledApps", config.m_vpnDisabledApps)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Daemon::deactivate(bool emitSignals) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
// Deactivate the main interface.
|
||||
if (m_connections.contains(0)) {
|
||||
const ConnectionState& state = m_connections.value(0);
|
||||
if (!run(Down, state.m_config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (emitSignals) {
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
// Cleanup DNS
|
||||
if (supportDnsUtils() && !dnsutils()->restoreResolvers()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!wgutils()->interfaceExists()) {
|
||||
logger.warning() << "Wireguard interface does not exist.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cleanup peers and routing
|
||||
for (const ConnectionState& state : m_connections) {
|
||||
const InterfaceConfig& config = state.m_config;
|
||||
logger.debug() << "Deleting routes for hop" << config.m_hopindex;
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
wgutils()->deleteRoutePrefix(ip, config.m_hopindex);
|
||||
}
|
||||
wgutils()->deletePeer(config);
|
||||
}
|
||||
|
||||
// Cleanup routing for excluded addresses.
|
||||
for (auto iterator = m_excludedAddrSet.constBegin();
|
||||
iterator != m_excludedAddrSet.constEnd(); ++iterator) {
|
||||
wgutils()->deleteExclusionRoute(iterator.key());
|
||||
}
|
||||
m_excludedAddrSet.clear();
|
||||
|
||||
// Delete the interface
|
||||
if (!wgutils()->deleteInterface()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_connections.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
QString Daemon::logs() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void Daemon::cleanLogs() { }
|
||||
|
||||
bool Daemon::supportServerSwitching(const InterfaceConfig& config) const {
|
||||
if (!m_connections.contains(config.m_hopindex)) {
|
||||
return false;
|
||||
}
|
||||
const InterfaceConfig& current =
|
||||
m_connections.value(config.m_hopindex).m_config;
|
||||
|
||||
return current.m_privateKey == config.m_privateKey &&
|
||||
current.m_deviceIpv4Address == config.m_deviceIpv4Address &&
|
||||
current.m_deviceIpv6Address == config.m_deviceIpv6Address &&
|
||||
current.m_serverIpv4Gateway == config.m_serverIpv4Gateway &&
|
||||
current.m_serverIpv6Gateway == config.m_serverIpv6Gateway;
|
||||
}
|
||||
|
||||
bool Daemon::switchServer(const InterfaceConfig& config) {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
logger.debug() << "Switching server for hop" << config.m_hopindex;
|
||||
|
||||
Q_ASSERT(m_connections.contains(config.m_hopindex));
|
||||
const InterfaceConfig& lastConfig =
|
||||
m_connections.value(config.m_hopindex).m_config;
|
||||
|
||||
// Configure routing for new excluded addresses.
|
||||
for (const QString& i : config.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
if (m_excludedAddrSet.contains(address)) {
|
||||
m_excludedAddrSet[address]++;
|
||||
continue;
|
||||
}
|
||||
wgutils()->addExclusionRoute(address);
|
||||
m_excludedAddrSet[address] = 1;
|
||||
}
|
||||
|
||||
// Activate the new peer and its routes.
|
||||
if (!wgutils()->updatePeer(config)) {
|
||||
logger.error() << "Server switch failed to update the wireguard interface";
|
||||
return false;
|
||||
}
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
if (!wgutils()->updateRoutePrefix(ip, config.m_hopindex)) {
|
||||
logger.error() << "Server switch failed to update the routing table";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove routing entries for the old peer.
|
||||
for (const QString& i : lastConfig.m_excludedAddresses) {
|
||||
QHostAddress address(i);
|
||||
Q_ASSERT(m_excludedAddrSet.contains(address));
|
||||
if (m_excludedAddrSet[address] > 1) {
|
||||
m_excludedAddrSet[address]--;
|
||||
continue;
|
||||
}
|
||||
wgutils()->deleteExclusionRoute(address);
|
||||
m_excludedAddrSet.remove(address);
|
||||
}
|
||||
for (const IPAddress& ip : lastConfig.m_allowedIPAddressRanges) {
|
||||
if (!config.m_allowedIPAddressRanges.contains(ip)) {
|
||||
wgutils()->deleteRoutePrefix(ip, config.m_hopindex);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the old peer if it is no longer necessary.
|
||||
if (config.m_serverPublicKey != lastConfig.m_serverPublicKey) {
|
||||
if (!wgutils()->deletePeer(lastConfig)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_connections[config.m_hopindex] = ConnectionState(config);
|
||||
return true;
|
||||
}
|
||||
|
||||
QJsonObject Daemon::getStatus() {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
QJsonObject json;
|
||||
logger.debug() << "Status request";
|
||||
|
||||
if (!m_connections.contains(0) || !wgutils()->interfaceExists()) {
|
||||
json.insert("connected", QJsonValue(false));
|
||||
return json;
|
||||
}
|
||||
|
||||
const ConnectionState& connection = m_connections.value(0);
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
if (status.m_pubkey != connection.m_config.m_serverPublicKey) {
|
||||
continue;
|
||||
}
|
||||
json.insert("connected", QJsonValue(true));
|
||||
json.insert("serverIpv4Gateway",
|
||||
QJsonValue(connection.m_config.m_serverIpv4Gateway));
|
||||
json.insert("deviceIpv4Address",
|
||||
QJsonValue(connection.m_config.m_deviceIpv4Address));
|
||||
json.insert("date", connection.m_date.toString());
|
||||
json.insert("txBytes", QJsonValue(status.m_txBytes));
|
||||
json.insert("rxBytes", QJsonValue(status.m_rxBytes));
|
||||
return json;
|
||||
}
|
||||
|
||||
json.insert("connected", QJsonValue(false));
|
||||
return json;
|
||||
}
|
||||
|
||||
void Daemon::checkHandshake() {
|
||||
Q_ASSERT(wgutils() != nullptr);
|
||||
|
||||
logger.debug() << "Checking for handshake...";
|
||||
|
||||
int pendingHandshakes = 0;
|
||||
QList<WireguardUtils::PeerStatus> peers = wgutils()->getPeerStatus();
|
||||
for (ConnectionState& connection : m_connections) {
|
||||
const InterfaceConfig& config = connection.m_config;
|
||||
if (connection.m_date.isValid()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if the handshake has completed.
|
||||
for (const WireguardUtils::PeerStatus& status : peers) {
|
||||
if (config.m_serverPublicKey != status.m_pubkey) {
|
||||
continue;
|
||||
}
|
||||
if (status.m_handshake != 0) {
|
||||
connection.m_date.setMSecsSinceEpoch(status.m_handshake);
|
||||
emit connected(status.m_pubkey);
|
||||
}
|
||||
}
|
||||
|
||||
if (!connection.m_date.isValid()) {
|
||||
pendingHandshakes++;
|
||||
}
|
||||
}
|
||||
|
||||
// Check again if there were connections that haven't completed a handshake.
|
||||
if (pendingHandshakes > 0) {
|
||||
m_handshakeTimer.start(HANDSHAKE_POLL_MSEC);
|
||||
}
|
||||
}
|
83
client/daemon/daemon.h
Normal file
83
client/daemon/daemon.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DAEMON_H
|
||||
#define DAEMON_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QTimer>
|
||||
|
||||
#include "dnsutils.h"
|
||||
#include "interfaceconfig.h"
|
||||
#include "iputils.h"
|
||||
#include "wireguardutils.h"
|
||||
|
||||
class Daemon : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Op {
|
||||
Up,
|
||||
Down,
|
||||
Switch,
|
||||
};
|
||||
|
||||
explicit Daemon(QObject* parent);
|
||||
~Daemon();
|
||||
|
||||
static Daemon* instance();
|
||||
|
||||
static bool parseConfig(const QJsonObject& obj, InterfaceConfig& config);
|
||||
|
||||
virtual bool activate(const InterfaceConfig& config);
|
||||
virtual bool deactivate(bool emitSignals = true);
|
||||
virtual QJsonObject getStatus();
|
||||
|
||||
// Callback before any Activating measure is done
|
||||
virtual void prepareActivation(const InterfaceConfig& config){
|
||||
Q_UNUSED(config)};
|
||||
|
||||
QString logs();
|
||||
void cleanLogs();
|
||||
|
||||
signals:
|
||||
void connected(const QString& pubkey);
|
||||
void disconnected();
|
||||
void backendFailure();
|
||||
|
||||
private:
|
||||
bool maybeUpdateResolvers(const InterfaceConfig& config);
|
||||
|
||||
protected:
|
||||
virtual bool run(Op op, const InterfaceConfig& config) {
|
||||
Q_UNUSED(op);
|
||||
Q_UNUSED(config);
|
||||
return true;
|
||||
}
|
||||
virtual bool supportServerSwitching(const InterfaceConfig& config) const;
|
||||
virtual bool switchServer(const InterfaceConfig& config);
|
||||
virtual WireguardUtils* wgutils() const = 0;
|
||||
virtual bool supportIPUtils() const { return false; }
|
||||
virtual IPUtils* iputils() { return nullptr; }
|
||||
virtual bool supportDnsUtils() const { return false; }
|
||||
virtual DnsUtils* dnsutils() { return nullptr; }
|
||||
|
||||
static bool parseStringList(const QJsonObject& obj, const QString& name,
|
||||
QStringList& list);
|
||||
|
||||
void checkHandshake();
|
||||
|
||||
class ConnectionState {
|
||||
public:
|
||||
ConnectionState(){};
|
||||
ConnectionState(const InterfaceConfig& config) { m_config = config; }
|
||||
QDateTime m_date;
|
||||
InterfaceConfig m_config;
|
||||
};
|
||||
QMap<int, ConnectionState> m_connections;
|
||||
QHash<QHostAddress, int> m_excludedAddrSet;
|
||||
QTimer m_handshakeTimer;
|
||||
};
|
||||
|
||||
#endif // DAEMON_H
|
98
client/daemon/daemonlocalserver.cpp
Normal file
98
client/daemon/daemonlocalserver.cpp
Normal file
|
@ -0,0 +1,98 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "daemonlocalserver.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "daemonlocalserverconnection.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#ifdef MZ_MACOS
|
||||
# include <sys/stat.h>
|
||||
# include <sys/types.h>
|
||||
# include <unistd.h>
|
||||
|
||||
constexpr const char* TMP_PATH = "/tmp/amneziavpn.socket";
|
||||
constexpr const char* VAR_PATH = "/var/run/amneziavpn/daemon.socket";
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
Logger logger("DaemonLocalServer");
|
||||
} // namespace
|
||||
|
||||
DaemonLocalServer::DaemonLocalServer(QObject* parent) : QObject(parent) {
|
||||
MZ_COUNT_CTOR(DaemonLocalServer);
|
||||
}
|
||||
|
||||
DaemonLocalServer::~DaemonLocalServer() { MZ_COUNT_DTOR(DaemonLocalServer); }
|
||||
|
||||
bool DaemonLocalServer::initialize() {
|
||||
m_server.setSocketOptions(QLocalServer::WorldAccessOption);
|
||||
|
||||
QString path = daemonPath();
|
||||
logger.debug() << "Server path:" << path;
|
||||
|
||||
if (QFileInfo::exists(path)) {
|
||||
QFile::remove(path);
|
||||
}
|
||||
|
||||
if (!m_server.listen(path)) {
|
||||
logger.error() << "Failed to listen the daemon path";
|
||||
return false;
|
||||
}
|
||||
|
||||
connect(&m_server, &QLocalServer::newConnection, [&] {
|
||||
logger.debug() << "New connection received";
|
||||
|
||||
if (!m_server.hasPendingConnections()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QLocalSocket* socket = m_server.nextPendingConnection();
|
||||
Q_ASSERT(socket);
|
||||
|
||||
DaemonLocalServerConnection* connection =
|
||||
new DaemonLocalServerConnection(&m_server, socket);
|
||||
connect(socket, &QLocalSocket::disconnected, connection,
|
||||
&DaemonLocalServerConnection::deleteLater);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QString DaemonLocalServer::daemonPath() const {
|
||||
#if defined(MZ_WINDOWS)
|
||||
return "\\\\.\\pipe\\amneziavpn";
|
||||
#elif defined(MZ_MACOS)
|
||||
QDir dir("/var/run");
|
||||
if (!dir.exists()) {
|
||||
logger.warning() << "/var/run doesn't exist. Fallback /tmp.";
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
if (dir.exists("amneziavpn")) {
|
||||
logger.debug() << "/var/run/amneziavpn seems to be usable";
|
||||
return VAR_PATH;
|
||||
}
|
||||
|
||||
if (!dir.mkdir("amneziavpn")) {
|
||||
logger.warning() << "Failed to create /var/run/amneziavpn";
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
if (chmod("/var/run/amneziavpn", S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
|
||||
logger.warning()
|
||||
<< "Failed to set the right permissions to /var/run/amneziavpn";
|
||||
return TMP_PATH;
|
||||
}
|
||||
|
||||
return VAR_PATH;
|
||||
#else
|
||||
# error Unsupported platform
|
||||
#endif
|
||||
}
|
26
client/daemon/daemonlocalserver.h
Normal file
26
client/daemon/daemonlocalserver.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DAEMONLOCALSERVER_H
|
||||
#define DAEMONLOCALSERVER_H
|
||||
|
||||
#include <QLocalServer>
|
||||
|
||||
class DaemonLocalServer final : public QObject {
|
||||
Q_DISABLE_COPY_MOVE(DaemonLocalServer)
|
||||
|
||||
public:
|
||||
explicit DaemonLocalServer(QObject* parent);
|
||||
~DaemonLocalServer();
|
||||
|
||||
bool initialize();
|
||||
|
||||
private:
|
||||
QString daemonPath() const;
|
||||
|
||||
private:
|
||||
QLocalServer m_server;
|
||||
};
|
||||
|
||||
#endif // DAEMONLOCALSERVER_H
|
162
client/daemon/daemonlocalserverconnection.cpp
Normal file
162
client/daemon/daemonlocalserverconnection.cpp
Normal file
|
@ -0,0 +1,162 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "daemonlocalserverconnection.h"
|
||||
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QLocalSocket>
|
||||
|
||||
#include "daemon.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("DaemonLocalServerConnection");
|
||||
}
|
||||
|
||||
DaemonLocalServerConnection::DaemonLocalServerConnection(QObject* parent,
|
||||
QLocalSocket* socket)
|
||||
: QObject(parent) {
|
||||
MZ_COUNT_CTOR(DaemonLocalServerConnection);
|
||||
|
||||
logger.debug() << "Connection created";
|
||||
|
||||
Q_ASSERT(socket);
|
||||
m_socket = socket;
|
||||
|
||||
connect(m_socket, &QLocalSocket::readyRead, this,
|
||||
&DaemonLocalServerConnection::readData);
|
||||
|
||||
Daemon* daemon = Daemon::instance();
|
||||
connect(daemon, &Daemon::connected, this,
|
||||
&DaemonLocalServerConnection::connected);
|
||||
connect(daemon, &Daemon::disconnected, this,
|
||||
&DaemonLocalServerConnection::disconnected);
|
||||
connect(daemon, &Daemon::backendFailure, this,
|
||||
&DaemonLocalServerConnection::backendFailure);
|
||||
}
|
||||
|
||||
DaemonLocalServerConnection::~DaemonLocalServerConnection() {
|
||||
MZ_COUNT_DTOR(DaemonLocalServerConnection);
|
||||
|
||||
logger.debug() << "Connection released";
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::readData() {
|
||||
logger.debug() << "Read Data";
|
||||
|
||||
Q_ASSERT(m_socket);
|
||||
|
||||
while (true) {
|
||||
int pos = m_buffer.indexOf("\n");
|
||||
if (pos == -1) {
|
||||
QByteArray input = m_socket->readAll();
|
||||
if (input.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
m_buffer.append(input);
|
||||
continue;
|
||||
}
|
||||
|
||||
QByteArray line = m_buffer.left(pos);
|
||||
m_buffer.remove(0, pos + 1);
|
||||
|
||||
QByteArray command(line);
|
||||
command = command.trimmed();
|
||||
|
||||
if (command.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::parseCommand(const QByteArray& data) {
|
||||
QJsonDocument json = QJsonDocument::fromJson(data);
|
||||
if (!json.isObject()) {
|
||||
logger.error() << "Invalid input";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
QJsonValue typeValue = obj.value("type");
|
||||
if (!typeValue.isString()) {
|
||||
logger.warning() << "No type command. Ignoring request.";
|
||||
return;
|
||||
}
|
||||
QString type = typeValue.toString();
|
||||
|
||||
logger.debug() << "Command received:" << type;
|
||||
|
||||
if (type == "activate") {
|
||||
InterfaceConfig config;
|
||||
if (!Daemon::parseConfig(obj, config)) {
|
||||
logger.error() << "Invalid configuration";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Daemon::instance()->activate(config)) {
|
||||
logger.error() << "Failed to activate the interface";
|
||||
emit disconnected();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "deactivate") {
|
||||
Daemon::instance()->deactivate();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "status") {
|
||||
QJsonObject obj = Daemon::instance()->getStatus();
|
||||
obj.insert("type", "status");
|
||||
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "logs") {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "logs");
|
||||
obj.insert("logs", Daemon::instance()->logs().replace("\n", "|"));
|
||||
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "cleanlogs") {
|
||||
Daemon::instance()->cleanLogs();
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warning() << "Invalid command:" << type;
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::connected(const QString& pubkey) {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "connected");
|
||||
obj.insert("pubkey", QJsonValue(pubkey));
|
||||
write(obj);
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::disconnected() {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "disconnected");
|
||||
write(obj);
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::backendFailure() {
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "backendFailure");
|
||||
write(obj);
|
||||
}
|
||||
|
||||
void DaemonLocalServerConnection::write(const QJsonObject& obj) {
|
||||
m_socket->write(QJsonDocument(obj).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
}
|
36
client/daemon/daemonlocalserverconnection.h
Normal file
36
client/daemon/daemonlocalserverconnection.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DAEMONLOCALSERVERCONNECTION_H
|
||||
#define DAEMONLOCALSERVERCONNECTION_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QLocalSocket;
|
||||
|
||||
class DaemonLocalServerConnection final : public QObject {
|
||||
Q_DISABLE_COPY_MOVE(DaemonLocalServerConnection)
|
||||
|
||||
public:
|
||||
DaemonLocalServerConnection(QObject* parent, QLocalSocket* socket);
|
||||
~DaemonLocalServerConnection();
|
||||
|
||||
private:
|
||||
void readData();
|
||||
|
||||
void parseCommand(const QByteArray& json);
|
||||
|
||||
void connected(const QString& pubkey);
|
||||
void disconnected();
|
||||
void backendFailure();
|
||||
|
||||
void write(const QJsonObject& obj);
|
||||
|
||||
private:
|
||||
QLocalSocket* m_socket = nullptr;
|
||||
|
||||
QByteArray m_buffer;
|
||||
};
|
||||
|
||||
#endif // DAEMONLOCALSERVERCONNECTION_H
|
34
client/daemon/dnsutils.h
Normal file
34
client/daemon/dnsutils.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DNSUTILS_H
|
||||
#define DNSUTILS_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QString>
|
||||
|
||||
#include "dnsutils.h"
|
||||
|
||||
class DnsUtils : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DnsUtils(QObject* parent) : QObject(parent){};
|
||||
virtual ~DnsUtils() = default;
|
||||
|
||||
virtual bool updateResolvers(const QString& ifname,
|
||||
const QList<QHostAddress>& resolvers) {
|
||||
Q_UNUSED(ifname);
|
||||
Q_UNUSED(resolvers);
|
||||
qFatal("Have you forgotten to implement DnsUtils::updateResolvers?");
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual bool restoreResolvers() {
|
||||
qFatal("Have you forgotten to implement DnsUtils::restoreResolvers?");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // DNSUTILS_H
|
31
client/daemon/interfaceconfig.h
Normal file
31
client/daemon/interfaceconfig.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef INTERFACECONFIG_H
|
||||
#define INTERFACECONFIG_H
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
|
||||
#include "ipaddress.h"
|
||||
|
||||
struct InterfaceConfig {
|
||||
int m_hopindex = 0;
|
||||
QString m_privateKey;
|
||||
QString m_deviceIpv4Address;
|
||||
QString m_deviceIpv6Address;
|
||||
QString m_serverIpv4Gateway;
|
||||
QString m_serverIpv6Gateway;
|
||||
QString m_serverPublicKey;
|
||||
QString m_serverPskKey;
|
||||
QString m_serverIpv4AddrIn;
|
||||
QString m_serverIpv6AddrIn;
|
||||
QString m_dnsServer;
|
||||
int m_serverPort = 0;
|
||||
QList<IPAddress> m_allowedIPAddressRanges;
|
||||
QStringList m_excludedAddresses;
|
||||
QStringList m_vpnDisabledApps;
|
||||
};
|
||||
|
||||
#endif // INTERFACECONFIG_H
|
31
client/daemon/iputils.h
Normal file
31
client/daemon/iputils.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IPUTILS_H
|
||||
#define IPUTILS_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QObject>
|
||||
|
||||
#include "interfaceconfig.h"
|
||||
|
||||
class IPUtils : public QObject {
|
||||
public:
|
||||
explicit IPUtils(QObject* parent) : QObject(parent){};
|
||||
virtual ~IPUtils() = default;
|
||||
|
||||
virtual bool addInterfaceIPs(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
qFatal("Have you forgotten to implement IPUtils::addInterfaceIPs?");
|
||||
return false;
|
||||
};
|
||||
|
||||
virtual bool setMTUAndUp(const InterfaceConfig& config) {
|
||||
Q_UNUSED(config);
|
||||
qFatal("Have you forgotten to implement IPUtils::setMTUAndUp?");
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // IPUTILS_H
|
51
client/daemon/wireguardutils.h
Normal file
51
client/daemon/wireguardutils.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef WIREGUARDUTILS_H
|
||||
#define WIREGUARDUTILS_H
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include "interfaceconfig.h"
|
||||
|
||||
constexpr const char* WG_INTERFACE = "moz0";
|
||||
|
||||
constexpr uint16_t WG_KEEPALIVE_PERIOD = 60;
|
||||
|
||||
class WireguardUtils : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
class PeerStatus {
|
||||
public:
|
||||
PeerStatus(const QString& pubkey = QString()) { m_pubkey = pubkey; }
|
||||
QString m_pubkey;
|
||||
qint64 m_handshake = 0;
|
||||
qint64 m_rxBytes = 0;
|
||||
qint64 m_txBytes = 0;
|
||||
};
|
||||
|
||||
explicit WireguardUtils(QObject* parent) : QObject(parent){};
|
||||
virtual ~WireguardUtils() = default;
|
||||
|
||||
virtual bool interfaceExists() = 0;
|
||||
virtual QString interfaceName() { return WG_INTERFACE; }
|
||||
virtual bool addInterface(const InterfaceConfig& config) = 0;
|
||||
virtual bool deleteInterface() = 0;
|
||||
|
||||
virtual bool updatePeer(const InterfaceConfig& config) = 0;
|
||||
virtual bool deletePeer(const InterfaceConfig& config) = 0;
|
||||
virtual QList<PeerStatus> getPeerStatus() = 0;
|
||||
|
||||
virtual bool updateRoutePrefix(const IPAddress& prefix, int hopindex) = 0;
|
||||
virtual bool deleteRoutePrefix(const IPAddress& prefix, int hopindex) = 0;
|
||||
|
||||
virtual bool addExclusionRoute(const QHostAddress& address) = 0;
|
||||
virtual bool deleteExclusionRoute(const QHostAddress& address) = 0;
|
||||
};
|
||||
|
||||
#endif // WIREGUARDUTILS_H
|
|
@ -4,6 +4,8 @@
|
|||
#include <QDebug>
|
||||
#include <QDesktopServices>
|
||||
#include <QDir>
|
||||
#include <QMetaEnum>
|
||||
#include <QJsonDocument>
|
||||
#include <QStandardPaths>
|
||||
#include <QUrl>
|
||||
|
||||
|
@ -180,3 +182,80 @@ void Logger::cleanUp()
|
|||
|
||||
clearServiceLogs();
|
||||
}
|
||||
|
||||
Logger::Log::Log(Logger* logger, LogLevel logLevel)
|
||||
: m_logger(logger), m_logLevel(logLevel), m_data(new Data()) {}
|
||||
|
||||
Logger::Log::~Log() {
|
||||
qDebug() << "Amnezia" << m_logger->className() << m_data->m_buffer.trimmed();
|
||||
delete m_data;
|
||||
}
|
||||
|
||||
Logger::Log Logger::error() { return Log(this, LogLevel::Error); }
|
||||
Logger::Log Logger::warning() { return Log(this, LogLevel::Warning); }
|
||||
Logger::Log Logger::info() { return Log(this, LogLevel::Info); }
|
||||
Logger::Log Logger::debug() { return Log(this, LogLevel::Debug); }
|
||||
QString Logger::sensitive(const QString& input) {
|
||||
#ifdef Q_DEBUG
|
||||
return input;
|
||||
#else
|
||||
Q_UNUSED(input);
|
||||
return QString(8, 'X');
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#define CREATE_LOG_OP_REF(x) \
|
||||
Logger::Log& Logger::Log::operator<<(x t) { \
|
||||
m_data->m_ts << t << ' '; \
|
||||
return *this; \
|
||||
}
|
||||
|
||||
CREATE_LOG_OP_REF(uint64_t);
|
||||
CREATE_LOG_OP_REF(const char*);
|
||||
CREATE_LOG_OP_REF(const QString&);
|
||||
CREATE_LOG_OP_REF(const QByteArray&);
|
||||
CREATE_LOG_OP_REF(const void*);
|
||||
|
||||
#undef CREATE_LOG_OP_REF
|
||||
|
||||
Logger::Log& Logger::Log::operator<<(const QStringList& t) {
|
||||
m_data->m_ts << '[' << t.join(",") << ']' << ' ';
|
||||
return *this;
|
||||
}
|
||||
|
||||
Logger::Log& Logger::Log::operator<<(const QJsonObject& t) {
|
||||
m_data->m_ts << QJsonDocument(t).toJson(QJsonDocument::Indented) << ' ';
|
||||
return *this;
|
||||
}
|
||||
|
||||
Logger::Log& Logger::Log::operator<<(QTextStreamFunction t) {
|
||||
m_data->m_ts << t;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Logger::Log::addMetaEnum(quint64 value, const QMetaObject* meta,
|
||||
const char* name) {
|
||||
QMetaEnum me = meta->enumerator(meta->indexOfEnumerator(name));
|
||||
|
||||
QString out;
|
||||
QTextStream ts(&out);
|
||||
|
||||
if (const char* scope = me.scope()) {
|
||||
ts << scope << "::";
|
||||
}
|
||||
|
||||
const char* key = me.valueToKey(static_cast<int>(value));
|
||||
const bool scoped = me.isScoped();
|
||||
if (scoped || !key) {
|
||||
ts << me.enumName() << (!key ? "(" : "::");
|
||||
}
|
||||
|
||||
if (key) {
|
||||
ts << key;
|
||||
} else {
|
||||
ts << value << ")";
|
||||
}
|
||||
|
||||
m_data->m_ts << out;
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#include "ui/property_helper.h"
|
||||
|
||||
#include "mozilla/shared/loglevel.h"
|
||||
|
||||
class Logger : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -34,6 +36,56 @@ public:
|
|||
static QString userLogsFilePath();
|
||||
static QString getLogFile();
|
||||
|
||||
// compat with Mozilla logger
|
||||
Logger(const QString &className) { m_className = className; }
|
||||
const QString& className() const { return m_className; }
|
||||
|
||||
class Log {
|
||||
public:
|
||||
Log(Logger* logger, LogLevel level);
|
||||
~Log();
|
||||
|
||||
Log& operator<<(uint64_t t);
|
||||
Log& operator<<(const char* t);
|
||||
Log& operator<<(const QString& t);
|
||||
Log& operator<<(const QStringList& t);
|
||||
Log& operator<<(const QByteArray& t);
|
||||
Log& operator<<(const QJsonObject& t);
|
||||
Log& operator<<(QTextStreamFunction t);
|
||||
Log& operator<<(const void* t);
|
||||
|
||||
// Q_ENUM
|
||||
template <typename T>
|
||||
typename std::enable_if<QtPrivate::IsQEnumHelper<T>::Value, Log&>::type
|
||||
operator<<(T t) {
|
||||
const QMetaObject* meta = qt_getEnumMetaObject(t);
|
||||
const char* name = qt_getEnumName(t);
|
||||
addMetaEnum(typename QFlags<T>::Int(t), meta, name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
void addMetaEnum(quint64 value, const QMetaObject* meta, const char* name);
|
||||
|
||||
Logger* m_logger;
|
||||
LogLevel m_logLevel;
|
||||
|
||||
struct Data {
|
||||
Data() : m_ts(&m_buffer, QIODevice::WriteOnly) {}
|
||||
|
||||
QString m_buffer;
|
||||
QTextStream m_ts;
|
||||
};
|
||||
|
||||
Data* m_data;
|
||||
};
|
||||
|
||||
Log error();
|
||||
Log warning();
|
||||
Log info();
|
||||
Log debug();
|
||||
QString sensitive(const QString& input);
|
||||
|
||||
private:
|
||||
Logger() {}
|
||||
Logger(Logger const &) = delete;
|
||||
|
@ -46,6 +98,9 @@ private:
|
|||
static QString m_logFileName;
|
||||
|
||||
friend void debugMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg);
|
||||
|
||||
// compat with Mozilla logger
|
||||
QString m_className;
|
||||
};
|
||||
|
||||
#endif // LOGGER_H
|
||||
|
|
89
client/mozilla/controllerimpl.h
Normal file
89
client/mozilla/controllerimpl.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef CONTROLLERIMPL_H
|
||||
#define CONTROLLERIMPL_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <functional>
|
||||
|
||||
class Keys;
|
||||
class Device;
|
||||
class Server;
|
||||
class QDateTime;
|
||||
class IPAddress;
|
||||
class QHostAddress;
|
||||
|
||||
// This object is allocated when the VPN is about to be activated.
|
||||
// It's kept alive, basically forever, except in these scenarios, in which it's
|
||||
// recreated:
|
||||
// - the user does a logout
|
||||
// - there is an authentication falure
|
||||
class ControllerImpl : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ControllerImpl() = default;
|
||||
|
||||
virtual ~ControllerImpl() = default;
|
||||
|
||||
// This method is called to initialize the controller. The initialization
|
||||
// is completed when the signal "initialized" is emitted.
|
||||
virtual void initialize(const Device* device, const Keys* keys) = 0;
|
||||
|
||||
// This method is called when the VPN client needs to activate the VPN
|
||||
// tunnel. It's called only at the end of the initialization process. When
|
||||
// this method is called, the VPN client is in "connecting" state. This
|
||||
// state terminates when the "connected" (or the "disconnected") signal is
|
||||
// received.
|
||||
virtual void activate(const QJsonObject& config) = 0;
|
||||
|
||||
// This method terminates the VPN tunnel. The VPN client is in
|
||||
// "disconnecting" state until the "disconnected" signal is received.
|
||||
virtual void deactivate() = 0;
|
||||
|
||||
// This method is used to retrieve the VPN tunnel status (mainly the number
|
||||
// of bytes sent and received). It's called always when the VPN tunnel is
|
||||
// active.
|
||||
virtual void checkStatus() = 0;
|
||||
|
||||
// This method is used to retrieve the logs from the backend service. Use
|
||||
// the callback to report logs when available.
|
||||
virtual void getBackendLogs(
|
||||
std::function<void(const QString& logs)>&& callback) = 0;
|
||||
|
||||
// Cleanup the backend logs.
|
||||
virtual void cleanupBackendLogs() = 0;
|
||||
|
||||
// Whether the controller supports multihop
|
||||
virtual bool multihopSupported() { return false; }
|
||||
|
||||
virtual bool silentServerSwitchingSupported() const { return true; }
|
||||
|
||||
signals:
|
||||
// This signal is emitted when the controller is initialized. Note that the
|
||||
// VPN tunnel can be already active. In this case, "connected" should be set
|
||||
// to true and the "connectionDate" should be set to the activation date if
|
||||
// known.
|
||||
// If "status" is set to false, the backend service is considered unavailable.
|
||||
void initialized(bool status, bool connected,
|
||||
const QDateTime& connectionDate);
|
||||
|
||||
// These 2 signals can be dispatched at any time.
|
||||
void connected(const QString& pubkey,
|
||||
const QDateTime& connectionTimestamp = QDateTime());
|
||||
void disconnected();
|
||||
|
||||
// This method should be emitted after a checkStatus() call.
|
||||
// "serverIpv4Gateway" is the current VPN tunnel gateway.
|
||||
// "deviceIpv4Address" is the address of the VPN client.
|
||||
// "txBytes" and "rxBytes" contain the number of transmitted and received
|
||||
// bytes since the last statusUpdated signal.
|
||||
void statusUpdated(const QString& serverIpv4Gateway,
|
||||
const QString& deviceIpv4Address, uint64_t txBytes,
|
||||
uint64_t rxBytes);
|
||||
};
|
||||
|
||||
#endif // CONTROLLERIMPL_H
|
117
client/mozilla/dnspingsender.cpp
Normal file
117
client/mozilla/dnspingsender.cpp
Normal file
|
@ -0,0 +1,117 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "dnspingsender.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include <QNetworkDatagram>
|
||||
#include <QtEndian>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr const quint16 DNS_PORT = 53;
|
||||
|
||||
// A quick and dirty DNS Header structure definition from RFC1035,
|
||||
// Section 4.1.1: Header Section format.
|
||||
struct dnsHeader {
|
||||
quint16 id;
|
||||
quint16 flags;
|
||||
quint16 qdcount;
|
||||
quint16 ancount;
|
||||
quint16 nscount;
|
||||
quint16 arcount;
|
||||
};
|
||||
|
||||
// Bit definitions for the DNS flags field.
|
||||
#define DNS_FLAG_QR 0x8000
|
||||
#define DNS_FLAG_OPCODE 0x7800
|
||||
#define DNS_FLAG_OPCODE_QUERY (0x0 << 11)
|
||||
#define DNS_FLAG_OPCODE_IQUERY (0x1 << 11)
|
||||
#define DNS_FLAG_OPCODE_STATUS (0x2 << 11)
|
||||
#define DNS_FLAG_AA 0x0400
|
||||
#define DNS_FLAG_TC 0x0200
|
||||
#define DNS_FLAG_RD 0x0100
|
||||
#define DNS_FLAG_RA 0x0080
|
||||
#define DNS_FLAG_Z 0x0070
|
||||
#define DNS_FLAG_RCODE 0x000F
|
||||
#define DNS_FLAG_RCODE_NO_ERROR (0x0 << 0)
|
||||
#define DNS_FLAG_RCODE_FORMAT_ERROR (0x1 << 0)
|
||||
#define DNS_FLAG_RCODE_SERVER_FAILURE (0x2 << 0)
|
||||
#define DNS_FLAG_RCODE_NAME_ERROR (0x3 << 0)
|
||||
#define DNS_FLAG_RCODE_NOT_IMPLEMENTED (0x4 << 0)
|
||||
#define DNS_FLAG_RCODE_REFUSED (0x5 << 0)
|
||||
|
||||
namespace {
|
||||
Logger logger("DnsPingSender");
|
||||
}
|
||||
|
||||
DnsPingSender::DnsPingSender(const QHostAddress& source, QObject* parent)
|
||||
: PingSender(parent) {
|
||||
MZ_COUNT_CTOR(DnsPingSender);
|
||||
|
||||
if (source.isNull()) {
|
||||
m_socket.bind();
|
||||
} else {
|
||||
m_socket.bind(source);
|
||||
}
|
||||
|
||||
connect(&m_socket, &QUdpSocket::readyRead, this, &DnsPingSender::readData);
|
||||
}
|
||||
|
||||
DnsPingSender::~DnsPingSender() { MZ_COUNT_DTOR(DnsPingSender); }
|
||||
|
||||
void DnsPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
QByteArray packet;
|
||||
|
||||
// Assemble a DNS query header.
|
||||
struct dnsHeader header;
|
||||
memset(&header, 0, sizeof(header));
|
||||
header.id = qToBigEndian<quint16>(sequence);
|
||||
header.flags = qToBigEndian<quint16>(DNS_FLAG_OPCODE_QUERY);
|
||||
header.qdcount = qToBigEndian<quint16>(1);
|
||||
header.ancount = 0;
|
||||
header.nscount = 0;
|
||||
header.arcount = 0;
|
||||
packet.append(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
|
||||
// Add a query for the root nameserver: {<root>, type A, class IN}
|
||||
const char query[] = {0x00, 0x00, 0x01, 0x00, 0x01};
|
||||
packet.append(query, sizeof(query));
|
||||
|
||||
// Send the datagram.
|
||||
m_socket.writeDatagram(packet, dest, DNS_PORT);
|
||||
}
|
||||
|
||||
void DnsPingSender::readData() {
|
||||
while (m_socket.hasPendingDatagrams()) {
|
||||
QNetworkDatagram reply = m_socket.receiveDatagram();
|
||||
if (!reply.isValid()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Extract the header from the DNS response.
|
||||
QByteArray payload = reply.data();
|
||||
struct dnsHeader header;
|
||||
if (payload.length() < static_cast<int>(sizeof(header))) {
|
||||
logger.debug() << "Received bogus DNS reply: truncated header";
|
||||
continue;
|
||||
}
|
||||
memcpy(&header, payload.constData(), sizeof(header));
|
||||
|
||||
// Perfom some checks to ensure this is the reply we were expecting.
|
||||
quint16 flags = qFromBigEndian<quint16>(header.flags);
|
||||
if ((flags & DNS_FLAG_QR) == 0) {
|
||||
logger.debug() << "Received bogus DNS reply: QR == query";
|
||||
continue;
|
||||
}
|
||||
if ((flags & DNS_FLAG_OPCODE) != DNS_FLAG_OPCODE_QUERY) {
|
||||
logger.debug() << "Received bogus DNS reply: OPCODE != query";
|
||||
continue;
|
||||
}
|
||||
|
||||
emit recvPing(qFromBigEndian<quint16>(header.id));
|
||||
}
|
||||
}
|
29
client/mozilla/dnspingsender.h
Normal file
29
client/mozilla/dnspingsender.h
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DNSPINGSENDER_H
|
||||
#define DNSPINGSENDER_H
|
||||
|
||||
#include <QUdpSocket>
|
||||
|
||||
#include "pingsender.h"
|
||||
|
||||
class DnsPingSender final : public PingSender {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(DnsPingSender)
|
||||
|
||||
public:
|
||||
DnsPingSender(const QHostAddress& source, QObject* parent = nullptr);
|
||||
~DnsPingSender();
|
||||
|
||||
void sendPing(const QHostAddress& dest, quint16 sequence) override;
|
||||
|
||||
private:
|
||||
void readData();
|
||||
|
||||
private:
|
||||
QUdpSocket m_socket;
|
||||
};
|
||||
|
||||
#endif // DNSPINGSENDER_H
|
384
client/mozilla/localsocketcontroller.cpp
Normal file
384
client/mozilla/localsocketcontroller.cpp
Normal file
|
@ -0,0 +1,384 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "protocols/protocols_defs.h"
|
||||
#include "localsocketcontroller.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QHostAddress>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "ipaddress.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "models/server.h"
|
||||
|
||||
// How many times do we try to reconnect.
|
||||
constexpr int MAX_CONNECTION_RETRY = 10;
|
||||
|
||||
// How long do we wait between one try and the next one.
|
||||
constexpr int CONNECTION_RETRY_TIMER_MSEC = 500;
|
||||
|
||||
namespace {
|
||||
Logger logger("LocalSocketController");
|
||||
}
|
||||
|
||||
LocalSocketController::LocalSocketController() {
|
||||
MZ_COUNT_CTOR(LocalSocketController);
|
||||
|
||||
m_socket = new QLocalSocket(this);
|
||||
connect(m_socket, &QLocalSocket::connected, this,
|
||||
&LocalSocketController::daemonConnected);
|
||||
connect(m_socket, &QLocalSocket::disconnected, this,
|
||||
&LocalSocketController::disconnected);
|
||||
connect(m_socket, &QLocalSocket::errorOccurred, this,
|
||||
&LocalSocketController::errorOccurred);
|
||||
connect(m_socket, &QLocalSocket::readyRead, this,
|
||||
&LocalSocketController::readData);
|
||||
|
||||
m_initializingTimer.setSingleShot(true);
|
||||
connect(&m_initializingTimer, &QTimer::timeout, this,
|
||||
&LocalSocketController::initializeInternal);
|
||||
}
|
||||
|
||||
LocalSocketController::~LocalSocketController() {
|
||||
MZ_COUNT_DTOR(LocalSocketController);
|
||||
}
|
||||
|
||||
void LocalSocketController::errorOccurred(
|
||||
QLocalSocket::LocalSocketError error) {
|
||||
logger.error() << "Error occurred:" << error;
|
||||
|
||||
if (m_daemonState == eInitializing) {
|
||||
if (m_initializingRetry++ < MAX_CONNECTION_RETRY) {
|
||||
m_initializingTimer.start(CONNECTION_RETRY_TIMER_MSEC);
|
||||
return;
|
||||
}
|
||||
|
||||
emit initialized(false, false, QDateTime());
|
||||
}
|
||||
|
||||
qCritical() << "ControllerError";
|
||||
disconnectInternal();
|
||||
}
|
||||
|
||||
void LocalSocketController::disconnectInternal() {
|
||||
// We're still eReady as the Deamon is alive
|
||||
// and can make a new connection.
|
||||
m_daemonState = eReady;
|
||||
m_initializingRetry = 0;
|
||||
m_initializingTimer.stop();
|
||||
emit disconnected();
|
||||
}
|
||||
|
||||
void LocalSocketController::initialize(const Device* device, const Keys* keys) {
|
||||
logger.debug() << "Initializing";
|
||||
|
||||
Q_UNUSED(device);
|
||||
Q_UNUSED(keys);
|
||||
|
||||
Q_ASSERT(m_daemonState == eUnknown);
|
||||
m_initializingRetry = 0;
|
||||
|
||||
initializeInternal();
|
||||
}
|
||||
|
||||
void LocalSocketController::initializeInternal() {
|
||||
m_daemonState = eInitializing;
|
||||
|
||||
#ifdef MZ_WINDOWS
|
||||
QString path = "\\\\.\\pipe\\amneziavpn";
|
||||
#else
|
||||
QString path = "/var/run/amneziavpn/daemon.socket";
|
||||
if (!QFileInfo::exists(path)) {
|
||||
path = "/tmp/amneziavpn.socket";
|
||||
}
|
||||
#endif
|
||||
|
||||
logger.debug() << "Connecting to:" << path;
|
||||
m_socket->connectToServer(path);
|
||||
}
|
||||
|
||||
void LocalSocketController::daemonConnected() {
|
||||
logger.debug() << "Daemon connected";
|
||||
Q_ASSERT(m_daemonState == eInitializing);
|
||||
checkStatus();
|
||||
}
|
||||
|
||||
void LocalSocketController::activate(const QJsonObject &rawConfig) {
|
||||
|
||||
qDebug() << rawConfig;
|
||||
QJsonObject wgConfig = rawConfig.value("wireguard_config_data").toObject();
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "activate");
|
||||
// json.insert("hopindex", QJsonValue((double)hop.m_hopindex));
|
||||
json.insert("privateKey", wgConfig.value(amnezia::config_key::client_priv_key));
|
||||
json.insert("deviceIpv4Address", wgConfig.value(amnezia::config_key::client_ip));
|
||||
json.insert("deviceIpv6Address", "dead::1");
|
||||
json.insert("serverPublicKey", wgConfig.value(amnezia::config_key::server_pub_key));
|
||||
json.insert("serverPskKey", wgConfig.value(amnezia::config_key::psk_key));
|
||||
json.insert("serverIpv4AddrIn", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6AddrIn", QJsonValue(hop.m_server.ipv6AddrIn()));
|
||||
json.insert("serverPort", wgConfig.value(amnezia::config_key::port).toInt());
|
||||
|
||||
json.insert("serverIpv4Gateway", wgConfig.value(amnezia::config_key::hostName));
|
||||
// json.insert("serverIpv6Gateway", QJsonValue(hop.m_server.ipv6Gateway()));
|
||||
json.insert("dnsServer", rawConfig.value(amnezia::config_key::dns1));
|
||||
|
||||
QJsonArray jsAllowedIPAddesses;
|
||||
|
||||
QJsonObject range_ipv4;
|
||||
range_ipv4.insert("address", "0.0.0.0");
|
||||
range_ipv4.insert("range", 0);
|
||||
range_ipv4.insert("isIpv6", false);
|
||||
jsAllowedIPAddesses.append(range_ipv4);
|
||||
|
||||
QJsonObject range_ipv6;
|
||||
range_ipv6.insert("address", "::");
|
||||
range_ipv6.insert("range", 0);
|
||||
range_ipv6.insert("isIpv6", true);
|
||||
jsAllowedIPAddesses.append(range_ipv6);
|
||||
|
||||
json.insert("allowedIPAddressRanges", jsAllowedIPAddesses);
|
||||
|
||||
|
||||
QJsonArray jsExcludedAddresses;
|
||||
jsExcludedAddresses.append(wgConfig.value(amnezia::config_key::hostName));
|
||||
json.insert("excludedAddresses", jsExcludedAddresses);
|
||||
|
||||
|
||||
// QJsonArray splitTunnelApps;
|
||||
// for (const auto& uri : hop.m_vpnDisabledApps) {
|
||||
// splitTunnelApps.append(QJsonValue(uri));
|
||||
// }
|
||||
// json.insert("vpnDisabledApps", splitTunnelApps);
|
||||
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::deactivate() {
|
||||
logger.debug() << "Deactivating";
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
logger.debug() << "No disconnect, controller is not ready";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "deactivate");
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::checkStatus() {
|
||||
logger.debug() << "Check status";
|
||||
|
||||
if (m_daemonState == eReady || m_daemonState == eInitializing) {
|
||||
Q_ASSERT(m_socket);
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "status");
|
||||
write(json);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalSocketController::getBackendLogs(
|
||||
std::function<void(const QString&)>&& a_callback) {
|
||||
logger.debug() << "Backend logs";
|
||||
|
||||
if (m_logCallback) {
|
||||
m_logCallback("");
|
||||
m_logCallback = nullptr;
|
||||
}
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
std::function<void(const QString&)> callback = a_callback;
|
||||
callback("");
|
||||
return;
|
||||
}
|
||||
|
||||
m_logCallback = std::move(a_callback);
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "logs");
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::cleanupBackendLogs() {
|
||||
logger.debug() << "Cleanup logs";
|
||||
|
||||
if (m_logCallback) {
|
||||
m_logCallback("");
|
||||
m_logCallback = nullptr;
|
||||
}
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject json;
|
||||
json.insert("type", "cleanlogs");
|
||||
write(json);
|
||||
}
|
||||
|
||||
void LocalSocketController::readData() {
|
||||
logger.debug() << "Reading";
|
||||
|
||||
Q_ASSERT(m_socket);
|
||||
Q_ASSERT(m_daemonState == eInitializing || m_daemonState == eReady);
|
||||
QByteArray input = m_socket->readAll();
|
||||
m_buffer.append(input);
|
||||
|
||||
while (true) {
|
||||
int pos = m_buffer.indexOf("\n");
|
||||
if (pos == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
QByteArray line = m_buffer.left(pos);
|
||||
m_buffer.remove(0, pos + 1);
|
||||
|
||||
QByteArray command(line);
|
||||
command = command.trimmed();
|
||||
|
||||
if (command.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
parseCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
void LocalSocketController::parseCommand(const QByteArray& command) {
|
||||
QJsonDocument json = QJsonDocument::fromJson(command);
|
||||
if (!json.isObject()) {
|
||||
logger.error() << "Invalid JSON - object expected";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
QJsonValue typeValue = obj.value("type");
|
||||
if (!typeValue.isString()) {
|
||||
logger.error() << "Invalid JSON - no type";
|
||||
return;
|
||||
}
|
||||
QString type = typeValue.toString();
|
||||
|
||||
logger.debug() << "Parse command:" << type;
|
||||
|
||||
if (m_daemonState == eInitializing && type == "status") {
|
||||
m_daemonState = eReady;
|
||||
|
||||
QJsonValue connected = obj.value("connected");
|
||||
if (!connected.isBool()) {
|
||||
logger.error() << "Invalid JSON for status - connected expected";
|
||||
return;
|
||||
}
|
||||
|
||||
QDateTime datetime;
|
||||
if (connected.toBool()) {
|
||||
QJsonValue date = obj.value("date");
|
||||
if (!date.isString()) {
|
||||
logger.error() << "Invalid JSON for status - date expected";
|
||||
return;
|
||||
}
|
||||
|
||||
datetime = QDateTime::fromString(date.toString());
|
||||
if (!datetime.isValid()) {
|
||||
logger.error() << "Invalid JSON for status - date is invalid";
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emit initialized(true, connected.toBool(), datetime);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_daemonState != eReady) {
|
||||
logger.error() << "Unexpected command";
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "status") {
|
||||
QJsonValue serverIpv4Gateway = obj.value("serverIpv4Gateway");
|
||||
if (!serverIpv4Gateway.isString()) {
|
||||
logger.error() << "Unexpected serverIpv4Gateway value";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue deviceIpv4Address = obj.value("deviceIpv4Address");
|
||||
if (!deviceIpv4Address.isString()) {
|
||||
logger.error() << "Unexpected deviceIpv4Address value";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue txBytes = obj.value("txBytes");
|
||||
if (!txBytes.isDouble()) {
|
||||
logger.error() << "Unexpected txBytes value";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue rxBytes = obj.value("rxBytes");
|
||||
if (!rxBytes.isDouble()) {
|
||||
logger.error() << "Unexpected rxBytes value";
|
||||
return;
|
||||
}
|
||||
|
||||
emit statusUpdated(serverIpv4Gateway.toString(),
|
||||
deviceIpv4Address.toString(), txBytes.toDouble(),
|
||||
rxBytes.toDouble());
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "disconnected") {
|
||||
disconnectInternal();
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "connected") {
|
||||
QJsonValue pubkey = obj.value("pubkey");
|
||||
if (!pubkey.isString()) {
|
||||
logger.error() << "Unexpected pubkey value";
|
||||
return;
|
||||
}
|
||||
|
||||
logger.debug() << "Handshake completed with:"
|
||||
<< pubkey.toString();
|
||||
emit connected(pubkey.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "backendFailure") {
|
||||
qCritical() << "backendFailure";
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == "logs") {
|
||||
// We don't care if we are not waiting for logs.
|
||||
if (!m_logCallback) {
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue logs = obj.value("logs");
|
||||
m_logCallback(logs.isString() ? logs.toString().replace("|", "\n")
|
||||
: QString());
|
||||
m_logCallback = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
logger.warning() << "Invalid command received:" << command;
|
||||
}
|
||||
|
||||
void LocalSocketController::write(const QJsonObject& json) {
|
||||
Q_ASSERT(m_socket);
|
||||
m_socket->write(QJsonDocument(json).toJson(QJsonDocument::Compact));
|
||||
m_socket->write("\n");
|
||||
m_socket->flush();
|
||||
}
|
67
client/mozilla/localsocketcontroller.h
Normal file
67
client/mozilla/localsocketcontroller.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef LOCALSOCKETCONTROLLER_H
|
||||
#define LOCALSOCKETCONTROLLER_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QLocalSocket>
|
||||
#include <QTimer>
|
||||
#include <functional>
|
||||
|
||||
#include "controllerimpl.h"
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
class LocalSocketController final : public ControllerImpl {
|
||||
Q_DISABLE_COPY_MOVE(LocalSocketController)
|
||||
|
||||
public:
|
||||
LocalSocketController();
|
||||
~LocalSocketController();
|
||||
|
||||
void initialize(const Device* device, const Keys* keys) override;
|
||||
|
||||
void activate(const QJsonObject& rawConfig) override;
|
||||
|
||||
void deactivate() override;
|
||||
|
||||
void checkStatus() override;
|
||||
|
||||
void getBackendLogs(std::function<void(const QString&)>&& callback) override;
|
||||
|
||||
void cleanupBackendLogs() override;
|
||||
|
||||
bool multihopSupported() override { return true; }
|
||||
|
||||
private:
|
||||
void initializeInternal();
|
||||
void disconnectInternal();
|
||||
|
||||
void daemonConnected();
|
||||
void errorOccurred(QLocalSocket::LocalSocketError socketError);
|
||||
void readData();
|
||||
void parseCommand(const QByteArray& command);
|
||||
|
||||
void write(const QJsonObject& json);
|
||||
|
||||
private:
|
||||
enum {
|
||||
eUnknown,
|
||||
eInitializing,
|
||||
eReady,
|
||||
eDisconnected,
|
||||
} m_daemonState = eUnknown;
|
||||
|
||||
QLocalSocket* m_socket = nullptr;
|
||||
|
||||
QByteArray m_buffer;
|
||||
|
||||
std::function<void(const QString&)> m_logCallback = nullptr;
|
||||
|
||||
QTimer m_initializingTimer;
|
||||
uint32_t m_initializingRetry = 0;
|
||||
};
|
||||
|
||||
#endif // LOCALSOCKETCONTROLLER_H
|
209
client/mozilla/models/server.cpp
Normal file
209
client/mozilla/models/server.cpp
Normal file
|
@ -0,0 +1,209 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "server.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#include "leakdetector.h"
|
||||
|
||||
Server::Server() { MZ_COUNT_CTOR(Server); }
|
||||
|
||||
Server::Server(const QString& countryCode, const QString& cityName) {
|
||||
MZ_COUNT_CTOR(Server);
|
||||
m_countryCode = countryCode;
|
||||
m_cityName = cityName;
|
||||
}
|
||||
|
||||
Server::Server(const Server& other) {
|
||||
MZ_COUNT_CTOR(Server);
|
||||
*this = other;
|
||||
}
|
||||
|
||||
Server& Server::operator=(const Server& other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_hostname = other.m_hostname;
|
||||
m_ipv4AddrIn = other.m_ipv4AddrIn;
|
||||
m_ipv4Gateway = other.m_ipv4Gateway;
|
||||
m_ipv6AddrIn = other.m_ipv6AddrIn;
|
||||
m_ipv6Gateway = other.m_ipv6Gateway;
|
||||
m_portRanges = other.m_portRanges;
|
||||
m_publicKey = other.m_publicKey;
|
||||
m_weight = other.m_weight;
|
||||
m_socksName = other.m_socksName;
|
||||
m_multihopPort = other.m_multihopPort;
|
||||
m_countryCode = other.m_countryCode;
|
||||
m_cityName = other.m_cityName;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Server::~Server() { MZ_COUNT_DTOR(Server); }
|
||||
|
||||
bool Server::fromJson(const QJsonObject& obj) {
|
||||
// Reset.
|
||||
m_hostname = "";
|
||||
|
||||
QJsonValue hostname = obj.value("hostname");
|
||||
if (!hostname.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue ipv4AddrIn = obj.value("ipv4_addr_in");
|
||||
if (!ipv4AddrIn.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue ipv4Gateway = obj.value("ipv4_gateway");
|
||||
if (!ipv4Gateway.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue ipv6AddrIn = obj.value("ipv6_addr_in");
|
||||
// If this object comes from the IOS migration, the ipv6_addr_in is missing.
|
||||
|
||||
QJsonValue ipv6Gateway = obj.value("ipv6_gateway");
|
||||
if (!ipv6Gateway.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue publicKey = obj.value("public_key");
|
||||
if (!publicKey.isString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue weight = obj.value("weight");
|
||||
if (!weight.isDouble()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue portRanges = obj.value("port_ranges");
|
||||
if (!portRanges.isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// optional properties.
|
||||
QJsonValue socks5_name = obj.value("socks5_name");
|
||||
QJsonValue multihop_port = obj.value("multihop_port");
|
||||
|
||||
QList<QPair<uint32_t, uint32_t>> prList;
|
||||
QJsonArray portRangesArray = portRanges.toArray();
|
||||
for (const QJsonValue& portRangeValue : portRangesArray) {
|
||||
if (!portRangeValue.isArray()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonArray port = portRangeValue.toArray();
|
||||
if (port.count() != 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue a = port.at(0);
|
||||
if (!a.isDouble()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QJsonValue b = port.at(1);
|
||||
if (!b.isDouble()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prList.append(QPair<uint32_t, uint32_t>(a.toInt(), b.toInt()));
|
||||
}
|
||||
|
||||
m_hostname = hostname.toString();
|
||||
m_ipv4AddrIn = ipv4AddrIn.toString();
|
||||
m_ipv4Gateway = ipv4Gateway.toString();
|
||||
m_ipv6AddrIn = ipv6AddrIn.toString();
|
||||
m_ipv6Gateway = ipv6Gateway.toString();
|
||||
m_portRanges.swap(prList);
|
||||
m_publicKey = publicKey.toString();
|
||||
m_weight = weight.toInt();
|
||||
m_socksName = socks5_name.toString();
|
||||
m_multihopPort = multihop_port.toInt();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Server::fromMultihop(const Server& exit, const Server& entry) {
|
||||
m_hostname = exit.m_hostname;
|
||||
m_ipv4Gateway = exit.m_ipv4Gateway;
|
||||
m_ipv6Gateway = exit.m_ipv6Gateway;
|
||||
m_publicKey = exit.m_publicKey;
|
||||
m_socksName = exit.m_socksName;
|
||||
m_multihopPort = exit.m_multihopPort;
|
||||
|
||||
m_ipv4AddrIn = entry.m_ipv4AddrIn;
|
||||
m_ipv6AddrIn = entry.m_ipv6AddrIn;
|
||||
return forcePort(exit.m_multihopPort);
|
||||
}
|
||||
|
||||
bool Server::forcePort(uint32_t port) {
|
||||
m_portRanges.clear();
|
||||
m_portRanges.append(QPair<uint32_t, uint32_t>(port, port));
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
const Server& Server::weightChooser(const QList<Server>& servers) {
|
||||
static const Server emptyServer;
|
||||
Q_ASSERT(!emptyServer.initialized());
|
||||
if (servers.isEmpty()) {
|
||||
return emptyServer;
|
||||
}
|
||||
|
||||
uint32_t weightSum = 0;
|
||||
|
||||
for (const Server& server : servers) {
|
||||
weightSum += server.weight();
|
||||
}
|
||||
|
||||
quint32 r = QRandomGenerator::global()->generate() % (weightSum + 1);
|
||||
|
||||
for (const Server& server : servers) {
|
||||
if (server.weight() >= r) {
|
||||
return server;
|
||||
}
|
||||
|
||||
r -= server.weight();
|
||||
}
|
||||
|
||||
// This should not happen.
|
||||
Q_ASSERT(false);
|
||||
return emptyServer;
|
||||
}
|
||||
|
||||
uint32_t Server::choosePort() const {
|
||||
if (m_portRanges.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Count the total number of potential ports.
|
||||
quint32 length = 0;
|
||||
for (const QPair<uint32_t, uint32_t>& range : m_portRanges) {
|
||||
Q_ASSERT(range.first <= range.second);
|
||||
length += range.second - range.first + 1;
|
||||
}
|
||||
Q_ASSERT(length < 65536);
|
||||
Q_ASSERT(length > 0);
|
||||
|
||||
// Pick a port at random.
|
||||
quint32 r = QRandomGenerator::global()->generate() % length;
|
||||
quint32 port = 0;
|
||||
|
||||
for (const QPair<uint32_t, uint32_t>& range : m_portRanges) {
|
||||
if (r <= (range.second - range.first)) {
|
||||
port = r + range.first;
|
||||
break;
|
||||
}
|
||||
r -= (range.second - range.first + 1);
|
||||
}
|
||||
Q_ASSERT(port != 0);
|
||||
return port;
|
||||
}
|
79
client/mozilla/models/server.h
Normal file
79
client/mozilla/models/server.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SERVER_H
|
||||
#define SERVER_H
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QString>
|
||||
|
||||
class QJsonObject;
|
||||
|
||||
class Server final {
|
||||
public:
|
||||
Server();
|
||||
Server(const QString& countryCode, const QString& cityName);
|
||||
Server(const Server& other);
|
||||
Server& operator=(const Server& other);
|
||||
~Server();
|
||||
|
||||
[[nodiscard]] bool fromJson(const QJsonObject& obj);
|
||||
bool fromMultihop(const Server& exit, const Server& entry);
|
||||
|
||||
static const Server& weightChooser(const QList<Server>& servers);
|
||||
|
||||
bool initialized() const { return !m_hostname.isEmpty(); }
|
||||
|
||||
const QString& hostname() const { return m_hostname; }
|
||||
|
||||
const QString& ipv4AddrIn() const { return m_ipv4AddrIn; }
|
||||
|
||||
const QString& ipv4Gateway() const { return m_ipv4Gateway; }
|
||||
|
||||
const QString& ipv6AddrIn() const { return m_ipv6AddrIn; }
|
||||
|
||||
const QString& ipv6Gateway() const { return m_ipv6Gateway; }
|
||||
|
||||
const QString& publicKey() const { return m_publicKey; }
|
||||
|
||||
const QString& socksName() const { return m_socksName; }
|
||||
|
||||
uint32_t weight() const { return m_weight; }
|
||||
|
||||
uint32_t choosePort() const;
|
||||
|
||||
uint32_t multihopPort() const { return m_multihopPort; }
|
||||
|
||||
const QString& countryCode() const { return m_countryCode; }
|
||||
|
||||
const QString& cityName() const { return m_cityName; }
|
||||
|
||||
bool forcePort(uint32_t port);
|
||||
|
||||
bool operator==(const Server& other) const {
|
||||
return m_publicKey == other.m_publicKey;
|
||||
}
|
||||
// Allow checking against QString, so we can easily search a QList<Server> for
|
||||
// a public key.
|
||||
bool operator==(const QString& otherPublicKey) const {
|
||||
return m_publicKey == otherPublicKey;
|
||||
}
|
||||
|
||||
private:
|
||||
QString m_hostname;
|
||||
QString m_ipv4AddrIn;
|
||||
QString m_ipv4Gateway;
|
||||
QString m_ipv6AddrIn;
|
||||
QString m_ipv6Gateway;
|
||||
QList<QPair<uint32_t, uint32_t>> m_portRanges;
|
||||
QString m_publicKey;
|
||||
QString m_socksName;
|
||||
uint32_t m_weight = 0;
|
||||
uint32_t m_multihopPort = 0;
|
||||
QString m_countryCode;
|
||||
QString m_cityName;
|
||||
};
|
||||
|
||||
#endif // SERVER_H
|
106
client/mozilla/networkwatcher.cpp
Normal file
106
client/mozilla/networkwatcher.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "networkwatcher.h"
|
||||
|
||||
#include <QMetaEnum>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "networkwatcherimpl.h"
|
||||
#include "platforms/dummy/dummynetworkwatcher.h"
|
||||
|
||||
#ifdef MZ_WINDOWS
|
||||
//# include "platforms/windows/windowsnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_LINUX
|
||||
//# include "platforms/linux/linuxnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_MACOS
|
||||
# include "platforms/macos/macosnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_WASM
|
||||
# include "platforms/wasm/wasmnetworkwatcher.h"
|
||||
#endif
|
||||
#ifdef MZ_ANDROID
|
||||
# include "platforms/android/androidnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
#ifdef MZ_IOS
|
||||
# include "platforms/ios/iosnetworkwatcher.h"
|
||||
#endif
|
||||
|
||||
// How often we notify the same unsecured network
|
||||
#ifndef UNIT_TEST
|
||||
constexpr uint32_t NETWORK_WATCHER_TIMER_MSEC = 20000;
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
Logger logger("NetworkWatcher");
|
||||
}
|
||||
|
||||
NetworkWatcher::NetworkWatcher() { MZ_COUNT_CTOR(NetworkWatcher); }
|
||||
|
||||
NetworkWatcher::~NetworkWatcher() { MZ_COUNT_DTOR(NetworkWatcher); }
|
||||
|
||||
void NetworkWatcher::initialize() {
|
||||
logger.debug() << "Initialize";
|
||||
|
||||
#if defined(MZ_WINDOWS)
|
||||
//m_impl = new WindowsNetworkWatcher(this);
|
||||
#elif defined(MZ_LINUX)
|
||||
//m_impl = new LinuxNetworkWatcher(this);
|
||||
#elif defined(MZ_MACOS)
|
||||
m_impl = new MacOSNetworkWatcher(this);
|
||||
#elif defined(MZ_WASM)
|
||||
m_impl = new WasmNetworkWatcher(this);
|
||||
#elif defined(MZ_ANDROID)
|
||||
m_impl = new AndroidNetworkWatcher(this);
|
||||
#elif defined(MZ_IOS)
|
||||
m_impl = new IOSNetworkWatcher(this);
|
||||
#else
|
||||
m_impl = new DummyNetworkWatcher(this);
|
||||
#endif
|
||||
|
||||
connect(m_impl, &NetworkWatcherImpl::unsecuredNetwork, this,
|
||||
&NetworkWatcher::unsecuredNetwork);
|
||||
connect(m_impl, &NetworkWatcherImpl::networkChanged, this,
|
||||
&NetworkWatcher::networkChange);
|
||||
|
||||
m_impl->initialize();
|
||||
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
||||
|
||||
void NetworkWatcher::settingsChanged() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
|
||||
if (m_active) {
|
||||
logger.debug()
|
||||
<< "Starting Network Watcher; Reporting of Unsecured Networks: "
|
||||
<< m_reportUnsecuredNetwork;
|
||||
m_impl->start();
|
||||
} else {
|
||||
logger.debug() << "Stopping Network Watcher";
|
||||
m_impl->stop();
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkWatcher::unsecuredNetwork(const QString& networkName,
|
||||
const QString& networkId) {
|
||||
logger.debug() << "Unsecured network:" << logger.sensitive(networkName)
|
||||
<< "id:" << logger.sensitive(networkId);
|
||||
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
||||
|
||||
QString NetworkWatcher::getCurrentTransport() {
|
||||
auto type = m_impl->getTransportType();
|
||||
QMetaEnum metaEnum = QMetaEnum::fromType<NetworkWatcherImpl::TransportType>();
|
||||
return QString(metaEnum.valueToKey(type))
|
||||
.remove("TransportType_", Qt::CaseSensitive);
|
||||
}
|
49
client/mozilla/networkwatcher.h
Normal file
49
client/mozilla/networkwatcher.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef NETWORKWATCHER_H
|
||||
#define NETWORKWATCHER_H
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
|
||||
class NetworkWatcherImpl;
|
||||
|
||||
// This class watches for network changes to detect unsecured wifi.
|
||||
class NetworkWatcher final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcher)
|
||||
|
||||
public:
|
||||
NetworkWatcher();
|
||||
~NetworkWatcher();
|
||||
|
||||
void initialize();
|
||||
|
||||
// public for the inspector.
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
|
||||
QString getCurrentTransport();
|
||||
|
||||
signals:
|
||||
void networkChange();
|
||||
|
||||
private:
|
||||
void settingsChanged();
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
bool m_reportUnsecuredNetwork = false;
|
||||
|
||||
// Platform-specific implementation.
|
||||
NetworkWatcherImpl* m_impl = nullptr;
|
||||
|
||||
QMap<QString, QElapsedTimer> m_networks;
|
||||
|
||||
// This is used to connect NotificationHandler lazily.
|
||||
bool m_firstNotification = true;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHER_H
|
54
client/mozilla/networkwatcherimpl.h
Normal file
54
client/mozilla/networkwatcherimpl.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef NETWORKWATCHERIMPL_H
|
||||
#define NETWORKWATCHERIMPL_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class NetworkWatcherImpl : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(NetworkWatcherImpl)
|
||||
|
||||
public:
|
||||
NetworkWatcherImpl(QObject* parent) : QObject(parent) {}
|
||||
|
||||
virtual ~NetworkWatcherImpl() = default;
|
||||
|
||||
virtual void initialize() = 0;
|
||||
|
||||
virtual void start() { m_active = true; }
|
||||
virtual void stop() { m_active = false; }
|
||||
|
||||
bool isActive() const { return m_active; }
|
||||
|
||||
enum TransportType {
|
||||
TransportType_Unknown = 0,
|
||||
TransportType_Ethernet = 1,
|
||||
TransportType_WiFi = 2,
|
||||
TransportType_Cellular = 3, // In Case the API does not retun the gsm type
|
||||
TransportType_Other = 4, // I.e USB thethering
|
||||
TransportType_None = 5 // I.e Airplane Mode or no active network device
|
||||
};
|
||||
Q_ENUM(TransportType);
|
||||
|
||||
// Returns the current type of Network Connection
|
||||
virtual TransportType getTransportType() = 0;
|
||||
|
||||
signals:
|
||||
// Fires when the Device Connects to an unsecured Network
|
||||
void unsecuredNetwork(const QString& networkName, const QString& networkId);
|
||||
// Fires on when the connected WIFI Changes
|
||||
// TODO: Only windows-networkwatcher has this, the other plattforms should
|
||||
// too.
|
||||
void networkChanged(QString newBSSID);
|
||||
|
||||
// Fired when the Device changed the Type of Transport
|
||||
void transportChanged(NetworkWatcherImpl::TransportType transportType);
|
||||
|
||||
private:
|
||||
bool m_active = false;
|
||||
};
|
||||
|
||||
#endif // NETWORKWATCHERIMPL_H
|
189
client/mozilla/pinghelper.cpp
Normal file
189
client/mozilla/pinghelper.cpp
Normal file
|
@ -0,0 +1,189 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "pinghelper.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <cmath>
|
||||
|
||||
#include "dnspingsender.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "pingsender.h"
|
||||
#include "pingsenderfactory.h"
|
||||
|
||||
// Any X seconds, a new ping.
|
||||
constexpr uint32_t PING_TIMEOUT_SEC = 1;
|
||||
|
||||
// Maximum window size for ping statistics.
|
||||
constexpr int PING_STATS_WINDOW = 32;
|
||||
|
||||
namespace {
|
||||
Logger logger("PingHelper");
|
||||
}
|
||||
|
||||
PingHelper::PingHelper() {
|
||||
MZ_COUNT_CTOR(PingHelper);
|
||||
|
||||
m_sequence = 0;
|
||||
m_pingData.resize(PING_STATS_WINDOW);
|
||||
|
||||
connect(&m_pingTimer, &QTimer::timeout, this, &PingHelper::nextPing);
|
||||
}
|
||||
|
||||
PingHelper::~PingHelper() { MZ_COUNT_DTOR(PingHelper); }
|
||||
|
||||
void PingHelper::start(const QString& serverIpv4Gateway,
|
||||
const QString& deviceIpv4Address) {
|
||||
logger.debug() << "PingHelper activated for server:"
|
||||
<< logger.sensitive(serverIpv4Gateway);
|
||||
|
||||
m_gateway = QHostAddress(serverIpv4Gateway);
|
||||
m_source = QHostAddress(deviceIpv4Address.section('/', 0, 0));
|
||||
m_pingSender = PingSenderFactory::create(m_source, this);
|
||||
|
||||
// Some platforms require root access to send and receive ICMP pings. If
|
||||
// we happen to be on one of these unlucky devices, create a DnsPingSender
|
||||
// instead.
|
||||
if (!m_pingSender->isValid()) {
|
||||
delete m_pingSender;
|
||||
m_pingSender = new DnsPingSender(m_source, this);
|
||||
}
|
||||
|
||||
connect(m_pingSender, &PingSender::recvPing, this, &PingHelper::pingReceived,
|
||||
Qt::QueuedConnection);
|
||||
connect(m_pingSender, &PingSender::criticalPingError, this,
|
||||
[]() { logger.info() << "Encountered Unrecoverable ping error"; });
|
||||
|
||||
// Reset the ping statistics
|
||||
m_sequence = 0;
|
||||
for (int i = 0; i < PING_STATS_WINDOW; i++) {
|
||||
m_pingData[i].timestamp = -1;
|
||||
m_pingData[i].latency = -1;
|
||||
m_pingData[i].sequence = 0;
|
||||
}
|
||||
|
||||
m_pingTimer.start(PING_TIMEOUT_SEC * 1000);
|
||||
}
|
||||
|
||||
void PingHelper::stop() {
|
||||
logger.debug() << "PingHelper deactivated";
|
||||
|
||||
if (m_pingSender) {
|
||||
delete m_pingSender;
|
||||
m_pingSender = nullptr;
|
||||
}
|
||||
|
||||
m_pingTimer.stop();
|
||||
}
|
||||
|
||||
void PingHelper::nextPing() {
|
||||
#ifdef MZ_DEBUG
|
||||
logger.debug() << "Sending ping seq:" << m_sequence;
|
||||
#endif
|
||||
|
||||
// The ICMP sequence number is used to match replies with their originating
|
||||
// request, and serves as an index into the circular buffer. Overflows of
|
||||
// the sequence number acceptable.
|
||||
int index = m_sequence % PING_STATS_WINDOW;
|
||||
m_pingData[index].timestamp = QDateTime::currentMSecsSinceEpoch();
|
||||
m_pingData[index].latency = -1;
|
||||
m_pingData[index].sequence = m_sequence;
|
||||
m_pingSender->sendPing(m_gateway, m_sequence);
|
||||
|
||||
m_sequence++;
|
||||
}
|
||||
|
||||
void PingHelper::pingReceived(quint16 sequence) {
|
||||
int index = sequence % PING_STATS_WINDOW;
|
||||
if (m_pingData[index].sequence == sequence) {
|
||||
qint64 sendTime = m_pingData[index].timestamp;
|
||||
m_pingData[index].latency = QDateTime::currentMSecsSinceEpoch() - sendTime;
|
||||
emit pingSentAndReceived(m_pingData[index].latency);
|
||||
#ifdef MZ_DEBUG
|
||||
logger.debug() << "Ping answer received seq:" << sequence
|
||||
<< "avg:" << latency()
|
||||
<< "loss:" << QString("%1%").arg(loss() * 100.0)
|
||||
<< "stddev:" << stddev();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
uint PingHelper::latency() const {
|
||||
int recvCount = 0;
|
||||
qint64 totalMsec = 0;
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency < 0) {
|
||||
continue;
|
||||
}
|
||||
recvCount++;
|
||||
totalMsec += data.latency;
|
||||
}
|
||||
|
||||
if (recvCount <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
// Add half the denominator to produce nearest-integer rounding.
|
||||
totalMsec += recvCount / 2;
|
||||
return static_cast<uint>(totalMsec / recvCount);
|
||||
}
|
||||
|
||||
uint PingHelper::stddev() const {
|
||||
int recvCount = 0;
|
||||
qint64 totalVariance = 0;
|
||||
uint average = PingHelper::latency();
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency < 0) {
|
||||
continue;
|
||||
}
|
||||
recvCount++;
|
||||
totalVariance += (average - data.latency) * (average - data.latency);
|
||||
}
|
||||
|
||||
if (recvCount <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
return std::sqrt((double)totalVariance / recvCount);
|
||||
}
|
||||
|
||||
uint PingHelper::maximum() const {
|
||||
uint maxRtt = 0;
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency < 0) {
|
||||
continue;
|
||||
}
|
||||
if (data.latency > maxRtt &&
|
||||
data.latency < std::numeric_limits<uint>::max()) {
|
||||
maxRtt = static_cast<uint>(data.latency);
|
||||
}
|
||||
}
|
||||
return maxRtt;
|
||||
}
|
||||
|
||||
double PingHelper::loss() const {
|
||||
int sendCount = 0;
|
||||
int recvCount = 0;
|
||||
// Don't count pings that are possibly still in flight as losses.
|
||||
qint64 sendBefore =
|
||||
QDateTime::currentMSecsSinceEpoch() - (PING_TIMEOUT_SEC * 1000);
|
||||
|
||||
for (const PingSendData& data : m_pingData) {
|
||||
if (data.latency >= 0) {
|
||||
recvCount++;
|
||||
sendCount++;
|
||||
} else if ((data.timestamp > 0) && (data.timestamp < sendBefore)) {
|
||||
sendCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if (sendCount <= 0) {
|
||||
return 0.0;
|
||||
}
|
||||
return (double)(sendCount - recvCount) / PING_STATS_WINDOW;
|
||||
}
|
64
client/mozilla/pinghelper.h
Normal file
64
client/mozilla/pinghelper.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PINGHELPER_H
|
||||
#define PINGHELPER_H
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QTimer>
|
||||
#include <QVector>
|
||||
|
||||
class PingSender;
|
||||
|
||||
class PingHelper final : public QObject {
|
||||
private:
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(PingHelper)
|
||||
|
||||
public:
|
||||
PingHelper();
|
||||
~PingHelper();
|
||||
|
||||
void start(const QString& serverIpv4Gateway,
|
||||
const QString& deviceIpv4Address);
|
||||
|
||||
void stop();
|
||||
uint latency() const;
|
||||
uint stddev() const;
|
||||
uint maximum() const;
|
||||
double loss() const;
|
||||
|
||||
signals:
|
||||
void pingSentAndReceived(qint64 msec);
|
||||
|
||||
private:
|
||||
void nextPing();
|
||||
|
||||
void pingReceived(quint16 sequence);
|
||||
|
||||
private:
|
||||
QHostAddress m_gateway;
|
||||
QHostAddress m_source;
|
||||
quint16 m_sequence = 0;
|
||||
|
||||
class PingSendData {
|
||||
public:
|
||||
PingSendData() {
|
||||
timestamp = -1;
|
||||
latency = -1;
|
||||
sequence = 0;
|
||||
}
|
||||
qint64 timestamp;
|
||||
qint64 latency;
|
||||
quint16 sequence;
|
||||
};
|
||||
QVector<PingSendData> m_pingData;
|
||||
|
||||
QTimer m_pingTimer;
|
||||
PingSender* m_pingSender = nullptr;
|
||||
};
|
||||
|
||||
#endif // PINGHELPER_H
|
48
client/mozilla/pingsender.cpp
Normal file
48
client/mozilla/pingsender.cpp
Normal file
|
@ -0,0 +1,48 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "pingsender.h"
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("PingSender");
|
||||
}
|
||||
|
||||
quint16 PingSender::inetChecksum(const void* data, size_t len) {
|
||||
int nleft, sum;
|
||||
quint16* w;
|
||||
union {
|
||||
quint16 us;
|
||||
quint8 uc[2];
|
||||
} last;
|
||||
quint16 answer;
|
||||
|
||||
nleft = static_cast<int>(len);
|
||||
sum = 0;
|
||||
w = (quint16*)data;
|
||||
|
||||
/*
|
||||
* Our algorithm is simple, using a 32 bit accumulator (sum), we add
|
||||
* sequential 16 bit words to it, and at the end, fold back all the
|
||||
* carry bits from the top 16 bits into the lower 16 bits.
|
||||
*/
|
||||
while (nleft > 1) {
|
||||
sum += *w++;
|
||||
nleft -= 2;
|
||||
}
|
||||
|
||||
/* mop up an odd byte, if necessary */
|
||||
if (nleft == 1) {
|
||||
last.uc[0] = *(quint8*)w;
|
||||
last.uc[1] = 0;
|
||||
sum += last.us;
|
||||
}
|
||||
|
||||
/* add back carry outs from top 16 bits to low 16 bits */
|
||||
sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
|
||||
sum += (sum >> 16); /* add carry */
|
||||
answer = ~sum; /* truncate to 16 bits */
|
||||
return (answer);
|
||||
}
|
31
client/mozilla/pingsender.h
Normal file
31
client/mozilla/pingsender.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PINGSENDER_H
|
||||
#define PINGSENDER_H
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QHostAddress>
|
||||
#include <QObject>
|
||||
|
||||
class PingSender : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(PingSender)
|
||||
|
||||
public:
|
||||
PingSender(QObject* parent = nullptr) : QObject(parent) {}
|
||||
virtual ~PingSender() = default;
|
||||
|
||||
virtual bool isValid() { return true; };
|
||||
|
||||
virtual void sendPing(const QHostAddress& destination, quint16 sequence) = 0;
|
||||
|
||||
static quint16 inetChecksum(const void* data, size_t length);
|
||||
|
||||
signals:
|
||||
void recvPing(quint16 sequence);
|
||||
void criticalPingError();
|
||||
};
|
||||
|
||||
#endif // PINGSENDER_H
|
32
client/mozilla/pingsenderfactory.cpp
Normal file
32
client/mozilla/pingsenderfactory.cpp
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "pingsenderfactory.h"
|
||||
|
||||
#if defined(MZ_LINUX) || defined(MZ_ANDROID)
|
||||
//# include "platforms/linux/linuxpingsender.h"
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
# include "platforms/macos/macospingsender.h"
|
||||
#elif defined(MZ_WINDOWS)
|
||||
// #include "platforms/windows/windowspingsender.h"
|
||||
#elif defined(MZ_DUMMY) || defined(UNIT_TEST)
|
||||
# include "platforms/dummy/dummypingsender.h"
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
||||
PingSender* PingSenderFactory::create(const QHostAddress& source,
|
||||
QObject* parent) {
|
||||
#if defined(MZ_LINUX) || defined(MZ_ANDROID)
|
||||
return nullptr;
|
||||
//return new LinuxPingSender(source, parent);
|
||||
#elif defined(MZ_MACOS) || defined(MZ_IOS)
|
||||
return new MacOSPingSender(source, parent);
|
||||
#elif defined(MZ_WINDOWS)
|
||||
return nullptr;
|
||||
//return new WindowsPingSender(source, parent);
|
||||
#else
|
||||
return new DummyPingSender(source, parent);
|
||||
#endif
|
||||
}
|
18
client/mozilla/pingsenderfactory.h
Normal file
18
client/mozilla/pingsenderfactory.h
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef PINGSENDERFACTORY_H
|
||||
#define PINGSENDERFACTORY_H
|
||||
|
||||
class PingSender;
|
||||
class QHostAddress;
|
||||
class QObject;
|
||||
|
||||
class PingSenderFactory final {
|
||||
public:
|
||||
PingSenderFactory() = delete;
|
||||
static PingSender* create(const QHostAddress& source, QObject* parent);
|
||||
};
|
||||
|
||||
#endif // PINGSENDERFACTORY_H
|
286
client/mozilla/shared/ipaddress.cpp
Normal file
286
client/mozilla/shared/ipaddress.cpp
Normal file
|
@ -0,0 +1,286 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ipaddress.h"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
#include "leakdetector.h"
|
||||
|
||||
IPAddress::IPAddress() { MZ_COUNT_CTOR(IPAddress); }
|
||||
|
||||
IPAddress::IPAddress(const QString& ip) {
|
||||
MZ_COUNT_CTOR(IPAddress);
|
||||
if (ip.contains("/")) {
|
||||
QPair<QHostAddress, int> p = QHostAddress::parseSubnet(ip);
|
||||
m_address = p.first;
|
||||
m_prefixLength = p.second;
|
||||
} else {
|
||||
m_address = QHostAddress(ip);
|
||||
m_prefixLength = 999999;
|
||||
}
|
||||
|
||||
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
if (m_prefixLength >= 32) {
|
||||
m_prefixLength = 32;
|
||||
}
|
||||
} else if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
if (m_prefixLength >= 128) {
|
||||
m_prefixLength = 128;
|
||||
}
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const IPAddress& other) {
|
||||
MZ_COUNT_CTOR(IPAddress);
|
||||
*this = other;
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(const IPAddress& other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_address = other.m_address;
|
||||
m_prefixLength = other.m_prefixLength;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const QHostAddress& address) : m_address(address) {
|
||||
MZ_COUNT_CTOR(IPAddress);
|
||||
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
m_prefixLength = 32;
|
||||
} else {
|
||||
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
m_prefixLength = 128;
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const QHostAddress& address, int prefixLength)
|
||||
: m_address(address), m_prefixLength(prefixLength) {
|
||||
MZ_COUNT_CTOR(IPAddress);
|
||||
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
Q_ASSERT(prefixLength >= 0 && prefixLength <= 32);
|
||||
} else {
|
||||
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
Q_ASSERT(prefixLength >= 0 && prefixLength <= 128);
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress::~IPAddress() { MZ_COUNT_DTOR(IPAddress); }
|
||||
|
||||
QAbstractSocket::NetworkLayerProtocol IPAddress::type() const {
|
||||
return m_address.protocol();
|
||||
}
|
||||
|
||||
QHostAddress IPAddress::netmask() const {
|
||||
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
Q_IPV6ADDR rawNetmask = {0};
|
||||
Q_ASSERT(m_prefixLength <= 128);
|
||||
memset(&rawNetmask, 0xff, m_prefixLength / 8);
|
||||
if (m_prefixLength % 8) {
|
||||
rawNetmask[m_prefixLength / 8] = 0xFF ^ (0xFF >> (m_prefixLength % 8));
|
||||
}
|
||||
return QHostAddress(rawNetmask);
|
||||
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
quint32 rawNetmask = 0xffffffff;
|
||||
Q_ASSERT(m_prefixLength <= 32);
|
||||
if (m_prefixLength < 32) {
|
||||
rawNetmask ^= (0xffffffff >> m_prefixLength);
|
||||
}
|
||||
return QHostAddress(rawNetmask);
|
||||
} else {
|
||||
return QHostAddress();
|
||||
}
|
||||
}
|
||||
|
||||
QHostAddress IPAddress::hostmask() const {
|
||||
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
Q_IPV6ADDR rawHostmask = {0};
|
||||
int offset = (m_prefixLength + 7) / 8;
|
||||
Q_ASSERT(m_prefixLength <= 128);
|
||||
memset(&rawHostmask[offset], 0xff, sizeof(rawHostmask) - offset);
|
||||
if (m_prefixLength % 8) {
|
||||
rawHostmask[m_prefixLength / 8] = 0xFF >> (m_prefixLength % 8);
|
||||
}
|
||||
return QHostAddress(rawHostmask);
|
||||
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
if (m_prefixLength < 32) {
|
||||
return QHostAddress(0xffffffff >> m_prefixLength);
|
||||
} else {
|
||||
quint32 zero = 0;
|
||||
return QHostAddress(zero);
|
||||
}
|
||||
} else {
|
||||
return QHostAddress();
|
||||
}
|
||||
}
|
||||
|
||||
QHostAddress IPAddress::broadcastAddress() const {
|
||||
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
Q_IPV6ADDR rawAddress = m_address.toIPv6Address();
|
||||
int offset = (m_prefixLength + 7) / 8;
|
||||
memset(&rawAddress[offset], 0xff, sizeof(rawAddress) - offset);
|
||||
if (m_prefixLength % 8) {
|
||||
rawAddress[m_prefixLength / 8] |= 0xFF >> (m_prefixLength % 8);
|
||||
}
|
||||
return QHostAddress(rawAddress);
|
||||
} else if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
quint32 rawAddress = m_address.toIPv4Address();
|
||||
if (m_prefixLength < 32) {
|
||||
rawAddress |= (0xffffffff >> m_prefixLength);
|
||||
}
|
||||
return QHostAddress(rawAddress);
|
||||
} else {
|
||||
return QHostAddress();
|
||||
}
|
||||
}
|
||||
|
||||
bool IPAddress::overlaps(const IPAddress& other) const {
|
||||
if (m_prefixLength < other.m_prefixLength) {
|
||||
return contains(other.m_address);
|
||||
} else {
|
||||
return other.contains(m_address);
|
||||
}
|
||||
}
|
||||
|
||||
bool IPAddress::contains(const QHostAddress& address) const {
|
||||
if (address.protocol() != m_address.protocol()) {
|
||||
return false;
|
||||
}
|
||||
if (m_prefixLength == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
Q_IPV6ADDR a = m_address.toIPv6Address();
|
||||
Q_IPV6ADDR b = address.toIPv6Address();
|
||||
int bytes = m_prefixLength / 8;
|
||||
if (bytes > 0) {
|
||||
if (memcmp(&a, &b, bytes) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_prefixLength % 8) {
|
||||
quint8 diff = (a[bytes] ^ b[bytes]) >> (8 - m_prefixLength % 8);
|
||||
return (diff == 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
quint32 diff = m_address.toIPv4Address() ^ address.toIPv4Address();
|
||||
if (m_prefixLength < 32) {
|
||||
diff >>= (32 - m_prefixLength);
|
||||
}
|
||||
return (diff == 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const IPAddress& other) const {
|
||||
return m_address == other.m_address && m_prefixLength == other.m_prefixLength;
|
||||
}
|
||||
|
||||
bool IPAddress::subnetOf(const IPAddress& other) const {
|
||||
if (other.m_address.protocol() != m_address.protocol()) {
|
||||
return false;
|
||||
}
|
||||
if (m_prefixLength < other.m_prefixLength) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return other.contains(m_address);
|
||||
}
|
||||
|
||||
QList<IPAddress> IPAddress::subnets() const {
|
||||
QList<IPAddress> list;
|
||||
|
||||
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
if (m_prefixLength >= 32) {
|
||||
list.append(*this);
|
||||
return list;
|
||||
}
|
||||
|
||||
quint32 rawAddress = m_address.toIPv4Address();
|
||||
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||
|
||||
rawAddress |= (0x80000000 >> m_prefixLength);
|
||||
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
|
||||
if (m_prefixLength >= 128) {
|
||||
list.append(*this);
|
||||
return list;
|
||||
}
|
||||
|
||||
Q_IPV6ADDR rawAddress = m_address.toIPv6Address();
|
||||
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||
|
||||
rawAddress[m_prefixLength / 8] |= (0x80 >> (m_prefixLength % 8));
|
||||
list.append(IPAddress(QHostAddress(rawAddress), m_prefixLength + 1));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// static
|
||||
QList<IPAddress> IPAddress::excludeAddresses(
|
||||
const QList<IPAddress>& sourceList, const QList<IPAddress>& excludeList) {
|
||||
QList<IPAddress> results = sourceList;
|
||||
|
||||
for (const IPAddress& exclude : excludeList) {
|
||||
QList<IPAddress> newResults;
|
||||
|
||||
for (const IPAddress& ip : results) {
|
||||
if (!ip.overlaps(exclude)) {
|
||||
newResults.append(ip);
|
||||
} else if (exclude.subnetOf(ip) && exclude != ip) {
|
||||
QList<IPAddress> range = ip.excludeAddresses(exclude);
|
||||
newResults.append(range);
|
||||
}
|
||||
}
|
||||
|
||||
results = newResults;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
QList<IPAddress> IPAddress::excludeAddresses(const IPAddress& ip) const {
|
||||
QList<IPAddress> sn = subnets();
|
||||
Q_ASSERT(sn.length() >= 2);
|
||||
|
||||
QList<IPAddress> result;
|
||||
while (sn[0] != ip && sn[1] != ip) {
|
||||
if (ip.subnetOf(sn[0])) {
|
||||
result.append(sn[1]);
|
||||
sn = sn[0].subnets();
|
||||
} else if (ip.subnetOf(sn[1])) {
|
||||
result.append(sn[0]);
|
||||
sn = sn[1].subnets();
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (sn[0] == ip) {
|
||||
result.append(sn[1]);
|
||||
} else if (sn[1] == ip) {
|
||||
result.append(sn[0]);
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -9,11 +9,13 @@
|
|||
|
||||
class IPAddress final {
|
||||
public:
|
||||
static IPAddress create(const QString& ip);
|
||||
static QList<IPAddress> excludeAddresses(const QList<IPAddress>& sourceList,
|
||||
const QList<IPAddress>& excludeList);
|
||||
|
||||
IPAddress();
|
||||
IPAddress(const QString& ip);
|
||||
IPAddress(const QHostAddress& address);
|
||||
IPAddress(const QHostAddress& address, int prefixLength);
|
||||
IPAddress(const IPAddress& other);
|
||||
IPAddress& operator=(const IPAddress& other);
|
||||
~IPAddress();
|
||||
|
@ -24,9 +26,9 @@ class IPAddress final {
|
|||
|
||||
const QHostAddress& address() const { return m_address; }
|
||||
int prefixLength() const { return m_prefixLength; }
|
||||
const QHostAddress& netmask() const { return m_netmask; }
|
||||
const QHostAddress& hostmask() const { return m_hostmask; }
|
||||
const QHostAddress& broadcastAddress() const { return m_broadcastAddress; }
|
||||
QHostAddress netmask() const;
|
||||
QHostAddress hostmask() const;
|
||||
QHostAddress broadcastAddress() const;
|
||||
|
||||
bool overlaps(const IPAddress& other) const;
|
||||
|
||||
|
@ -43,17 +45,9 @@ class IPAddress final {
|
|||
|
||||
QAbstractSocket::NetworkLayerProtocol type() const;
|
||||
|
||||
private:
|
||||
IPAddress(const QHostAddress& address);
|
||||
IPAddress(const QHostAddress& address, int prefixLength);
|
||||
|
||||
private:
|
||||
QHostAddress m_address;
|
||||
int m_prefixLength;
|
||||
|
||||
QHostAddress m_netmask;
|
||||
QHostAddress m_hostmask;
|
||||
QHostAddress m_broadcastAddress;
|
||||
};
|
||||
|
||||
#endif // IPADDRESS_H
|
|
@ -9,23 +9,23 @@
|
|||
#include <QObject>
|
||||
#include <QTextStream>
|
||||
|
||||
#ifdef MVPN_DEBUG
|
||||
#ifdef MZ_DEBUG
|
||||
static QMutex s_leakDetector;
|
||||
|
||||
QHash<QString, QHash<void*, uint32_t>> s_leaks;
|
||||
#endif
|
||||
|
||||
LeakDetector::LeakDetector() {
|
||||
#ifndef MVPN_DEBUG
|
||||
#ifndef MZ_DEBUG
|
||||
qFatal("LeakDetector _must_ be created in debug builds only!");
|
||||
#endif
|
||||
}
|
||||
|
||||
LeakDetector::~LeakDetector() {
|
||||
#ifdef MVPN_DEBUG
|
||||
#ifdef MZ_DEBUG
|
||||
QTextStream out(stderr);
|
||||
|
||||
out << "== Mozilla VPN - Leak report ===================" << Qt::endl;
|
||||
out << "== MZ - Leak report ===================" << Qt::endl;
|
||||
|
||||
bool hasLeaks = false;
|
||||
for (auto i = s_leaks.begin(); i != s_leaks.end(); ++i) {
|
||||
|
@ -49,7 +49,7 @@ LeakDetector::~LeakDetector() {
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef MVPN_DEBUG
|
||||
#ifdef MZ_DEBUG
|
||||
void LeakDetector::logCtor(void* ptr, const char* typeName, uint32_t size) {
|
||||
QMutexLocker lock(&s_leakDetector);
|
||||
|
|
@ -7,15 +7,15 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#ifdef MVPN_DEBUG
|
||||
# define MVPN_COUNT_CTOR(_type) \
|
||||
#ifdef MZ_DEBUG
|
||||
# define MZ_COUNT_CTOR(_type) \
|
||||
do { \
|
||||
static_assert(std::is_class<_type>(), \
|
||||
"Token '" #_type "' is not a class type."); \
|
||||
LeakDetector::logCtor((void*)this, #_type, sizeof(*this)); \
|
||||
} while (0)
|
||||
|
||||
# define MVPN_COUNT_DTOR(_type) \
|
||||
# define MZ_COUNT_DTOR(_type) \
|
||||
do { \
|
||||
static_assert(std::is_class<_type>(), \
|
||||
"Token '" #_type "' is not a class type."); \
|
||||
|
@ -23,8 +23,8 @@
|
|||
} while (0)
|
||||
|
||||
#else
|
||||
# define MVPN_COUNT_CTOR(_type)
|
||||
# define MVPN_COUNT_DTOR(_type)
|
||||
# define MZ_COUNT_CTOR(_type)
|
||||
# define MZ_COUNT_DTOR(_type)
|
||||
#endif
|
||||
|
||||
class LeakDetector {
|
||||
|
@ -32,7 +32,7 @@ class LeakDetector {
|
|||
LeakDetector();
|
||||
~LeakDetector();
|
||||
|
||||
#ifdef MVPN_DEBUG
|
||||
#ifdef MZ_DEBUG
|
||||
static void logCtor(void* ptr, const char* typeName, uint32_t size);
|
||||
static void logDtor(void* ptr, const char* typeName, uint32_t size);
|
||||
#endif
|
|
@ -2,14 +2,15 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IOSDATAMIGRATION_H
|
||||
#define IOSDATAMIGRATION_H
|
||||
#ifndef LOGLEVEL_H
|
||||
#define LOGLEVEL_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class IOSDataMigration final {
|
||||
public:
|
||||
static void migrate();
|
||||
enum LogLevel {
|
||||
Trace = 0,
|
||||
Debug,
|
||||
Info,
|
||||
Warning,
|
||||
Error,
|
||||
};
|
||||
|
||||
#endif // IOSDATAMIGRATION_H
|
||||
#endif // LOGLEVEL_H
|
76
client/mozilla/shared/signalhandler.cpp
Normal file
76
client/mozilla/shared/signalhandler.cpp
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "signalhandler.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger("SignalHandler");
|
||||
|
||||
int s_signalpipe = -1;
|
||||
|
||||
} // namespace
|
||||
|
||||
SignalHandler::SignalHandler() {
|
||||
Q_ASSERT(s_signalpipe < 0);
|
||||
|
||||
int quitSignals[] = {SIGQUIT, SIGINT, SIGTERM, SIGHUP};
|
||||
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
for (auto sig : quitSignals) {
|
||||
sigaddset(&mask, sig);
|
||||
}
|
||||
|
||||
if (pipe(m_pipefds) != 0) {
|
||||
logger.error() << "Unable to create signal wakeup pipe";
|
||||
return;
|
||||
}
|
||||
fcntl(m_pipefds[0], F_SETFL, fcntl(m_pipefds[0], F_GETFL) | O_NONBLOCK);
|
||||
s_signalpipe = m_pipefds[1];
|
||||
m_notifier = new QSocketNotifier(m_pipefds[0], QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this,
|
||||
&SignalHandler::pipeReadReady);
|
||||
|
||||
struct sigaction sa;
|
||||
sa.sa_handler = SignalHandler::saHandler;
|
||||
sa.sa_mask = mask;
|
||||
sa.sa_flags = 0;
|
||||
|
||||
for (auto sig : quitSignals) {
|
||||
sigaction(sig, &sa, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
SignalHandler::~SignalHandler() {
|
||||
s_signalpipe = -1;
|
||||
if (m_pipefds[0] >= 0) {
|
||||
close(m_pipefds[0]);
|
||||
}
|
||||
if (m_pipefds[1] >= 1) {
|
||||
close(m_pipefds[1]);
|
||||
}
|
||||
}
|
||||
|
||||
void SignalHandler::pipeReadReady() {
|
||||
int signal;
|
||||
if (read(m_pipefds[0], &signal, sizeof(signal)) == sizeof(signal)) {
|
||||
logger.debug() << "Signal" << signal;
|
||||
emit quitRequested();
|
||||
}
|
||||
}
|
||||
|
||||
void SignalHandler::saHandler(int signal) {
|
||||
if (s_signalpipe >= 0) {
|
||||
if (write(s_signalpipe, &signal, sizeof(signal)) != sizeof(signal)) {
|
||||
logger.warning() << "Unable to write in the pipe";
|
||||
}
|
||||
}
|
||||
}
|
31
client/mozilla/shared/signalhandler.h
Normal file
31
client/mozilla/shared/signalhandler.h
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef SIGNALHANDLER_H
|
||||
#define SIGNALHANDLER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
class SignalHandler final : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
SignalHandler();
|
||||
~SignalHandler();
|
||||
|
||||
private slots:
|
||||
void pipeReadReady();
|
||||
|
||||
private:
|
||||
static void saHandler(int signal);
|
||||
|
||||
int m_pipefds[2] = {-1, -1};
|
||||
QSocketNotifier* m_notifier = nullptr;
|
||||
|
||||
signals:
|
||||
void quitRequested();
|
||||
};
|
||||
|
||||
#endif // SIGNALHANDLER_H
|
29
client/platforms/dummy/dummyapplistprovider.cpp
Normal file
29
client/platforms/dummy/dummyapplistprovider.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "dummyapplistprovider.h"
|
||||
|
||||
#include "leakdetector.h"
|
||||
|
||||
DummyAppListProvider::DummyAppListProvider(QObject* parent)
|
||||
: AppListProvider(parent) {
|
||||
MZ_COUNT_CTOR(DummyAppListProvider);
|
||||
}
|
||||
|
||||
DummyAppListProvider::~DummyAppListProvider() {
|
||||
MZ_COUNT_DTOR(DummyAppListProvider);
|
||||
}
|
||||
|
||||
void DummyAppListProvider::getApplicationList() {
|
||||
QMap<QString, QString> appList;
|
||||
appList["com.example.one"] = "a Example App 1";
|
||||
appList["com.example.two"] = "B Example App 2";
|
||||
appList["org.example.one"] = "c Example App 3";
|
||||
appList["org.example.two"] = "D Example App 4";
|
||||
appList["com.example.a"] = "e Example App 5";
|
||||
appList["com.example.b"] = "F Example App 6";
|
||||
appList["org.example.c"] = "g Example App 7";
|
||||
appList["org.example.d"] = "H Example App 8";
|
||||
emit newAppList(appList);
|
||||
}
|
20
client/platforms/dummy/dummyapplistprovider.h
Normal file
20
client/platforms/dummy/dummyapplistprovider.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DUMMYAPPLISTPROVIDER_H
|
||||
#define DUMMYAPPLISTPROVIDER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "applistprovider.h"
|
||||
|
||||
class DummyAppListProvider : public AppListProvider {
|
||||
Q_OBJECT
|
||||
public:
|
||||
DummyAppListProvider(QObject* parent);
|
||||
~DummyAppListProvider();
|
||||
void getApplicationList() override;
|
||||
};
|
||||
|
||||
#endif // DUMMYAPPLISTPROVIDER_H
|
18
client/platforms/dummy/dummynetworkwatcher.cpp
Normal file
18
client/platforms/dummy/dummynetworkwatcher.cpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "dummynetworkwatcher.h"
|
||||
|
||||
#include "leakdetector.h"
|
||||
|
||||
DummyNetworkWatcher::DummyNetworkWatcher(QObject* parent)
|
||||
: NetworkWatcherImpl(parent) {
|
||||
MZ_COUNT_CTOR(DummyNetworkWatcher);
|
||||
}
|
||||
|
||||
DummyNetworkWatcher::~DummyNetworkWatcher() {
|
||||
MZ_COUNT_DTOR(DummyNetworkWatcher);
|
||||
}
|
||||
|
||||
void DummyNetworkWatcher::initialize() {}
|
22
client/platforms/dummy/dummynetworkwatcher.h
Normal file
22
client/platforms/dummy/dummynetworkwatcher.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DUMMYNETWORKWATCHER_H
|
||||
#define DUMMYNETWORKWATCHER_H
|
||||
|
||||
#include "networkwatcherimpl.h"
|
||||
|
||||
class DummyNetworkWatcher final : public NetworkWatcherImpl {
|
||||
public:
|
||||
DummyNetworkWatcher(QObject* parent);
|
||||
~DummyNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
NetworkWatcherImpl::TransportType getTransportType() override {
|
||||
return TransportType_Other;
|
||||
};
|
||||
};
|
||||
|
||||
#endif // DUMMYNETWORKWATCHER_H
|
25
client/platforms/dummy/dummypingsender.cpp
Normal file
25
client/platforms/dummy/dummypingsender.cpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "dummypingsender.h"
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger("DummyPingSender");
|
||||
}
|
||||
|
||||
DummyPingSender::DummyPingSender(const QHostAddress& source, QObject* parent)
|
||||
: PingSender(parent) {
|
||||
MZ_COUNT_CTOR(DummyPingSender);
|
||||
Q_UNUSED(source);
|
||||
}
|
||||
|
||||
DummyPingSender::~DummyPingSender() { MZ_COUNT_DTOR(DummyPingSender); }
|
||||
|
||||
void DummyPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
logger.debug() << "Dummy ping to:" << dest.toString();
|
||||
emit recvPing(sequence);
|
||||
}
|
21
client/platforms/dummy/dummypingsender.h
Normal file
21
client/platforms/dummy/dummypingsender.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef DUMMYPINGSENDER_H
|
||||
#define DUMMYPINGSENDER_H
|
||||
|
||||
#include "pingsender.h"
|
||||
|
||||
class DummyPingSender final : public PingSender {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(DummyPingSender)
|
||||
|
||||
public:
|
||||
DummyPingSender(const QHostAddress& source, QObject* parent = nullptr);
|
||||
~DummyPingSender();
|
||||
|
||||
void sendPing(const QHostAddress& dest, quint16 sequence) override;
|
||||
};
|
||||
|
||||
#endif // DUMMYPINGSENDER_H
|
|
@ -1,127 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BIGINT_H
|
||||
#define BIGINT_H
|
||||
|
||||
#include <QVector>
|
||||
|
||||
// This BigInt implementation is meant to be used for IPv6 addresses. It
|
||||
// doesn't support dynamic resize: when the max size is reached, the value
|
||||
// overflows. If you need to change the size, use `resize()`.
|
||||
|
||||
class BigInt final {
|
||||
public:
|
||||
explicit BigInt(uint8_t bytes) {
|
||||
m_value.resize(bytes);
|
||||
memset(m_value.data(), 0, bytes);
|
||||
}
|
||||
|
||||
BigInt(const BigInt& other) { m_value = other.m_value; }
|
||||
|
||||
const uint8_t* value() const { return m_value.data(); }
|
||||
|
||||
uint8_t size() const { return m_value.size(); }
|
||||
|
||||
// Assign operator.
|
||||
|
||||
BigInt& operator=(const BigInt& other) {
|
||||
m_value = other.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Comparison operators.
|
||||
|
||||
bool operator==(const BigInt& other) const {
|
||||
return m_value == other.m_value;
|
||||
}
|
||||
|
||||
bool operator!=(const BigInt& other) const { return !(*this == other); }
|
||||
|
||||
bool operator<(const BigInt& other) const { return cmp(other) < 0; }
|
||||
|
||||
bool operator>(const BigInt& other) const { return cmp(other) > 0; }
|
||||
|
||||
bool operator<=(const BigInt& other) const { return cmp(other) <= 0; }
|
||||
|
||||
bool operator>=(const BigInt& other) const { return cmp(other) >= 0; }
|
||||
|
||||
// math operators (only some of them are implemented)
|
||||
|
||||
BigInt& operator++() {
|
||||
for (int i = size() - 1; i >= 0; --i) {
|
||||
if (m_value[i] < UINT8_MAX) {
|
||||
++m_value[i];
|
||||
return *this;
|
||||
}
|
||||
m_value[i] = 0;
|
||||
}
|
||||
|
||||
// overflow
|
||||
memset(m_value.data(), 0, size());
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigInt& operator+=(const BigInt& other) {
|
||||
Q_ASSERT(other.size() == size());
|
||||
|
||||
uint8_t carry = 0;
|
||||
for (int i = m_value.size() - 1; i >= 0; --i) {
|
||||
uint16_t total = carry + m_value[i] + other.m_value[i];
|
||||
m_value[i] = (uint8_t)(total & UINT8_MAX);
|
||||
carry = (uint8_t)((total & 0xFF00) >> 8);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Shift operators
|
||||
|
||||
BigInt operator>>(int shift) {
|
||||
BigInt x(size());
|
||||
x = *this;
|
||||
|
||||
for (int i = 0; i < shift; i++) {
|
||||
BigInt a(size());
|
||||
a = x;
|
||||
|
||||
a.m_value[size() - 1] = x.m_value[size() - 1] >> 1;
|
||||
for (int j = size() - 2; j >= 0; j--) {
|
||||
a.m_value[j] = x.m_value[j] >> 1;
|
||||
if ((x.m_value[j] & 1) != 0) {
|
||||
a.m_value[j + 1] |= 128; // Set most significant bit or a uint8_t
|
||||
}
|
||||
}
|
||||
|
||||
x = a;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
void setValueAt(uint8_t value, uint8_t pos) {
|
||||
Q_ASSERT(pos < size());
|
||||
m_value[pos] = value;
|
||||
}
|
||||
|
||||
uint8_t valueAt(uint8_t pos) const {
|
||||
Q_ASSERT(size() > pos);
|
||||
return m_value[pos];
|
||||
}
|
||||
|
||||
private:
|
||||
int cmp(const BigInt& other) const {
|
||||
Q_ASSERT(size() == other.size());
|
||||
for (int i = 0; i < size(); i++) {
|
||||
int diff = (m_value[i] - other.m_value[i]);
|
||||
if (diff != 0) return diff;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
QVector<uint8_t> m_value;
|
||||
};
|
||||
|
||||
#endif // BIGINT_H
|
|
@ -1,86 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef BIGINTIPV6ADDR_H
|
||||
#define BIGINTIPV6ADDR_H
|
||||
|
||||
#include "bigint.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
|
||||
class BigIntIPv6Addr final {
|
||||
public:
|
||||
BigIntIPv6Addr() : m_value(16) {}
|
||||
|
||||
explicit BigIntIPv6Addr(const Q_IPV6ADDR& a) : m_value(16) {
|
||||
for (int i = 0; i < 16; ++i) m_value.setValueAt(a[i], i);
|
||||
}
|
||||
|
||||
BigIntIPv6Addr(const BigIntIPv6Addr& other) : m_value(16) { *this = other; }
|
||||
|
||||
Q_IPV6ADDR value() const {
|
||||
Q_IPV6ADDR addr;
|
||||
for (int i = 0; i < 16; ++i) addr[i] = m_value.valueAt(i);
|
||||
return addr;
|
||||
}
|
||||
|
||||
// Assign operator.
|
||||
|
||||
BigIntIPv6Addr& operator=(const BigIntIPv6Addr& other) {
|
||||
m_value = other.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Comparison operators.
|
||||
|
||||
bool operator==(const BigIntIPv6Addr& other) const {
|
||||
return m_value == other.m_value;
|
||||
}
|
||||
|
||||
bool operator!=(const BigIntIPv6Addr& other) const {
|
||||
return m_value != other.m_value;
|
||||
}
|
||||
|
||||
bool operator<(const BigIntIPv6Addr& other) const {
|
||||
return m_value < other.m_value;
|
||||
}
|
||||
|
||||
bool operator>(const BigIntIPv6Addr& other) const {
|
||||
return m_value > other.m_value;
|
||||
}
|
||||
|
||||
bool operator<=(const BigIntIPv6Addr& other) const {
|
||||
return m_value <= other.m_value;
|
||||
}
|
||||
|
||||
bool operator>=(const BigIntIPv6Addr& other) const {
|
||||
return m_value >= other.m_value;
|
||||
}
|
||||
|
||||
// math operators (only some of them are implemented)
|
||||
|
||||
BigIntIPv6Addr& operator++() {
|
||||
++m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BigIntIPv6Addr& operator+=(const BigIntIPv6Addr& b) {
|
||||
m_value += b.m_value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Shift operators
|
||||
|
||||
BigIntIPv6Addr operator>>(int shift) {
|
||||
BigIntIPv6Addr x;
|
||||
|
||||
x.m_value = m_value >> shift;
|
||||
return x;
|
||||
}
|
||||
|
||||
private:
|
||||
BigInt m_value;
|
||||
};
|
||||
|
||||
#endif // BIGINTIPV6ADDR_H
|
|
@ -1,16 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IOSADJUSTHELPER_H
|
||||
#define IOSADJUSTHELPER_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class IOSAdjustHelper final {
|
||||
public:
|
||||
static void initialize();
|
||||
static void trackEvent(const QString& eventToken);
|
||||
};
|
||||
|
||||
#endif // IOSADJUSTHELPER_H
|
|
@ -1,37 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "iosadjusthelper.h"
|
||||
#include "logger.h"
|
||||
#include "constants.h"
|
||||
|
||||
#import <AdjustSdk/Adjust.h>
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger(LOG_IOS, "IOSAdjustHelper");
|
||||
|
||||
} // namespace
|
||||
|
||||
void IOSAdjustHelper::initialize() {
|
||||
|
||||
NSString *adjustToken = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"ADJUST_SDK_TOKEN"];
|
||||
|
||||
if(adjustToken.length) {
|
||||
NSString *environment = Constants::inProduction() ? ADJEnvironmentProduction : ADJEnvironmentSandbox;
|
||||
ADJConfig *adjustConfig = [ADJConfig configWithAppToken:adjustToken
|
||||
environment:environment];
|
||||
[adjustConfig setLogLevel:ADJLogLevelDebug];
|
||||
[Adjust appDidLaunch:adjustConfig];
|
||||
}
|
||||
}
|
||||
|
||||
void IOSAdjustHelper::trackEvent(const QString& eventToken) {
|
||||
NSString *adjustToken = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"ADJUST_SDK_TOKEN"];
|
||||
|
||||
if(adjustToken.length) {
|
||||
ADJEvent *event = [ADJEvent eventWithEventToken:eventToken.toNSString()];
|
||||
[Adjust trackEvent:event];
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IOSAUTHENTICATIONLISTENER_H
|
||||
#define IOSAUTHENTICATIONLISTENER_H
|
||||
|
||||
#include "authenticationlistener.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class IOSAuthenticationListener final : public AuthenticationListener {
|
||||
Q_DISABLE_COPY_MOVE(IOSAuthenticationListener)
|
||||
|
||||
public:
|
||||
IOSAuthenticationListener(QObject* parent);
|
||||
~IOSAuthenticationListener();
|
||||
|
||||
void start(const QString& codeChallenge, const QString& codeChallengeMethod,
|
||||
const QString& emailAddress) override;
|
||||
};
|
||||
|
||||
#endif // IOSAUTHENTICATIONLISTENER_H
|
|
@ -1,139 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "iosauthenticationlistener.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "mozillavpn.h"
|
||||
#include "qmlengineholder.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QUrl>
|
||||
#include <QUrlQuery>
|
||||
#include <QtGui/qpa/qplatformnativeinterface.h>
|
||||
#include <QtGui>
|
||||
#include <QWindow>
|
||||
#include <QQmlApplicationEngine>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <AuthenticationServices/ASWebAuthenticationSession.h>
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger({LOG_IOS, LOG_MAIN}, "IOSAuthenticationListener");
|
||||
|
||||
ASWebAuthenticationSession* session = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
|
||||
@interface ContextProvider : NSObject <ASWebAuthenticationPresentationContextProviding> {
|
||||
UIView* m_view;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation ContextProvider
|
||||
|
||||
- (id)initWithUIView:(UIView*)uiView {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
m_view = uiView;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
# pragma mark - ASWebAuthenticationPresentationContextProviding
|
||||
- (nonnull ASPresentationAnchor)presentationAnchorForWebAuthenticationSession:
|
||||
(nonnull ASWebAuthenticationSession*)session API_AVAILABLE(ios(13.0)) {
|
||||
return m_view.window;
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
IOSAuthenticationListener::IOSAuthenticationListener(QObject* parent)
|
||||
: AuthenticationListener(parent) {
|
||||
MVPN_COUNT_CTOR(IOSAuthenticationListener);
|
||||
}
|
||||
|
||||
IOSAuthenticationListener::~IOSAuthenticationListener() {
|
||||
MVPN_COUNT_DTOR(IOSAuthenticationListener);
|
||||
}
|
||||
|
||||
void IOSAuthenticationListener::start(const QString& codeChallenge,
|
||||
const QString& codeChallengeMethod,
|
||||
const QString& emailAddress) {
|
||||
logger.debug() << "IOSAuthenticationListener initialize";
|
||||
|
||||
QUrl url(createAuthenticationUrl(AmneziaVPN::AuthenticationInBrowser, codeChallenge,
|
||||
codeChallengeMethod, emailAddress));
|
||||
QUrlQuery query(url.query());
|
||||
query.addQueryItem("platform", "ios");
|
||||
url.setQuery(query);
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
logger.debug() << "Authentication URL:" << url.toString();
|
||||
#endif
|
||||
|
||||
if (session) {
|
||||
[session dealloc];
|
||||
session = nullptr;
|
||||
}
|
||||
|
||||
session = [[ASWebAuthenticationSession alloc]
|
||||
initWithURL:url.toNSURL()
|
||||
callbackURLScheme:@"mozilla-vpn"
|
||||
completionHandler:^(NSURL* _Nullable callbackURL, NSError* _Nullable error) {
|
||||
[session dealloc];
|
||||
session = nullptr;
|
||||
|
||||
if (error) {
|
||||
logger.error() << "Authentication failed:"
|
||||
<< QString::fromNSString([error localizedDescription]);
|
||||
logger.error() << "Code:" << [error code];
|
||||
logger.error() << "Suggestion:"
|
||||
<< QString::fromNSString([error localizedRecoverySuggestion]);
|
||||
logger.error() << "Reason:" << QString::fromNSString([error localizedFailureReason]);
|
||||
|
||||
if ([error code] == ASWebAuthenticationSessionErrorCodeCanceledLogin) {
|
||||
emit abortedByUser();
|
||||
} else {
|
||||
emit failed(ErrorHandler::RemoteServiceError);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
QUrl callbackUrl = QUrl::fromNSURL(callbackURL);
|
||||
logger.debug() << "Authentication completed";
|
||||
|
||||
Q_ASSERT(callbackUrl.hasQuery());
|
||||
|
||||
QUrlQuery callbackUrlQuery(callbackUrl.query());
|
||||
Q_ASSERT(callbackUrlQuery.hasQueryItem("code"));
|
||||
QString code = callbackUrlQuery.queryItemValue("code");
|
||||
emit completed(code);
|
||||
}];
|
||||
|
||||
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
|
||||
QObject* rootObject = QmlEngineHolder::instance()->engine()->rootObjects().first();
|
||||
QWindow* window = qobject_cast<QWindow*>(rootObject);
|
||||
Q_ASSERT(window);
|
||||
|
||||
UIView* view = static_cast<UIView*>(
|
||||
QGuiApplication::platformNativeInterface()->nativeResourceForWindow("uiview", window));
|
||||
|
||||
if (@available(iOS 13, *)) {
|
||||
session.presentationContextProvider = [[ContextProvider alloc] initWithUIView:view];
|
||||
}
|
||||
#endif
|
||||
|
||||
if (![session start]) {
|
||||
[session dealloc];
|
||||
session = nullptr;
|
||||
|
||||
logger.error() << "Authentication failed: session doesn't start.";
|
||||
emit failed(ErrorHandler::RemoteServiceError);
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IOSCONTROLLER_H
|
||||
#define IOSCONTROLLER_H
|
||||
|
||||
#include "vpnprotocol.h"
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class IOSVPNProtocol final : public VpnProtocol {
|
||||
Q_DISABLE_COPY_MOVE(IOSVPNProtocol)
|
||||
|
||||
public:
|
||||
IOSController();
|
||||
~IOSController();
|
||||
|
||||
void initialize(const Device* device, const Keys* keys) override;
|
||||
|
||||
void activate(const QList<Server>& serverList, const Device* device,
|
||||
const Keys* keys,
|
||||
const QList<IPAddressRange>& allowedIPAddressRanges,
|
||||
const QList<QString>& vpnDisabledApps,
|
||||
const QHostAddress& dnsServer, Reason reason) override;
|
||||
|
||||
void deactivate(Reason reason) override;
|
||||
|
||||
void checkStatus() override;
|
||||
|
||||
void getBackendLogs(std::function<void(const QString&)>&& callback) override;
|
||||
|
||||
void cleanupBackendLogs() override;
|
||||
|
||||
private:
|
||||
bool m_checkingStatus = false;
|
||||
};
|
||||
|
||||
#endif // IOSCONTROLLER_H
|
|
@ -1,240 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ioscontroller.h"
|
||||
#include "Mozilla_VPN-Swift.h"
|
||||
#include "device.h"
|
||||
#include "ipaddressrange.h"
|
||||
#include "keys.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "mozillavpn.h"
|
||||
#include "server.h"
|
||||
#include "settingsholder.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QFile>
|
||||
#include <QHostAddress>
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger({LOG_IOS, LOG_CONTROLLER}, "IOSController");
|
||||
|
||||
// Our Swift singleton.
|
||||
IOSControllerImpl* impl = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
IOSController::IOSController() {
|
||||
MVPN_COUNT_CTOR(IOSController);
|
||||
|
||||
logger.debug() << "created";
|
||||
|
||||
Q_ASSERT(!impl);
|
||||
}
|
||||
|
||||
IOSController::~IOSController() {
|
||||
MVPN_COUNT_DTOR(IOSController);
|
||||
|
||||
logger.debug() << "deallocated";
|
||||
|
||||
if (impl) {
|
||||
[impl dealloc];
|
||||
impl = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void IOSController::initialize(const Device* device, const Keys* keys) {
|
||||
Q_ASSERT(!impl);
|
||||
Q_UNUSED(device);
|
||||
|
||||
logger.debug() << "Initializing Swift Controller";
|
||||
|
||||
static bool creating = false;
|
||||
// No nested creation!
|
||||
Q_ASSERT(creating == false);
|
||||
creating = true;
|
||||
|
||||
QByteArray key = QByteArray::fromBase64(keys->privateKey().toLocal8Bit());
|
||||
|
||||
impl = [[IOSControllerImpl alloc] initWithBundleID:@VPN_NE_BUNDLEID
|
||||
privateKey:key.toNSData()
|
||||
deviceIpv4Address:device->ipv4Address().toNSString()
|
||||
deviceIpv6Address:device->ipv6Address().toNSString()
|
||||
closure:^(ConnectionState state, NSDate* date) {
|
||||
logger.debug() << "Creation completed with connection state:" << state;
|
||||
creating = false;
|
||||
|
||||
switch (state) {
|
||||
case ConnectionStateError: {
|
||||
[impl dealloc];
|
||||
impl = nullptr;
|
||||
emit initialized(false, false, QDateTime());
|
||||
return;
|
||||
}
|
||||
case ConnectionStateConnected: {
|
||||
Q_ASSERT(date);
|
||||
QDateTime qtDate(QDateTime::fromNSDate(date));
|
||||
emit initialized(true, true, qtDate);
|
||||
return;
|
||||
}
|
||||
case ConnectionStateDisconnected:
|
||||
// Just in case we are connecting, let's call disconnect.
|
||||
[impl disconnect];
|
||||
emit initialized(true, false, QDateTime());
|
||||
return;
|
||||
}
|
||||
}
|
||||
callback:^(BOOL a_connected) {
|
||||
logger.debug() << "State changed: " << a_connected;
|
||||
if (a_connected) {
|
||||
emit connected();
|
||||
return;
|
||||
}
|
||||
|
||||
emit disconnected();
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSController::activate(const QList<Server>& serverList, const Device* device,
|
||||
const Keys* keys, const QList<IPAddressRange>& allowedIPAddressRanges,
|
||||
const QList<QString>& vpnDisabledApps, const QHostAddress& dnsServer,
|
||||
Reason reason) {
|
||||
Q_UNUSED(device);
|
||||
Q_UNUSED(keys);
|
||||
|
||||
Q_ASSERT(serverList.length() == 1);
|
||||
const Server& server = serverList[0];
|
||||
|
||||
// This feature is not supported on macos/ios yet.
|
||||
Q_ASSERT(vpnDisabledApps.isEmpty());
|
||||
|
||||
logger.debug() << "IOSController activating" << server.hostname();
|
||||
|
||||
if (!impl) {
|
||||
logger.error() << "Controller not correctly initialized";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
NSMutableArray<VPNIPAddressRange*>* allowedIPAddressRangesNS =
|
||||
[NSMutableArray<VPNIPAddressRange*> arrayWithCapacity:allowedIPAddressRanges.length()];
|
||||
for (const IPAddressRange& i : allowedIPAddressRanges) {
|
||||
VPNIPAddressRange* range =
|
||||
[[VPNIPAddressRange alloc] initWithAddress:i.ipAddress().toNSString()
|
||||
networkPrefixLength:i.range()
|
||||
isIpv6:i.type() == IPAddressRange::IPv6];
|
||||
[allowedIPAddressRangesNS addObject:[range autorelease]];
|
||||
}
|
||||
|
||||
[impl connectWithDnsServer:dnsServer.toString().toNSString()
|
||||
serverIpv6Gateway:server.ipv6Gateway().toNSString()
|
||||
serverPublicKey:server.publicKey().toNSString()
|
||||
serverIpv4AddrIn:server.ipv4AddrIn().toNSString()
|
||||
serverPort:server.choosePort()
|
||||
allowedIPAddressRanges:allowedIPAddressRangesNS
|
||||
ipv6Enabled:SettingsHolder::instance()->ipv6Enabled()
|
||||
reason:reason
|
||||
failureCallback:^() {
|
||||
logger.error() << "IOSSWiftController - connection failed";
|
||||
emit disconnected();
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSController::deactivate(Reason reason) {
|
||||
logger.debug() << "IOSController deactivated";
|
||||
|
||||
if (reason != ReasonNone) {
|
||||
logger.debug() << "We do not need to disable the VPN for switching or connection check.";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!impl) {
|
||||
logger.error() << "Controller not correctly initialized";
|
||||
emit disconnected();
|
||||
return;
|
||||
}
|
||||
|
||||
[impl disconnect];
|
||||
}
|
||||
|
||||
void IOSController::checkStatus() {
|
||||
logger.debug() << "Checking status";
|
||||
|
||||
if (m_checkingStatus) {
|
||||
logger.warning() << "We are still waiting for the previous status.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!impl) {
|
||||
logger.error() << "Controller not correctly initialized";
|
||||
return;
|
||||
}
|
||||
|
||||
m_checkingStatus = true;
|
||||
|
||||
[impl checkStatusWithCallback:^(NSString* serverIpv4Gateway, NSString* deviceIpv4Address,
|
||||
NSString* configString) {
|
||||
QString config = QString::fromNSString(configString);
|
||||
|
||||
m_checkingStatus = false;
|
||||
|
||||
if (config.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t txBytes = 0;
|
||||
uint64_t rxBytes = 0;
|
||||
|
||||
QStringList lines = config.split("\n");
|
||||
for (const QString& line : lines) {
|
||||
if (line.startsWith("tx_bytes=")) {
|
||||
txBytes = line.split("=")[1].toULongLong();
|
||||
} else if (line.startsWith("rx_bytes=")) {
|
||||
rxBytes = line.split("=")[1].toULongLong();
|
||||
}
|
||||
|
||||
if (txBytes && rxBytes) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug() << "ServerIpv4Gateway:" << QString::fromNSString(serverIpv4Gateway)
|
||||
<< "DeviceIpv4Address:" << QString::fromNSString(deviceIpv4Address)
|
||||
<< "RxBytes:" << rxBytes << "TxBytes:" << txBytes;
|
||||
emit statusUpdated(QString::fromNSString(serverIpv4Gateway),
|
||||
QString::fromNSString(deviceIpv4Address), txBytes, rxBytes);
|
||||
}];
|
||||
}
|
||||
|
||||
void IOSController::getBackendLogs(std::function<void(const QString&)>&& a_callback) {
|
||||
std::function<void(const QString&)> callback = std::move(a_callback);
|
||||
|
||||
QString groupId(GROUP_ID);
|
||||
NSURL* groupPath = [[NSFileManager defaultManager]
|
||||
containerURLForSecurityApplicationGroupIdentifier:groupId.toNSString()];
|
||||
|
||||
NSURL* path = [groupPath URLByAppendingPathComponent:@"networkextension.log"];
|
||||
|
||||
QFile file(QString::fromNSString([path path]));
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
callback("Network extension log file missing or unreadable.");
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray content = file.readAll();
|
||||
callback(content);
|
||||
}
|
||||
|
||||
void IOSController::cleanupBackendLogs() {
|
||||
QString groupId(GROUP_ID);
|
||||
NSURL* groupPath = [[NSFileManager defaultManager]
|
||||
containerURLForSecurityApplicationGroupIdentifier:groupId.toNSString()];
|
||||
|
||||
NSURL* path = [groupPath URLByAppendingPathComponent:@"networkextension.log"];
|
||||
|
||||
QFile file(QString::fromNSString([path path]));
|
||||
file.remove();
|
||||
}
|
|
@ -1,288 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
let vpnName = "Mozilla VPN"
|
||||
var vpnBundleID = "";
|
||||
|
||||
@objc class VPNIPAddressRange : NSObject {
|
||||
public var address: NSString = ""
|
||||
public var networkPrefixLength: UInt8 = 0
|
||||
public var isIpv6: Bool = false
|
||||
|
||||
@objc init(address: NSString, networkPrefixLength: UInt8, isIpv6: Bool) {
|
||||
super.init()
|
||||
|
||||
self.address = address
|
||||
self.networkPrefixLength = networkPrefixLength
|
||||
self.isIpv6 = isIpv6
|
||||
}
|
||||
}
|
||||
|
||||
public class IOSControllerImpl : NSObject {
|
||||
|
||||
private var tunnel: NETunnelProviderManager? = nil
|
||||
private var stateChangeCallback: ((Bool) -> Void?)? = nil
|
||||
private var privateKey : PrivateKey? = nil
|
||||
private var deviceIpv4Address: String? = nil
|
||||
private var deviceIpv6Address: String? = nil
|
||||
|
||||
@objc enum ConnectionState: Int { case Error, Connected, Disconnected }
|
||||
|
||||
@objc init(bundleID: String, privateKey: Data, deviceIpv4Address: String, deviceIpv6Address: String, closure: @escaping (ConnectionState, Date?) -> Void, callback: @escaping (Bool) -> Void) {
|
||||
super.init()
|
||||
|
||||
Logger.configureGlobal(tagged: "APP", withFilePath: "")
|
||||
|
||||
vpnBundleID = bundleID;
|
||||
precondition(!vpnBundleID.isEmpty)
|
||||
|
||||
stateChangeCallback = callback
|
||||
self.privateKey = PrivateKey(rawValue: privateKey)
|
||||
self.deviceIpv4Address = deviceIpv4Address
|
||||
self.deviceIpv6Address = deviceIpv6Address
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(self.vpnStatusDidChange(notification:)), name: Notification.Name.NEVPNStatusDidChange, object: nil)
|
||||
|
||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||
if let error = error {
|
||||
Logger.global?.log(message: "Loading from preference failed: \(error)")
|
||||
closure(ConnectionState.Error, nil)
|
||||
return
|
||||
}
|
||||
|
||||
if self == nil {
|
||||
Logger.global?.log(message: "We are shutting down.")
|
||||
return
|
||||
}
|
||||
|
||||
let nsManagers = managers ?? []
|
||||
Logger.global?.log(message: "We have received \(nsManagers.count) managers.")
|
||||
|
||||
let tunnel = nsManagers.first(where: IOSControllerImpl.isOurManager(_:))
|
||||
if tunnel == nil {
|
||||
Logger.global?.log(message: "Creating the tunnel")
|
||||
self!.tunnel = NETunnelProviderManager()
|
||||
closure(ConnectionState.Disconnected, nil)
|
||||
return
|
||||
}
|
||||
|
||||
Logger.global?.log(message: "Tunnel already exists")
|
||||
|
||||
self!.tunnel = tunnel
|
||||
if tunnel?.connection.status == .connected {
|
||||
closure(ConnectionState.Connected, tunnel?.connection.connectedDate)
|
||||
} else {
|
||||
closure(ConnectionState.Disconnected, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func vpnStatusDidChange(notification: Notification) {
|
||||
guard let session = (notification.object as? NETunnelProviderSession), tunnel?.connection == session else { return }
|
||||
|
||||
switch session.status {
|
||||
case .connected:
|
||||
Logger.global?.log(message: "STATE CHANGED: connected")
|
||||
case .connecting:
|
||||
Logger.global?.log(message: "STATE CHANGED: connecting")
|
||||
case .disconnected:
|
||||
Logger.global?.log(message: "STATE CHANGED: disconnected")
|
||||
case .disconnecting:
|
||||
Logger.global?.log(message: "STATE CHANGED: disconnecting")
|
||||
case .invalid:
|
||||
Logger.global?.log(message: "STATE CHANGED: invalid")
|
||||
case .reasserting:
|
||||
Logger.global?.log(message: "STATE CHANGED: reasserting")
|
||||
default:
|
||||
Logger.global?.log(message: "STATE CHANGED: unknown status")
|
||||
}
|
||||
|
||||
// We care about "unknown" state changes.
|
||||
if (session.status != .connected && session.status != .disconnected) {
|
||||
return
|
||||
}
|
||||
|
||||
stateChangeCallback?(session.status == .connected)
|
||||
}
|
||||
|
||||
private static func isOurManager(_ manager: NETunnelProviderManager) -> Bool {
|
||||
guard
|
||||
let proto = manager.protocolConfiguration,
|
||||
let tunnelProto = proto as? NETunnelProviderProtocol
|
||||
else {
|
||||
Logger.global?.log(message: "Ignoring manager because the proto is invalid.")
|
||||
return false
|
||||
}
|
||||
|
||||
if (tunnelProto.providerBundleIdentifier == nil) {
|
||||
Logger.global?.log(message: "Ignoring manager because the bundle identifier is null.")
|
||||
return false
|
||||
}
|
||||
|
||||
if (tunnelProto.providerBundleIdentifier != vpnBundleID) {
|
||||
Logger.global?.log(message: "Ignoring manager because the bundle identifier doesn't match.")
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.global?.log(message: "Found the manager with the correct bundle identifier: \(tunnelProto.providerBundleIdentifier!)")
|
||||
return true
|
||||
}
|
||||
|
||||
@objc func connect(dnsServer: String, serverIpv6Gateway: String, serverPublicKey: String, serverIpv4AddrIn: String, serverPort: Int, allowedIPAddressRanges: Array<VPNIPAddressRange>, ipv6Enabled: Bool, reason: Int, failureCallback: @escaping () -> Void) {
|
||||
Logger.global?.log(message: "Connecting")
|
||||
assert(tunnel != nil)
|
||||
|
||||
// Let's remove the previous config if it exists.
|
||||
(tunnel!.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
|
||||
|
||||
let keyData = PublicKey(base64Key: serverPublicKey)!
|
||||
let dnsServerIP = IPv4Address(dnsServer)
|
||||
let ipv6GatewayIP = IPv6Address(serverIpv6Gateway)
|
||||
|
||||
var peerConfiguration = PeerConfiguration(publicKey: keyData)
|
||||
peerConfiguration.endpoint = Endpoint(from: serverIpv4AddrIn + ":\(serverPort )")
|
||||
peerConfiguration.allowedIPs = []
|
||||
|
||||
allowedIPAddressRanges.forEach {
|
||||
if (!$0.isIpv6) {
|
||||
peerConfiguration.allowedIPs.append(IPAddressRange(address: IPv4Address($0.address as String)!, networkPrefixLength: $0.networkPrefixLength))
|
||||
} else if (ipv6Enabled) {
|
||||
peerConfiguration.allowedIPs.append(IPAddressRange(address: IPv6Address($0.address as String)!, networkPrefixLength: $0.networkPrefixLength))
|
||||
}
|
||||
}
|
||||
|
||||
var peerConfigurations: [PeerConfiguration] = []
|
||||
peerConfigurations.append(peerConfiguration)
|
||||
|
||||
var interface = InterfaceConfiguration(privateKey: privateKey!)
|
||||
|
||||
if let ipv4Address = IPAddressRange(from: deviceIpv4Address!),
|
||||
let ipv6Address = IPAddressRange(from: deviceIpv6Address!) {
|
||||
interface.addresses = [ipv4Address]
|
||||
if (ipv6Enabled) {
|
||||
interface.addresses.append(ipv6Address)
|
||||
}
|
||||
}
|
||||
interface.dns = [ DNSServer(address: dnsServerIP!)]
|
||||
|
||||
if (ipv6Enabled) {
|
||||
interface.dns.append(DNSServer(address: ipv6GatewayIP!))
|
||||
}
|
||||
|
||||
let config = TunnelConfiguration(name: vpnName, interface: interface, peers: peerConfigurations)
|
||||
|
||||
self.configureTunnel(config: config, reason: reason, failureCallback: failureCallback)
|
||||
}
|
||||
|
||||
func configureTunnel(config: TunnelConfiguration, reason: Int, failureCallback: @escaping () -> Void) {
|
||||
let proto = NETunnelProviderProtocol(tunnelConfiguration: config)
|
||||
proto!.providerBundleIdentifier = vpnBundleID
|
||||
|
||||
tunnel!.protocolConfiguration = proto
|
||||
tunnel!.localizedDescription = vpnName
|
||||
tunnel!.isEnabled = true
|
||||
|
||||
tunnel!.saveToPreferences { [unowned self] saveError in
|
||||
if let error = saveError {
|
||||
Logger.global?.log(message: "Connect Tunnel Save Error: \(error)")
|
||||
failureCallback()
|
||||
return
|
||||
}
|
||||
|
||||
Logger.global?.log(message: "Saving the tunnel succeeded")
|
||||
|
||||
self.tunnel!.loadFromPreferences { error in
|
||||
if let error = error {
|
||||
Logger.global?.log(message: "Connect Tunnel Load Error: \(error)")
|
||||
failureCallback()
|
||||
return
|
||||
}
|
||||
|
||||
Logger.global?.log(message: "Loading the tunnel succeeded")
|
||||
|
||||
do {
|
||||
if (reason == 1 /* ReasonSwitching */) {
|
||||
let settings = config.asWgQuickConfig()
|
||||
let settingsData = settings.data(using: .utf8)!
|
||||
try (self.tunnel!.connection as? NETunnelProviderSession)?
|
||||
.sendProviderMessage(settingsData) { data in
|
||||
guard let data = data,
|
||||
let configString = String(data: data, encoding: .utf8)
|
||||
else {
|
||||
Logger.global?.log(message: "Failed to convert response to string")
|
||||
return
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try (self.tunnel!.connection as? NETunnelProviderSession)?.startTunnel()
|
||||
}
|
||||
} catch let error {
|
||||
Logger.global?.log(message: "Something went wrong: \(error)")
|
||||
failureCallback()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func disconnect() {
|
||||
Logger.global?.log(message: "Disconnecting")
|
||||
assert(tunnel != nil)
|
||||
(tunnel!.connection as? NETunnelProviderSession)?.stopTunnel()
|
||||
}
|
||||
|
||||
@objc func checkStatus(callback: @escaping (String, String, String) -> Void) {
|
||||
assert(tunnel != nil)
|
||||
|
||||
let proto = tunnel!.protocolConfiguration as? NETunnelProviderProtocol
|
||||
if proto == nil {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
let tunnelConfiguration = proto?.asTunnelConfiguration()
|
||||
if tunnelConfiguration == nil {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
let serverIpv4Gateway = tunnelConfiguration?.interface.dns[0].address
|
||||
if serverIpv4Gateway == nil {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
let deviceIpv4Address = tunnelConfiguration?.interface.addresses[0].address
|
||||
if deviceIpv4Address == nil {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
guard let session = tunnel?.connection as? NETunnelProviderSession
|
||||
else {
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
do {
|
||||
try session.sendProviderMessage(Data([UInt8(0)])) { [callback] data in
|
||||
guard let data = data,
|
||||
let configString = String(data: data, encoding: .utf8)
|
||||
else {
|
||||
Logger.global?.log(message: "Failed to convert data to string")
|
||||
callback("", "", "")
|
||||
return
|
||||
}
|
||||
|
||||
callback("\(serverIpv4Gateway!)", "\(deviceIpv4Address!)", configString)
|
||||
}
|
||||
} catch {
|
||||
Logger.global?.log(message: "Failed to retrieve data from session")
|
||||
callback("", "", "")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "iosdatamigration.h"
|
||||
#include "device.h"
|
||||
#include "logger.h"
|
||||
#include "mozillavpn.h"
|
||||
#include "user.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_IOS, "IOSDataMigration");
|
||||
|
||||
void migrateUserDefaultData() {
|
||||
AmneziaVPN* vpn = AmneziaVPN::instance();
|
||||
Q_ASSERT(vpn);
|
||||
|
||||
NSUserDefaults* sud = [NSUserDefaults standardUserDefaults];
|
||||
if (!sud) {
|
||||
return;
|
||||
}
|
||||
|
||||
NSData* userData = [sud dataForKey:@"user"];
|
||||
if (userData) {
|
||||
QByteArray json = QByteArray::fromNSData(userData);
|
||||
if (!json.isEmpty()) {
|
||||
logger.debug() << "User data to be migrated";
|
||||
vpn->accountChecked(json);
|
||||
}
|
||||
}
|
||||
|
||||
NSData* deviceData = [sud dataForKey:@"device"];
|
||||
if (deviceData) {
|
||||
QByteArray json = QByteArray::fromNSData(deviceData);
|
||||
logger.debug() << "Device data to be migrated";
|
||||
// Nothing has to be done here because the device data is part of the user data.
|
||||
}
|
||||
|
||||
NSData* serversData = [sud dataForKey:@"vpnServers"];
|
||||
if (serversData) {
|
||||
QByteArray json = QByteArray::fromNSData(serversData);
|
||||
if (!json.isEmpty()) {
|
||||
logger.debug() << "Server list data to be migrated";
|
||||
|
||||
// We need to wrap the server list in a object to make it similar to the REST API response.
|
||||
QJsonDocument serverList = QJsonDocument::fromJson(json);
|
||||
if (!serverList.isArray()) {
|
||||
logger.error() << "Server list should be an array!";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject countriesObj;
|
||||
countriesObj.insert("countries", QJsonValue(serverList.array()));
|
||||
|
||||
QJsonDocument doc;
|
||||
doc.setObject(countriesObj);
|
||||
if (!vpn->setServerList(doc.toJson())) {
|
||||
logger.error() << "Server list cannot be imported";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NSData* selectedCityData = [sud dataForKey:@"selectedCity"];
|
||||
if (selectedCityData) {
|
||||
QByteArray json = QByteArray::fromNSData(selectedCityData);
|
||||
logger.debug() << "SelectedCity data to be migrated" << json;
|
||||
// Nothing has to be done here because the device data is part of the user data.
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(json);
|
||||
if (!doc.isObject()) {
|
||||
logger.error() << "SelectedCity should be an object";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject obj = doc.object();
|
||||
QJsonValue code = obj.value("flagCode");
|
||||
if (!code.isString()) {
|
||||
logger.error() << "SelectedCity code should be a string";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue name = obj.value("code");
|
||||
if (!name.isString()) {
|
||||
logger.error() << "SelectedCity name should be a string";
|
||||
return;
|
||||
}
|
||||
|
||||
ServerData serverData;
|
||||
if (vpn->serverCountryModel()->pickIfExists(code.toString(), name.toString(), serverData)) {
|
||||
logger.debug() << "ServerCity found";
|
||||
serverData.writeSettings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void migrateKeychainData() {
|
||||
NSData* service = [@"org.mozilla.guardian.credentials" dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSMutableDictionary* query = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
|
||||
[query setObject:service forKey:(id)kSecAttrService];
|
||||
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
|
||||
[query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
|
||||
|
||||
NSData* dataNS = NULL;
|
||||
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)(void*)&dataNS);
|
||||
[query release];
|
||||
|
||||
if (status != noErr) {
|
||||
logger.error() << "No credentials found";
|
||||
return;
|
||||
}
|
||||
|
||||
QByteArray data = QByteArray::fromNSData(dataNS);
|
||||
logger.debug() << "Credentials:" << logger.sensitive(data);
|
||||
|
||||
QJsonDocument json = QJsonDocument::fromJson(data);
|
||||
if (!json.isObject()) {
|
||||
logger.error() << "JSON object expected";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
QJsonValue deviceKeyValue = obj.value("deviceKeys");
|
||||
if (!deviceKeyValue.isObject()) {
|
||||
logger.error() << "JSON object should have a deviceKeys object";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject deviceKeyObj = deviceKeyValue.toObject();
|
||||
QJsonValue publicKey = deviceKeyObj.value("publicKey");
|
||||
if (!publicKey.isString()) {
|
||||
logger.error() << "JSON deviceKey object should contain a publicKey value as string";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue privateKey = deviceKeyObj.value("privateKey");
|
||||
if (!privateKey.isString()) {
|
||||
logger.error() << "JSON deviceKey object should contain a privateKey value as string";
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonValue token = obj.value("verificationToken");
|
||||
if (!token.isString()) {
|
||||
logger.error() << "JSON object should contain a verificationToken value s string";
|
||||
return;
|
||||
}
|
||||
|
||||
AmneziaVPN::instance()->deviceAdded(Device::currentDeviceName(), publicKey.toString(),
|
||||
privateKey.toString());
|
||||
|
||||
AmneziaVPN::instance()->setToken(token.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void IOSDataMigration::migrate() {
|
||||
logger.debug() << "IOS Data Migration";
|
||||
|
||||
migrateKeychainData();
|
||||
migrateUserDefaultData();
|
||||
}
|
|
@ -1,369 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "platforms/ios/iosiaphandler.h"
|
||||
#include "constants.h"
|
||||
#include "iosutils.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "mozillavpn.h"
|
||||
#include "networkrequest.h"
|
||||
#include "settingsholder.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonValue>
|
||||
#include <QScopeGuard>
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <StoreKit/StoreKit.h>
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_IAP, "IOSIAPHandler");
|
||||
} // namespace
|
||||
|
||||
@interface IOSIAPHandlerDelegate
|
||||
: NSObject <SKRequestDelegate, SKProductsRequestDelegate, SKPaymentTransactionObserver> {
|
||||
IOSIAPHandler* m_handler;
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation IOSIAPHandlerDelegate
|
||||
|
||||
- (id)initWithObject:(IOSIAPHandler*)handler {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
m_handler = handler;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)productsRequest:(nonnull SKProductsRequest*)request
|
||||
didReceiveResponse:(nonnull SKProductsResponse*)response {
|
||||
logger.debug() << "Registration completed";
|
||||
|
||||
if (response.invalidProductIdentifiers) {
|
||||
NSArray<NSString*>* products = response.invalidProductIdentifiers;
|
||||
logger.error() << "Registration failure" << [products count];
|
||||
|
||||
for (unsigned long i = 0, count = [products count]; i < count; ++i) {
|
||||
NSString* identifier = [products objectAtIndex:i];
|
||||
QMetaObject::invokeMethod(m_handler, "unknownProductRegistered", Qt::QueuedConnection,
|
||||
Q_ARG(QString, QString::fromNSString(identifier)));
|
||||
}
|
||||
}
|
||||
|
||||
NSArray<SKProduct*>* products = response.products;
|
||||
if (products) {
|
||||
logger.debug() << "Products registered" << [products count];
|
||||
|
||||
for (unsigned long i = 0, count = [products count]; i < count; ++i) {
|
||||
SKProduct* product = [[products objectAtIndex:i] retain];
|
||||
QMetaObject::invokeMethod(m_handler, "productRegistered", Qt::QueuedConnection,
|
||||
Q_ARG(void*, product));
|
||||
}
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(m_handler, "productsRegistrationCompleted", Qt::QueuedConnection);
|
||||
|
||||
[request release];
|
||||
}
|
||||
|
||||
- (void)paymentQueue:(nonnull SKPaymentQueue*)queue
|
||||
updatedTransactions:(nonnull NSArray<SKPaymentTransaction*>*)transactions {
|
||||
logger.debug() << "payment queue:" << [transactions count];
|
||||
|
||||
QStringList completedTransactionIds;
|
||||
bool failedTransactions = false;
|
||||
bool canceledTransactions = false;
|
||||
bool completedTransactions = false;
|
||||
|
||||
for (SKPaymentTransaction* transaction in transactions) {
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStateFailed:
|
||||
logger.error() << "transaction failed";
|
||||
|
||||
if (transaction.error.code == SKErrorPaymentCancelled) {
|
||||
canceledTransactions = true;
|
||||
} else {
|
||||
failedTransactions = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SKPaymentTransactionStateRestored:
|
||||
[[fallthrough]];
|
||||
case SKPaymentTransactionStatePurchased: {
|
||||
QString identifier = QString::fromNSString(transaction.transactionIdentifier);
|
||||
QDateTime date = QDateTime::fromNSDate(transaction.transactionDate);
|
||||
logger.debug() << "transaction purchased - identifier: " << identifier
|
||||
<< "- date:" << date.toString();
|
||||
|
||||
if (transaction.transactionState == SKPaymentTransactionStateRestored) {
|
||||
SKPaymentTransaction* originalTransaction = transaction.originalTransaction;
|
||||
if (originalTransaction) {
|
||||
QString originalIdentifier =
|
||||
QString::fromNSString(originalTransaction.transactionIdentifier);
|
||||
QDateTime originalDate = QDateTime::fromNSDate(originalTransaction.transactionDate);
|
||||
logger.debug() << "original transaction identifier: " << originalIdentifier
|
||||
<< "- date:" << originalDate.toString();
|
||||
}
|
||||
}
|
||||
|
||||
completedTransactions = true;
|
||||
|
||||
SettingsHolder* settingsHolder = SettingsHolder::instance();
|
||||
if (settingsHolder->hasSubscriptionTransaction(identifier)) {
|
||||
logger.warning() << "This transaction has already been processed. Let's ignore it.";
|
||||
} else {
|
||||
completedTransactionIds.append(identifier);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case SKPaymentTransactionStatePurchasing:
|
||||
logger.debug() << "transaction purchasing";
|
||||
break;
|
||||
case SKPaymentTransactionStateDeferred:
|
||||
logger.debug() << "transaction deferred";
|
||||
break;
|
||||
default:
|
||||
logger.warning() << "transaction unknown state";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!completedTransactions && !canceledTransactions && !failedTransactions) {
|
||||
// Nothing completed, nothing restored, nothing failed. Just purchasing transactions.
|
||||
return;
|
||||
}
|
||||
|
||||
if (canceledTransactions) {
|
||||
logger.debug() << "Subscription canceled";
|
||||
QMetaObject::invokeMethod(m_handler, "stopSubscription", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_handler, "subscriptionCanceled", Qt::QueuedConnection);
|
||||
} else if (failedTransactions) {
|
||||
logger.error() << "Subscription failed";
|
||||
QMetaObject::invokeMethod(m_handler, "stopSubscription", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_handler, "subscriptionCanceled", Qt::QueuedConnection);
|
||||
} else if (completedTransactionIds.isEmpty()) {
|
||||
Q_ASSERT(completedTransactions);
|
||||
logger.debug() << "Subscription completed - but all the transactions are known";
|
||||
QMetaObject::invokeMethod(m_handler, "stopSubscription", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_handler, "subscriptionCanceled", Qt::QueuedConnection);
|
||||
} else if (AmneziaVPN::instance()->userAuthenticated()) {
|
||||
Q_ASSERT(completedTransactions);
|
||||
logger.debug() << "Subscription completed. Let's start the validation";
|
||||
QMetaObject::invokeMethod(m_handler, "processCompletedTransactions", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, completedTransactionIds));
|
||||
} else {
|
||||
Q_ASSERT(completedTransactions);
|
||||
logger.debug() << "Subscription completed - but the user is not authenticated yet";
|
||||
QMetaObject::invokeMethod(m_handler, "stopSubscription", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_handler, "subscriptionCanceled", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
for (SKPaymentTransaction* transaction in transactions) {
|
||||
switch (transaction.transactionState) {
|
||||
case SKPaymentTransactionStateFailed:
|
||||
[[fallthrough]];
|
||||
case SKPaymentTransactionStateRestored:
|
||||
[[fallthrough]];
|
||||
case SKPaymentTransactionStatePurchased:
|
||||
[queue finishTransaction:transaction];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)requestDidFinish:(SKRequest*)request {
|
||||
logger.debug() << "Receipt refreshed correctly";
|
||||
QMetaObject::invokeMethod(m_handler, "stopSubscription", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_handler, "processCompletedTransactions", Qt::QueuedConnection,
|
||||
Q_ARG(QStringList, QStringList()));
|
||||
}
|
||||
|
||||
- (void)request:(SKRequest*)request didFailWithError:(NSError*)error {
|
||||
logger.error() << "Failed to refresh the receipt"
|
||||
<< QString::fromNSString(error.localizedDescription);
|
||||
QMetaObject::invokeMethod(m_handler, "stopSubscription", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(m_handler, "subscriptionFailed", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
IOSIAPHandler::IOSIAPHandler(QObject* parent) : IAPHandler(parent) {
|
||||
MVPN_COUNT_CTOR(IOSIAPHandler);
|
||||
|
||||
m_delegate = [[IOSIAPHandlerDelegate alloc] initWithObject:this];
|
||||
[[SKPaymentQueue defaultQueue]
|
||||
addTransactionObserver:static_cast<IOSIAPHandlerDelegate*>(m_delegate)];
|
||||
}
|
||||
|
||||
IOSIAPHandler::~IOSIAPHandler() {
|
||||
MVPN_COUNT_DTOR(IOSIAPHandler);
|
||||
|
||||
IOSIAPHandlerDelegate* delegate = static_cast<IOSIAPHandlerDelegate*>(m_delegate);
|
||||
[[SKPaymentQueue defaultQueue] removeTransactionObserver:delegate];
|
||||
|
||||
[delegate dealloc];
|
||||
m_delegate = nullptr;
|
||||
}
|
||||
|
||||
void IOSIAPHandler::nativeRegisterProducts() {
|
||||
NSSet<NSString*>* productIdentifiers = [NSSet<NSString*> set];
|
||||
for (const Product& product : m_products) {
|
||||
productIdentifiers = [productIdentifiers setByAddingObject:product.m_name.toNSString()];
|
||||
}
|
||||
|
||||
logger.debug() << "We are about to register" << [productIdentifiers count] << "products";
|
||||
|
||||
SKProductsRequest* productsRequest =
|
||||
[[SKProductsRequest alloc] initWithProductIdentifiers:productIdentifiers];
|
||||
|
||||
IOSIAPHandlerDelegate* delegate = static_cast<IOSIAPHandlerDelegate*>(m_delegate);
|
||||
productsRequest.delegate = delegate;
|
||||
[productsRequest start];
|
||||
}
|
||||
|
||||
void IOSIAPHandler::nativeStartSubscription(Product* product) {
|
||||
Q_ASSERT(product->m_extra);
|
||||
SKProduct* skProduct = static_cast<SKProduct*>(product->m_extra);
|
||||
SKPayment* payment = [SKPayment paymentWithProduct:skProduct];
|
||||
[[SKPaymentQueue defaultQueue] addPayment:payment];
|
||||
}
|
||||
|
||||
void IOSIAPHandler::productRegistered(void* a_product) {
|
||||
SKProduct* product = static_cast<SKProduct*>(a_product);
|
||||
|
||||
Q_ASSERT(m_productsRegistrationState == eRegistering);
|
||||
|
||||
logger.debug() << "Product registered";
|
||||
|
||||
NSString* nsProductIdentifier = [product productIdentifier];
|
||||
QString productIdentifier = QString::fromNSString(nsProductIdentifier);
|
||||
|
||||
Product* productData = findProduct(productIdentifier);
|
||||
Q_ASSERT(productData);
|
||||
|
||||
logger.debug() << "Id:" << productIdentifier;
|
||||
logger.debug() << "Title:" << QString::fromNSString([product localizedTitle]);
|
||||
logger.debug() << "Description:" << QString::fromNSString([product localizedDescription]);
|
||||
|
||||
QString priceValue;
|
||||
{
|
||||
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
[numberFormatter setNumberStyle:(NSNumberFormatterStyle)NSNumberFormatterCurrencyStyle];
|
||||
[numberFormatter setLocale:product.priceLocale];
|
||||
|
||||
NSString* price = [numberFormatter stringFromNumber:product.price];
|
||||
priceValue = QString::fromNSString(price);
|
||||
[numberFormatter release];
|
||||
}
|
||||
|
||||
logger.debug() << "Price:" << priceValue;
|
||||
|
||||
QString monthlyPriceValue;
|
||||
NSDecimalNumber* monthlyPriceNS = nullptr;
|
||||
{
|
||||
NSNumberFormatter* numberFormatter = [[NSNumberFormatter alloc] init];
|
||||
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
|
||||
[numberFormatter setNumberStyle:(NSNumberFormatterStyle)NSNumberFormatterCurrencyStyle];
|
||||
[numberFormatter setLocale:product.priceLocale];
|
||||
|
||||
int32_t mounthCount = productTypeToMonthCount(productData->m_type);
|
||||
Q_ASSERT(mounthCount >= 1);
|
||||
|
||||
if (mounthCount == 1) {
|
||||
monthlyPriceNS = product.price;
|
||||
} else {
|
||||
NSDecimalNumber* divider = [[NSDecimalNumber alloc] initWithDouble:(double)mounthCount];
|
||||
monthlyPriceNS = [product.price decimalNumberByDividingBy:divider];
|
||||
[divider release];
|
||||
}
|
||||
|
||||
NSString* price = [numberFormatter stringFromNumber:monthlyPriceNS];
|
||||
monthlyPriceValue = QString::fromNSString(price);
|
||||
|
||||
[numberFormatter release];
|
||||
}
|
||||
|
||||
logger.debug() << "Monthly Price:" << monthlyPriceValue;
|
||||
|
||||
productData->m_price = priceValue;
|
||||
productData->m_monthlyPrice = monthlyPriceValue;
|
||||
productData->m_nonLocalizedMonthlyPrice = [monthlyPriceNS doubleValue];
|
||||
productData->m_extra = product;
|
||||
}
|
||||
|
||||
void IOSIAPHandler::processCompletedTransactions(const QStringList& ids) {
|
||||
logger.debug() << "process completed transactions";
|
||||
|
||||
if (m_subscriptionState != eActive) {
|
||||
logger.warning() << "Random transaction to be completed. Let's ignore it";
|
||||
return;
|
||||
}
|
||||
|
||||
QString receipt = IOSUtils::IAPReceipt();
|
||||
if (receipt.isEmpty()) {
|
||||
logger.warning() << "Empty receipt found";
|
||||
emit subscriptionFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
NetworkRequest* request = NetworkRequest::createForIOSPurchase(this, receipt);
|
||||
|
||||
connect(request, &NetworkRequest::requestFailed,
|
||||
[this](QNetworkReply::NetworkError error, const QByteArray& data) {
|
||||
logger.error() << "Purchase request failed" << error;
|
||||
|
||||
if (m_subscriptionState != eActive) {
|
||||
logger.warning() << "We have been canceled in the meantime";
|
||||
return;
|
||||
}
|
||||
|
||||
stopSubscription();
|
||||
|
||||
QJsonDocument json = QJsonDocument::fromJson(data);
|
||||
if (!json.isObject()) {
|
||||
AmneziaVPN::instance()->errorHandle(ErrorHandler::toErrorType(error));
|
||||
emit subscriptionFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
QJsonObject obj = json.object();
|
||||
QJsonValue errorValue = obj.value("errno");
|
||||
if (!errorValue.isDouble()) {
|
||||
AmneziaVPN::instance()->errorHandle(ErrorHandler::toErrorType(error));
|
||||
emit subscriptionFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
int errorNumber = errorValue.toInt();
|
||||
if (errorNumber != 145) {
|
||||
AmneziaVPN::instance()->errorHandle(ErrorHandler::toErrorType(error));
|
||||
emit subscriptionFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
emit alreadySubscribed();
|
||||
});
|
||||
|
||||
connect(request, &NetworkRequest::requestCompleted, [this, ids](const QByteArray&) {
|
||||
logger.debug() << "Purchase request completed";
|
||||
SettingsHolder::instance()->addSubscriptionTransactions(ids);
|
||||
|
||||
if (m_subscriptionState != eActive) {
|
||||
logger.warning() << "We have been canceled in the meantime";
|
||||
return;
|
||||
}
|
||||
|
||||
stopSubscription();
|
||||
emit subscriptionCompleted();
|
||||
});
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
import Foundation
|
||||
import Darwin
|
||||
|
||||
typealias InetFamily = UInt8
|
||||
typealias Flags = Int32
|
||||
|
||||
func destinationAddress(_ data: ifaddrs) -> UnsafeMutablePointer<sockaddr>! { return data.ifa_dstaddr }
|
||||
func socketLength4(_ addr: sockaddr) -> UInt32 { return socklen_t(addr.sa_len) }
|
||||
|
||||
/**
|
||||
* This class represents a network interface in your system. For example, `en0` with a certain IP address.
|
||||
* It is a wrapper around the `getifaddrs` system call.
|
||||
*
|
||||
* Typical use of this class is to first call `Interface.allInterfaces()` and then use the properties of the interface(s) that you need.
|
||||
*
|
||||
* - See: `/usr/include/ifaddrs.h`
|
||||
*/
|
||||
open class Interface : CustomStringConvertible, CustomDebugStringConvertible {
|
||||
public var id = UUID()
|
||||
/// The network interface family (IPv4 or IPv6, otherwise error).
|
||||
public enum Family : Int {
|
||||
case ipv4
|
||||
case ipv6
|
||||
case other
|
||||
|
||||
public func toString() -> String {
|
||||
switch (self) {
|
||||
case .ipv4: return "IPv4"
|
||||
case .ipv6: return "IPv6"
|
||||
default: return "other"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all network interfaces in your system. If you have an interface name (e.g. `en0`) that has
|
||||
* multiple IP addresses (e.g. one IPv4 address and a few IPv6 addresses), then they will be returned
|
||||
* as separate instances of Interface.
|
||||
* - Returns: An array containing all network interfaces in your system.
|
||||
*/
|
||||
public static func allInterfaces() -> [Interface] {
|
||||
var interfaces : [Interface] = []
|
||||
|
||||
var ifaddrsPtr : UnsafeMutablePointer<ifaddrs>? = nil
|
||||
if getifaddrs(&ifaddrsPtr) == 0 {
|
||||
var ifaddrPtr = ifaddrsPtr
|
||||
while ifaddrPtr != nil {
|
||||
let addr = ifaddrPtr?.pointee.ifa_addr.pointee
|
||||
if addr?.sa_family == InetFamily(AF_INET) || addr?.sa_family == InetFamily(AF_INET6) {
|
||||
interfaces.append(Interface(data: (ifaddrPtr?.pointee)!))
|
||||
}
|
||||
ifaddrPtr = ifaddrPtr?.pointee.ifa_next
|
||||
}
|
||||
freeifaddrs(ifaddrsPtr)
|
||||
}
|
||||
|
||||
return interfaces
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new Interface instance that does not represent a real network interface, but can be used for (unit) testing.
|
||||
* - Returns: An instance of Interface that does *not* represent a real network interface.
|
||||
*/
|
||||
public static func createTestDummy(_ name:String, family:Family, address:String, multicastSupported:Bool, broadcastAddress:String?) -> Interface
|
||||
{
|
||||
return Interface(name: name, family: family, address: address, netmask: nil, running: true, up: true, loopback: false, multicastSupported: multicastSupported, broadcastAddress: broadcastAddress)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new Interface with the given properties.
|
||||
*/
|
||||
public init(name:String, family:Family, address:String?, netmask:String?, running:Bool, up:Bool, loopback:Bool, multicastSupported:Bool, broadcastAddress:String?) {
|
||||
self.name = name
|
||||
self.family = family
|
||||
self.address = address
|
||||
self.netmask = netmask
|
||||
self.running = running
|
||||
self.up = up
|
||||
self.loopback = loopback
|
||||
self.multicastSupported = multicastSupported
|
||||
self.broadcastAddress = broadcastAddress
|
||||
}
|
||||
|
||||
convenience init(data:ifaddrs) {
|
||||
let flags = Flags(data.ifa_flags)
|
||||
let broadcastValid : Bool = ((flags & IFF_BROADCAST) == IFF_BROADCAST)
|
||||
self.init(name: String(cString: data.ifa_name),
|
||||
family: Interface.extractFamily(data),
|
||||
address: Interface.extractAddress(data.ifa_addr),
|
||||
netmask: Interface.extractAddress(data.ifa_netmask),
|
||||
running: ((flags & IFF_RUNNING) == IFF_RUNNING),
|
||||
up: ((flags & IFF_UP) == IFF_UP),
|
||||
loopback: ((flags & IFF_LOOPBACK) == IFF_LOOPBACK),
|
||||
multicastSupported: ((flags & IFF_MULTICAST) == IFF_MULTICAST),
|
||||
broadcastAddress: ((broadcastValid && destinationAddress(data) != nil) ? Interface.extractAddress(destinationAddress(data)) : nil))
|
||||
}
|
||||
|
||||
fileprivate static func extractFamily(_ data:ifaddrs) -> Family {
|
||||
var family : Family = .other
|
||||
let addr = data.ifa_addr.pointee
|
||||
if addr.sa_family == InetFamily(AF_INET) {
|
||||
family = .ipv4
|
||||
}
|
||||
else if addr.sa_family == InetFamily(AF_INET6) {
|
||||
family = .ipv6
|
||||
}
|
||||
else {
|
||||
family = .other
|
||||
}
|
||||
return family
|
||||
}
|
||||
|
||||
fileprivate static func extractAddress(_ address: UnsafeMutablePointer<sockaddr>?) -> String? {
|
||||
guard let address = address else { return nil }
|
||||
return address.withMemoryRebound(to: sockaddr_storage.self, capacity: 1) {
|
||||
if (address.pointee.sa_family == sa_family_t(AF_INET)) {
|
||||
return extractAddress_ipv4($0)
|
||||
}
|
||||
else if (address.pointee.sa_family == sa_family_t(AF_INET6)) {
|
||||
return extractAddress_ipv6($0)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate static func extractAddress_ipv4(_ address:UnsafeMutablePointer<sockaddr_storage>) -> String? {
|
||||
return address.withMemoryRebound(to: sockaddr.self, capacity: 1) { addr in
|
||||
var address : String? = nil
|
||||
var hostname = [CChar](repeating: 0, count: Int(2049))
|
||||
if (getnameinfo(&addr.pointee, socklen_t(socketLength4(addr.pointee)), &hostname,
|
||||
socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) == 0) {
|
||||
address = String(cString: hostname)
|
||||
}
|
||||
else {
|
||||
// var error = String.fromCString(gai_strerror(errno))!
|
||||
// println("ERROR: \(error)")
|
||||
}
|
||||
return address
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate static func extractAddress_ipv6(_ address:UnsafeMutablePointer<sockaddr_storage>) -> String? {
|
||||
var addr = address.pointee
|
||||
var ip : [Int8] = [Int8](repeating: Int8(0), count: Int(INET6_ADDRSTRLEN))
|
||||
return inetNtoP(&addr, ip: &ip)
|
||||
}
|
||||
|
||||
fileprivate static func inetNtoP(_ addr:UnsafeMutablePointer<sockaddr_storage>, ip:UnsafeMutablePointer<Int8>) -> String? {
|
||||
return addr.withMemoryRebound(to: sockaddr_in6.self, capacity: 1) { addr6 in
|
||||
let conversion:UnsafePointer<CChar> = inet_ntop(AF_INET6, &addr6.pointee.sin6_addr, ip, socklen_t(INET6_ADDRSTRLEN))
|
||||
return String(cString: conversion)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the network format representation of the interface's IP address. Wraps `inet_pton`.
|
||||
*/
|
||||
open var addressBytes: [UInt8]? {
|
||||
guard let addr = address else { return nil }
|
||||
|
||||
let af:Int32
|
||||
let len:Int
|
||||
switch family {
|
||||
case .ipv4:
|
||||
af = AF_INET
|
||||
len = 4
|
||||
case .ipv6:
|
||||
af = AF_INET6
|
||||
len = 16
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
var bytes = [UInt8](repeating: 0, count: len)
|
||||
let result = inet_pton(af, addr, &bytes)
|
||||
return ( result == 1 ) ? bytes : nil
|
||||
}
|
||||
|
||||
/// `IFF_RUNNING` flag of `ifaddrs->ifa_flags`.
|
||||
open var isRunning: Bool { return running }
|
||||
|
||||
/// `IFF_UP` flag of `ifaddrs->ifa_flags`.
|
||||
open var isUp: Bool { return up }
|
||||
|
||||
/// `IFF_LOOPBACK` flag of `ifaddrs->ifa_flags`.
|
||||
open var isLoopback: Bool { return loopback }
|
||||
|
||||
/// `IFF_MULTICAST` flag of `ifaddrs->ifa_flags`.
|
||||
open var supportsMulticast: Bool { return multicastSupported }
|
||||
|
||||
/// Field `ifaddrs->ifa_name`.
|
||||
public let name : String
|
||||
|
||||
/// Field `ifaddrs->ifa_addr->sa_family`.
|
||||
public let family : Family
|
||||
|
||||
/// Extracted from `ifaddrs->ifa_addr`, supports both IPv4 and IPv6.
|
||||
public let address : String?
|
||||
|
||||
/// Extracted from `ifaddrs->ifa_netmask`, supports both IPv4 and IPv6.
|
||||
public let netmask : String?
|
||||
|
||||
/// Extracted from `ifaddrs->ifa_dstaddr`. Not applicable for IPv6.
|
||||
public let broadcastAddress : String?
|
||||
|
||||
fileprivate let running : Bool
|
||||
fileprivate let up : Bool
|
||||
fileprivate let loopback : Bool
|
||||
fileprivate let multicastSupported : Bool
|
||||
|
||||
/// Returns the interface name.
|
||||
open var description: String { return name }
|
||||
|
||||
/// Returns a string containing a few properties of the Interface.
|
||||
open var debugDescription: String {
|
||||
var s = "Interface name:\(name) family:\(family)"
|
||||
if let ip = address {
|
||||
s += " ip:\(ip)"
|
||||
}
|
||||
s += isUp ? " (up)" : " (down)"
|
||||
s += isRunning ? " (running)" : "(not running)"
|
||||
return s
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=5)
|
||||
extension Interface: Identifiable {}
|
||||
#endif
|
32
client/platforms/ios/iosnetworkwatcher.h
Normal file
32
client/platforms/ios/iosnetworkwatcher.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IOSNETWORKWATCHER_H
|
||||
#define IOSNETWORKWATCHER_H
|
||||
|
||||
#include <Network/Network.h>
|
||||
|
||||
#include "networkwatcherimpl.h"
|
||||
|
||||
class IOSNetworkWatcher : public NetworkWatcherImpl {
|
||||
public:
|
||||
explicit IOSNetworkWatcher(QObject* parent);
|
||||
~IOSNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
NetworkWatcherImpl::TransportType getTransportType() override;
|
||||
|
||||
private:
|
||||
NetworkWatcherImpl::TransportType toTransportType(nw_path_t path);
|
||||
void controllerStateChanged();
|
||||
|
||||
NetworkWatcherImpl::TransportType m_currentDefaultTransport =
|
||||
NetworkWatcherImpl::TransportType_Unknown;
|
||||
NetworkWatcherImpl::TransportType m_currentVPNTransport =
|
||||
NetworkWatcherImpl::TransportType_Unknown;
|
||||
nw_path_monitor_t m_networkMonitor = nil;
|
||||
nw_connection_t m_observableConnection = nil;
|
||||
};
|
||||
|
||||
#endif // IOSNETWORKWATCHER_H
|
79
client/platforms/ios/iosnetworkwatcher.mm
Normal file
79
client/platforms/ios/iosnetworkwatcher.mm
Normal file
|
@ -0,0 +1,79 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "iosnetworkwatcher.h"
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#import <Network/Network.h>
|
||||
|
||||
namespace {
|
||||
Logger logger("IOSNetworkWatcher");
|
||||
dispatch_queue_t s_queue = dispatch_queue_create("VPNNetwork.queue", DISPATCH_QUEUE_SERIAL);
|
||||
}
|
||||
|
||||
IOSNetworkWatcher::IOSNetworkWatcher(QObject* parent) : NetworkWatcherImpl(parent) {
|
||||
MZ_COUNT_CTOR(IOSNetworkWatcher);
|
||||
}
|
||||
|
||||
IOSNetworkWatcher::~IOSNetworkWatcher() {
|
||||
MZ_COUNT_DTOR(IOSNetworkWatcher);
|
||||
if (m_networkMonitor != nil) {
|
||||
nw_path_monitor_cancel(m_networkMonitor);
|
||||
nw_release(m_networkMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
void IOSNetworkWatcher::initialize() {
|
||||
m_networkMonitor = nw_path_monitor_create();
|
||||
nw_path_monitor_set_queue(m_networkMonitor, s_queue);
|
||||
nw_path_monitor_set_update_handler(m_networkMonitor, ^(nw_path_t _Nonnull path) {
|
||||
m_currentDefaultTransport = toTransportType(path);
|
||||
});
|
||||
nw_path_monitor_start(m_networkMonitor);
|
||||
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
||||
|
||||
NetworkWatcherImpl::TransportType IOSNetworkWatcher::getTransportType() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
|
||||
if (m_observableConnection != nil) {
|
||||
return m_currentVPNTransport;
|
||||
}
|
||||
// If we don't have an open tunnel-observer, m_currentVPNTransport is probably wrong.
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
}
|
||||
|
||||
NetworkWatcherImpl::TransportType IOSNetworkWatcher::toTransportType(nw_path_t path) {
|
||||
if (path == nil) {
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
}
|
||||
auto status = nw_path_get_status(path);
|
||||
if (status != nw_path_status_satisfied && status != nw_path_status_satisfiable) {
|
||||
// We're offline.
|
||||
return NetworkWatcherImpl::TransportType_None;
|
||||
}
|
||||
if (nw_path_uses_interface_type(path, nw_interface_type_wifi)) {
|
||||
return NetworkWatcherImpl::TransportType_WiFi;
|
||||
}
|
||||
if (nw_path_uses_interface_type(path, nw_interface_type_wired)) {
|
||||
return NetworkWatcherImpl::TransportType_Ethernet;
|
||||
}
|
||||
if (nw_path_uses_interface_type(path, nw_interface_type_cellular)) {
|
||||
return NetworkWatcherImpl::TransportType_Cellular;
|
||||
}
|
||||
if (nw_path_uses_interface_type(path, nw_interface_type_other)) {
|
||||
return NetworkWatcherImpl::TransportType_Other;
|
||||
}
|
||||
if (nw_path_uses_interface_type(path, nw_interface_type_loopback)) {
|
||||
return NetworkWatcherImpl::TransportType_Other;
|
||||
}
|
||||
return NetworkWatcherImpl::TransportType_Unknown;
|
||||
}
|
||||
|
||||
void IOSNetworkWatcher::controllerStateChanged() {
|
||||
//TODO IMPL FOR AMNEZIA
|
||||
}
|
|
@ -73,7 +73,7 @@ void IOSNotificationHandler::notify(NotificationHandler::Message type, const QSt
|
|||
UNTimeIntervalNotificationTrigger* trigger =
|
||||
[UNTimeIntervalNotificationTrigger triggerWithTimeInterval:timerSec repeats:NO];
|
||||
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"mozillavpn"
|
||||
UNNotificationRequest* request = [UNNotificationRequest requestWithIdentifier:@"amneziavpn"
|
||||
content:content
|
||||
trigger:trigger];
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef iosopenvpn2ssadapter_h
|
||||
#define iosopenvpn2ssadapter_h
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "ssadapterpacketflow.h"
|
||||
#import <OpenVPNAdapter/OpenVPNAdapterPacketFlow.h>
|
||||
|
||||
@interface ShadowSocksAdapterFlowBridge: NSObject
|
||||
|
||||
@property (nonatomic, weak) id<ShadowSocksAdapterPacketFlow> ssPacketFlow;
|
||||
@property (nonatomic, readonly) CFSocketRef ssSocket;
|
||||
@property (nonatomic, readonly) CFSocketRef packetFlowSocket;
|
||||
|
||||
- (BOOL)configureSocketWithError:(NSError **)error;
|
||||
- (void)invalidateSocketsIfNeeded;
|
||||
- (void)processPackets;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#endif /* iosopenvpn2ssadapter_h */
|
|
@ -1,139 +0,0 @@
|
|||
#import "iosopenvpn2ssadapter.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#import "sspacket.h"
|
||||
#import "ssadapterpacketflow.h"
|
||||
|
||||
@implementation ShadowSocksAdapterFlowBridge
|
||||
|
||||
# pragma mark - Sockets Configuration
|
||||
|
||||
static void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *obj) {
|
||||
if (type != kCFSocketDataCallBack) {
|
||||
return;
|
||||
}
|
||||
SSPacket *packet = [[SSPacket alloc] initWithSSData:data];
|
||||
ShadowSocksAdapterFlowBridge *bridge = (__bridge ShadowSocksAdapterFlowBridge*)obj;
|
||||
[bridge writePackets:@[packet] toPacketFlow:bridge.ssPacketFlow];
|
||||
}
|
||||
|
||||
- (BOOL)configureSocketWithError:(NSError * __autoreleasing *)error {
|
||||
int sockets[2];
|
||||
if (socketpair(PF_LOCAL, SOCK_DGRAM, IPPROTO_IP, sockets) == -1) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = @{
|
||||
// TODO: handle
|
||||
};
|
||||
*error = [NSError errorWithDomain:@"Some domain" code:100 userInfo:userInfo];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
CFSocketContext socketCtx = {0, (__bridge void *)self, NULL, NULL, NULL};
|
||||
_packetFlowSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[0], kCFSocketDataCallBack, SocketCallback, &socketCtx);
|
||||
_ssSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[1], kCFSocketNoCallBack, NULL, NULL);
|
||||
if (!(_packetFlowSocket && _ssSocket)) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = @{
|
||||
// TODO: handle
|
||||
};
|
||||
*error = [NSError errorWithDomain:@"Some domain" code:100 userInfo:userInfo];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
if (!([self configureOptionsForSocket:_packetFlowSocket error:error] && [self configureOptionsForSocket:_ssSocket error:error])) {
|
||||
return NO;
|
||||
}
|
||||
CFRunLoopSourceRef packetFlowSocketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _packetFlowSocket, 0);
|
||||
CFRunLoopAddSource(CFRunLoopGetMain(), packetFlowSocketSource, kCFRunLoopDefaultMode);
|
||||
CFRelease(packetFlowSocketSource);
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)invalidateSocketsIfNeeded {
|
||||
if (_ssSocket) {
|
||||
CFSocketInvalidate(_ssSocket);
|
||||
CFRelease(_ssSocket);
|
||||
_ssSocket = NULL;
|
||||
}
|
||||
if (_packetFlowSocket) {
|
||||
CFSocketInvalidate(_packetFlowSocket);
|
||||
CFRelease(_packetFlowSocket);
|
||||
_packetFlowSocket = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)processPackets {
|
||||
NSAssert(self.ssPacketFlow != nil, @"packetFlow property shouldn't be nil, set it before start reading packets.");
|
||||
__weak typeof(self) weakSelf = self;
|
||||
[self.ssPacketFlow readPacketsWithCompletionHandler:^(NSArray<NSData *> *packets, NSArray<NSNumber *> *protocols) {
|
||||
__strong typeof(self) self = weakSelf;
|
||||
[self writePackets:packets protocols:protocols toSocket:self.packetFlowSocket];
|
||||
[self processPackets];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[self invalidateSocketsIfNeeded];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
# pragma mark - Socket configuration
|
||||
- (BOOL)configureOptionsForSocket:(CFSocketRef)socket error:(NSError * __autoreleasing *)error {
|
||||
CFSocketNativeHandle socketHandle = CFSocketGetNative(socket);
|
||||
|
||||
int buf_value = 65536;
|
||||
socklen_t buf_len = sizeof(buf_value);
|
||||
|
||||
if (setsockopt(socketHandle, SOL_SOCKET, SO_RCVBUF, &buf_value, buf_len) == -1) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = @{
|
||||
// TODO: handle
|
||||
};
|
||||
*error = [NSError errorWithDomain:@"Some domain" code:100 userInfo:userInfo];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
if (setsockopt(socketHandle, SOL_SOCKET, SO_SNDBUF, &buf_value, buf_len) == -1) {
|
||||
if (error) {
|
||||
NSDictionary *userInfo = @{
|
||||
// TODO: handle
|
||||
};
|
||||
*error = [NSError errorWithDomain:@"Some domain" code:100 userInfo:userInfo];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
# pragma mark - Protocol methods
|
||||
- (void)writePackets:(NSArray<SSPacket *> *)packets toPacketFlow:(id<ShadowSocksAdapterPacketFlow>)packetFlow {
|
||||
NSAssert(self.ssPacketFlow != nil, @"packetFlow shouldn't be nil, check provided parameter before start writing packets.");
|
||||
|
||||
NSMutableArray<NSData *> *flowPackets = [[NSMutableArray alloc] init];
|
||||
NSMutableArray<NSNumber *> *protocols = [[NSMutableArray alloc] init];
|
||||
|
||||
[packets enumerateObjectsUsingBlock:^(SSPacket * _Nonnull packet, NSUInteger idx, BOOL * _Nonnull stop) {
|
||||
[flowPackets addObject:packet.ssPacketFlowData];
|
||||
[protocols addObject:packet.protocolFamily];
|
||||
}];
|
||||
|
||||
[packetFlow writePackets:flowPackets withProtocols:protocols];
|
||||
}
|
||||
|
||||
- (void)writePackets:(NSArray<NSData *> *)packets protocols:(NSArray<NSNumber *> *)protocols toSocket:(CFSocketRef)socket {
|
||||
if (socket == NULL) { return; }
|
||||
|
||||
[packets enumerateObjectsUsingBlock:^(NSData *data, NSUInteger idx, BOOL *stop) {
|
||||
NSNumber *protocolFamily = protocols[idx];
|
||||
SSPacket *packet = [[SSPacket alloc] initWithPacketFlowData:data protocolFamily:protocolFamily];
|
||||
|
||||
CFSocketSendData(socket, NULL, (CFDataRef)packet.vpnData, 0.05);
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,17 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef IOSUTILS_H
|
||||
#define IOSUTILS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
class IOSUtils final {
|
||||
public:
|
||||
static QString computerName();
|
||||
|
||||
static QString IAPReceipt();
|
||||
};
|
||||
|
||||
#endif // IOSUTILS_H
|
|
@ -1,63 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "iosutils.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QString>
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_IOS, "IOSUtils");
|
||||
}
|
||||
|
||||
// static
|
||||
QString IOSUtils::computerName() {
|
||||
NSString* name = [[UIDevice currentDevice] name];
|
||||
return QString::fromNSString(name);
|
||||
}
|
||||
|
||||
// static
|
||||
QString IOSUtils::IAPReceipt() {
|
||||
logger.debug() << "Retrieving IAP receipt";
|
||||
|
||||
NSURL* receiptURL = [[NSBundle mainBundle] appStoreReceiptURL];
|
||||
NSData* receipt = [NSData dataWithContentsOfURL:receiptURL];
|
||||
|
||||
// All the following is for debug only.
|
||||
NSString* path = [receiptURL path];
|
||||
Q_ASSERT(path);
|
||||
|
||||
logger.debug() << "Receipt URL:" << QString::fromNSString(path);
|
||||
|
||||
NSFileManager* fileManager = [NSFileManager defaultManager];
|
||||
Q_ASSERT(fileManager);
|
||||
|
||||
NSDictionary* fileAttributes = [fileManager attributesOfItemAtPath:path error:NULL];
|
||||
if (fileAttributes) {
|
||||
NSNumber* fileSize = [fileAttributes objectForKey:NSFileSize];
|
||||
if (fileSize) {
|
||||
logger.debug() << "File size:" << [fileSize unsignedLongLongValue];
|
||||
}
|
||||
|
||||
NSString* fileOwner = [fileAttributes objectForKey:NSFileOwnerAccountName];
|
||||
if (fileOwner) {
|
||||
logger.debug() << "Owner:" << QString::fromNSString(fileOwner);
|
||||
}
|
||||
|
||||
NSDate* fileModDate = [fileAttributes objectForKey:NSFileModificationDate];
|
||||
if (fileModDate) {
|
||||
logger.debug() << "Modification date:" << QDateTime::fromNSDate(fileModDate).toString();
|
||||
}
|
||||
}
|
||||
|
||||
if (!receipt) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
NSString* encodedReceipt = [receipt base64EncodedStringWithOptions:0];
|
||||
return QString::fromNSString(encodedReceipt);
|
||||
}
|
|
@ -1,313 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "ipaddress.h"
|
||||
#include "bigintipv6addr.h"
|
||||
|
||||
#include <QtMath>
|
||||
|
||||
namespace {
|
||||
|
||||
quint32 s_allIpV4Ones = static_cast<quint32>(qPow(2, 32) - 1);
|
||||
|
||||
BigIntIPv6Addr s_allIPv6Ones;
|
||||
bool s_ipv6Initialized = false;
|
||||
|
||||
void maybeInitialize() {
|
||||
if (s_ipv6Initialized) return;
|
||||
|
||||
s_ipv6Initialized = true;
|
||||
|
||||
Q_IPV6ADDR allOnes;
|
||||
memset((void*)&allOnes, static_cast<quint8>(qPow(2, 8) - 1), sizeof(allOnes));
|
||||
|
||||
s_allIPv6Ones = BigIntIPv6Addr(allOnes);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
IPAddress IPAddress::create(const QString& ip) {
|
||||
if (ip.contains("/")) {
|
||||
QPair<QHostAddress, int> p = QHostAddress::parseSubnet(ip);
|
||||
|
||||
if (p.first.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
if (p.second < 32) {
|
||||
return IPAddress(p.first, p.second);
|
||||
}
|
||||
return IPAddress(p.first);
|
||||
}
|
||||
|
||||
if (p.first.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
if (p.second < 128) {
|
||||
return IPAddress(p.first, p.second);
|
||||
}
|
||||
return IPAddress(p.first);
|
||||
}
|
||||
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
return IPAddress(QHostAddress(ip));
|
||||
}
|
||||
|
||||
IPAddress::IPAddress() {
|
||||
maybeInitialize();
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const IPAddress& other) {
|
||||
maybeInitialize();
|
||||
*this = other;
|
||||
}
|
||||
|
||||
IPAddress& IPAddress::operator=(const IPAddress& other) {
|
||||
if (this == &other) return *this;
|
||||
|
||||
m_address = other.m_address;
|
||||
m_prefixLength = other.m_prefixLength;
|
||||
m_netmask = other.m_netmask;
|
||||
m_hostmask = other.m_hostmask;
|
||||
m_broadcastAddress = other.m_broadcastAddress;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const QHostAddress& address)
|
||||
: m_address(address), m_broadcastAddress(address) {
|
||||
maybeInitialize();
|
||||
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
m_prefixLength = 32;
|
||||
m_netmask = QHostAddress(s_allIpV4Ones);
|
||||
m_hostmask = QHostAddress((quint32)(0));
|
||||
} else {
|
||||
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
m_prefixLength = 128;
|
||||
|
||||
m_netmask = QHostAddress(s_allIPv6Ones.value());
|
||||
|
||||
{
|
||||
Q_IPV6ADDR ipv6;
|
||||
memset((void*)&ipv6, 0, sizeof(ipv6));
|
||||
m_hostmask = QHostAddress(ipv6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress::IPAddress(const QHostAddress& address, int prefixLength)
|
||||
: m_address(address), m_prefixLength(prefixLength) {
|
||||
maybeInitialize();
|
||||
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
Q_ASSERT(prefixLength >= 0 && prefixLength <= 32);
|
||||
m_netmask = QHostAddress(s_allIpV4Ones ^ (s_allIpV4Ones >> prefixLength));
|
||||
m_hostmask = QHostAddress(m_netmask.toIPv4Address() ^ s_allIpV4Ones);
|
||||
m_broadcastAddress =
|
||||
QHostAddress(address.toIPv4Address() | m_hostmask.toIPv4Address());
|
||||
} else {
|
||||
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
Q_ASSERT(prefixLength >= 0 && prefixLength <= 128);
|
||||
|
||||
Q_IPV6ADDR netmask;
|
||||
{
|
||||
BigIntIPv6Addr tmp = (s_allIPv6Ones >> prefixLength);
|
||||
for (int i = 0; i < 16; ++i)
|
||||
netmask[i] = s_allIPv6Ones.value()[i] ^ tmp.value()[i];
|
||||
}
|
||||
m_netmask = QHostAddress(netmask);
|
||||
|
||||
{
|
||||
Q_IPV6ADDR tmp;
|
||||
for (int i = 0; i < 16; ++i)
|
||||
tmp[i] = netmask[i] ^ s_allIPv6Ones.value()[i];
|
||||
m_hostmask = QHostAddress(tmp);
|
||||
}
|
||||
|
||||
{
|
||||
Q_IPV6ADDR ipv6Address = address.toIPv6Address();
|
||||
Q_IPV6ADDR ipv6Hostname = m_hostmask.toIPv6Address();
|
||||
for (int i = 0; i < 16; ++i) ipv6Address[i] |= ipv6Hostname[i];
|
||||
m_broadcastAddress = QHostAddress(ipv6Address);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPAddress::~IPAddress() { }
|
||||
|
||||
QAbstractSocket::NetworkLayerProtocol IPAddress::type() const {
|
||||
return m_address.protocol();
|
||||
}
|
||||
|
||||
bool IPAddress::overlaps(const IPAddress& other) const {
|
||||
return other.contains(m_address) || other.contains(m_broadcastAddress) ||
|
||||
contains(other.m_address) || contains(other.m_broadcastAddress);
|
||||
}
|
||||
|
||||
bool IPAddress::contains(const QHostAddress& address) const {
|
||||
if (address.protocol() != m_address.protocol()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
return (m_address.toIPv4Address() <= address.toIPv4Address()) &&
|
||||
(address.toIPv4Address() <= m_broadcastAddress.toIPv4Address());
|
||||
}
|
||||
|
||||
Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
return (BigIntIPv6Addr(m_address.toIPv6Address()) <=
|
||||
BigIntIPv6Addr(address.toIPv6Address())) &&
|
||||
(BigIntIPv6Addr(address.toIPv6Address()) <=
|
||||
BigIntIPv6Addr(m_broadcastAddress.toIPv6Address()));
|
||||
}
|
||||
|
||||
bool IPAddress::operator==(const IPAddress& other) const {
|
||||
return m_address == other.m_address && m_netmask == other.m_netmask;
|
||||
}
|
||||
|
||||
bool IPAddress::subnetOf(const IPAddress& other) const {
|
||||
if (other.m_address.protocol() != m_address.protocol()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
return other.m_address.toIPv4Address() <= m_address.toIPv4Address() &&
|
||||
other.m_broadcastAddress.toIPv4Address() >=
|
||||
m_broadcastAddress.toIPv4Address();
|
||||
}
|
||||
|
||||
Q_ASSERT(m_address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
return BigIntIPv6Addr(other.m_address.toIPv6Address()) <=
|
||||
BigIntIPv6Addr(m_address.toIPv6Address()) &&
|
||||
BigIntIPv6Addr(other.m_broadcastAddress.toIPv6Address()) >=
|
||||
BigIntIPv6Addr(m_broadcastAddress.toIPv6Address());
|
||||
}
|
||||
|
||||
QList<IPAddress> IPAddress::subnets() const {
|
||||
QList<IPAddress> list;
|
||||
|
||||
if (m_address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
if (m_prefixLength == 32) {
|
||||
list.append(*this);
|
||||
return list;
|
||||
}
|
||||
|
||||
quint64 start = m_address.toIPv4Address();
|
||||
quint64 end = quint64(m_broadcastAddress.toIPv4Address()) + 1;
|
||||
quint64 step = ((quint64)m_hostmask.toIPv4Address() + 1) >> 1;
|
||||
|
||||
while (start < end) {
|
||||
int newPrefixLength = m_prefixLength + 1;
|
||||
if (newPrefixLength == 32) {
|
||||
list.append(IPAddress(QHostAddress(static_cast<quint32>(start))));
|
||||
} else {
|
||||
list.append(IPAddress(QHostAddress(static_cast<quint32>(start)),
|
||||
m_prefixLength + 1));
|
||||
}
|
||||
start += step;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
Q_ASSERT(m_address.protocol() == QAbstractSocket::IPv6Protocol);
|
||||
|
||||
if (m_prefixLength == 128) {
|
||||
list.append(*this);
|
||||
return list;
|
||||
}
|
||||
|
||||
BigInt start(17);
|
||||
{
|
||||
Q_IPV6ADDR addr = m_address.toIPv6Address();
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
start.setValueAt(addr[i], i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
BigInt end(17);
|
||||
{
|
||||
Q_IPV6ADDR addr = m_broadcastAddress.toIPv6Address();
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
end.setValueAt(addr[i], i + 1);
|
||||
}
|
||||
++end;
|
||||
}
|
||||
|
||||
BigInt step(17);
|
||||
{
|
||||
Q_IPV6ADDR addr = m_hostmask.toIPv6Address();
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
step.setValueAt(addr[i], i + 1);
|
||||
}
|
||||
step = (++step) >> 1;
|
||||
}
|
||||
|
||||
while (start < end) {
|
||||
int newPrefixLength = m_prefixLength + 1;
|
||||
Q_IPV6ADDR startIPv6;
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
startIPv6[i] = start.valueAt(i + 1);
|
||||
}
|
||||
|
||||
if (newPrefixLength == 128) {
|
||||
list.append(IPAddress(QHostAddress(startIPv6)));
|
||||
} else {
|
||||
list.append(IPAddress(QHostAddress(startIPv6), m_prefixLength + 1));
|
||||
}
|
||||
start += step;
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
// static
|
||||
QList<IPAddress> IPAddress::excludeAddresses(
|
||||
const QList<IPAddress>& sourceList, const QList<IPAddress>& excludeList) {
|
||||
QList<IPAddress> results = sourceList;
|
||||
|
||||
for (const IPAddress& exclude : excludeList) {
|
||||
QList<IPAddress> newResults;
|
||||
|
||||
for (const IPAddress& ip : results) {
|
||||
if (ip.overlaps(exclude)) {
|
||||
QList<IPAddress> range = ip.excludeAddresses(exclude);
|
||||
newResults.append(range);
|
||||
} else {
|
||||
newResults.append(ip);
|
||||
}
|
||||
}
|
||||
|
||||
results = newResults;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
QList<IPAddress> IPAddress::excludeAddresses(const IPAddress& ip) const {
|
||||
QList<IPAddress> sn = subnets();
|
||||
Q_ASSERT(sn.length() >= 2);
|
||||
|
||||
QList<IPAddress> result;
|
||||
while (sn[0] != ip && sn[1] != ip) {
|
||||
if (ip.subnetOf(sn[0])) {
|
||||
result.append(sn[1]);
|
||||
sn = sn[0].subnets();
|
||||
} else if (ip.subnetOf(sn[1])) {
|
||||
result.append(sn[0]);
|
||||
sn = sn[1].subnets();
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (sn[0] == ip) {
|
||||
result.append(sn[1]);
|
||||
} else if (sn[1] == ip) {
|
||||
result.append(sn[0]);
|
||||
} else {
|
||||
Q_ASSERT(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -3,23 +3,24 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "dnsutilsmacos.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <systemconfiguration/scdynamicstore.h>
|
||||
#include <systemconfiguration/scpreferences.h>
|
||||
|
||||
#include <QScopeGuard>
|
||||
|
||||
#include <systemconfiguration/scpreferences.h>
|
||||
#include <systemconfiguration/scdynamicstore.h>
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "DnsUtilsMacos");
|
||||
Logger logger("DnsUtilsMacos");
|
||||
}
|
||||
|
||||
DnsUtilsMacos::DnsUtilsMacos(QObject* parent) : DnsUtils(parent) {
|
||||
MVPN_COUNT_CTOR(DnsUtilsMacos);
|
||||
MZ_COUNT_CTOR(DnsUtilsMacos);
|
||||
|
||||
m_scStore = SCDynamicStoreCreate(kCFAllocatorSystemDefault,
|
||||
CFSTR("mozillavpn"), nullptr, nullptr);
|
||||
CFSTR("amneziavpn"), nullptr, nullptr);
|
||||
if (m_scStore == nullptr) {
|
||||
logger.error() << "Failed to create system configuration store ref";
|
||||
}
|
||||
|
@ -28,7 +29,7 @@ DnsUtilsMacos::DnsUtilsMacos(QObject* parent) : DnsUtils(parent) {
|
|||
}
|
||||
|
||||
DnsUtilsMacos::~DnsUtilsMacos() {
|
||||
MVPN_COUNT_DTOR(DnsUtilsMacos);
|
||||
MZ_COUNT_DTOR(DnsUtilsMacos);
|
||||
restoreResolvers();
|
||||
logger.debug() << "DnsUtilsMacos destroyed.";
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@
|
|||
#ifndef DNSUTILSMACOS_H
|
||||
#define DNSUTILSMACOS_H
|
||||
|
||||
#include "dnsutils.h"
|
||||
#include <systemconfiguration/scdynamicstore.h>
|
||||
#include <systemconfiguration/systemconfiguration.h>
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
#include <systemconfiguration/scdynamicstore.h>
|
||||
#include <systemconfiguration/systemconfiguration.h>
|
||||
#include "daemon/dnsutils.h"
|
||||
|
||||
class DnsUtilsMacos final : public DnsUtils {
|
||||
Q_OBJECT
|
||||
|
|
|
@ -3,13 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "iputilsmacos.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "macosdaemon.h"
|
||||
#include "daemon/wireguardutils.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QScopeGuard>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
@ -19,33 +12,33 @@
|
|||
#include <sys/ioctl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QScopeGuard>
|
||||
|
||||
#include "daemon/wireguardutils.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "macosdaemon.h"
|
||||
|
||||
constexpr uint32_t ETH_MTU = 1500;
|
||||
constexpr uint32_t WG_MTU_OVERHEAD = 80;
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "IPUtilsMacos");
|
||||
Logger logger("IPUtilsMacos");
|
||||
}
|
||||
|
||||
IPUtilsMacos::IPUtilsMacos(QObject* parent) : IPUtils(parent) {
|
||||
MVPN_COUNT_CTOR(IPUtilsMacos);
|
||||
MZ_COUNT_CTOR(IPUtilsMacos);
|
||||
logger.debug() << "IPUtilsMacos created.";
|
||||
}
|
||||
|
||||
IPUtilsMacos::~IPUtilsMacos() {
|
||||
MVPN_COUNT_DTOR(IPUtilsMacos);
|
||||
MZ_COUNT_DTOR(IPUtilsMacos);
|
||||
logger.debug() << "IPUtilsMacos destroyed.";
|
||||
}
|
||||
|
||||
bool IPUtilsMacos::addInterfaceIPs(const InterfaceConfig& config) {
|
||||
if (!addIP4AddressToDevice(config)) {
|
||||
return false;
|
||||
}
|
||||
if (config.m_ipv6Enabled) {
|
||||
if (!addIP6AddressToDevice(config)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return addIP4AddressToDevice(config) && addIP6AddressToDevice(config);
|
||||
}
|
||||
|
||||
bool IPUtilsMacos::setMTUAndUp(const InterfaceConfig& config) {
|
||||
|
@ -132,7 +125,7 @@ bool IPUtilsMacos::addIP4AddressToDevice(const InterfaceConfig& config) {
|
|||
// Set ifr to interface
|
||||
int ret = ioctl(sockfd, SIOCAIFADDR, &ifr);
|
||||
if (ret) {
|
||||
logger.error() << "Failed to set IPv4: " << deviceAddr
|
||||
logger.error() << "Failed to set IPv4: " << logger.sensitive(deviceAddr)
|
||||
<< "error:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
@ -172,7 +165,7 @@ bool IPUtilsMacos::addIP6AddressToDevice(const InterfaceConfig& config) {
|
|||
// Set ifr to interface
|
||||
int ret = ioctl(sockfd, SIOCAIFADDR_IN6, &ifr6);
|
||||
if (ret) {
|
||||
logger.error() << "Failed to set IPv6: " << deviceAddr
|
||||
logger.error() << "Failed to set IPv6: " << logger.sensitive(deviceAddr)
|
||||
<< "error:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
#ifndef IPUTILSMACOS_H
|
||||
#define IPUTILSMACOS_H
|
||||
|
||||
#include "daemon/iputils.h"
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "daemon/iputils.h"
|
||||
|
||||
class IPUtilsMacos final : public IPUtils {
|
||||
public:
|
||||
IPUtilsMacos(QObject* parent);
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "macosdaemon.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "wgquickprocess.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
|
@ -18,13 +15,16 @@
|
|||
#include <QTextStream>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "MacOSDaemon");
|
||||
Logger logger("MacOSDaemon");
|
||||
MacOSDaemon* s_daemon = nullptr;
|
||||
} // namespace
|
||||
|
||||
MacOSDaemon::MacOSDaemon() : Daemon(nullptr) {
|
||||
MVPN_COUNT_CTOR(MacOSDaemon);
|
||||
MZ_COUNT_CTOR(MacOSDaemon);
|
||||
|
||||
logger.debug() << "Daemon created";
|
||||
|
||||
|
@ -37,7 +37,7 @@ MacOSDaemon::MacOSDaemon() : Daemon(nullptr) {
|
|||
}
|
||||
|
||||
MacOSDaemon::~MacOSDaemon() {
|
||||
MVPN_COUNT_DTOR(MacOSDaemon);
|
||||
MZ_COUNT_DTOR(MacOSDaemon);
|
||||
|
||||
logger.debug() << "Daemon released";
|
||||
|
||||
|
@ -50,25 +50,3 @@ MacOSDaemon* MacOSDaemon::instance() {
|
|||
Q_ASSERT(s_daemon);
|
||||
return s_daemon;
|
||||
}
|
||||
|
||||
QByteArray MacOSDaemon::getStatus() {
|
||||
logger.debug() << "Status request";
|
||||
|
||||
bool connected = m_connections.contains(0);
|
||||
QJsonObject obj;
|
||||
obj.insert("type", "status");
|
||||
obj.insert("connected", connected);
|
||||
|
||||
if (connected) {
|
||||
const ConnectionState& state = m_connections.value(0).m_config;
|
||||
WireguardUtils::peerStatus status =
|
||||
m_wgutils->getPeerStatus(state.m_config.m_serverPublicKey);
|
||||
obj.insert("serverIpv4Gateway", state.m_config.m_serverIpv4Gateway);
|
||||
obj.insert("deviceIpv4Address", state.m_config.m_deviceIpv4Address);
|
||||
obj.insert("date", state.m_date.toString());
|
||||
obj.insert("txBytes", QJsonValue(status.txBytes));
|
||||
obj.insert("rxBytes", QJsonValue(status.rxBytes));
|
||||
}
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Compact);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#ifndef MACOSDAEMON_H
|
||||
#define MACOSDAEMON_H
|
||||
|
||||
#include "daemon.h"
|
||||
#include "daemon/daemon.h"
|
||||
#include "dnsutilsmacos.h"
|
||||
#include "iputilsmacos.h"
|
||||
#include "wireguardutilsmacos.h"
|
||||
|
@ -19,8 +19,6 @@ class MacOSDaemon final : public Daemon {
|
|||
|
||||
static MacOSDaemon* instance();
|
||||
|
||||
QByteArray getStatus() override;
|
||||
|
||||
protected:
|
||||
WireguardUtils* wgutils() const override { return m_wgutils; }
|
||||
bool supportDnsUtils() const override { return true; }
|
||||
|
|
|
@ -1,60 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "macosdaemonserver.h"
|
||||
#include "commandlineparser.h"
|
||||
#include "daemon/daemonlocalserver.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "macosdaemon.h"
|
||||
#include "mozillavpn.h"
|
||||
#include "signalhandler.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "MacOSDaemonServer");
|
||||
}
|
||||
|
||||
MacOSDaemonServer::MacOSDaemonServer(QObject* parent)
|
||||
: Command(parent, "macosdaemon", "Activate the macos daemon") {
|
||||
MVPN_COUNT_CTOR(MacOSDaemonServer);
|
||||
}
|
||||
|
||||
MacOSDaemonServer::~MacOSDaemonServer() { MVPN_COUNT_DTOR(MacOSDaemonServer); }
|
||||
|
||||
int MacOSDaemonServer::run(QStringList& tokens) {
|
||||
Q_ASSERT(!tokens.isEmpty());
|
||||
QString appName = tokens[0];
|
||||
|
||||
QCoreApplication app(CommandLineParser::argc(), CommandLineParser::argv());
|
||||
|
||||
QCoreApplication::setApplicationName("Mozilla VPN Daemon");
|
||||
QCoreApplication::setApplicationVersion(APP_VERSION);
|
||||
|
||||
if (tokens.length() > 1) {
|
||||
QList<CommandLineParser::Option*> options;
|
||||
return CommandLineParser::unknownOption(this, appName, tokens[1], options,
|
||||
false);
|
||||
}
|
||||
|
||||
MacOSDaemon daemon;
|
||||
|
||||
DaemonLocalServer server(qApp);
|
||||
if (!server.initialize()) {
|
||||
logger.error() << "Failed to initialize the server";
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Signal handling for a proper shutdown.
|
||||
SignalHandler sh;
|
||||
QObject::connect(&sh, &SignalHandler::quitRequested,
|
||||
[]() { MacOSDaemon::instance()->deactivate(); });
|
||||
QObject::connect(&sh, &SignalHandler::quitRequested, &app,
|
||||
&QCoreApplication::quit, Qt::QueuedConnection);
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
static Command::RegistrationProxy<MacOSDaemonServer> s_commandMacOSDaemon;
|
|
@ -1,18 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MACOSDAEMONSERVER_H
|
||||
#define MACOSDAEMONSERVER_H
|
||||
|
||||
#include "command.h"
|
||||
|
||||
class MacOSDaemonServer final : public Command {
|
||||
public:
|
||||
explicit MacOSDaemonServer(QObject* parent);
|
||||
~MacOSDaemonServer();
|
||||
|
||||
int run(QStringList& tokens) override;
|
||||
};
|
||||
|
||||
#endif // MACOSDAEMONSERVER_H
|
|
@ -3,15 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "macosroutemonitor.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <QHostAddress>
|
||||
#include <QScopeGuard>
|
||||
#include <QTimer>
|
||||
#include <QProcess>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
|
@ -24,24 +15,21 @@
|
|||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QProcess>
|
||||
#include <QScopeGuard>
|
||||
#include <QTimer>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "MacosRouteMonitor");
|
||||
|
||||
template <typename T>
|
||||
static T* sockaddr_cast(QByteArray& data) {
|
||||
const struct sockaddr* sa = (const struct sockaddr*)data.constData();
|
||||
Q_ASSERT(sa->sa_len <= data.length());
|
||||
if (data.length() >= (int)sizeof(T)) {
|
||||
return (T*)data.data();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Logger logger("MacosRouteMonitor");
|
||||
} // namespace
|
||||
|
||||
MacosRouteMonitor::MacosRouteMonitor(const QString& ifname, QObject* parent)
|
||||
: QObject(parent), m_ifname(ifname) {
|
||||
MVPN_COUNT_CTOR(MacosRouteMonitor);
|
||||
MZ_COUNT_CTOR(MacosRouteMonitor);
|
||||
logger.debug() << "MacosRouteMonitor created.";
|
||||
|
||||
m_rtsock = socket(PF_ROUTE, SOCK_RAW, 0);
|
||||
|
@ -50,96 +38,250 @@ MacosRouteMonitor::MacosRouteMonitor(const QString& ifname, QObject* parent)
|
|||
return;
|
||||
}
|
||||
|
||||
// Disable replies to our own messages.
|
||||
int off = 0;
|
||||
setsockopt(m_rtsock, SOL_SOCKET, SO_USELOOPBACK, &off, sizeof(off));
|
||||
m_ifindex = if_nametoindex(qPrintable(ifname));
|
||||
|
||||
m_notifier = new QSocketNotifier(m_rtsock, QSocketNotifier::Read, this);
|
||||
connect(m_notifier, &QSocketNotifier::activated, this,
|
||||
&MacosRouteMonitor::rtsockReady);
|
||||
|
||||
// Grab the default routes at startup.
|
||||
rtmFetchRoutes(AF_INET);
|
||||
rtmFetchRoutes(AF_INET6);
|
||||
}
|
||||
|
||||
MacosRouteMonitor::~MacosRouteMonitor() {
|
||||
MVPN_COUNT_DTOR(MacosRouteMonitor);
|
||||
MZ_COUNT_DTOR(MacosRouteMonitor);
|
||||
flushExclusionRoutes();
|
||||
if (m_rtsock >= 0) {
|
||||
close(m_rtsock);
|
||||
}
|
||||
logger.debug() << "MacosRouteMonitor destroyed.";
|
||||
}
|
||||
|
||||
void MacosRouteMonitor::handleRtmAdd(const struct rt_msghdr* rtm,
|
||||
const QByteArray& payload) {
|
||||
QList<QByteArray> addrlist = parseAddrList(payload);
|
||||
|
||||
// Ignore routing changes on the tunnel interfaces
|
||||
if ((rtm->rtm_addrs & RTA_DST) && (rtm->rtm_addrs & RTA_GATEWAY)) {
|
||||
if (m_ifname == addrToString(addrlist[1])) {
|
||||
return;
|
||||
}
|
||||
// Compare memory against zero.
|
||||
static int memcmpzero(const void* data, size_t len) {
|
||||
const quint8* ptr = static_cast<const quint8*>(data);
|
||||
while (len--) {
|
||||
if (*ptr++) return 1;
|
||||
}
|
||||
|
||||
QStringList list;
|
||||
for (auto addr : addrlist) {
|
||||
list.append(addrToString(addr));
|
||||
}
|
||||
logger.debug() << "Route added by" << rtm->rtm_pid
|
||||
<< QString("addrs(%1):").arg(rtm->rtm_addrs) << list.join(" ");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MacosRouteMonitor::handleRtmDelete(const struct rt_msghdr* rtm,
|
||||
const QByteArray& payload) {
|
||||
QList<QByteArray> addrlist = parseAddrList(payload);
|
||||
|
||||
// Ignore routing changes on the tunnel interfaces
|
||||
if ((rtm->rtm_addrs & RTA_DST) && (rtm->rtm_addrs & RTA_GATEWAY)) {
|
||||
if (m_ifname == addrToString(addrlist[1])) {
|
||||
return;
|
||||
}
|
||||
// Ignore routing changes on the tunnel interface.
|
||||
if (rtm->rtm_index == m_ifindex) {
|
||||
return;
|
||||
}
|
||||
|
||||
QStringList list;
|
||||
#ifdef MZ_DEBUG
|
||||
for (auto addr : addrlist) {
|
||||
list.append(addrToString(addr));
|
||||
}
|
||||
logger.debug() << "Route deleted by" << rtm->rtm_pid
|
||||
<< QString("addrs(%1):").arg(rtm->rtm_addrs) << list.join(" ");
|
||||
#endif
|
||||
char ifname[IF_NAMESIZE] = "null";
|
||||
if (rtm->rtm_index != 0) {
|
||||
if_indextoname(rtm->rtm_index, ifname);
|
||||
}
|
||||
logger.debug() << "Route deleted via" << ifname
|
||||
<< QString("addrs(%1):").arg(rtm->rtm_addrs, 0, 16)
|
||||
<< list.join(" ");
|
||||
|
||||
// We expect all useful routes to contain a destination, netmask and gateway.
|
||||
if (!(rtm->rtm_addrs & RTA_DST) || !(rtm->rtm_addrs & RTA_GATEWAY) ||
|
||||
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for a default route, which should have a netmask of zero.
|
||||
const struct sockaddr* sa =
|
||||
reinterpret_cast<const struct sockaddr*>(addrlist[2].constData());
|
||||
if (sa->sa_family == AF_INET) {
|
||||
struct sockaddr_in sin;
|
||||
Q_ASSERT(sa->sa_len <= sizeof(sin));
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
memcpy(&sin, sa, sa->sa_len);
|
||||
if (memcmpzero(&sin.sin_addr, sizeof(sin.sin_addr)) != 0) {
|
||||
return;
|
||||
}
|
||||
} else if (sa->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 sin6;
|
||||
Q_ASSERT(sa->sa_len <= sizeof(sin6));
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
memcpy(&sin6, sa, sa->sa_len);
|
||||
if (memcmpzero(&sin6.sin6_addr, sizeof(sin6.sin6_addr)) != 0) {
|
||||
return;
|
||||
}
|
||||
} else if (sa->sa_family != AF_UNSPEC) {
|
||||
// We have sometimes seen the default route reported with AF_UNSPEC.
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear the default gateway
|
||||
const struct sockaddr* dst =
|
||||
reinterpret_cast<const struct sockaddr*>(addrlist[0].constData());
|
||||
QAbstractSocket::NetworkLayerProtocol protocol;
|
||||
unsigned int plen;
|
||||
if (dst->sa_family == AF_INET) {
|
||||
m_defaultGatewayIpv4.clear();
|
||||
m_defaultIfindexIpv4 = 0;
|
||||
protocol = QAbstractSocket::IPv4Protocol;
|
||||
plen = 32;
|
||||
} else if (dst->sa_family == AF_INET6) {
|
||||
m_defaultGatewayIpv6.clear();
|
||||
m_defaultIfindexIpv6 = 0;
|
||||
protocol = QAbstractSocket::IPv6Protocol;
|
||||
plen = 128;
|
||||
}
|
||||
|
||||
logger.debug() << "Lost default route via" << ifname
|
||||
<< logger.sensitive(addrToString(addrlist[1]));
|
||||
for (const QHostAddress& addr : m_exclusionRoutes) {
|
||||
if (addr.protocol() == protocol) {
|
||||
logger.debug() << "Removing exclusion route to"
|
||||
<< logger.sensitive(addr.toString());
|
||||
rtmSendRoute(RTM_DELETE, addr, plen, rtm->rtm_index, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacosRouteMonitor::handleRtmChange(const struct rt_msghdr* rtm,
|
||||
void MacosRouteMonitor::handleRtmUpdate(const struct rt_msghdr* rtm,
|
||||
const QByteArray& payload) {
|
||||
QList<QByteArray> addrlist = parseAddrList(payload);
|
||||
int ifindex = rtm->rtm_index;
|
||||
char ifname[IF_NAMESIZE] = "null";
|
||||
|
||||
// Ignore routing changes on the tunnel interfaces
|
||||
if ((rtm->rtm_addrs & RTA_DST) && (rtm->rtm_addrs & RTA_GATEWAY)) {
|
||||
if (m_ifname == addrToString(addrlist[1])) {
|
||||
// We expect all useful routes to contain a destination, netmask and gateway.
|
||||
if (!(rtm->rtm_addrs & RTA_DST) || !(rtm->rtm_addrs & RTA_GATEWAY) ||
|
||||
!(rtm->rtm_addrs & RTA_NETMASK) || (addrlist.count() < 3)) {
|
||||
return;
|
||||
}
|
||||
// Ignore route changes that we caused, or routes on the tunnel interface.
|
||||
if (rtm->rtm_index == m_ifindex) {
|
||||
return;
|
||||
}
|
||||
if ((rtm->rtm_pid == getpid()) && (rtm->rtm_type != RTM_GET)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: If RTA_IFP is set, then we should get the interface index
|
||||
// from the address list instead of rtm_index.
|
||||
if (rtm->rtm_addrs & RTA_IFP) {
|
||||
int addridx = 0;
|
||||
for (int mask = 1; mask < RTA_IFP; mask <<= 1) {
|
||||
if (rtm->rtm_addrs & mask) {
|
||||
addridx++;
|
||||
}
|
||||
}
|
||||
if (addridx >= addrlist.count()) {
|
||||
return;
|
||||
}
|
||||
const char* sdl_data = addrlist[addridx].constData();
|
||||
const struct sockaddr_dl* sdl =
|
||||
reinterpret_cast<const struct sockaddr_dl*>(sdl_data);
|
||||
if (sdl->sdl_family == AF_LINK) {
|
||||
ifindex = sdl->sdl_index;
|
||||
}
|
||||
}
|
||||
|
||||
// Log relevant updates to the routing table.
|
||||
QStringList list;
|
||||
#ifdef MZ_DEBUG
|
||||
for (auto addr : addrlist) {
|
||||
list.append(addrToString(addr));
|
||||
}
|
||||
logger.debug() << "Route changed by" << rtm->rtm_pid
|
||||
<< QString("addrs(%1):").arg(rtm->rtm_addrs) << list.join(" ");
|
||||
#endif
|
||||
if_indextoname(ifindex, ifname);
|
||||
logger.debug() << "Route update via" << ifname
|
||||
<< QString("addrs(%1):").arg(rtm->rtm_addrs, 0, 16)
|
||||
<< list.join(" ");
|
||||
|
||||
// Check for a default route, which should have a netmask of zero.
|
||||
const struct sockaddr* sa =
|
||||
reinterpret_cast<const struct sockaddr*>(addrlist[2].constData());
|
||||
if (sa->sa_family == AF_INET) {
|
||||
struct sockaddr_in sin;
|
||||
Q_ASSERT(sa->sa_len <= sizeof(sin));
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
memcpy(&sin, sa, sa->sa_len);
|
||||
if (memcmpzero(&sin.sin_addr, sizeof(sin.sin_addr)) != 0) {
|
||||
return;
|
||||
}
|
||||
} else if (sa->sa_family == AF_INET6) {
|
||||
struct sockaddr_in6 sin6;
|
||||
Q_ASSERT(sa->sa_len <= sizeof(sin6));
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
memcpy(&sin6, sa, sa->sa_len);
|
||||
if (memcmpzero(&sin6.sin6_addr, sizeof(sin6.sin6_addr)) != 0) {
|
||||
return;
|
||||
}
|
||||
} else if (sa->sa_family != AF_UNSPEC) {
|
||||
// The default route sometimes sets a netmask of AF_UNSPEC.
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if this is the IPv4 or IPv6 default route.
|
||||
const struct sockaddr* dst =
|
||||
reinterpret_cast<const struct sockaddr*>(addrlist[0].constData());
|
||||
QAbstractSocket::NetworkLayerProtocol protocol;
|
||||
unsigned int plen;
|
||||
int rtm_type = RTM_ADD;
|
||||
if (dst->sa_family == AF_INET) {
|
||||
if (m_defaultIfindexIpv4 != 0) {
|
||||
rtm_type = RTM_CHANGE;
|
||||
}
|
||||
m_defaultGatewayIpv4 = addrlist[1];
|
||||
m_defaultIfindexIpv4 = ifindex;
|
||||
protocol = QAbstractSocket::IPv4Protocol;
|
||||
plen = 32;
|
||||
} else if (dst->sa_family == AF_INET6) {
|
||||
if (m_defaultIfindexIpv6 != 0) {
|
||||
rtm_type = RTM_CHANGE;
|
||||
}
|
||||
m_defaultGatewayIpv6 = addrlist[1];
|
||||
m_defaultIfindexIpv6 = ifindex;
|
||||
protocol = QAbstractSocket::IPv6Protocol;
|
||||
plen = 128;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the exclusion routes with the new default route.
|
||||
logger.debug() << "Updating default route via" << ifname
|
||||
<< addrToString(addrlist[1]);
|
||||
for (const QHostAddress& addr : m_exclusionRoutes) {
|
||||
if (addr.protocol() == protocol) {
|
||||
logger.debug() << "Updating exclusion route to"
|
||||
<< logger.sensitive(addr.toString());
|
||||
rtmSendRoute(rtm_type, addr, plen, ifindex, addrlist[1].constData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacosRouteMonitor::handleIfaceInfo(const struct if_msghdr* ifm,
|
||||
const QByteArray& payload) {
|
||||
QList<QByteArray> addrlist = parseAddrList(payload);
|
||||
QStringList list;
|
||||
|
||||
if (ifm->ifm_index != if_nametoindex(qPrintable(m_ifname))) {
|
||||
return;
|
||||
}
|
||||
m_ifflags = ifm->ifm_flags;
|
||||
|
||||
QStringList list;
|
||||
#ifdef MZ_DEBUG
|
||||
QList<QByteArray> addrlist = parseAddrList(payload);
|
||||
for (auto addr : addrlist) {
|
||||
list.append(addrToString(addr));
|
||||
}
|
||||
logger.debug() << "Interface " << ifm->ifm_index
|
||||
<< "changed flags:" << ifm->ifm_flags
|
||||
<< QString("addrs(%1):").arg(ifm->ifm_addrs) << list.join(" ");
|
||||
#else
|
||||
Q_UNUSED(payload);
|
||||
#endif
|
||||
logger.debug() << "Interface" << ifm->ifm_index
|
||||
<< "chagned flags:" << ifm->ifm_flags
|
||||
<< QString("addrs(%1):").arg(ifm->ifm_addrs, 0, 16)
|
||||
<< list.join(" ");
|
||||
}
|
||||
|
||||
void MacosRouteMonitor::rtsockReady() {
|
||||
|
@ -154,11 +296,13 @@ void MacosRouteMonitor::rtsockReady() {
|
|||
(struct rt_msghdr*)((char*)(_rtm_) + (_rtm_)->rtm_msglen)
|
||||
#endif
|
||||
|
||||
struct rt_msghdr* rtm = (struct rt_msghdr*)buf;
|
||||
struct rt_msghdr* end = (struct rt_msghdr*)(&buf[len]);
|
||||
struct rt_msghdr* rtm = reinterpret_cast<struct rt_msghdr*>(buf);
|
||||
struct rt_msghdr* end = reinterpret_cast<struct rt_msghdr*>(&buf[len]);
|
||||
while (rtm < end) {
|
||||
// Ensure the message fits within the buffer
|
||||
if (RTMSG_NEXT(rtm) > end) {
|
||||
logger.debug() << "Routing message overflowed with length"
|
||||
<< rtm->rtm_msglen;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -167,7 +311,7 @@ void MacosRouteMonitor::rtsockReady() {
|
|||
switch (rtm->rtm_type) {
|
||||
case RTM_ADD:
|
||||
message.remove(0, sizeof(struct rt_msghdr));
|
||||
handleRtmAdd(rtm, message);
|
||||
handleRtmUpdate(rtm, message);
|
||||
break;
|
||||
case RTM_DELETE:
|
||||
message.remove(0, sizeof(struct rt_msghdr));
|
||||
|
@ -175,14 +319,17 @@ void MacosRouteMonitor::rtsockReady() {
|
|||
break;
|
||||
case RTM_CHANGE:
|
||||
message.remove(0, sizeof(struct rt_msghdr));
|
||||
handleRtmChange(rtm, message);
|
||||
handleRtmUpdate(rtm, message);
|
||||
break;
|
||||
case RTM_GET:
|
||||
message.remove(0, sizeof(struct rt_msghdr));
|
||||
handleRtmUpdate(rtm, message);
|
||||
break;
|
||||
case RTM_IFINFO:
|
||||
message.remove(0, sizeof(struct if_msghdr));
|
||||
handleIfaceInfo((struct if_msghdr*)rtm, message);
|
||||
break;
|
||||
default:
|
||||
logger.debug() << "Unknown routing message:" << rtm->rtm_type;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -192,7 +339,7 @@ void MacosRouteMonitor::rtsockReady() {
|
|||
|
||||
void MacosRouteMonitor::rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen,
|
||||
int rtaddr, const void* sa) {
|
||||
size_t sa_len = ((struct sockaddr*)sa)->sa_len;
|
||||
size_t sa_len = static_cast<const struct sockaddr*>(sa)->sa_len;
|
||||
Q_ASSERT((rtm->rtm_addrs & rtaddr) == 0);
|
||||
if ((rtm->rtm_msglen + sa_len) > maxlen) {
|
||||
return;
|
||||
|
@ -206,18 +353,19 @@ void MacosRouteMonitor::rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen,
|
|||
}
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddressRange& prefix) {
|
||||
bool MacosRouteMonitor::rtmSendRoute(int action, const QHostAddress& prefix,
|
||||
unsigned int plen, unsigned int ifindex,
|
||||
const void* gateway) {
|
||||
constexpr size_t rtm_max_size = sizeof(struct rt_msghdr) +
|
||||
sizeof(struct sockaddr_in6) * 2 +
|
||||
sizeof(struct sockaddr_dl);
|
||||
char buf[rtm_max_size];
|
||||
struct rt_msghdr* rtm = (struct rt_msghdr*)buf;
|
||||
sizeof(struct sockaddr_storage);
|
||||
char buf[rtm_max_size] = {0};
|
||||
struct rt_msghdr* rtm = reinterpret_cast<struct rt_msghdr*>(buf);
|
||||
|
||||
memset(rtm, 0, rtm_max_size);
|
||||
rtm->rtm_msglen = sizeof(struct rt_msghdr);
|
||||
rtm->rtm_version = RTM_VERSION;
|
||||
rtm->rtm_type = action;
|
||||
rtm->rtm_index = if_nametoindex(qPrintable(m_ifname));
|
||||
rtm->rtm_index = ifindex;
|
||||
rtm->rtm_flags = RTF_STATIC | RTF_UP;
|
||||
rtm->rtm_addrs = 0;
|
||||
rtm->rtm_pid = 0;
|
||||
|
@ -227,9 +375,9 @@ bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddressRange& prefix) {
|
|||
memset(&rtm->rtm_rmx, 0, sizeof(rtm->rtm_rmx));
|
||||
|
||||
// Append RTA_DST
|
||||
if (prefix.type() == IPAddressRange::IPv6) {
|
||||
if (prefix.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
struct sockaddr_in6 sin6;
|
||||
Q_IPV6ADDR dst = QHostAddress(prefix.ipAddress()).toIPv6Address();
|
||||
Q_IPV6ADDR dst = prefix.toIPv6Address();
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_len = sizeof(sin6);
|
||||
|
@ -237,7 +385,7 @@ bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddressRange& prefix) {
|
|||
rtmAppendAddr(rtm, rtm_max_size, RTA_DST, &sin6);
|
||||
} else {
|
||||
struct sockaddr_in sin;
|
||||
quint32 dst = QHostAddress(prefix.ipAddress()).toIPv4Address();
|
||||
quint32 dst = prefix.toIPv4Address();
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_len = sizeof(sin);
|
||||
|
@ -246,23 +394,16 @@ bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddressRange& prefix) {
|
|||
}
|
||||
|
||||
// Append RTA_GATEWAY
|
||||
if (action != RTM_DELETE) {
|
||||
struct sockaddr_dl sdl;
|
||||
memset(&sdl, 0, sizeof(sdl));
|
||||
sdl.sdl_family = AF_LINK;
|
||||
sdl.sdl_len = offsetof(struct sockaddr_dl, sdl_data) + m_ifname.length();
|
||||
sdl.sdl_index = rtm->rtm_index;
|
||||
sdl.sdl_type = IFT_OTHER;
|
||||
sdl.sdl_nlen = m_ifname.length();
|
||||
sdl.sdl_alen = 0;
|
||||
sdl.sdl_slen = 0;
|
||||
memcpy(&sdl.sdl_data, qPrintable(m_ifname), sdl.sdl_nlen);
|
||||
rtmAppendAddr(rtm, rtm_max_size, RTA_GATEWAY, &sdl);
|
||||
if (gateway != nullptr) {
|
||||
int family = static_cast<const struct sockaddr*>(gateway)->sa_family;
|
||||
if ((family == AF_INET) || (family == AF_INET6)) {
|
||||
rtm->rtm_flags |= RTF_GATEWAY;
|
||||
}
|
||||
rtmAppendAddr(rtm, rtm_max_size, RTA_GATEWAY, gateway);
|
||||
}
|
||||
|
||||
// Append RTA_NETMASK
|
||||
int plen = prefix.range();
|
||||
if (prefix.type() == IPAddressRange::IPv6) {
|
||||
if (prefix.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
struct sockaddr_in6 sin6;
|
||||
memset(&sin6, 0, sizeof(sin6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
|
@ -272,7 +413,7 @@ bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddressRange& prefix) {
|
|||
sin6.sin6_addr.s6_addr[plen / 8] = 0xFF ^ (0xFF >> (plen % 8));
|
||||
}
|
||||
rtmAppendAddr(rtm, rtm_max_size, RTA_NETMASK, &sin6);
|
||||
} else if (prefix.type() == IPAddressRange::IPv4) {
|
||||
} else if (prefix.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
|
@ -299,12 +440,122 @@ bool MacosRouteMonitor::rtmSendRoute(int action, const IPAddressRange& prefix) {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::insertRoute(const IPAddressRange& prefix) {
|
||||
return rtmSendRoute(RTM_ADD, prefix);
|
||||
bool MacosRouteMonitor::rtmFetchRoutes(int family) {
|
||||
constexpr size_t rtm_max_size =
|
||||
sizeof(struct rt_msghdr) + sizeof(struct sockaddr_storage) * 2;
|
||||
char buf[rtm_max_size] = {0};
|
||||
struct rt_msghdr* rtm = reinterpret_cast<struct rt_msghdr*>(buf);
|
||||
|
||||
rtm->rtm_msglen = sizeof(struct rt_msghdr);
|
||||
rtm->rtm_version = RTM_VERSION;
|
||||
rtm->rtm_type = RTM_GET;
|
||||
rtm->rtm_flags = RTF_UP | RTF_GATEWAY;
|
||||
rtm->rtm_addrs = 0;
|
||||
rtm->rtm_pid = 0;
|
||||
rtm->rtm_seq = m_rtseq++;
|
||||
rtm->rtm_errno = 0;
|
||||
rtm->rtm_inits = 0;
|
||||
memset(&rtm->rtm_rmx, 0, sizeof(rtm->rtm_rmx));
|
||||
|
||||
if (family == AF_INET) {
|
||||
struct sockaddr_in sin;
|
||||
memset(&sin, 0, sizeof(struct sockaddr_in));
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_len = sizeof(struct sockaddr_in);
|
||||
rtmAppendAddr(rtm, rtm_max_size, RTA_DST, &sin);
|
||||
rtmAppendAddr(rtm, rtm_max_size, RTA_NETMASK, &sin);
|
||||
} else if (family == AF_INET6) {
|
||||
struct sockaddr_in6 sin6;
|
||||
memset(&sin6, 0, sizeof(struct sockaddr_in6));
|
||||
sin6.sin6_family = AF_INET6;
|
||||
sin6.sin6_len = sizeof(struct sockaddr_in6);
|
||||
rtmAppendAddr(rtm, rtm_max_size, RTA_DST, &sin6);
|
||||
rtmAppendAddr(rtm, rtm_max_size, RTA_NETMASK, &sin6);
|
||||
} else {
|
||||
logger.warning() << "Unsupported address family";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send the routing message into the kernel.
|
||||
int len = write(m_rtsock, rtm, rtm->rtm_msglen);
|
||||
if (len == rtm->rtm_msglen) {
|
||||
return true;
|
||||
}
|
||||
logger.warning() << "Failed to request routing table:" << strerror(errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::deleteRoute(const IPAddressRange& prefix) {
|
||||
return rtmSendRoute(RTM_DELETE, prefix);
|
||||
bool MacosRouteMonitor::insertRoute(const IPAddress& prefix) {
|
||||
struct sockaddr_dl datalink;
|
||||
memset(&datalink, 0, sizeof(datalink));
|
||||
datalink.sdl_family = AF_LINK;
|
||||
datalink.sdl_len = offsetof(struct sockaddr_dl, sdl_data) + m_ifname.length();
|
||||
datalink.sdl_index = m_ifindex;
|
||||
datalink.sdl_type = IFT_OTHER;
|
||||
datalink.sdl_nlen = m_ifname.length();
|
||||
datalink.sdl_alen = 0;
|
||||
datalink.sdl_slen = 0;
|
||||
memcpy(&datalink.sdl_data, qPrintable(m_ifname), datalink.sdl_nlen);
|
||||
|
||||
return rtmSendRoute(RTM_ADD, prefix.address(), prefix.prefixLength(),
|
||||
m_ifindex, &datalink);
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::deleteRoute(const IPAddress& prefix) {
|
||||
return rtmSendRoute(RTM_DELETE, prefix.address(), prefix.prefixLength(),
|
||||
m_ifindex, nullptr);
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::addExclusionRoute(const QHostAddress& address) {
|
||||
logger.debug() << "Adding exclusion route for"
|
||||
<< logger.sensitive(address.toString());
|
||||
|
||||
if (m_exclusionRoutes.contains(address)) {
|
||||
logger.warning() << "Exclusion route already exists";
|
||||
return false;
|
||||
}
|
||||
m_exclusionRoutes.append(address);
|
||||
|
||||
// If the default route is known, then updte the routing table immediately.
|
||||
if ((address.protocol() == QAbstractSocket::IPv4Protocol) &&
|
||||
(m_defaultIfindexIpv4 != 0) && !m_defaultGatewayIpv4.isEmpty()) {
|
||||
return rtmSendRoute(RTM_ADD, address, 32, m_defaultIfindexIpv4,
|
||||
m_defaultGatewayIpv4.constData());
|
||||
}
|
||||
if ((address.protocol() == QAbstractSocket::IPv6Protocol) &&
|
||||
(m_defaultIfindexIpv6 != 0) && !m_defaultGatewayIpv6.isEmpty()) {
|
||||
return rtmSendRoute(RTM_ADD, address, 128, m_defaultIfindexIpv6,
|
||||
m_defaultGatewayIpv6.constData());
|
||||
}
|
||||
|
||||
// Otherwise, the default route isn't known yet. Do nothing.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacosRouteMonitor::deleteExclusionRoute(const QHostAddress& address) {
|
||||
logger.debug() << "Deleting exclusion route for"
|
||||
<< logger.sensitive(address.toString());
|
||||
|
||||
m_exclusionRoutes.removeAll(address);
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
return rtmSendRoute(RTM_DELETE, address, 32, m_defaultIfindexIpv4, nullptr);
|
||||
} else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
return rtmSendRoute(RTM_DELETE, address, 128, m_defaultIfindexIpv6,
|
||||
nullptr);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MacosRouteMonitor::flushExclusionRoutes() {
|
||||
while (!m_exclusionRoutes.isEmpty()) {
|
||||
QHostAddress address = m_exclusionRoutes.takeFirst();
|
||||
if (address.protocol() == QAbstractSocket::IPv4Protocol) {
|
||||
rtmSendRoute(RTM_DELETE, address, 32, m_defaultIfindexIpv4, nullptr);
|
||||
} else if (address.protocol() == QAbstractSocket::IPv6Protocol) {
|
||||
rtmSendRoute(RTM_DELETE, address, 128, m_defaultIfindexIpv6, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
|
@ -341,7 +592,10 @@ QString MacosRouteMonitor::addrToString(const struct sockaddr* sa) {
|
|||
}
|
||||
if (sa->sa_family == AF_LINK) {
|
||||
const struct sockaddr_dl* sdl = (const struct sockaddr_dl*)sa;
|
||||
return QString(link_ntoa(sdl));
|
||||
return QString("link#%1:").arg(sdl->sdl_index) + QString(link_ntoa(sdl));
|
||||
}
|
||||
if (sa->sa_family == AF_UNSPEC) {
|
||||
return QString("unspec");
|
||||
}
|
||||
return QString("unknown(af=%1)").arg(sa->sa_family);
|
||||
}
|
||||
|
|
|
@ -5,13 +5,14 @@
|
|||
#ifndef MACOSROUTEMONITOR_H
|
||||
#define MACOSROUTEMONITOR_H
|
||||
|
||||
#include "ipaddressrange.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QHostAddress>
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include "ipaddress.h"
|
||||
|
||||
struct if_msghdr;
|
||||
struct rt_msghdr;
|
||||
struct sockaddr;
|
||||
|
@ -23,16 +24,21 @@ class MacosRouteMonitor final : public QObject {
|
|||
MacosRouteMonitor(const QString& ifname, QObject* parent = nullptr);
|
||||
~MacosRouteMonitor();
|
||||
|
||||
bool insertRoute(const IPAddressRange& prefix);
|
||||
bool deleteRoute(const IPAddressRange& prefix);
|
||||
bool insertRoute(const IPAddress& prefix);
|
||||
bool deleteRoute(const IPAddress& prefix);
|
||||
int interfaceFlags() { return m_ifflags; }
|
||||
|
||||
bool addExclusionRoute(const QHostAddress& address);
|
||||
bool deleteExclusionRoute(const QHostAddress& address);
|
||||
void flushExclusionRoutes();
|
||||
|
||||
private:
|
||||
void handleRtmAdd(const struct rt_msghdr* msg, const QByteArray& payload);
|
||||
void handleRtmDelete(const struct rt_msghdr* msg, const QByteArray& payload);
|
||||
void handleRtmChange(const struct rt_msghdr* msg, const QByteArray& payload);
|
||||
void handleRtmUpdate(const struct rt_msghdr* msg, const QByteArray& payload);
|
||||
void handleIfaceInfo(const struct if_msghdr* msg, const QByteArray& payload);
|
||||
bool rtmSendRoute(int action, const IPAddressRange& prefix);
|
||||
bool rtmSendRoute(int action, const QHostAddress& prefix, unsigned int plen,
|
||||
unsigned int ifindex, const void* gateway);
|
||||
bool rtmFetchRoutes(int family);
|
||||
static void rtmAppendAddr(struct rt_msghdr* rtm, size_t maxlen, int rtaddr,
|
||||
const void* sa);
|
||||
static QList<QByteArray> parseAddrList(const QByteArray& data);
|
||||
|
@ -44,7 +50,14 @@ class MacosRouteMonitor final : public QObject {
|
|||
static QString addrToString(const struct sockaddr* sa);
|
||||
static QString addrToString(const QByteArray& data);
|
||||
|
||||
QList<QHostAddress> m_exclusionRoutes;
|
||||
QByteArray m_defaultGatewayIpv4;
|
||||
QByteArray m_defaultGatewayIpv6;
|
||||
unsigned int m_defaultIfindexIpv4 = 0;
|
||||
unsigned int m_defaultIfindexIpv6 = 0;
|
||||
|
||||
QString m_ifname;
|
||||
unsigned int m_ifindex = 0;
|
||||
int m_ifflags = 0;
|
||||
int m_rtsock = -1;
|
||||
int m_rtseq = 0;
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "wireguardutilsmacos.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QDir>
|
||||
|
@ -12,19 +12,20 @@
|
|||
#include <QLocalSocket>
|
||||
#include <QTimer>
|
||||
|
||||
#include <errno.h>
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
constexpr const int WG_TUN_PROC_TIMEOUT = 5000;
|
||||
constexpr const char* WG_RUNTIME_DIR = "/var/run/wireguard";
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "WireguardUtilsMacos");
|
||||
Logger logwireguard(LOG_MACOS, "WireguardGo");
|
||||
Logger logger("WireguardUtilsMacos");
|
||||
Logger logwireguard("WireguardGo");
|
||||
}; // namespace
|
||||
|
||||
WireguardUtilsMacos::WireguardUtilsMacos(QObject* parent)
|
||||
: WireguardUtils(parent), m_tunnel(this) {
|
||||
MVPN_COUNT_CTOR(WireguardUtilsMacos);
|
||||
MZ_COUNT_CTOR(WireguardUtilsMacos);
|
||||
logger.debug() << "WireguardUtilsMacos created.";
|
||||
|
||||
connect(&m_tunnel, SIGNAL(readyReadStandardOutput()), this,
|
||||
|
@ -34,7 +35,7 @@ WireguardUtilsMacos::WireguardUtilsMacos(QObject* parent)
|
|||
}
|
||||
|
||||
WireguardUtilsMacos::~WireguardUtilsMacos() {
|
||||
MVPN_COUNT_DTOR(WireguardUtilsMacos);
|
||||
MZ_COUNT_DTOR(WireguardUtilsMacos);
|
||||
logger.debug() << "WireguardUtilsMacos destroyed.";
|
||||
}
|
||||
|
||||
|
@ -68,15 +69,12 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
|||
QProcessEnvironment pe = QProcessEnvironment::systemEnvironment();
|
||||
QString wgNameFile = wgRuntimeDir.filePath(QString(WG_INTERFACE) + ".name");
|
||||
pe.insert("WG_TUN_NAME_FILE", wgNameFile);
|
||||
#ifdef QT_DEBUG
|
||||
#ifdef MZ_DEBUG
|
||||
pe.insert("LOG_LEVEL", "debug");
|
||||
#endif
|
||||
m_tunnel.setProcessEnvironment(pe);
|
||||
|
||||
QDir appPath(QCoreApplication::applicationDirPath());
|
||||
appPath.cdUp();
|
||||
appPath.cd("Resources");
|
||||
appPath.cd("utils");
|
||||
QStringList wgArgs = {"-f", "utun"};
|
||||
m_tunnel.start(appPath.filePath("wireguard-go"), wgArgs);
|
||||
if (!m_tunnel.waitForStarted(WG_TUN_PROC_TIMEOUT)) {
|
||||
|
@ -99,6 +97,7 @@ bool WireguardUtilsMacos::addInterface(const InterfaceConfig& config) {
|
|||
// Send a UAPI command to configure the interface
|
||||
QString message("set=1\n");
|
||||
QByteArray privateKey = QByteArray::fromBase64(config.m_privateKey.toUtf8());
|
||||
|
||||
QTextStream out(&message);
|
||||
out << "private_key=" << QString(privateKey.toHex()) << "\n";
|
||||
out << "replace_peers=true\n";
|
||||
|
@ -134,14 +133,15 @@ bool WireguardUtilsMacos::deleteInterface() {
|
|||
|
||||
// dummy implementations for now
|
||||
bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
||||
QByteArray publicKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
QByteArray publicKey = QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
QByteArray pskKey = QByteArray::fromBase64(qPrintable(config.m_serverPskKey));
|
||||
|
||||
// Update/create the peer config
|
||||
QString message;
|
||||
QTextStream out(&message);
|
||||
out << "set=1\n";
|
||||
out << "public_key=" << QString(publicKey.toHex()) << "\n";
|
||||
out << "preshared_key=" << QString(pskKey.toHex()) << "\n";
|
||||
if (!config.m_serverIpv4AddrIn.isNull()) {
|
||||
out << "endpoint=" << config.m_serverIpv4AddrIn << ":";
|
||||
} else if (!config.m_serverIpv6AddrIn.isNull()) {
|
||||
|
@ -153,10 +153,12 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
|||
out << config.m_serverPort << "\n";
|
||||
|
||||
out << "replace_allowed_ips=true\n";
|
||||
for (const IPAddressRange& ip : config.m_allowedIPAddressRanges) {
|
||||
out << "persistent_keepalive_interval=" << WG_KEEPALIVE_PERIOD << "\n";
|
||||
for (const IPAddress& ip : config.m_allowedIPAddressRanges) {
|
||||
out << "allowed_ip=" << ip.toString() << "\n";
|
||||
}
|
||||
|
||||
logger.debug() << message;
|
||||
int err = uapiErrno(uapiCommand(message));
|
||||
if (err != 0) {
|
||||
logger.error() << "Peer configuration failed:" << strerror(err);
|
||||
|
@ -164,8 +166,9 @@ bool WireguardUtilsMacos::updatePeer(const InterfaceConfig& config) {
|
|||
return (err == 0);
|
||||
}
|
||||
|
||||
bool WireguardUtilsMacos::deletePeer(const QString& pubkey) {
|
||||
QByteArray publicKey = QByteArray::fromBase64(qPrintable(pubkey));
|
||||
bool WireguardUtilsMacos::deletePeer(const InterfaceConfig& config) {
|
||||
QByteArray publicKey =
|
||||
QByteArray::fromBase64(qPrintable(config.m_serverPublicKey));
|
||||
|
||||
QString message;
|
||||
QTextStream out(&message);
|
||||
|
@ -180,13 +183,10 @@ bool WireguardUtilsMacos::deletePeer(const QString& pubkey) {
|
|||
return (err == 0);
|
||||
}
|
||||
|
||||
WireguardUtils::peerStatus WireguardUtilsMacos::getPeerStatus(
|
||||
const QString& pubkey) {
|
||||
peerStatus status = {0, 0};
|
||||
QString hexkey = QByteArray::fromBase64(pubkey.toUtf8()).toHex();
|
||||
QList<WireguardUtils::PeerStatus> WireguardUtilsMacos::getPeerStatus() {
|
||||
QString reply = uapiCommand("get=1");
|
||||
bool match = false;
|
||||
|
||||
PeerStatus status;
|
||||
QList<PeerStatus> peerList;
|
||||
for (const QString& line : reply.split('\n')) {
|
||||
int eq = line.indexOf('=');
|
||||
if (eq <= 0) {
|
||||
|
@ -196,39 +196,90 @@ WireguardUtils::peerStatus WireguardUtilsMacos::getPeerStatus(
|
|||
QString value = line.mid(eq + 1);
|
||||
|
||||
if (name == "public_key") {
|
||||
match = (value == hexkey);
|
||||
continue;
|
||||
} else if (!match) {
|
||||
continue;
|
||||
if (!status.m_pubkey.isEmpty()) {
|
||||
peerList.append(status);
|
||||
}
|
||||
QByteArray pubkey = QByteArray::fromHex(value.toUtf8());
|
||||
status = PeerStatus(pubkey.toBase64());
|
||||
}
|
||||
|
||||
if (name == "tx_bytes") {
|
||||
status.txBytes = value.toDouble();
|
||||
status.m_txBytes = value.toDouble();
|
||||
}
|
||||
if (name == "rx_bytes") {
|
||||
status.rxBytes = value.toDouble();
|
||||
status.m_rxBytes = value.toDouble();
|
||||
}
|
||||
if (name == "last_handshake_time_sec") {
|
||||
status.m_handshake += value.toLongLong() * 1000;
|
||||
}
|
||||
if (name == "last_handshake_time_nsec") {
|
||||
status.m_handshake += value.toLongLong() / 1000000;
|
||||
}
|
||||
}
|
||||
if (!status.m_pubkey.isEmpty()) {
|
||||
peerList.append(status);
|
||||
}
|
||||
|
||||
return status;
|
||||
return peerList;
|
||||
}
|
||||
|
||||
bool WireguardUtilsMacos::updateRoutePrefix(const IPAddressRange& prefix,
|
||||
bool WireguardUtilsMacos::updateRoutePrefix(const IPAddress& prefix,
|
||||
int hopindex) {
|
||||
Q_UNUSED(hopindex);
|
||||
if (!m_rtmonitor) {
|
||||
return false;
|
||||
}
|
||||
return m_rtmonitor->insertRoute(prefix);
|
||||
if (prefix.prefixLength() > 0) {
|
||||
return m_rtmonitor->insertRoute(prefix);
|
||||
}
|
||||
|
||||
// Ensure that we do not replace the default route.
|
||||
if (prefix.type() == QAbstractSocket::IPv4Protocol) {
|
||||
return m_rtmonitor->insertRoute(IPAddress("0.0.0.0/1")) &&
|
||||
m_rtmonitor->insertRoute(IPAddress("128.0.0.0/1"));
|
||||
}
|
||||
if (prefix.type() == QAbstractSocket::IPv6Protocol) {
|
||||
return m_rtmonitor->insertRoute(IPAddress("::/1")) &&
|
||||
m_rtmonitor->insertRoute(IPAddress("8000::/1"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WireguardUtilsMacos::deleteRoutePrefix(const IPAddressRange& prefix,
|
||||
bool WireguardUtilsMacos::deleteRoutePrefix(const IPAddress& prefix,
|
||||
int hopindex) {
|
||||
Q_UNUSED(hopindex);
|
||||
if (!m_rtmonitor) {
|
||||
return false;
|
||||
}
|
||||
return m_rtmonitor->deleteRoute(prefix);
|
||||
if (prefix.prefixLength() > 0) {
|
||||
return m_rtmonitor->insertRoute(prefix);
|
||||
}
|
||||
|
||||
// Ensure that we do not replace the default route.
|
||||
if (prefix.type() == QAbstractSocket::IPv4Protocol) {
|
||||
return m_rtmonitor->deleteRoute(IPAddress("0.0.0.0/1")) &&
|
||||
m_rtmonitor->deleteRoute(IPAddress("128.0.0.0/1"));
|
||||
} else if (prefix.type() == QAbstractSocket::IPv6Protocol) {
|
||||
return m_rtmonitor->deleteRoute(IPAddress("::/1")) &&
|
||||
m_rtmonitor->deleteRoute(IPAddress("8000::/1"));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool WireguardUtilsMacos::addExclusionRoute(const QHostAddress& address) {
|
||||
if (!m_rtmonitor) {
|
||||
return false;
|
||||
}
|
||||
return m_rtmonitor->addExclusionRoute(address);
|
||||
}
|
||||
|
||||
bool WireguardUtilsMacos::deleteExclusionRoute(const QHostAddress& address) {
|
||||
if (!m_rtmonitor) {
|
||||
return false;
|
||||
}
|
||||
return m_rtmonitor->deleteExclusionRoute(address);
|
||||
}
|
||||
|
||||
QString WireguardUtilsMacos::uapiCommand(const QString& command) {
|
||||
|
|
|
@ -5,12 +5,12 @@
|
|||
#ifndef WIREGUARDUTILSMACOS_H
|
||||
#define WIREGUARDUTILSMACOS_H
|
||||
|
||||
#include "daemon/wireguardutils.h"
|
||||
#include "macosroutemonitor.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
|
||||
#include "daemon/wireguardutils.h"
|
||||
#include "macosroutemonitor.h"
|
||||
|
||||
class WireguardUtilsMacos final : public WireguardUtils {
|
||||
Q_OBJECT
|
||||
|
||||
|
@ -26,11 +26,14 @@ class WireguardUtilsMacos final : public WireguardUtils {
|
|||
bool deleteInterface() override;
|
||||
|
||||
bool updatePeer(const InterfaceConfig& config) override;
|
||||
bool deletePeer(const QString& pubkey) override;
|
||||
peerStatus getPeerStatus(const QString& pubkey) override;
|
||||
bool deletePeer(const InterfaceConfig& config) override;
|
||||
QList<PeerStatus> getPeerStatus() override;
|
||||
|
||||
bool updateRoutePrefix(const IPAddressRange& prefix, int hopindex) override;
|
||||
bool deleteRoutePrefix(const IPAddressRange& prefix, int hopindex) override;
|
||||
bool updateRoutePrefix(const IPAddress& prefix, int hopindex) override;
|
||||
bool deleteRoutePrefix(const IPAddress& prefix, int hopindex) override;
|
||||
|
||||
bool addExclusionRoute(const QHostAddress& address) override;
|
||||
bool deleteExclusionRoute(const QHostAddress& address) override;
|
||||
|
||||
signals:
|
||||
void backendFailure();
|
||||
|
|
|
@ -1,136 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "cryptosettings.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QRandomGenerator>
|
||||
|
||||
constexpr const NSString* SERVICE = @"Mozilla VPN";
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger({LOG_MACOS, LOG_MAIN}, "MacOSCryptoSettings");
|
||||
|
||||
bool initialized = false;
|
||||
QByteArray key;
|
||||
|
||||
} // anonymous
|
||||
|
||||
// static
|
||||
void CryptoSettings::resetKey() {
|
||||
logger.debug() << "Reset the key in the keychain";
|
||||
|
||||
NSData* service = [SERVICE dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSString* appId = [[NSBundle mainBundle] bundleIdentifier];
|
||||
|
||||
NSMutableDictionary* query = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
|
||||
[query setObject:service forKey:(id)kSecAttrGeneric];
|
||||
[query setObject:service forKey:(id)kSecAttrAccount];
|
||||
[query setObject:appId forKey:(id)kSecAttrService];
|
||||
|
||||
SecItemDelete((CFDictionaryRef)query);
|
||||
|
||||
[query release];
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool CryptoSettings::getKey(uint8_t output[CRYPTO_SETTINGS_KEY_SIZE]) {
|
||||
#if defined(MVPN_IOS) || defined(MVPN_MACOS_NETWORKEXTENSION) || defined(MVPN_MACOS_DAEMON)
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
logger.debug() << "Retrieving the key from the keychain";
|
||||
|
||||
NSData* service = [SERVICE dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSString* appId = [[NSBundle mainBundle] bundleIdentifier];
|
||||
NSMutableDictionary* query = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
|
||||
[query setObject:service forKey:(id)kSecAttrGeneric];
|
||||
[query setObject:service forKey:(id)kSecAttrAccount];
|
||||
[query setObject:appId forKey:(id)kSecAttrService];
|
||||
|
||||
[query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData];
|
||||
[query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit];
|
||||
|
||||
NSData* keyData = NULL;
|
||||
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)(void*)&keyData);
|
||||
[query release];
|
||||
|
||||
if (status == noErr) {
|
||||
key = QByteArray::fromNSData(keyData);
|
||||
|
||||
logger.debug() << "Key found with length:" << key.length();
|
||||
if (key.length() == CRYPTO_SETTINGS_KEY_SIZE) {
|
||||
memcpy(output, key.data(), CRYPTO_SETTINGS_KEY_SIZE);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
logger.warning() << "Key not found. Let's create it. Error:" << status;
|
||||
key = QByteArray(CRYPTO_SETTINGS_KEY_SIZE, 0x00);
|
||||
QRandomGenerator* rg = QRandomGenerator::system();
|
||||
for (int i = 0; i < CRYPTO_SETTINGS_KEY_SIZE; ++i) {
|
||||
key[i] = rg->generate() & 0xFF;
|
||||
}
|
||||
|
||||
query = [[NSMutableDictionary alloc] init];
|
||||
|
||||
[query setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass];
|
||||
[query setObject:service forKey:(id)kSecAttrGeneric];
|
||||
[query setObject:service forKey:(id)kSecAttrAccount];
|
||||
[query setObject:appId forKey:(id)kSecAttrService];
|
||||
|
||||
SecItemDelete((CFDictionaryRef)query);
|
||||
|
||||
keyData = key.toNSData();
|
||||
[query setObject:keyData forKey:(id)kSecValueData];
|
||||
|
||||
status = SecItemAdd((CFDictionaryRef)query, NULL);
|
||||
|
||||
if (status != noErr) {
|
||||
logger.error() << "Failed to store the key. Error:" << status;
|
||||
key = QByteArray();
|
||||
}
|
||||
|
||||
[query release];
|
||||
}
|
||||
|
||||
if (key.length() == CRYPTO_SETTINGS_KEY_SIZE) {
|
||||
memcpy(output, key.data(), CRYPTO_SETTINGS_KEY_SIZE);
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.error() << "Invalid key";
|
||||
#else
|
||||
Q_UNUSED(output);
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
CryptoSettings::Version CryptoSettings::getSupportedVersion() {
|
||||
logger.debug() << "Get supported settings method";
|
||||
|
||||
#if defined(MVPN_IOS) || defined(MVPN_MACOS_NETWORKEXTENSION) || defined(MVPN_MACOS_DAEMON)
|
||||
uint8_t key[CRYPTO_SETTINGS_KEY_SIZE];
|
||||
if (getKey(key)) {
|
||||
logger.debug() << "Encryption supported!";
|
||||
return CryptoSettings::EncryptionChachaPolyV1;
|
||||
}
|
||||
#endif
|
||||
|
||||
logger.debug() << "No encryption";
|
||||
return CryptoSettings::NoEncryption;
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "macosmenubar.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "mozillavpn.h"
|
||||
#include "qmlengineholder.h"
|
||||
#ifdef MVPN_MACOS
|
||||
# include "platforms/macos/macosutils.h"
|
||||
#endif
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QMenuBar>
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "MacOSManuBar");
|
||||
MacOSMenuBar* s_instance = nullptr;
|
||||
} // namespace
|
||||
|
||||
MacOSMenuBar::MacOSMenuBar() {
|
||||
MVPN_COUNT_CTOR(MacOSMenuBar);
|
||||
|
||||
Q_ASSERT(!s_instance);
|
||||
s_instance = this;
|
||||
}
|
||||
|
||||
MacOSMenuBar::~MacOSMenuBar() {
|
||||
MVPN_COUNT_DTOR(MacOSMenuBar);
|
||||
|
||||
Q_ASSERT(s_instance == this);
|
||||
s_instance = nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
MacOSMenuBar* MacOSMenuBar::instance() {
|
||||
Q_ASSERT(s_instance);
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
void MacOSMenuBar::initialize() {
|
||||
logger.debug() << "Creating menubar";
|
||||
|
||||
AmneziaVPN* vpn = AmneziaVPN::instance();
|
||||
|
||||
m_menuBar = new QMenuBar(nullptr);
|
||||
|
||||
//% "File"
|
||||
QMenu* fileMenu = m_menuBar->addMenu(qtTrId("menubar.file.title"));
|
||||
|
||||
// Do not use qtTrId here!
|
||||
QAction* quit =
|
||||
fileMenu->addAction("quit", vpn->controller(), &Controller::quit);
|
||||
quit->setMenuRole(QAction::QuitRole);
|
||||
|
||||
// Do not use qtTrId here!
|
||||
m_aboutAction =
|
||||
fileMenu->addAction("about.vpn", vpn, &AmneziaVPN::requestAbout);
|
||||
m_aboutAction->setMenuRole(QAction::AboutRole);
|
||||
m_aboutAction->setVisible(vpn->state() == AmneziaVPN::StateMain);
|
||||
|
||||
// Do not use qtTrId here!
|
||||
m_preferencesAction =
|
||||
fileMenu->addAction("preferences", vpn, &AmneziaVPN::requestSettings);
|
||||
m_preferencesAction->setMenuRole(QAction::PreferencesRole);
|
||||
m_preferencesAction->setVisible(vpn->state() == AmneziaVPN::StateMain);
|
||||
|
||||
m_closeAction = fileMenu->addAction("close", []() {
|
||||
QmlEngineHolder::instance()->hideWindow();
|
||||
#ifdef MVPN_MACOS
|
||||
MacOSUtils::hideDockIcon();
|
||||
#endif
|
||||
});
|
||||
m_closeAction->setShortcut(QKeySequence::Close);
|
||||
|
||||
m_helpMenu = m_menuBar->addMenu("");
|
||||
|
||||
retranslate();
|
||||
};
|
||||
|
||||
void MacOSMenuBar::controllerStateChanged() {
|
||||
AmneziaVPN* vpn = AmneziaVPN::instance();
|
||||
m_preferencesAction->setVisible(vpn->state() == AmneziaVPN::StateMain);
|
||||
m_aboutAction->setVisible(vpn->state() == AmneziaVPN::StateMain);
|
||||
}
|
||||
|
||||
void MacOSMenuBar::retranslate() {
|
||||
logger.debug() << "Retranslate";
|
||||
|
||||
//% "Close"
|
||||
m_closeAction->setText(qtTrId("menubar.file.close"));
|
||||
|
||||
//% "Help"
|
||||
m_helpMenu->setTitle(qtTrId("menubar.help.title"));
|
||||
for (QAction* action : m_helpMenu->actions()) {
|
||||
m_helpMenu->removeAction(action);
|
||||
}
|
||||
|
||||
AmneziaVPN* vpn = AmneziaVPN::instance();
|
||||
vpn->helpModel()->forEach([&](const char* nameId, int id) {
|
||||
m_helpMenu->addAction(qtTrId(nameId),
|
||||
[help = vpn->helpModel(), id]() { help->open(id); });
|
||||
});
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef MACOSMENUBAR_H
|
||||
#define MACOSMENUBAR_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class QMenuBar;
|
||||
|
||||
class MacOSMenuBar final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY_MOVE(MacOSMenuBar)
|
||||
|
||||
public:
|
||||
MacOSMenuBar();
|
||||
~MacOSMenuBar();
|
||||
|
||||
static MacOSMenuBar* instance();
|
||||
|
||||
void initialize();
|
||||
|
||||
void retranslate();
|
||||
|
||||
QMenuBar* menuBar() const { return m_menuBar; }
|
||||
|
||||
public slots:
|
||||
void controllerStateChanged();
|
||||
|
||||
private:
|
||||
QMenuBar* m_menuBar = nullptr;
|
||||
|
||||
QAction* m_aboutAction = nullptr;
|
||||
QAction* m_preferencesAction = nullptr;
|
||||
QAction* m_closeAction = nullptr;
|
||||
QMenu* m_helpMenu = nullptr;
|
||||
};
|
||||
|
||||
#endif // MACOSMENUBAR_H
|
|
@ -5,19 +5,24 @@
|
|||
#ifndef MACOSNETWORKWATCHER_H
|
||||
#define MACOSNETWORKWATCHER_H
|
||||
|
||||
#import <Network/Network.h>
|
||||
|
||||
#include "../ios/iosnetworkwatcher.h"
|
||||
#include "networkwatcherimpl.h"
|
||||
|
||||
class MacOSNetworkWatcher final : public NetworkWatcherImpl {
|
||||
class QString;
|
||||
|
||||
class MacOSNetworkWatcher final : public IOSNetworkWatcher {
|
||||
public:
|
||||
MacOSNetworkWatcher(QObject* parent);
|
||||
~MacOSNetworkWatcher();
|
||||
|
||||
void initialize() override;
|
||||
|
||||
void start() override;
|
||||
|
||||
void checkInterface();
|
||||
|
||||
void controllerStateChanged();
|
||||
|
||||
private:
|
||||
void* m_delegate = nullptr;
|
||||
};
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
#include "logger.h"
|
||||
|
||||
#import <CoreWLAN/CoreWLAN.h>
|
||||
#import <Network/Network.h>
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "MacOSNetworkWatcher");
|
||||
Logger logger("MacOSNetworkWatcher");
|
||||
}
|
||||
|
||||
@interface MacOSNetworkWatcherDelegate : NSObject <CWEventDelegate> {
|
||||
|
@ -37,13 +38,12 @@ Logger logger(LOG_MACOS, "MacOSNetworkWatcher");
|
|||
|
||||
@end
|
||||
|
||||
MacOSNetworkWatcher::MacOSNetworkWatcher(QObject* parent) : NetworkWatcherImpl(parent) {
|
||||
MVPN_COUNT_CTOR(MacOSNetworkWatcher);
|
||||
MacOSNetworkWatcher::MacOSNetworkWatcher(QObject* parent) : IOSNetworkWatcher(parent) {
|
||||
MZ_COUNT_CTOR(MacOSNetworkWatcher);
|
||||
}
|
||||
|
||||
MacOSNetworkWatcher::~MacOSNetworkWatcher() {
|
||||
MVPN_COUNT_DTOR(MacOSNetworkWatcher);
|
||||
|
||||
MZ_COUNT_DTOR(MacOSNetworkWatcher);
|
||||
if (m_delegate) {
|
||||
CWWiFiClient* client = CWWiFiClient.sharedWiFiClient;
|
||||
if (!client) {
|
||||
|
@ -57,10 +57,6 @@ MacOSNetworkWatcher::~MacOSNetworkWatcher() {
|
|||
}
|
||||
}
|
||||
|
||||
void MacOSNetworkWatcher::initialize() {
|
||||
// Nothing to do here
|
||||
}
|
||||
|
||||
void MacOSNetworkWatcher::start() {
|
||||
NetworkWatcherImpl::start();
|
||||
|
||||
|
@ -129,3 +125,4 @@ void MacOSNetworkWatcher::checkInterface() {
|
|||
|
||||
logger.debug() << "Secure WiFi interface";
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,6 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "macospingsender.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <QSocketNotifier>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
|
@ -14,19 +10,26 @@
|
|||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_icmp.h>
|
||||
#include <sys/errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <QSocketNotifier>
|
||||
#include <QtEndian>
|
||||
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
|
||||
namespace {
|
||||
|
||||
Logger logger({LOG_MACOS, LOG_NETWORKING}, "MacOSPingSender");
|
||||
Logger logger("MacOSPingSender");
|
||||
|
||||
int identifier() { return (getpid() & 0xFFFF); }
|
||||
|
||||
}; // namespace
|
||||
|
||||
MacOSPingSender::MacOSPingSender(const QString& source, QObject* parent)
|
||||
MacOSPingSender::MacOSPingSender(const QHostAddress& source, QObject* parent)
|
||||
: PingSender(parent) {
|
||||
MVPN_COUNT_CTOR(MacOSPingSender);
|
||||
MZ_COUNT_CTOR(MacOSPingSender);
|
||||
|
||||
if (getuid()) {
|
||||
m_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
|
||||
|
@ -38,15 +41,15 @@ MacOSPingSender::MacOSPingSender(const QString& source, QObject* parent)
|
|||
return;
|
||||
}
|
||||
|
||||
quint32 ipv4addr = INADDR_ANY;
|
||||
if (!source.isNull()) {
|
||||
ipv4addr = source.toIPv4Address();
|
||||
}
|
||||
struct sockaddr_in addr;
|
||||
bzero(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_len = sizeof(addr);
|
||||
|
||||
if (inet_aton(source.toLocal8Bit().constData(), &addr.sin_addr) == 0) {
|
||||
logger.error() << "source address error";
|
||||
return;
|
||||
}
|
||||
addr.sin_addr.s_addr = qToBigEndian<quint32>(ipv4addr);
|
||||
|
||||
if (bind(m_socket, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||
logger.error() << "bind error:" << strerror(errno);
|
||||
|
@ -59,22 +62,19 @@ MacOSPingSender::MacOSPingSender(const QString& source, QObject* parent)
|
|||
}
|
||||
|
||||
MacOSPingSender::~MacOSPingSender() {
|
||||
MVPN_COUNT_DTOR(MacOSPingSender);
|
||||
MZ_COUNT_DTOR(MacOSPingSender);
|
||||
if (m_socket >= 0) {
|
||||
close(m_socket);
|
||||
}
|
||||
}
|
||||
|
||||
void MacOSPingSender::sendPing(const QString& dest, quint16 sequence) {
|
||||
void MacOSPingSender::sendPing(const QHostAddress& dest, quint16 sequence) {
|
||||
quint32 ipv4dest = dest.toIPv4Address();
|
||||
struct sockaddr_in addr;
|
||||
bzero(&addr, sizeof(addr));
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_len = sizeof(addr);
|
||||
|
||||
if (inet_aton(dest.toLocal8Bit().constData(), &addr.sin_addr) == 0) {
|
||||
logger.error() << "DNS lookup failed";
|
||||
return;
|
||||
}
|
||||
addr.sin_addr.s_addr = qToBigEndian<quint32>(ipv4dest);
|
||||
|
||||
struct icmp packet;
|
||||
bzero(&packet, sizeof packet);
|
||||
|
@ -86,6 +86,7 @@ void MacOSPingSender::sendPing(const QString& dest, quint16 sequence) {
|
|||
if (sendto(m_socket, (char*)&packet, sizeof(packet), 0,
|
||||
(struct sockaddr*)&addr, sizeof(addr)) != sizeof(packet)) {
|
||||
logger.error() << "ping sending failed:" << strerror(errno);
|
||||
emit criticalPingError();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@ class MacOSPingSender final : public PingSender {
|
|||
Q_DISABLE_COPY_MOVE(MacOSPingSender)
|
||||
|
||||
public:
|
||||
MacOSPingSender(const QString& source, QObject* parent = nullptr);
|
||||
MacOSPingSender(const QHostAddress& source, QObject* parent = nullptr);
|
||||
~MacOSPingSender();
|
||||
|
||||
void sendPing(const QString& dest, quint16 sequence) override;
|
||||
void sendPing(const QHostAddress& dest, quint16 sequence) override;
|
||||
|
||||
private slots:
|
||||
void socketReady();
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "macosstartatbootwatcher.h"
|
||||
#include "leakdetector.h"
|
||||
#include "logger.h"
|
||||
#include "macosutils.h"
|
||||
|
||||
namespace {
|
||||
Logger logger(LOG_MACOS, "MacOSStartAtBootWatcher");
|
||||
}
|
||||
|
||||
MacOSStartAtBootWatcher::MacOSStartAtBootWatcher(bool startAtBoot) {
|
||||
MVPN_COUNT_CTOR(MacOSStartAtBootWatcher);
|
||||
|
||||
logger.debug() << "StartAtBoot watcher";
|
||||
MacOSUtils::enableLoginItem(startAtBoot);
|
||||
}
|
||||
|
||||
MacOSStartAtBootWatcher::~MacOSStartAtBootWatcher() {
|
||||
MVPN_COUNT_DTOR(MacOSStartAtBootWatcher);
|
||||
}
|
||||
|
||||
void MacOSStartAtBootWatcher::startAtBootChanged(bool startAtBoot) {
|
||||
logger.debug() << "StartAtBoot changed:" << startAtBoot;
|
||||
MacOSUtils::enableLoginItem(startAtBoot);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue