Merge branch 'dev' of github.com:amnezia-vpn/amnezia-client into feature/new-gui

This commit is contained in:
vladimir.kuznetsov 2023-07-18 05:36:17 +03:00
commit 5b8a0881b7
180 changed files with 5446 additions and 3661 deletions

3
.gitmodules vendored
View file

@ -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/

View file

@ -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.1
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

@ -0,0 +1 @@
Subproject commit 052af4a8072bbbd3bfe7edf46fe3c1b350f71f08

View file

@ -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.
@ -24,7 +21,10 @@ set(PACKAGES
Core5Compat Concurrent LinguistTools
)
if(IOS)
set(PACKAGES ${PACKAGES} Multimedia)
set(PACKAGES
${PACKAGES}
Multimedia
)
endif()
find_package(Qt6 REQUIRED COMPONENTS ${PACKAGES})
@ -37,10 +37,25 @@ set(LIBS ${LIBS}
)
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)
@ -57,6 +72,7 @@ if(IS_CI)
endif()
endif()
include(${CMAKE_CURRENT_LIST_DIR}/cmake/3rdparty.cmake)
include_directories(
@ -77,8 +93,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
@ -89,6 +105,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
@ -104,7 +133,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
@ -113,6 +141,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
@ -158,11 +198,7 @@ set(SOURCES ${SOURCES}
${UI_CONTROLLERS_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
@ -213,40 +249,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()
@ -281,8 +286,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}
@ -303,180 +306,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)
@ -531,11 +366,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)
@ -569,6 +400,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})

View file

@ -16,6 +16,7 @@
#include "platforms/ios/QRCodeReaderBase.h"
#include "ui/pages.h"
#include "protocols/qml_register_protocols.h"
#if defined(Q_OS_IOS)
#include "platforms/ios/QtAppDelegate-C-Interface.h"

View file

@ -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()

View file

@ -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"

View file

@ -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("")
}

View file

@ -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

View file

@ -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)

157
client/cmake/ios.cmake Normal file
View file

@ -0,0 +1,157 @@
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/AmneziaVPNLaunchScreen.storyboard
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/Media.xcassets
)
set_property(TARGET ${PROJECT} APPEND PROPERTY RESOURCE
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/AmneziaVPNLaunchScreen.storyboard
${CMAKE_CURRENT_SOURCE_DIR}/ios/app/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
View 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/
)

View file

@ -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();

View file

@ -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;

View file

@ -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,

View file

@ -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

View file

@ -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;

View file

@ -53,7 +53,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);
@ -78,7 +78,7 @@ 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);
@ -87,7 +87,7 @@ ErrorCode ServerController::runScript(const ServerCredentials &credentials, QStr
}
}
qDebug() << "ServerController::runScript finished\n";
qDebug().noquote() << "ServerController::runScript finished\n";
return ErrorCode::NoError;
}
@ -194,11 +194,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());
}
@ -215,10 +210,7 @@ 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");
error = m_sshClient.sftpFileCopy(overwriteMode, localFile.fileName().toStdString(), remotePath.toStdString(), "non_desc");
if (error != ErrorCode::NoError) {
return error;
}
@ -241,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);
@ -425,11 +416,6 @@ ErrorCode ServerController::runContainerWorker(const ServerCredentials &credenti
genVarsForScript(credentials, container, config)),
cbReadStdOut);
qDebug() << "cbReadStdOut: " << stdOut;
if (stdOut.contains("docker: Error response from daemon"))
return ErrorCode::ServerDockerFailedError;
if (stdOut.contains("address already in use"))
return ErrorCode::ServerPortAlreadyAllocatedError;
if (stdOut.contains("is already in use by container"))
@ -492,7 +478,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;
@ -635,10 +620,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;
}

View file

@ -153,8 +153,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
View 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
View 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

View 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
}

View 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

View 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");
}

View 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
View 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

View 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
View 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

View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View file

@ -1,62 +0,0 @@
{
"images" : [
{
"filename" : "icon_40x40.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "icon_60x60.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "icon_58x58.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon_87x87.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "icon_80x80.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon_120x120.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "icon_120x120-1.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "icon_180x180.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "icon_1024x1024.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View file

@ -1,116 +0,0 @@
{
"images" : [
{
"filename" : "icon-ios-20@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "icon-ios-20@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "icon-ios-29@2x-1.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon-ios-29@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "icon-ios-40@2x-1.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon-ios-40@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "icon-ios-60@2x.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "icon-ios-60@3x.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "icon-ios-20@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "icon-ios-20@2x-1.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "icon-ios-29@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "icon-ios-29@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "icon-ios-40@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "icon-ios-40@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "icon-ios-76@1x.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "icon-ios-76@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "icon-ios-83.5@2x.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"filename" : "icon-ios-1024@1x.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 596 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 744 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -1,6 +0,0 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 767 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View file

@ -0,0 +1,290 @@
{
"images" : [
{
"filename" : "40.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "60.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"filename" : "29.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "58.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "87.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"filename" : "80.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "120.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"filename" : "57.png",
"idiom" : "iphone",
"scale" : "1x",
"size" : "57x57"
},
{
"filename" : "114.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "57x57"
},
{
"filename" : "120.png",
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"filename" : "180.png",
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"filename" : "20.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"filename" : "40.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"filename" : "29.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"filename" : "58.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"filename" : "40.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"filename" : "80.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"filename" : "50.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "50x50"
},
{
"filename" : "100.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "50x50"
},
{
"filename" : "72.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "72x72"
},
{
"filename" : "144.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "72x72"
},
{
"filename" : "76.png",
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"filename" : "152.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"filename" : "167.png",
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"filename" : "1024.png",
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "24x24",
"subtype" : "38mm"
},
{
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "27.5x27.5",
"subtype" : "42mm"
},
{
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "watch",
"role" : "companionSettings",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "watch",
"role" : "notificationCenter",
"scale" : "2x",
"size" : "33x33",
"subtype" : "45mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "40x40",
"subtype" : "38mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "44x44",
"subtype" : "40mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "46x46",
"subtype" : "41mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "50x50",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "51x51",
"subtype" : "45mm"
},
{
"idiom" : "watch",
"role" : "appLauncher",
"scale" : "2x",
"size" : "54x54",
"subtype" : "49mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "86x86",
"subtype" : "38mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "98x98",
"subtype" : "42mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "108x108",
"subtype" : "44mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "117x117",
"subtype" : "45mm"
},
{
"idiom" : "watch",
"role" : "quickLook",
"scale" : "2x",
"size" : "129x129",
"subtype" : "49mm"
},
{
"idiom" : "watch-marketing",
"scale" : "1x",
"size" : "1024x1024"
},
{
"idiom" : "car",
"scale" : "2x",
"size" : "60"
},
{
"idiom" : "car",
"scale" : "3x",
"size" : "60"
},
{
"idiom" : "watch",
"role" : "longLook",
"scale" : "2x",
"size" : "44x44",
"subtype" : "42mm"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 634 KiB

After

Width:  |  Height:  |  Size: 583 KiB

Before After
Before After

View file

@ -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;
}

View file

@ -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

View 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

View 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));
}
}

View 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

View 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();
}

View 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

View 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;
}

View 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

View 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);
}

View 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

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