diff --git a/.gitignore b/.gitignore index 598965e1..471df65c 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,29 @@ ui_*.h Makefile* *build-* +# Qt-es +client/.xcode/ +client/.qmake.cache +client/.qmake.stash +client/*.pro.user +client/*.pro.user.* +client/*.qbs.user +client/*.qbs.user.* +client/*.moc +client/moc_*.cpp +client/qrc_*.cpp +client/ui_*.h +client/ui_*.cpp +client/Makefile* +client/*build-* +client/AmneziaVPN.xcodeproj +client/Debug-iphonesimulator +client/amneziavpn_plugin_import.cpp +client/amneziavpn_qml_plugin_import.cpp +client/qmlcache_loader.cpp +client/rep_ipc_interface_replica.h +client/resources_qmlcache.qrc + # QtCreator *.autosave @@ -37,6 +60,7 @@ CMakeLists.txt.user* # MACOS files .DS_Store +client/.DS_Store ._.DS_Store ._* *.dmg diff --git a/client/3rd/OpenSSL/include/openssl/applink.c b/client/3rd/OpenSSL/include/openssl/applink.c index 238dbff3..903e6d47 100644 --- a/client/3rd/OpenSSL/include/openssl/applink.c +++ b/client/3rd/OpenSSL/include/openssl/applink.c @@ -1,4 +1,4 @@ -/* + /* * Copyright 2004-2016 The OpenSSL Project Authors. All Rights Reserved. * * Licensed under the OpenSSL license (the "License"). You may not use diff --git a/client/3rd/QtSsh/src/botan/botan.pri b/client/3rd/QtSsh/src/botan/botan.pri index 3ef7a602..6568e154 100644 --- a/client/3rd/QtSsh/src/botan/botan.pri +++ b/client/3rd/QtSsh/src/botan/botan.pri @@ -57,10 +57,16 @@ ios: { ARCH_TAG = "ios_armv7" } } + + CONFIG(iphonesimulator, iphoneos|iphonesimulator) { + INCLUDEPATH += $$PWD/ios/iphone + HEADERS += $$PWD/ios/iphone/botan_all.h + SOURCES += $$PWD/ios/iphone/botan_all.cpp + } - CONFIG(iphonesimulator, iphoneos|iphonesimulator) { - INCLUDEPATH += $$PWD/ios/simulator - HEADERS += $$PWD/ios/simulator/botan_all.h - SOURCES += $$PWD/ios/simulator/botan_all.cpp - } +# CONFIG(iphonesimulator, iphoneos|iphonesimulator) { +# INCLUDEPATH += $$PWD/ios/simulator +# HEADERS += $$PWD/ios/simulator/botan_all.h +# SOURCES += $$PWD/ios/simulator/botan_all.cpp +# } } diff --git a/client/AmneziaVPN-Swift.h b/client/AmneziaVPN-Swift.h new file mode 100644 index 00000000..35e3a6cc --- /dev/null +++ b/client/AmneziaVPN-Swift.h @@ -0,0 +1,247 @@ +#ifndef AmneziaVPN_Swift_h +#define AmneziaVPN_Swift_h +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgcc-compat" + +#if !defined(__has_include) +# define __has_include(x) 0 +#endif +#if !defined(__has_attribute) +# define __has_attribute(x) 0 +#endif +#if !defined(__has_feature) +# define __has_feature(x) 0 +#endif +#if !defined(__has_warning) +# define __has_warning(x) 0 +#endif + +#if __has_include() +# include +#endif + +#pragma clang diagnostic ignored "-Wauto-import" +#include +#include +#include +#include + +#if !defined(SWIFT_TYPEDEFS) +# define SWIFT_TYPEDEFS 1 +# if __has_include() +# include +# elif !defined(__cplusplus) +typedef uint_least16_t char16_t; +typedef uint_least32_t char32_t; +# endif +typedef float swift_float2 __attribute__((__ext_vector_type__(2))); +typedef float swift_float3 __attribute__((__ext_vector_type__(3))); +typedef float swift_float4 __attribute__((__ext_vector_type__(4))); +typedef double swift_double2 __attribute__((__ext_vector_type__(2))); +typedef double swift_double3 __attribute__((__ext_vector_type__(3))); +typedef double swift_double4 __attribute__((__ext_vector_type__(4))); +typedef int swift_int2 __attribute__((__ext_vector_type__(2))); +typedef int swift_int3 __attribute__((__ext_vector_type__(3))); +typedef int swift_int4 __attribute__((__ext_vector_type__(4))); +typedef unsigned int swift_uint2 __attribute__((__ext_vector_type__(2))); +typedef unsigned int swift_uint3 __attribute__((__ext_vector_type__(3))); +typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); +#endif + +#if !defined(SWIFT_PASTE) +# define SWIFT_PASTE_HELPER(x, y) x##y +# define SWIFT_PASTE(x, y) SWIFT_PASTE_HELPER(x, y) +#endif +#if !defined(SWIFT_METATYPE) +# define SWIFT_METATYPE(X) Class +#endif +#if !defined(SWIFT_CLASS_PROPERTY) +# if __has_feature(objc_class_property) +# define SWIFT_CLASS_PROPERTY(...) __VA_ARGS__ +# else +# define SWIFT_CLASS_PROPERTY(...) +# endif +#endif + +#if __has_attribute(objc_runtime_name) +# define SWIFT_RUNTIME_NAME(X) __attribute__((objc_runtime_name(X))) +#else +# define SWIFT_RUNTIME_NAME(X) +#endif +#if __has_attribute(swift_name) +# define SWIFT_COMPILE_NAME(X) __attribute__((swift_name(X))) +#else +# define SWIFT_COMPILE_NAME(X) +#endif +#if __has_attribute(objc_method_family) +# define SWIFT_METHOD_FAMILY(X) __attribute__((objc_method_family(X))) +#else +# define SWIFT_METHOD_FAMILY(X) +#endif +#if __has_attribute(noescape) +# define SWIFT_NOESCAPE __attribute__((noescape)) +#else +# define SWIFT_NOESCAPE +#endif +#if __has_attribute(ns_consumed) +# define SWIFT_RELEASES_ARGUMENT __attribute__((ns_consumed)) +#else +# define SWIFT_RELEASES_ARGUMENT +#endif +#if __has_attribute(warn_unused_result) +# define SWIFT_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else +# define SWIFT_WARN_UNUSED_RESULT +#endif +#if __has_attribute(noreturn) +# define SWIFT_NORETURN __attribute__((noreturn)) +#else +# define SWIFT_NORETURN +#endif +#if !defined(SWIFT_CLASS_EXTRA) +# define SWIFT_CLASS_EXTRA +#endif +#if !defined(SWIFT_PROTOCOL_EXTRA) +# define SWIFT_PROTOCOL_EXTRA +#endif +#if !defined(SWIFT_ENUM_EXTRA) +# define SWIFT_ENUM_EXTRA +#endif +#if !defined(SWIFT_CLASS) +# if __has_attribute(objc_subclassing_restricted) +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_subclassing_restricted)) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# else +# define SWIFT_CLASS(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# define SWIFT_CLASS_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_CLASS_EXTRA +# endif +#endif +#if !defined(SWIFT_RESILIENT_CLASS) +# if __has_attribute(objc_class_stub) +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) __attribute__((objc_class_stub)) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) __attribute__((objc_class_stub)) SWIFT_CLASS_NAMED(SWIFT_NAME) +# else +# define SWIFT_RESILIENT_CLASS(SWIFT_NAME) SWIFT_CLASS(SWIFT_NAME) +# define SWIFT_RESILIENT_CLASS_NAMED(SWIFT_NAME) SWIFT_CLASS_NAMED(SWIFT_NAME) +# endif +#endif + +#if !defined(SWIFT_PROTOCOL) +# define SWIFT_PROTOCOL(SWIFT_NAME) SWIFT_RUNTIME_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +# define SWIFT_PROTOCOL_NAMED(SWIFT_NAME) SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_PROTOCOL_EXTRA +#endif + +#if !defined(SWIFT_EXTENSION) +# define SWIFT_EXTENSION(M) SWIFT_PASTE(M##_Swift_, __LINE__) +#endif + +#if !defined(OBJC_DESIGNATED_INITIALIZER) +# if __has_attribute(objc_designated_initializer) +# define OBJC_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) +# else +# define OBJC_DESIGNATED_INITIALIZER +# endif +#endif +#if !defined(SWIFT_ENUM_ATTR) +# if defined(__has_attribute) && __has_attribute(enum_extensibility) +# define SWIFT_ENUM_ATTR(_extensibility) __attribute__((enum_extensibility(_extensibility))) +# else +# define SWIFT_ENUM_ATTR(_extensibility) +# endif +#endif +#if !defined(SWIFT_ENUM) +# define SWIFT_ENUM(_type, _name, _extensibility) enum _name : _type _name; enum SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# if __has_feature(generalized_swift_name) +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) enum _name : _type _name SWIFT_COMPILE_NAME(SWIFT_NAME); enum SWIFT_COMPILE_NAME(SWIFT_NAME) SWIFT_ENUM_ATTR(_extensibility) SWIFT_ENUM_EXTRA _name : _type +# else +# define SWIFT_ENUM_NAMED(_type, _name, SWIFT_NAME, _extensibility) SWIFT_ENUM(_type, _name, _extensibility) +# endif +#endif +#if !defined(SWIFT_UNAVAILABLE) +# define SWIFT_UNAVAILABLE __attribute__((unavailable)) +#endif +#if !defined(SWIFT_UNAVAILABLE_MSG) +# define SWIFT_UNAVAILABLE_MSG(msg) __attribute__((unavailable(msg))) +#endif +#if !defined(SWIFT_AVAILABILITY) +# define SWIFT_AVAILABILITY(plat, ...) __attribute__((availability(plat, __VA_ARGS__))) +#endif +#if !defined(SWIFT_WEAK_IMPORT) +# define SWIFT_WEAK_IMPORT __attribute__((weak_import)) +#endif +#if !defined(SWIFT_DEPRECATED) +# define SWIFT_DEPRECATED __attribute__((deprecated)) +#endif +#if !defined(SWIFT_DEPRECATED_MSG) +# define SWIFT_DEPRECATED_MSG(...) __attribute__((deprecated(__VA_ARGS__))) +#endif +#if __has_feature(attribute_diagnose_if_objc) +# define SWIFT_DEPRECATED_OBJC(Msg) __attribute__((diagnose_if(1, Msg, "warning"))) +#else +# define SWIFT_DEPRECATED_OBJC(Msg) SWIFT_DEPRECATED_MSG(Msg) +#endif +#if !defined(IBSegueAction) +# define IBSegueAction +#endif +#if __has_feature(modules) +#if __has_warning("-Watimport-in-framework-header") +#pragma clang diagnostic ignored "-Watimport-in-framework-header" +#endif +@import Foundation; +@import ObjectiveC; +#endif + +#pragma clang diagnostic ignored "-Wproperty-attribute-mismatch" +#pragma clang diagnostic ignored "-Wduplicate-method-arg" +#if __has_warning("-Wpragma-clang-attribute") +# pragma clang diagnostic ignored "-Wpragma-clang-attribute" +#endif +#pragma clang diagnostic ignored "-Wunknown-pragmas" +#pragma clang diagnostic ignored "-Wnullability" + +#if __has_attribute(external_source_symbol) +# pragma push_macro("any") +# undef any +# pragma clang attribute push(__attribute__((external_source_symbol(language="Swift", defined_in="AmneziaVPN",generated_declaration))), apply_to=any(function,enum,objc_interface,objc_category,objc_protocol)) +# pragma pop_macro("any") +#endif + + +@class NSString; +@class NSData; +enum ConnectionState : NSInteger; +@class NSDate; +@class NSNumber; +@class VPNIPAddressRange; + +SWIFT_CLASS("_TtC10AmneziaVPN18IOSVpnProtocolImpl") +@interface IOSVpnProtocolImpl : NSObject +- (nonnull instancetype)initWithBundleID:(NSString * _Nonnull)bundleID privateKey:(NSData * _Nonnull)privateKey deviceIpv4Address:(NSString * _Nonnull)deviceIpv4Address deviceIpv6Address:(NSString * _Nonnull)deviceIpv6Address closure:(void (^ _Nonnull)(enum ConnectionState, NSDate * _Nullable))closure callback:(void (^ _Nonnull)(BOOL))callback OBJC_DESIGNATED_INITIALIZER; +- (void)connectWithDnsServer:(NSString * _Nonnull)dnsServer serverIpv6Gateway:(NSString * _Nonnull)serverIpv6Gateway serverPublicKey:(NSString * _Nonnull)serverPublicKey serverIpv4AddrIn:(NSString * _Nonnull)serverIpv4AddrIn serverPort:(NSInteger)serverPort allowedIPAddressRanges:(NSArray * _Nonnull)allowedIPAddressRanges ipv6Enabled:(Boolean)enabled reason:(NSInteger)reason failureCallback:(void (^ _Nonnull)(void))failureCallback; +- (void)disconnect; +- (void)checkStatusWithCallback:(void (^ _Nonnull)(NSString * _Nonnull, NSString * _Nonnull, NSString * _Nonnull))callback; +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +typedef SWIFT_ENUM(NSInteger, ConnectionState, closed) { + ConnectionStateError = 0, + ConnectionStateConnected = 1, + ConnectionStateDisconnected = 2, +}; + + + +SWIFT_CLASS("_TtC10AmneziaVPN17VPNIPAddressRange") +@interface VPNIPAddressRange : NSObject +- (nonnull instancetype)initWithAddress:(NSString * _Nonnull)address networkPrefixLength:(uint8_t)networkPrefixLength isIpv6:(BOOL)isIpv6 OBJC_DESIGNATED_INITIALIZER; +- (nonnull instancetype)init SWIFT_UNAVAILABLE; ++ (nonnull instancetype)new SWIFT_UNAVAILABLE_MSG("-init is unavailable"); +@end + +#if __has_attribute(external_source_symbol) +# pragma clang attribute pop +#endif +#pragma clang diagnostic pop +#endif /* AmneziaVPN_Swift_h */ + diff --git a/client/AmneziaVPNRelease.entitlements b/client/AmneziaVPNRelease.entitlements new file mode 100644 index 00000000..5b8bd14b --- /dev/null +++ b/client/AmneziaVPNRelease.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.security.application-groups + + com.apple.security.files.user-selected.read-write + + + diff --git a/client/Debug-iphoneos/AmneziaVPN.app/AmneziaVPN b/client/Debug-iphoneos/AmneziaVPN.app/AmneziaVPN new file mode 100755 index 00000000..aca8d3f5 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/AmneziaVPN differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/Default-568h@2x.png b/client/Debug-iphoneos/AmneziaVPN.app/Default-568h@2x.png new file mode 100644 index 00000000..c881559d Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/Default-568h@2x.png differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/Info.plist b/client/Debug-iphoneos/AmneziaVPN.app/Info.plist new file mode 100644 index 00000000..89c9d5cb Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/Info.plist differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib b/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib new file mode 100644 index 00000000..ae258d83 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/Info.plist b/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/Info.plist new file mode 100644 index 00000000..32288e88 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/Info.plist differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib b/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib new file mode 100644 index 00000000..1b57c9a2 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/PkgInfo b/client/Debug-iphoneos/AmneziaVPN.app/PkgInfo new file mode 100644 index 00000000..bd04210f --- /dev/null +++ b/client/Debug-iphoneos/AmneziaVPN.app/PkgInfo @@ -0,0 +1 @@ +APPL???? \ No newline at end of file diff --git a/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/Info.plist b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/Info.plist new file mode 100644 index 00000000..1136c605 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/Info.plist differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/WireGuardNetworkExtension b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/WireGuardNetworkExtension new file mode 100755 index 00000000..268f441d Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/WireGuardNetworkExtension differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources new file mode 100644 index 00000000..0e303638 --- /dev/null +++ b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources @@ -0,0 +1,113 @@ + + + + + files + + Info.plist + + Vpf+GoPF6i8Z3silvQ81FJOPi2k= + + embedded.mobileprovision + + XsqCdj+fjmJRIQlbMFY/hQ8MMvA= + + + files2 + + embedded.mobileprovision + + hash2 + + MIAxFA1iZa5ZIg95xlnuoq1pWj1eqsV143zU3y/fs/w= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/embedded.mobileprovision b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/embedded.mobileprovision new file mode 100644 index 00000000..924a843d Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/PlugIns/WireGuardNetworkExtension.appex/embedded.mobileprovision differ diff --git a/client/Debug-iphoneos/AmneziaVPN.app/_CodeSignature/CodeResources b/client/Debug-iphoneos/AmneziaVPN.app/_CodeSignature/CodeResources new file mode 100644 index 00000000..7a607059 --- /dev/null +++ b/client/Debug-iphoneos/AmneziaVPN.app/_CodeSignature/CodeResources @@ -0,0 +1,205 @@ + + + + + files + + Default-568h@2x.png + + poDYGy1JWXBgPH3/wCEBNbVTOks= + + Info.plist + + TCqIXlhIQW7A69Ve4j1KetGfmt4= + + LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib + + QDw1rEcub/rLRhQ/kjizyHomgLM= + + LaunchScreen.storyboardc/Info.plist + + n2t8gsDpfE6XkhG31p7IQJRxTxU= + + LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib + + U0x8EM0781fCPb62BbZIXKeDlZ4= + + PkgInfo + + n57qDP4tZfLD1rCS43W0B4LQjzE= + + PlugIns/WireGuardNetworkExtension.appex/Info.plist + + Vpf+GoPF6i8Z3silvQ81FJOPi2k= + + PlugIns/WireGuardNetworkExtension.appex/WireGuardNetworkExtension + + lXak+Jrx39GLG9y+yV7abfizjj0= + + PlugIns/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources + + ghJ0YXx7j96o3qv/XzbZ1TssY4s= + + PlugIns/WireGuardNetworkExtension.appex/embedded.mobileprovision + + XsqCdj+fjmJRIQlbMFY/hQ8MMvA= + + embedded.mobileprovision + + CN/zNwvfKR8n8CTJE8vTbS8nhJY= + + + files2 + + Default-568h@2x.png + + hash2 + + UdHegDh1HZlDpSEYtzMl4sjA9q8uvK4K9OL0sRTFD6M= + + + LaunchScreen.storyboardc/01J-lp-oVM-view-Ze5-6b-2t3.nib + + hash2 + + dHm6NLKlFVMgSTmcNjdaMKOOuFZ5X4PUOOFDTUOOV1I= + + + LaunchScreen.storyboardc/Info.plist + + hash2 + + HyVdXMU7Ux4/KalAao30mpWOK/lEPT4gvYN09wf31cg= + + + LaunchScreen.storyboardc/UIViewController-01J-lp-oVM.nib + + hash2 + + cWq7q+iQcmpSoaFGecnyPdKFEPFwRD3slgUmxFADkVs= + + + PlugIns/WireGuardNetworkExtension.appex/Info.plist + + hash2 + + zGanBzJi5ruG2C96kDY8yR9v246oh2ZT26KMjE3t2Gs= + + + PlugIns/WireGuardNetworkExtension.appex/WireGuardNetworkExtension + + hash2 + + RrENvr9JToqfolIOWfhiXxXt3JyQZ9A7vr2MxwspB/g= + + + PlugIns/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources + + hash2 + + 0oir90Tb6hylweTKthB0OaNQrpXqvEdSW72QdH0u48w= + + + PlugIns/WireGuardNetworkExtension.appex/embedded.mobileprovision + + hash2 + + MIAxFA1iZa5ZIg95xlnuoq1pWj1eqsV143zU3y/fs/w= + + + embedded.mobileprovision + + hash2 + + phvsugRYPb6ozM0Feuw5oIla/PgY84jsyntA+R4HqGU= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/client/Debug-iphoneos/AmneziaVPN.app/embedded.mobileprovision b/client/Debug-iphoneos/AmneziaVPN.app/embedded.mobileprovision new file mode 100644 index 00000000..255a2e34 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.app/embedded.mobileprovision differ diff --git a/client/Debug-iphoneos/AmneziaVPN.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo new file mode 100644 index 00000000..bf60055f Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo differ diff --git a/client/Debug-iphoneos/AmneziaVPN.swiftmodule/Project/arm64.swiftsourceinfo b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/Project/arm64.swiftsourceinfo new file mode 100644 index 00000000..bf60055f Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/Project/arm64.swiftsourceinfo differ diff --git a/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64-apple-ios.swiftdoc b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64-apple-ios.swiftdoc new file mode 100644 index 00000000..bcb53543 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64-apple-ios.swiftdoc differ diff --git a/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64-apple-ios.swiftmodule b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64-apple-ios.swiftmodule new file mode 100644 index 00000000..33169bf1 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64-apple-ios.swiftmodule differ diff --git a/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64.swiftdoc b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64.swiftdoc new file mode 100644 index 00000000..bcb53543 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64.swiftdoc differ diff --git a/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64.swiftmodule b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64.swiftmodule new file mode 100644 index 00000000..33169bf1 Binary files /dev/null and b/client/Debug-iphoneos/AmneziaVPN.swiftmodule/arm64.swiftmodule differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.appex/Info.plist b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/Info.plist new file mode 100644 index 00000000..1136c605 Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/Info.plist differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.appex/WireGuardNetworkExtension b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/WireGuardNetworkExtension new file mode 100755 index 00000000..268f441d Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/WireGuardNetworkExtension differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources new file mode 100644 index 00000000..0e303638 --- /dev/null +++ b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/_CodeSignature/CodeResources @@ -0,0 +1,113 @@ + + + + + files + + Info.plist + + Vpf+GoPF6i8Z3silvQ81FJOPi2k= + + embedded.mobileprovision + + XsqCdj+fjmJRIQlbMFY/hQ8MMvA= + + + files2 + + embedded.mobileprovision + + hash2 + + MIAxFA1iZa5ZIg95xlnuoq1pWj1eqsV143zU3y/fs/w= + + + + rules + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^version.plist$ + + + rules2 + + .*\.dSYM($|/) + + weight + 11 + + ^(.*/)?\.DS_Store$ + + omit + + weight + 2000 + + ^.* + + ^.*\.lproj/ + + optional + + weight + 1000 + + ^.*\.lproj/locversion.plist$ + + omit + + weight + 1100 + + ^Base\.lproj/ + + weight + 1010 + + ^Info\.plist$ + + omit + + weight + 20 + + ^PkgInfo$ + + omit + + weight + 20 + + ^embedded\.provisionprofile$ + + weight + 20 + + ^version\.plist$ + + weight + 20 + + + + diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.appex/embedded.mobileprovision b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/embedded.mobileprovision new file mode 100644 index 00000000..924a843d Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.appex/embedded.mobileprovision differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo new file mode 100644 index 00000000..af867111 Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/Project/arm64-apple-ios.swiftsourceinfo differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/Project/arm64.swiftsourceinfo b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/Project/arm64.swiftsourceinfo new file mode 100644 index 00000000..af867111 Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/Project/arm64.swiftsourceinfo differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64-apple-ios.swiftdoc b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64-apple-ios.swiftdoc new file mode 100644 index 00000000..9c88b83b Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64-apple-ios.swiftdoc differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64-apple-ios.swiftmodule b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64-apple-ios.swiftmodule new file mode 100644 index 00000000..f7721c51 Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64-apple-ios.swiftmodule differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64.swiftdoc b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64.swiftdoc new file mode 100644 index 00000000..9c88b83b Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64.swiftdoc differ diff --git a/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64.swiftmodule b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64.swiftmodule new file mode 100644 index 00000000..f7721c51 Binary files /dev/null and b/client/Debug-iphoneos/WireGuardNetworkExtension.swiftmodule/arm64.swiftmodule differ diff --git a/client/Debug-iphoneos/libwg-go.a b/client/Debug-iphoneos/libwg-go.a new file mode 100644 index 00000000..06e5bd93 Binary files /dev/null and b/client/Debug-iphoneos/libwg-go.a differ diff --git a/client/Info.plist b/client/Info.plist new file mode 100644 index 00000000..03ba3e82 --- /dev/null +++ b/client/Info.plist @@ -0,0 +1,39 @@ + + + + + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + ${ASSETCATALOG_COMPILER_APPICON_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${QMAKE_SHORT_VERSION} + CFBundleSignature + ${QMAKE_PKGINFO_TYPEINFO} + CFBundleVersion + ${QMAKE_FULL_VERSION} + LSRequiresIPhoneOS + + MinimumOSVersion + ${IPHONEOS_DEPLOYMENT_TARGET} + NOTE + This file was generated by Qt/QMake. + UILaunchStoryboardName + LaunchScreen + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/client/WireGuard-Bridging-Header.h b/client/WireGuard-Bridging-Header.h new file mode 100644 index 00000000..40b6c89d --- /dev/null +++ b/client/WireGuard-Bridging-Header.h @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "wireguard-go-version.h" +#include "3rd/wireguard-apple/Sources/WireGuardKitC/WireGuardKitC.h" + +#include +#include + +#define WG_KEY_LEN (32) +#define WG_KEY_LEN_BASE64 (45) +#define WG_KEY_LEN_HEX (65) + +void key_to_base64(char base64[WG_KEY_LEN_BASE64], + const uint8_t key[WG_KEY_LEN]); +bool key_from_base64(uint8_t key[WG_KEY_LEN], const char* base64); + +void key_to_hex(char hex[WG_KEY_LEN_HEX], const uint8_t key[WG_KEY_LEN]); +bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex); + +bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]); + +void write_msg_to_log(const char* tag, const char* msg); + +#import "TargetConditionals.h" +#if TARGET_OS_OSX +# include +#endif diff --git a/client/client.pro b/client/client.pro index 5ed6a15c..d4f5a70c 100644 --- a/client/client.pro +++ b/client/client.pro @@ -260,29 +260,20 @@ ios { DEFINES += MVPN_IOS - SOURCES += \ -# platforms/macos/macospingsender.cpp - - OBJECTIVE_SOURCES += \ -# platforms/ios/iosiaphandler.mm \ -# platforms/ios/iosauthenticationlistener.mm \ -# platforms/ios/ioscontroller.mm \ -# platforms/ios/iosdatamigration.mm \ - platforms/ios/iosglue.mm \ -# platforms/ios/iosnotificationhandler.mm \ -# platforms/ios/iosutils.mm \ -# platforms/macos/macoscryptosettings.mm - HEADERS += \ -# platforms/macos/macospingsender.h - - OBJECTIVE_HEADERS += \ -# platforms/ios/iosiaphandler.h \ -# platforms/ios/iosauthenticationlistener.h \ -# platforms/ios/ioscontroller.h \ -# platforms/ios/iosdatamigration.h \ -# platforms/ios/iosnotificationhandler.h \ -# platforms/ios/iosutils.h + protocols/ios_vpnprotocol.h \ + platforms/ios/json.h \ + platforms/ios/bigint.h \ + platforms/ios/bigintipv6addr.h \ + platforms/ios/ipaddress.h \ + platforms/ios/ipaddressrange.h + + SOURCES += \ + protocols/ios_vpnprotocol.mm \ + platforms/ios/json.cpp \ + platforms/ios/iosglue.mm \ + platforms/ios/ipaddress.cpp \ + platforms/ios/ipaddressrange.cpp Q_ENABLE_BITCODE.value = NO Q_ENABLE_BITCODE.name = ENABLE_BITCODE @@ -313,19 +304,29 @@ ios { ARCH_TAG = "ios_armv7" } } + + CONFIG(iphonesimulator, iphoneos|iphonesimulator) { + message("Building for iPhone Simulator") + ARCH_TAG = "ios_x86_64" + + DEFINES += iphoneos + + LIBS += $$PWD/3rd/OpenSSL/lib/ios/iphone/libcrypto.a + LIBS += $$PWD/3rd/OpenSSL/lib/ios/iphone/libssl.a + } - CONFIG(iphonesimulator, iphoneos|iphonesimulator) { - message("Building for iPhone Simulator") - ARCH_TAG = "ios_x86_64" - - DEFINES += iphonesimulator - - LIBS += $$PWD/3rd/OpenSSL/lib/ios/simulator/libcrypto.a - LIBS += $$PWD/3rd/OpenSSL/lib/ios/simulator/libssl.a - } +# CONFIG(iphonesimulator, iphoneos|iphonesimulator) { +# message("Building for iPhone Simulator") +# ARCH_TAG = "ios_x86_64" +# +# DEFINES += iphonesimulator +# +# LIBS += $$PWD/3rd/OpenSSL/lib/ios/simulator/libcrypto.a +# LIBS += $$PWD/3rd/OpenSSL/lib/ios/simulator/libssl.a +# } NETWORKEXTENSION=1 - ! build_pass: system(ruby $$PWD/ios/xcode_patcher.rb "$$PWD" "$$OUT_PWD/AmneziaVPN.xcodeproj" "2.0" "2.0.0" "ios" "$$NETWORKEXTENSION"|| echo "Failed to merge xcode with wireguard") +# ! build_pass: system(ruby $$PWD/scripts/xcode_patcher.rb "$$PWD" "$$OUT_PWD/AmneziaVPN.xcodeproj" "2.0" "2.0.0" "ios" "$$NETWORKEXTENSION"|| echo "Failed to merge xcode with wireguard") diff --git a/client/configurators/vpn_configurator.cpp b/client/configurators/vpn_configurator.cpp index 0b4d9170..d9887f0a 100644 --- a/client/configurators/vpn_configurator.cpp +++ b/client/configurators/vpn_configurator.cpp @@ -18,22 +18,22 @@ Settings &VpnConfigurator::m_settings() } QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentials, - DockerContainer container, const QJsonObject &containerConfig, Protocol proto, ErrorCode *errorCode) + DockerContainer container, const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode) { switch (proto) { - case Protocol::OpenVpn: + case Proto::OpenVpn: return OpenVpnConfigurator::genOpenVpnConfig(credentials, container, containerConfig, errorCode); - case Protocol::ShadowSocks: + case Proto::ShadowSocks: return ShadowSocksConfigurator::genShadowSocksConfig(credentials, container, containerConfig, errorCode); - case Protocol::Cloak: + case Proto::Cloak: return CloakConfigurator::genCloakConfig(credentials, container, containerConfig, errorCode); - case Protocol::WireGuard: + case Proto::WireGuard: return WireguardConfigurator::genWireguardConfig(credentials, container, containerConfig, errorCode); - case Protocol::Ikev2: + case Proto::Ikev2: return Ikev2Configurator::genIkev2Config(credentials, container, containerConfig, errorCode); default: @@ -41,23 +41,23 @@ QString VpnConfigurator::genVpnProtocolConfig(const ServerCredentials &credentia } } -QString VpnConfigurator::processConfigWithLocalSettings(DockerContainer container, Protocol proto, QString config) +QString VpnConfigurator::processConfigWithLocalSettings(DockerContainer container, Proto proto, QString config) { config.replace("$PRIMARY_DNS", m_settings().primaryDns()); config.replace("$SECONDARY_DNS", m_settings().secondaryDns()); - if (proto == Protocol::OpenVpn) { + if (proto == Proto::OpenVpn) { return OpenVpnConfigurator::processConfigWithLocalSettings(config); } return config; } -QString VpnConfigurator::processConfigWithExportSettings(DockerContainer container, Protocol proto, QString config) +QString VpnConfigurator::processConfigWithExportSettings(DockerContainer container, Proto proto, QString config) { config.replace("$PRIMARY_DNS", m_settings().primaryDns()); config.replace("$SECONDARY_DNS", m_settings().secondaryDns()); - if (proto == Protocol::OpenVpn) { + if (proto == Proto::OpenVpn) { return OpenVpnConfigurator::processConfigWithExportSettings(config); } return config; @@ -66,7 +66,7 @@ QString VpnConfigurator::processConfigWithExportSettings(DockerContainer contain void VpnConfigurator::updateContainerConfigAfterInstallation(DockerContainer container, QJsonObject &containerConfig, const QString &stdOut) { - Protocol mainProto = ContainerProps::defaultProtocol(container); + Proto mainProto = ContainerProps::defaultProtocol(container); if (container == DockerContainer::TorWebSite) { QJsonObject protocol = containerConfig.value(ProtocolProps::protoToString(mainProto)).toObject(); diff --git a/client/configurators/vpn_configurator.h b/client/configurators/vpn_configurator.h index 930a6715..b7ced4d6 100644 --- a/client/configurators/vpn_configurator.h +++ b/client/configurators/vpn_configurator.h @@ -13,10 +13,10 @@ class VpnConfigurator public: static QString genVpnProtocolConfig(const ServerCredentials &credentials, DockerContainer container, - const QJsonObject &containerConfig, Protocol proto, ErrorCode *errorCode = nullptr); + const QJsonObject &containerConfig, Proto proto, ErrorCode *errorCode = nullptr); - static QString processConfigWithLocalSettings(DockerContainer container, Protocol proto, QString config); - static QString processConfigWithExportSettings(DockerContainer container, Protocol proto, QString config); + static QString processConfigWithLocalSettings(DockerContainer container, Proto proto, QString config); + static QString processConfigWithExportSettings(DockerContainer container, Proto proto, QString config); // workaround for containers which is not support normal configaration static void updateContainerConfigAfterInstallation(DockerContainer container, diff --git a/client/containers/containers_defs.cpp b/client/containers/containers_defs.cpp index d1efda53..0c72e4a7 100644 --- a/client/containers/containers_defs.cpp +++ b/client/containers/containers_defs.cpp @@ -27,29 +27,29 @@ QString ContainerProps::containerToString(amnezia::DockerContainer c){ return "amnezia-" + containerKey.toLower(); } -QVector ContainerProps::protocolsForContainer(amnezia::DockerContainer container) +QVector ContainerProps::protocolsForContainer(amnezia::DockerContainer container) { switch (container) { case DockerContainer::None: return { }; case DockerContainer::OpenVpn: - return { Protocol::OpenVpn }; + return { Proto::OpenVpn }; case DockerContainer::ShadowSocks: - return { Protocol::OpenVpn, Protocol::ShadowSocks }; + return { Proto::OpenVpn, Proto::ShadowSocks }; case DockerContainer::Cloak: - return { Protocol::OpenVpn, Protocol::ShadowSocks, Protocol::Cloak }; + return { Proto::OpenVpn, Proto::ShadowSocks, Proto::Cloak }; case DockerContainer::Ipsec: - return { Protocol::Ikev2 /*, Protocol::L2tp */}; + return { Proto::Ikev2 /*, Protocol::L2tp */}; case DockerContainer::Dns: return { }; case DockerContainer::Sftp: - return { Protocol::Sftp}; + return { Proto::Sftp}; default: return { defaultProtocol(container) }; @@ -118,21 +118,21 @@ amnezia::ServiceType ContainerProps::containerService(DockerContainer c) } } -Protocol ContainerProps::defaultProtocol(DockerContainer c) +Proto ContainerProps::defaultProtocol(DockerContainer c) { switch (c) { - case DockerContainer::None : return Protocol::Any; - case DockerContainer::OpenVpn : return Protocol::OpenVpn; - case DockerContainer::Cloak : return Protocol::Cloak; - case DockerContainer::ShadowSocks : return Protocol::ShadowSocks; - case DockerContainer::WireGuard : return Protocol::WireGuard; - case DockerContainer::Ipsec : return Protocol::Ikev2; + case DockerContainer::None : return Proto::Any; + case DockerContainer::OpenVpn : return Proto::OpenVpn; + case DockerContainer::Cloak : return Proto::Cloak; + case DockerContainer::ShadowSocks : return Proto::ShadowSocks; + case DockerContainer::WireGuard : return Proto::WireGuard; + case DockerContainer::Ipsec : return Proto::Ikev2; - case DockerContainer::TorWebSite : return Protocol::TorWebSite; - case DockerContainer::Dns : return Protocol::Dns; + case DockerContainer::TorWebSite : return Proto::TorWebSite; + case DockerContainer::Dns : return Proto::Dns; //case DockerContainer::FileShare : return Protocol::FileShare; - case DockerContainer::Sftp : return Protocol::Sftp; - default: return Protocol::Any; + case DockerContainer::Sftp : return Proto::Sftp; + default: return Proto::Any; } } diff --git a/client/containers/containers_defs.h b/client/containers/containers_defs.h index 25f2dfc7..c2cf5b7d 100644 --- a/client/containers/containers_defs.h +++ b/client/containers/containers_defs.h @@ -46,13 +46,13 @@ public: Q_INVOKABLE static QMap containerDescriptions(); // these protocols will be displayed in container settings - Q_INVOKABLE static QVector protocolsForContainer(DockerContainer container); + Q_INVOKABLE static QVector protocolsForContainer(DockerContainer container); Q_INVOKABLE static ServiceType containerService(DockerContainer c); // binding between Docker container and main protocol of given container // it may be changed fot future containers :) - Q_INVOKABLE static Protocol defaultProtocol(DockerContainer c); + Q_INVOKABLE static Proto defaultProtocol(DockerContainer c); }; diff --git a/client/core/servercontroller.cpp b/client/core/servercontroller.cpp index 9375cd1d..a5d32be0 100644 --- a/client/core/servercontroller.cpp +++ b/client/core/servercontroller.cpp @@ -449,7 +449,7 @@ ErrorCode ServerController::updateContainer(const ServerCredentials &credentials QJsonObject ServerController::createContainerInitialConfig(DockerContainer container, int port, TransportProto tp) { - Protocol mainProto = ContainerProps::defaultProtocol(container); + Proto mainProto = ContainerProps::defaultProtocol(container); QJsonObject config { { config_key::container, ContainerProps::containerToString(container) } @@ -616,11 +616,11 @@ ErrorCode ServerController::startupContainerWorker(const ServerCredentials &cred ServerController::Vars ServerController::genVarsForScript(const ServerCredentials &credentials, DockerContainer container, const QJsonObject &config) { - const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Protocol::OpenVpn)).toObject(); - const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Protocol::Cloak)).toObject(); - const QJsonObject &ssConfig = config.value(ProtocolProps::protoToString(Protocol::ShadowSocks)).toObject(); - const QJsonObject &wireguarConfig = config.value(ProtocolProps::protoToString(Protocol::WireGuard)).toObject(); - const QJsonObject &sftpConfig = config.value(ProtocolProps::protoToString(Protocol::Sftp)).toObject(); + const QJsonObject &openvpnConfig = config.value(ProtocolProps::protoToString(Proto::OpenVpn)).toObject(); + const QJsonObject &cloakConfig = config.value(ProtocolProps::protoToString(Proto::Cloak)).toObject(); + 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; @@ -689,7 +689,7 @@ ServerController::Vars ServerController::genVarsForScript(const ServerCredential // Sftp vars - vars.append({{"$SFTP_PORT", sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Protocol::Sftp))) }}); + vars.append({{"$SFTP_PORT", sftpConfig.value(config_key::port).toString(QString::number(ProtocolProps::defaultPort(Proto::Sftp))) }}); vars.append({{"$SFTP_USER", sftpConfig.value(config_key::userName).toString() }}); vars.append({{"$SFTP_PASSWORD", sftpConfig.value(config_key::password).toString() }}); diff --git a/client/ios/app/Info.plist b/client/ios/app/Info.plist index 6648ef7b..8bbb0d55 100644 --- a/client/ios/app/Info.plist +++ b/client/ios/app/Info.plist @@ -2,12 +2,14 @@ - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) + ADJUST_SDK_TOKEN + $(ADJUST_SDK_TOKEN) CFBundleAllowMixedLocalizations + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Mozilla VPN + AmneziaVPN CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIcons @@ -49,7 +51,5 @@ Light com.wireguard.ios.app_group_id group.org.mozilla.ios.Guardian - ADJUST_SDK_TOKEN - $(ADJUST_SDK_TOKEN) diff --git a/client/ios/networkextension/AmneziaVPNNetworkExtension.entitlements b/client/ios/networkextension/AmneziaVPNNetworkExtension.entitlements index eded281a..f6d173d9 100644 --- a/client/ios/networkextension/AmneziaVPNNetworkExtension.entitlements +++ b/client/ios/networkextension/AmneziaVPNNetworkExtension.entitlements @@ -2,13 +2,13 @@ + com.apple.developer.networking.networkextension + + packet-tunnel-provider + com.apple.security.application-groups $(GROUP_ID_IOS) - com.apple.developer.networking.networkextension - - packet-tunnel-provider - diff --git a/client/macos/gobridge/api.go b/client/macos/gobridge/api.go old mode 100755 new mode 100644 diff --git a/client/macos/gobridge/dummy.c b/client/macos/gobridge/dummy.c old mode 100755 new mode 100644 diff --git a/client/macos/gobridge/go.mod b/client/macos/gobridge/go.mod old mode 100755 new mode 100644 diff --git a/client/macos/gobridge/go.sum b/client/macos/gobridge/go.sum old mode 100755 new mode 100644 diff --git a/client/macos/gobridge/goruntime-boottime-over-monotonic.diff b/client/macos/gobridge/goruntime-boottime-over-monotonic.diff old mode 100755 new mode 100644 diff --git a/client/macos/gobridge/module.modulemap b/client/macos/gobridge/module.modulemap old mode 100755 new mode 100644 diff --git a/client/macos/gobridge/wireguard-go-version.h b/client/macos/gobridge/wireguard-go-version.h old mode 100755 new mode 100644 diff --git a/client/macos/gobridge/wireguard.h b/client/macos/gobridge/wireguard.h old mode 100755 new mode 100644 diff --git a/client/main.cpp b/client/main.cpp index 19c505d8..d3251f1e 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -116,15 +116,15 @@ int main(int argc, char *argv[]) app.setQuitOnLastWindowClosed(false); - qRegisterMetaType("VpnProtocol::ConnectionState"); + qRegisterMetaType("VpnProtocol::ConnectionState"); qRegisterMetaType("ServerCredentials"); qRegisterMetaType("DockerContainer"); qRegisterMetaType("TransportProto"); - qRegisterMetaType("Protocol"); + qRegisterMetaType("Protocol"); qRegisterMetaType("ServiceType"); qRegisterMetaType("Page"); - qRegisterMetaType("ConnectionState"); + qRegisterMetaType("ConnectionState"); qRegisterMetaType("PageProtocolLogicBase *"); @@ -160,7 +160,7 @@ int main(int argc, char *argv[]) engine->rootContext()->setContextProperty("NewServerProtocolsLogic", uiLogic->newServerProtocolsLogic()); engine->rootContext()->setContextProperty("ServerListLogic", uiLogic->serverListLogic()); engine->rootContext()->setContextProperty("ServerSettingsLogic", uiLogic->serverSettingsLogic()); - engine->rootContext()->setContextProperty("ServerContainersLogic", uiLogic->serverVpnProtocolsLogic()); + engine->rootContext()->setContextProperty("ServerContainersLogic", uiLogic->serverprotocolsLogic()); engine->rootContext()->setContextProperty("ShareConnectionLogic", uiLogic->shareConnectionLogic()); engine->rootContext()->setContextProperty("SitesLogic", uiLogic->sitesLogic()); engine->rootContext()->setContextProperty("StartPageLogic", uiLogic->startPageLogic()); diff --git a/client/platforms/ios/WireGuard-Bridging-Header.h b/client/platforms/ios/WireGuard-Bridging-Header.h new file mode 100644 index 00000000..40b6c89d --- /dev/null +++ b/client/platforms/ios/WireGuard-Bridging-Header.h @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "wireguard-go-version.h" +#include "3rd/wireguard-apple/Sources/WireGuardKitC/WireGuardKitC.h" + +#include +#include + +#define WG_KEY_LEN (32) +#define WG_KEY_LEN_BASE64 (45) +#define WG_KEY_LEN_HEX (65) + +void key_to_base64(char base64[WG_KEY_LEN_BASE64], + const uint8_t key[WG_KEY_LEN]); +bool key_from_base64(uint8_t key[WG_KEY_LEN], const char* base64); + +void key_to_hex(char hex[WG_KEY_LEN_HEX], const uint8_t key[WG_KEY_LEN]); +bool key_from_hex(uint8_t key[WG_KEY_LEN], const char* hex); + +bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN]); + +void write_msg_to_log(const char* tag, const char* msg); + +#import "TargetConditionals.h" +#if TARGET_OS_OSX +# include +#endif diff --git a/client/platforms/ios/bigint.h b/client/platforms/ios/bigint.h new file mode 100644 index 00000000..ce45cf69 --- /dev/null +++ b/client/platforms/ios/bigint.h @@ -0,0 +1,127 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BIGINT_H +#define BIGINT_H + +#include + +// This BigInt implementation is meant to be used for IPv6 addresses. It +// doesn't support dynamic resize: when the max size is reached, the value +// overflows. If you need to change the size, use `resize()`. + +class BigInt final { + public: + explicit BigInt(uint8_t bytes) { + m_value.resize(bytes); + memset(m_value.data(), 0, bytes); + } + + BigInt(const BigInt& other) { m_value = other.m_value; } + + const uint8_t* value() const { return m_value.data(); } + + uint8_t size() const { return m_value.size(); } + + // Assign operator. + + BigInt& operator=(const BigInt& other) { + m_value = other.m_value; + return *this; + } + + // Comparison operators. + + bool operator==(const BigInt& other) const { + return m_value == other.m_value; + } + + bool operator!=(const BigInt& other) const { return !(*this == other); } + + bool operator<(const BigInt& other) const { return cmp(other) < 0; } + + bool operator>(const BigInt& other) const { return cmp(other) > 0; } + + bool operator<=(const BigInt& other) const { return cmp(other) <= 0; } + + bool operator>=(const BigInt& other) const { return cmp(other) >= 0; } + + // math operators (only some of them are implemented) + + BigInt& operator++() { + for (int i = size() - 1; i >= 0; --i) { + if (m_value[i] < UINT8_MAX) { + ++m_value[i]; + return *this; + } + m_value[i] = 0; + } + + // overflow + memset(m_value.data(), 0, size()); + return *this; + } + + BigInt& operator+=(const BigInt& other) { + Q_ASSERT(other.size() == size()); + + uint8_t carry = 0; + for (int i = m_value.size() - 1; i >= 0; --i) { + uint16_t total = carry + m_value[i] + other.m_value[i]; + m_value[i] = (uint8_t)(total & UINT8_MAX); + carry = (uint8_t)((total & 0xFF00) >> 8); + } + + return *this; + } + + // Shift operators + + BigInt operator>>(int shift) { + BigInt x(size()); + x = *this; + + for (int i = 0; i < shift; i++) { + BigInt a(size()); + a = x; + + a.m_value[size() - 1] = x.m_value[size() - 1] >> 1; + for (int j = size() - 2; j >= 0; j--) { + a.m_value[j] = x.m_value[j] >> 1; + if ((x.m_value[j] & 1) != 0) { + a.m_value[j + 1] |= 128; // Set most significant bit or a uint8_t + } + } + + x = a; + } + + return x; + } + + void setValueAt(uint8_t value, uint8_t pos) { + Q_ASSERT(pos < size()); + m_value[pos] = value; + } + + uint8_t valueAt(uint8_t pos) const { + Q_ASSERT(size() > pos); + return m_value[pos]; + } + + private: + int cmp(const BigInt& other) const { + Q_ASSERT(size() == other.size()); + for (int i = 0; i < size(); i++) { + int diff = (m_value[i] - other.m_value[i]); + if (diff != 0) return diff; + } + return 0; + } + + private: + QVector m_value; +}; + +#endif // BIGINT_H diff --git a/client/platforms/ios/bigintipv6addr.h b/client/platforms/ios/bigintipv6addr.h new file mode 100644 index 00000000..a277de6d --- /dev/null +++ b/client/platforms/ios/bigintipv6addr.h @@ -0,0 +1,86 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef BIGINTIPV6ADDR_H +#define BIGINTIPV6ADDR_H + +#include "bigint.h" + +#include + +class BigIntIPv6Addr final { + public: + BigIntIPv6Addr() : m_value(16) {} + + explicit BigIntIPv6Addr(const Q_IPV6ADDR& a) : m_value(16) { + for (int i = 0; i < 16; ++i) m_value.setValueAt(a[i], i); + } + + BigIntIPv6Addr(const BigIntIPv6Addr& other) : m_value(16) { *this = other; } + + Q_IPV6ADDR value() const { + Q_IPV6ADDR addr; + for (int i = 0; i < 16; ++i) addr[i] = m_value.valueAt(i); + return addr; + } + + // Assign operator. + + BigIntIPv6Addr& operator=(const BigIntIPv6Addr& other) { + m_value = other.m_value; + return *this; + } + + // Comparison operators. + + bool operator==(const BigIntIPv6Addr& other) const { + return m_value == other.m_value; + } + + bool operator!=(const BigIntIPv6Addr& other) const { + return m_value != other.m_value; + } + + bool operator<(const BigIntIPv6Addr& other) const { + return m_value < other.m_value; + } + + bool operator>(const BigIntIPv6Addr& other) const { + return m_value > other.m_value; + } + + bool operator<=(const BigIntIPv6Addr& other) const { + return m_value <= other.m_value; + } + + bool operator>=(const BigIntIPv6Addr& other) const { + return m_value >= other.m_value; + } + + // math operators (only some of them are implemented) + + BigIntIPv6Addr& operator++() { + ++m_value; + return *this; + } + + BigIntIPv6Addr& operator+=(const BigIntIPv6Addr& b) { + m_value += b.m_value; + return *this; + } + + // Shift operators + + BigIntIPv6Addr operator>>(int shift) { + BigIntIPv6Addr x; + + x.m_value = m_value >> shift; + return x; + } + + private: + BigInt m_value; +}; + +#endif // BIGINTIPV6ADDR_H diff --git a/client/platforms/ios/ioscontroller.h b/client/platforms/ios/ioscontroller.h index 8ff2c329..da18d5fe 100644 --- a/client/platforms/ios/ioscontroller.h +++ b/client/platforms/ios/ioscontroller.h @@ -5,12 +5,12 @@ #ifndef IOSCONTROLLER_H #define IOSCONTROLLER_H -#include "controllerimpl.h" +#include "vpnprotocol.h" #include -class IOSController final : public ControllerImpl { - Q_DISABLE_COPY_MOVE(IOSController) +class IOSVPNProtocol final : public VpnProtocol { + Q_DISABLE_COPY_MOVE(IOSVPNProtocol) public: IOSController(); diff --git a/client/platforms/ios/iosglue.mm b/client/platforms/ios/iosglue.mm index 36a1b2a2..854dfbfb 100644 --- a/client/platforms/ios/iosglue.mm +++ b/client/platforms/ios/iosglue.mm @@ -8,7 +8,7 @@ #include #ifndef NETWORK_EXTENSION -# include "logger.h" +//# include "logger.h" #else # import # import @@ -183,13 +183,13 @@ EXPORT bool key_eq(const uint8_t key1[WG_KEY_LEN], const uint8_t key2[WG_KEY_LEN #ifndef NETWORK_EXTENSION namespace { -Logger logger(LOG_IOS, "IOSSGlue"); +//Logger logger(LOG_IOS, "IOSSGlue"); } #endif EXPORT void write_msg_to_log(const char* tag, const char* msg) { #ifndef NETWORK_EXTENSION - logger.debug() << "Swift log - tag:" << tag << "msg: " << msg; +// logger.debug() << "Swift log - tag:" << tag << "msg: " << msg; #else os_log_with_type(OS_LOG_DEFAULT, OS_LOG_TYPE_DEBUG, "tag: %s - msg: %s", tag, msg); diff --git a/client/platforms/ios/iostunnel.swift b/client/platforms/ios/iostunnel.swift index 757f6f4b..a316d054 100644 --- a/client/platforms/ios/iostunnel.swift +++ b/client/platforms/ios/iostunnel.swift @@ -7,7 +7,7 @@ import os class PacketTunnelProvider: NEPacketTunnelProvider { - private lazy var adapter: WireGuardAdapter = { + private lazy var wgAdapter: WireGuardAdapter = { return WireGuardAdapter(with: self) { logLevel, message in wg_log(logLevel.osLogLevel, message: message) } @@ -29,9 +29,9 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } // Start the tunnel - adapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in + wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in guard let adapterError = adapterError else { - let interfaceName = self.adapter.interfaceName ?? "unknown" + let interfaceName = self.wgAdapter.interfaceName ?? "unknown" wg_log(.info, message: "Tunnel interface is \(interfaceName)") @@ -72,7 +72,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { wg_log(.info, staticMessage: "Stopping tunnel") - adapter.stop { error in + wgAdapter.stop { error in ErrorNotifier.removeLastErrorFile() if let error = error { @@ -93,7 +93,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider { guard let completionHandler = completionHandler else { return } if messageData.count == 1 && messageData[0] == 0 { - adapter.getRuntimeConfiguration { settings in + wgAdapter.getRuntimeConfiguration { settings in var data: Data? if let settings = settings { data = settings.data(using: .utf8)! @@ -111,14 +111,14 @@ class PacketTunnelProvider: NEPacketTunnelProvider { do { let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString) - adapter.update(tunnelConfiguration: tunnelConfiguration) { error in + wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in if let error = error { wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)") completionHandler(nil) return } - self.adapter.getRuntimeConfiguration { settings in + self.wgAdapter.getRuntimeConfiguration { settings in var data: Data? if let settings = settings { data = settings.data(using: .utf8)! diff --git a/client/platforms/ios/iosvpnprotocol.swift b/client/platforms/ios/iosvpnprotocol.swift new file mode 100644 index 00000000..b14bc690 --- /dev/null +++ b/client/platforms/ios/iosvpnprotocol.swift @@ -0,0 +1,292 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import Foundation +import NetworkExtension + +let vpnName = "AmneziaVPN" +var vpnBundleID = ""; + +@objc class VPNIPAddressRange : NSObject { + public var address: NSString = "" + public var networkPrefixLength: UInt8 = 0 + public var isIpv6: Bool = false + + @objc init(address: NSString, networkPrefixLength: UInt8, isIpv6: Bool) { + super.init() + + self.address = address + self.networkPrefixLength = networkPrefixLength + self.isIpv6 = isIpv6 + } +} + +public class IOSVpnProtocolImpl : NSObject { + + private var tunnel: NETunnelProviderManager? = nil + private var stateChangeCallback: ((Bool) -> Void?)? = nil + private var privateKey : PrivateKey? = nil + private var deviceIpv4Address: String? = nil + private var deviceIpv6Address: String? = nil + + @objc enum IOSConnectionState: Int { case Error, Connected, Disconnected } + + @objc init(bundleID: String, privateKey: Data, deviceIpv4Address: String, deviceIpv6Address: String, closure: @escaping (IOSConnectionState, Date?) -> Void, callback: @escaping (Bool) -> Void) { + super.init() + + Logger.configureGlobal(tagged: "APP", withFilePath: "") + + vpnBundleID = bundleID; + precondition(!vpnBundleID.isEmpty) + + stateChangeCallback = callback + self.privateKey = PrivateKey(rawValue: privateKey) + self.deviceIpv4Address = deviceIpv4Address + self.deviceIpv6Address = deviceIpv6Address + + NotificationCenter.default.addObserver(self, selector: #selector(self.vpnStatusDidChange(notification:)), name: Notification.Name.NEVPNStatusDidChange, object: nil) + + NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in + if let error = error { + Logger.global?.log(message: "Loading from preference failed: \(error)") + closure(IOSConnectionState.Error, nil) + return + } + + if self == nil { + Logger.global?.log(message: "We are shutting down.") + return + } + + let nsManagers = managers ?? [] + Logger.global?.log(message: "We have received \(nsManagers.count) managers.") + + let tunnel = nsManagers.first(where: IOSVpnProtocolImpl.isOurManager(_:)) + if tunnel == nil { + Logger.global?.log(message: "Creating the tunnel") + self!.tunnel = NETunnelProviderManager() + closure(IOSConnectionState.Disconnected, nil) + return + } + + Logger.global?.log(message: "Tunnel already exists") + + self!.tunnel = tunnel + if tunnel?.connection.status == .connected { + closure(IOSConnectionState.Connected, tunnel?.connection.connectedDate) + } else { + closure(IOSConnectionState.Disconnected, nil) + } + } + } + + @objc private func vpnStatusDidChange(notification: Notification) { + guard let session = (notification.object as? NETunnelProviderSession), tunnel?.connection == session else { return } + + switch session.status { + case .connected: + Logger.global?.log(message: "STATE CHANGED: connected") + case .connecting: + Logger.global?.log(message: "STATE CHANGED: connecting") + case .disconnected: + Logger.global?.log(message: "STATE CHANGED: disconnected") + case .disconnecting: + Logger.global?.log(message: "STATE CHANGED: disconnecting") + case .invalid: + Logger.global?.log(message: "STATE CHANGED: invalid") + case .reasserting: + Logger.global?.log(message: "STATE CHANGED: reasserting") + default: + Logger.global?.log(message: "STATE CHANGED: unknown status") + } + + // We care about "unknown" state changes. + if (session.status != .connected && session.status != .disconnected) { + return + } + + stateChangeCallback?(session.status == .connected) + } + + private static func isOurManager(_ manager: NETunnelProviderManager) -> Bool { + guard + let proto = manager.protocolConfiguration, + let tunnelProto = proto as? NETunnelProviderProtocol + else { + Logger.global?.log(message: "Ignoring manager because the proto is invalid.") + return false + } + + if (tunnelProto.providerBundleIdentifier == nil) { + Logger.global?.log(message: "Ignoring manager because the bundle identifier is null.") + return false + } + + if (tunnelProto.providerBundleIdentifier != vpnBundleID) { + Logger.global?.log(message: "Ignoring manager because the bundle identifier doesn't match.") + return false; + } + + Logger.global?.log(message: "Found the manager with the correct bundle identifier: \(tunnelProto.providerBundleIdentifier!)") + return true + } + + @objc func connect(dnsServer: String, serverIpv6Gateway: String, serverPublicKey: String, serverIpv4AddrIn: String, serverPort: Int, allowedIPAddressRanges: Array, ipv6Enabled: Bool, reason: Int, failureCallback: @escaping () -> Void) { + Logger.global?.log(message: "Connecting") + assert(tunnel != nil) + + // Let's remove the previous config if it exists. + (tunnel?.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference() + + let keyData = PublicKey(base64Key: serverPublicKey)! + let dnsServerIP = IPv4Address(dnsServer) + let ipv6GatewayIP = IPv6Address(serverIpv6Gateway) + + var peerConfiguration = PeerConfiguration(publicKey: keyData) + peerConfiguration.endpoint = Endpoint(from: serverIpv4AddrIn + ":\(serverPort )") + peerConfiguration.allowedIPs = [] + + allowedIPAddressRanges.forEach { + if (!$0.isIpv6) { + peerConfiguration.allowedIPs.append(IPAddressRange(address: IPv4Address($0.address as String)!, networkPrefixLength: $0.networkPrefixLength)) + } else if (ipv6Enabled) { + peerConfiguration.allowedIPs.append(IPAddressRange(address: IPv6Address($0.address as String)!, networkPrefixLength: $0.networkPrefixLength)) + } + } + + var peerConfigurations: [PeerConfiguration] = [] + peerConfigurations.append(peerConfiguration) + + var interface = InterfaceConfiguration(privateKey: privateKey!) + + if let ipv4Address = IPAddressRange(from: deviceIpv4Address!), + let ipv6Address = IPAddressRange(from: deviceIpv6Address!) { + interface.addresses = [ipv4Address] + if (ipv6Enabled) { + interface.addresses.append(ipv6Address) + } + } + interface.dns = [ DNSServer(address: dnsServerIP!)] + + if (ipv6Enabled) { + interface.dns.append(DNSServer(address: ipv6GatewayIP!)) + } + + let config = TunnelConfiguration(name: vpnName, interface: interface, peers: peerConfigurations) + + self.configureTunnel(config: config, reason: reason, failureCallback: failureCallback) + } + + func configureTunnel(config: TunnelConfiguration, reason: Int, failureCallback: @escaping () -> Void) { + guard let proto = NETunnelProviderProtocol(tunnelConfiguration: config) else { + failureCallback() + return + } + proto.providerBundleIdentifier = vpnBundleID + + tunnel!.protocolConfiguration = proto + tunnel!.localizedDescription = vpnName + tunnel!.isEnabled = true + + tunnel!.saveToPreferences { [unowned self] saveError in + if let error = saveError { + Logger.global?.log(message: "Connect Tunnel Save Error: \(error)") + failureCallback() + return + } + + Logger.global?.log(message: "Saving the tunnel succeeded") + + self.tunnel!.loadFromPreferences { error in + if let error = error { + Logger.global?.log(message: "Connect Tunnel Load Error: \(error)") + failureCallback() + return + } + + Logger.global?.log(message: "Loading the tunnel succeeded") + + do { + if (reason == 1 /* ReasonSwitching */) { + let settings = config.asWgQuickConfig() + let settingsData = settings.data(using: .utf8)! + try (self.tunnel!.connection as? NETunnelProviderSession)? + .sendProviderMessage(settingsData) { data in + guard let data = data, + let configString = String(data: data, encoding: .utf8) + else { + Logger.global?.log(message: "Failed to convert response to string") + return + } + } + } else { + try (self.tunnel!.connection as? NETunnelProviderSession)?.startTunnel() + } + } catch let error { + Logger.global?.log(message: "Something went wrong: \(error)") + failureCallback() + return + } + } + } + } + + @objc func disconnect() { + Logger.global?.log(message: "Disconnecting") + assert(tunnel != nil) + (tunnel!.connection as? NETunnelProviderSession)?.stopTunnel() + } + + @objc func checkStatus(callback: @escaping (String, String, String) -> Void) { + Logger.global?.log(message: "Check status") + assert(tunnel != nil) + + let proto = tunnel!.protocolConfiguration as? NETunnelProviderProtocol + if proto == nil { + callback("", "", "") + return + } + + let tunnelConfiguration = proto?.asTunnelConfiguration() + if tunnelConfiguration == nil { + callback("", "", "") + return + } + + let serverIpv4Gateway = tunnelConfiguration?.interface.dns[0].address + if serverIpv4Gateway == nil { + callback("", "", "") + return + } + + let deviceIpv4Address = tunnelConfiguration?.interface.addresses[0].address + if deviceIpv4Address == nil { + callback("", "", "") + return + } + + guard let session = tunnel?.connection as? NETunnelProviderSession + else { + callback("", "", "") + return + } + + do { + try session.sendProviderMessage(Data([UInt8(0)])) { [callback] data in + guard let data = data, + let configString = String(data: data, encoding: .utf8) + else { + Logger.global?.log(message: "Failed to convert data to string") + callback("", "", "") + return + } + + callback("\(serverIpv4Gateway!)", "\(deviceIpv4Address!)", configString) + } + } catch { + Logger.global?.log(message: "Failed to retrieve data from session") + callback("", "", "") + } + } +} diff --git a/client/platforms/ios/ipaddress.cpp b/client/platforms/ios/ipaddress.cpp new file mode 100644 index 00000000..501701c1 --- /dev/null +++ b/client/platforms/ios/ipaddress.cpp @@ -0,0 +1,313 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ipaddress.h" +#include "bigintipv6addr.h" + +#include + +namespace { + +quint32 s_allIpV4Ones = static_cast(qPow(2, 32) - 1); + +BigIntIPv6Addr s_allIPv6Ones; +bool s_ipv6Initialized = false; + +void maybeInitialize() { + if (s_ipv6Initialized) return; + + s_ipv6Initialized = true; + + Q_IPV6ADDR allOnes; + memset((void*)&allOnes, static_cast(qPow(2, 8) - 1), sizeof(allOnes)); + + s_allIPv6Ones = BigIntIPv6Addr(allOnes); +} + +} // namespace + +// static +IPAddress IPAddress::create(const QString& ip) { + if (ip.contains("/")) { + QPair p = QHostAddress::parseSubnet(ip); + + if (p.first.protocol() == QAbstractSocket::IPv4Protocol) { + if (p.second < 32) { + return IPAddress(p.first, p.second); + } + return IPAddress(p.first); + } + + if (p.first.protocol() == QAbstractSocket::IPv6Protocol) { + if (p.second < 128) { + return IPAddress(p.first, p.second); + } + return IPAddress(p.first); + } + + Q_ASSERT(false); + } + + return IPAddress(QHostAddress(ip)); +} + +IPAddress::IPAddress() { + maybeInitialize(); +} + +IPAddress::IPAddress(const IPAddress& other) { + maybeInitialize(); + *this = other; +} + +IPAddress& IPAddress::operator=(const IPAddress& other) { + if (this == &other) return *this; + + m_address = other.m_address; + m_prefixLength = other.m_prefixLength; + m_netmask = other.m_netmask; + m_hostmask = other.m_hostmask; + m_broadcastAddress = other.m_broadcastAddress; + + return *this; +} + +IPAddress::IPAddress(const QHostAddress& address) + : m_address(address), m_broadcastAddress(address) { + maybeInitialize(); + + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + m_prefixLength = 32; + m_netmask = QHostAddress(s_allIpV4Ones); + m_hostmask = QHostAddress((quint32)(0)); + } else { + Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol); + m_prefixLength = 128; + + m_netmask = QHostAddress(s_allIPv6Ones.value()); + + { + Q_IPV6ADDR ipv6; + memset((void*)&ipv6, 0, sizeof(ipv6)); + m_hostmask = QHostAddress(ipv6); + } + } +} + +IPAddress::IPAddress(const QHostAddress& address, int prefixLength) + : m_address(address), m_prefixLength(prefixLength) { + maybeInitialize(); + + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + Q_ASSERT(prefixLength >= 0 && prefixLength <= 32); + m_netmask = QHostAddress(s_allIpV4Ones ^ (s_allIpV4Ones >> prefixLength)); + m_hostmask = QHostAddress(m_netmask.toIPv4Address() ^ s_allIpV4Ones); + m_broadcastAddress = + QHostAddress(address.toIPv4Address() | m_hostmask.toIPv4Address()); + } else { + Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol); + Q_ASSERT(prefixLength >= 0 && prefixLength <= 128); + + Q_IPV6ADDR netmask; + { + BigIntIPv6Addr tmp = (s_allIPv6Ones >> prefixLength); + for (int i = 0; i < 16; ++i) + netmask[i] = s_allIPv6Ones.value()[i] ^ tmp.value()[i]; + } + m_netmask = QHostAddress(netmask); + + { + Q_IPV6ADDR tmp; + for (int i = 0; i < 16; ++i) + tmp[i] = netmask[i] ^ s_allIPv6Ones.value()[i]; + m_hostmask = QHostAddress(tmp); + } + + { + Q_IPV6ADDR ipv6Address = address.toIPv6Address(); + Q_IPV6ADDR ipv6Hostname = m_hostmask.toIPv6Address(); + for (int i = 0; i < 16; ++i) ipv6Address[i] |= ipv6Hostname[i]; + m_broadcastAddress = QHostAddress(ipv6Address); + } + } +} + +IPAddress::~IPAddress() { } + +QAbstractSocket::NetworkLayerProtocol IPAddress::type() const { + return m_address.protocol(); +} + +bool IPAddress::overlaps(const IPAddress& other) const { + return other.contains(m_address) || other.contains(m_broadcastAddress) || + contains(other.m_address) || contains(other.m_broadcastAddress); +} + +bool IPAddress::contains(const QHostAddress& address) const { + if (address.protocol() != m_address.protocol()) { + return false; + } + + if (address.protocol() == QAbstractSocket::IPv4Protocol) { + return (m_address.toIPv4Address() <= address.toIPv4Address()) && + (address.toIPv4Address() <= m_broadcastAddress.toIPv4Address()); + } + + Q_ASSERT(address.protocol() == QAbstractSocket::IPv6Protocol); + return (BigIntIPv6Addr(m_address.toIPv6Address()) <= + BigIntIPv6Addr(address.toIPv6Address())) && + (BigIntIPv6Addr(address.toIPv6Address()) <= + BigIntIPv6Addr(m_broadcastAddress.toIPv6Address())); +} + +bool IPAddress::operator==(const IPAddress& other) const { + return m_address == other.m_address && m_netmask == other.m_netmask; +} + +bool IPAddress::subnetOf(const IPAddress& other) const { + if (other.m_address.protocol() != m_address.protocol()) { + return false; + } + + if (m_address.protocol() == QAbstractSocket::IPv4Protocol) { + return other.m_address.toIPv4Address() <= m_address.toIPv4Address() && + other.m_broadcastAddress.toIPv4Address() >= + m_broadcastAddress.toIPv4Address(); + } + + Q_ASSERT(m_address.protocol() == QAbstractSocket::IPv6Protocol); + return BigIntIPv6Addr(other.m_address.toIPv6Address()) <= + BigIntIPv6Addr(m_address.toIPv6Address()) && + BigIntIPv6Addr(other.m_broadcastAddress.toIPv6Address()) >= + BigIntIPv6Addr(m_broadcastAddress.toIPv6Address()); +} + +QList IPAddress::subnets() const { + QList list; + + if (m_address.protocol() == QAbstractSocket::IPv4Protocol) { + if (m_prefixLength == 32) { + list.append(*this); + return list; + } + + quint64 start = m_address.toIPv4Address(); + quint64 end = quint64(m_broadcastAddress.toIPv4Address()) + 1; + quint64 step = ((quint64)m_hostmask.toIPv4Address() + 1) >> 1; + + while (start < end) { + int newPrefixLength = m_prefixLength + 1; + if (newPrefixLength == 32) { + list.append(IPAddress(QHostAddress(static_cast(start)))); + } else { + list.append(IPAddress(QHostAddress(static_cast(start)), + m_prefixLength + 1)); + } + start += step; + } + + return list; + } + + Q_ASSERT(m_address.protocol() == QAbstractSocket::IPv6Protocol); + + if (m_prefixLength == 128) { + list.append(*this); + return list; + } + + BigInt start(17); + { + Q_IPV6ADDR addr = m_address.toIPv6Address(); + for (int i = 0; i < 16; ++i) { + start.setValueAt(addr[i], i + 1); + } + } + + BigInt end(17); + { + Q_IPV6ADDR addr = m_broadcastAddress.toIPv6Address(); + for (int i = 0; i < 16; ++i) { + end.setValueAt(addr[i], i + 1); + } + ++end; + } + + BigInt step(17); + { + Q_IPV6ADDR addr = m_hostmask.toIPv6Address(); + for (int i = 0; i < 16; ++i) { + step.setValueAt(addr[i], i + 1); + } + step = (++step) >> 1; + } + + while (start < end) { + int newPrefixLength = m_prefixLength + 1; + Q_IPV6ADDR startIPv6; + for (int i = 0; i < 16; ++i) { + startIPv6[i] = start.valueAt(i + 1); + } + + if (newPrefixLength == 128) { + list.append(IPAddress(QHostAddress(startIPv6))); + } else { + list.append(IPAddress(QHostAddress(startIPv6), m_prefixLength + 1)); + } + start += step; + } + + return list; +} + +// static +QList IPAddress::excludeAddresses( + const QList& sourceList, const QList& excludeList) { + QList results = sourceList; + + for (const IPAddress& exclude : excludeList) { + QList newResults; + + for (const IPAddress& ip : results) { + if (ip.overlaps(exclude)) { + QList range = ip.excludeAddresses(exclude); + newResults.append(range); + } else { + newResults.append(ip); + } + } + + results = newResults; + } + + return results; +} + +QList IPAddress::excludeAddresses(const IPAddress& ip) const { + QList sn = subnets(); + Q_ASSERT(sn.length() >= 2); + + QList result; + while (sn[0] != ip && sn[1] != ip) { + if (ip.subnetOf(sn[0])) { + result.append(sn[1]); + sn = sn[0].subnets(); + } else if (ip.subnetOf(sn[1])) { + result.append(sn[0]); + sn = sn[1].subnets(); + } else { + Q_ASSERT(false); + } + } + + if (sn[0] == ip) { + result.append(sn[1]); + } else if (sn[1] == ip) { + result.append(sn[0]); + } else { + Q_ASSERT(false); + } + + return result; +} diff --git a/client/platforms/ios/ipaddress.h b/client/platforms/ios/ipaddress.h new file mode 100644 index 00000000..f41ecd69 --- /dev/null +++ b/client/platforms/ios/ipaddress.h @@ -0,0 +1,59 @@ +/* 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 IPADDRESS_H +#define IPADDRESS_H + +#include + +class IPAddress final { + public: + static IPAddress create(const QString& ip); + static QList excludeAddresses(const QList& sourceList, + const QList& excludeList); + + IPAddress(); + IPAddress(const IPAddress& other); + IPAddress& operator=(const IPAddress& other); + ~IPAddress(); + + QString toString() const { + return QString("%1/%2").arg(m_address.toString()).arg(m_prefixLength); + } + + const QHostAddress& address() const { return m_address; } + int prefixLength() const { return m_prefixLength; } + const QHostAddress& netmask() const { return m_netmask; } + const QHostAddress& hostmask() const { return m_hostmask; } + const QHostAddress& broadcastAddress() const { return m_broadcastAddress; } + + bool overlaps(const IPAddress& other) const; + + bool contains(const QHostAddress& address) const; + + bool operator==(const IPAddress& other) const; + bool operator!=(const IPAddress& other) const { return !operator==(other); } + + bool subnetOf(const IPAddress& other) const; + + QList subnets() const; + + QList excludeAddresses(const IPAddress& ip) const; + + QAbstractSocket::NetworkLayerProtocol type() const; + + private: + IPAddress(const QHostAddress& address); + IPAddress(const QHostAddress& address, int prefixLength); + + private: + QHostAddress m_address; + int m_prefixLength; + + QHostAddress m_netmask; + QHostAddress m_hostmask; + QHostAddress m_broadcastAddress; +}; + +#endif // IPADDRESS_H diff --git a/client/platforms/ios/ipaddressrange.cpp b/client/platforms/ios/ipaddressrange.cpp new file mode 100644 index 00000000..8810a73c --- /dev/null +++ b/client/platforms/ios/ipaddressrange.cpp @@ -0,0 +1,60 @@ +/* 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 "ipaddressrange.h" +#include "ipaddress.h" + +IPAddressRange::IPAddressRange(const QString& ipAddress, uint32_t range, + IPAddressType type) + : m_ipAddress(ipAddress), m_range(range), m_type(type) {} + +IPAddressRange::IPAddressRange(const QString& prefix) { + QStringList split = prefix.split('/'); + m_ipAddress = split[0]; + if (m_ipAddress.contains(':')) { + // Probably IPv6 + m_type = IPv6; + m_range = 128; + } else { + // Assume IPv4 + m_type = IPv4; + m_range = 32; + } + if (split.count() > 1) { + m_range = split[1].toUInt(); + } +} + +IPAddressRange::IPAddressRange(const IPAddressRange& other) { + *this = other; +} + +IPAddressRange& IPAddressRange::operator=(const IPAddressRange& other) { + if (this == &other) return *this; + + m_ipAddress = other.m_ipAddress; + m_range = other.m_range; + m_type = other.m_type; + + return *this; +} + +bool IPAddressRange::operator==(const IPAddressRange& other) const { + if (this == &other) return true; + + return m_ipAddress == other.m_ipAddress && m_range == other.m_range && + m_type == other.m_type; +} + +IPAddressRange::~IPAddressRange() { } + +// static +QList IPAddressRange::fromIPAddressList( + const QList& list) { + QList result; + for (const IPAddress& ip : list) { + result.append(IPAddressRange(ip.toString())); + } + return result; +} diff --git a/client/platforms/ios/ipaddressrange.h b/client/platforms/ios/ipaddressrange.h new file mode 100644 index 00000000..db1733ae --- /dev/null +++ b/client/platforms/ios/ipaddressrange.h @@ -0,0 +1,42 @@ +/* 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 IPADDRESSRANGE_H +#define IPADDRESSRANGE_H + +#include +#include + +class IPAddress; + +class IPAddressRange final { + public: + enum IPAddressType { + IPv4, + IPv6, + }; + + static QList fromIPAddressList(const QList& list); + + IPAddressRange(const QString& prefix); + IPAddressRange(const QString& ipAddress, uint32_t range, IPAddressType type); + IPAddressRange(const IPAddressRange& other); + IPAddressRange& operator=(const IPAddressRange& other); + bool operator==(const IPAddressRange& other) const; + ~IPAddressRange(); + + const QString& ipAddress() const { return m_ipAddress; } + uint32_t range() const { return m_range; } + IPAddressType type() const { return m_type; } + const QString toString() const { + return QString("%1/%2").arg(m_ipAddress).arg(m_range); + } + + private: + QString m_ipAddress; + uint32_t m_range; + IPAddressType m_type; +}; + +#endif // IPADDRESSRANGE_H diff --git a/client/platforms/ios/json.cpp b/client/platforms/ios/json.cpp new file mode 100644 index 00000000..0cf2a0af --- /dev/null +++ b/client/platforms/ios/json.cpp @@ -0,0 +1,732 @@ +/** + * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. + * Copyright (C) 2011 Eeli Reilin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file json.cpp + */ + +#include +#include +#include "json.h" + +namespace QtJson { + static QString dateFormat, dateTimeFormat; + static bool prettySerialize = false; + + static QString sanitizeString(QString str); + static QByteArray join(const QList &list, const QByteArray &sep); + static QVariant parseValue(const QString &json, int &index, bool &success); + static QVariant parseObject(const QString &json, int &index, bool &success); + static QVariant parseArray(const QString &json, int &index, bool &success); + static QVariant parseString(const QString &json, int &index, bool &success); + static QVariant parseNumber(const QString &json, int &index); + static int lastIndexOfNumber(const QString &json, int index); + static void eatWhitespace(const QString &json, int &index); + static int lookAhead(const QString &json, int index); + static int nextToken(const QString &json, int &index); + + template + QByteArray serializeMap(const T &map, bool &success, int _level = 0) { + QByteArray newline; + QByteArray tabs; + QByteArray tabsFields; + if (prettySerialize && !map.isEmpty()) { + newline = "\n"; + for (uint l=1; l<_level; l++) { + tabs += " "; + } + tabsFields = tabs + " "; + } + + QByteArray str = "{" + newline; + QList pairs; + for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) { + bool otherSuccess = true; + QByteArray serializedValue = serialize(it.value(), otherSuccess, _level); + if (serializedValue.isNull()) { + success = false; + break; + } + pairs << tabsFields + sanitizeString(it.key()).toUtf8() + ":" + (prettySerialize ? " " : "") + serializedValue; + } + + str += join(pairs, "," + newline) + newline; + str += tabs + "}"; + return str; + } + + void insert(QVariant &v, const QString &key, const QVariant &value); + void append(QVariant &v, const QVariant &value); + + template + void cloneMap(QVariant &json, const T &map) { + for (typename T::const_iterator it = map.begin(), itend = map.end(); it != itend; ++it) { + insert(json, it.key(), (*it)); + } + } + + template + void cloneList(QVariant &json, const T &list) { + for (typename T::const_iterator it = list.begin(), itend = list.end(); it != itend; ++it) { + append(json, (*it)); + } + } + + /** + * parse + */ + QVariant parse(const QString &json) { + bool success = true; + return parse(json, success); + } + + /** + * parse + */ + QVariant parse(const QString &json, bool &success) { + success = true; + + // Return an empty QVariant if the JSON data is either null or empty + if (!json.isNull() || !json.isEmpty()) { + QString data = json; + // We'll start from index 0 + int index = 0; + + // Parse the first value + QVariant value = parseValue(data, index, success); + + // Return the parsed value + return value; + } else { + // Return the empty QVariant + return QVariant(); + } + } + + /** + * clone + */ + QVariant clone(const QVariant &data) { + QVariant v; + + if (data.type() == QVariant::Map) { + cloneMap(v, data.toMap()); + } else if (data.type() == QVariant::Hash) { + cloneMap(v, data.toHash()); + } else if (data.type() == QVariant::List) { + cloneList(v, data.toList()); + } else if (data.type() == QVariant::StringList) { + cloneList(v, data.toStringList()); + } else { + v = QVariant(data); + } + + return v; + } + + /** + * insert value (map case) + */ + void insert(QVariant &v, const QString &key, const QVariant &value) { + if (!v.canConvert()) v = QVariantMap(); + QVariantMap *p = (QVariantMap *)v.data(); + p->insert(key, clone(value)); + } + + /** + * append value (list case) + */ + void append(QVariant &v, const QVariant &value) { + if (!v.canConvert()) v = QVariantList(); + QVariantList *p = (QVariantList *)v.data(); + p->append(value); + } + + QByteArray serialize(const QVariant &data) { + bool success = true; + return serialize(data, success); + } + + QByteArray serialize(const QVariant &data, bool &success, int _level /*= 0*/) { + QByteArray newline; + QByteArray tabs; + QByteArray tabsFields; + if (prettySerialize) { + newline = "\n"; + for (uint l=0; l<_level; l++) { + tabs += " "; + } + tabsFields = tabs + " "; + } + + QByteArray str; + success = true; + + if (!data.isValid()) { // invalid or null? + str = "null"; + } else if ((data.type() == QVariant::List) || + (data.type() == QVariant::StringList)) { // variant is a list? + QList values; + const QVariantList list = data.toList(); + Q_FOREACH(const QVariant& v, list) { + bool otherSuccess = true; + QByteArray serializedValue = serialize(v, otherSuccess, _level+1); + if (serializedValue.isNull()) { + success = false; + break; + } + values << tabsFields + serializedValue; + } + + if (!values.isEmpty()) { + str = "[" + newline + join( values, "," + newline ) + newline + tabs + "]"; + } else { + str = "[]"; + } + } else if (data.type() == QVariant::Hash) { // variant is a hash? + str = serializeMap<>(data.toHash(), success, _level+1); + } else if (data.type() == QVariant::Map) { // variant is a map? + str = serializeMap<>(data.toMap(), success, _level+1); + } else if ((data.type() == QVariant::String) || + (data.type() == QVariant::ByteArray)) {// a string or a byte array? + str = sanitizeString(data.toString()).toUtf8(); + } else if (data.type() == QVariant::Double) { // double? + double value = data.toDouble(&success); + if (success) { + str = QByteArray::number(value, 'g'); + if (!str.contains(".") && ! str.contains("e")) { + str += ".0"; + } + } + } else if (data.type() == QVariant::Bool) { // boolean value? + str = data.toBool() ? "true" : "false"; + } else if (data.type() == QVariant::ULongLong) { // large unsigned number? + str = QByteArray::number(data.value()); + } else if (data.canConvert()) { // any signed number? + str = QByteArray::number(data.value()); + } else if (data.canConvert()) { //TODO: this code is never executed because all smaller types can be converted to qlonglong + str = QString::number(data.value()).toUtf8(); + } else if (data.type() == QVariant::DateTime) { // datetime value? + str = sanitizeString(dateTimeFormat.isEmpty() + ? data.toDateTime().toString() + : data.toDateTime().toString(dateTimeFormat)).toUtf8(); + } else if (data.type() == QVariant::Date) { // date value? + str = sanitizeString(dateTimeFormat.isEmpty() + ? data.toDate().toString() + : data.toDate().toString(dateFormat)).toUtf8(); + } else if (data.canConvert()) { // can value be converted to string? + // this will catch QUrl, ... (all other types which can be converted to string) + str = sanitizeString(data.toString()).toUtf8(); + } else { + success = false; + } + + if (success) { + return str; + } + return QByteArray(); + } + + QString serializeStr(const QVariant &data) { + return QString::fromUtf8(serialize(data)); + } + + QString serializeStr(const QVariant &data, bool &success) { + return QString::fromUtf8(serialize(data, success)); + } + + + /** + * \enum JsonToken + */ + enum JsonToken { + JsonTokenNone = 0, + JsonTokenCurlyOpen = 1, + JsonTokenCurlyClose = 2, + JsonTokenSquaredOpen = 3, + JsonTokenSquaredClose = 4, + JsonTokenColon = 5, + JsonTokenComma = 6, + JsonTokenString = 7, + JsonTokenNumber = 8, + JsonTokenTrue = 9, + JsonTokenFalse = 10, + JsonTokenNull = 11 + }; + + static QString sanitizeString(QString str) { + str.replace(QLatin1String("\\"), QLatin1String("\\\\")); + str.replace(QLatin1String("\""), QLatin1String("\\\"")); + str.replace(QLatin1String("\b"), QLatin1String("\\b")); + str.replace(QLatin1String("\f"), QLatin1String("\\f")); + str.replace(QLatin1String("\n"), QLatin1String("\\n")); + str.replace(QLatin1String("\r"), QLatin1String("\\r")); + str.replace(QLatin1String("\t"), QLatin1String("\\t")); + return QString(QLatin1String("\"%1\"")).arg(str); + } + + static QByteArray join(const QList &list, const QByteArray &sep) { + QByteArray res; + Q_FOREACH(const QByteArray &i, list) { + if (!res.isEmpty()) { + res += sep; + } + res += i; + } + return res; + } + + /** + * parseValue + */ + static QVariant parseValue(const QString &json, int &index, bool &success) { + // Determine what kind of data we should parse by + // checking out the upcoming token + switch(lookAhead(json, index)) { + case JsonTokenString: + return parseString(json, index, success); + case JsonTokenNumber: + return parseNumber(json, index); + case JsonTokenCurlyOpen: + return parseObject(json, index, success); + case JsonTokenSquaredOpen: + return parseArray(json, index, success); + case JsonTokenTrue: + nextToken(json, index); + return QVariant(true); + case JsonTokenFalse: + nextToken(json, index); + return QVariant(false); + case JsonTokenNull: + nextToken(json, index); + return QVariant(); + case JsonTokenNone: + break; + } + + // If there were no tokens, flag the failure and return an empty QVariant + success = false; + return QVariant(); + } + + /** + * parseObject + */ + static QVariant parseObject(const QString &json, int &index, bool &success) { + QVariantMap map; + int token; + + // Get rid of the whitespace and increment index + nextToken(json, index); + + // Loop through all of the key/value pairs of the object + bool done = false; + while (!done) { + // Get the upcoming token + token = lookAhead(json, index); + + if (token == JsonTokenNone) { + success = false; + return QVariantMap(); + } else if (token == JsonTokenComma) { + nextToken(json, index); + } else if (token == JsonTokenCurlyClose) { + nextToken(json, index); + return map; + } else { + // Parse the key/value pair's name + QString name = parseString(json, index, success).toString(); + + if (!success) { + return QVariantMap(); + } + + // Get the next token + token = nextToken(json, index); + + // If the next token is not a colon, flag the failure + // return an empty QVariant + if (token != JsonTokenColon) { + success = false; + return QVariant(QVariantMap()); + } + + // Parse the key/value pair's value + QVariant value = parseValue(json, index, success); + + if (!success) { + return QVariantMap(); + } + + // Assign the value to the key in the map + map[name] = value; + } + } + + // Return the map successfully + return QVariant(map); + } + + /** + * parseArray + */ + static QVariant parseArray(const QString &json, int &index, bool &success) { + QVariantList list; + + nextToken(json, index); + + bool done = false; + while(!done) { + int token = lookAhead(json, index); + + if (token == JsonTokenNone) { + success = false; + return QVariantList(); + } else if (token == JsonTokenComma) { + nextToken(json, index); + } else if (token == JsonTokenSquaredClose) { + nextToken(json, index); + break; + } else { + QVariant value = parseValue(json, index, success); + if (!success) { + return QVariantList(); + } + list.push_back(value); + } + } + + return QVariant(list); + } + + /** + * parseString + */ + static QVariant parseString(const QString &json, int &index, bool &success) { + QString s; + QChar c; + + eatWhitespace(json, index); + + c = json[index++]; + + bool complete = false; + while(!complete) { + if (index == json.size()) { + break; + } + + c = json[index++]; + + if (c == '\"') { + complete = true; + break; + } else if (c == '\\') { + if (index == json.size()) { + break; + } + + c = json[index++]; + + if (c == '\"') { + s.append('\"'); + } else if (c == '\\') { + s.append('\\'); + } else if (c == '/') { + s.append('/'); + } else if (c == 'b') { + s.append('\b'); + } else if (c == 'f') { + s.append('\f'); + } else if (c == 'n') { + s.append('\n'); + } else if (c == 'r') { + s.append('\r'); + } else if (c == 't') { + s.append('\t'); + } else if (c == 'u') { + int remainingLength = json.size() - index; + if (remainingLength >= 4) { + QString unicodeStr = json.mid(index, 4); + + int symbol = unicodeStr.toInt(0, 16); + + s.append(QChar(symbol)); + + index += 4; + } else { + break; + } + } + } else { + s.append(c); + } + } + + if (!complete) { + success = false; + return QVariant(); + } + + return QVariant(s); + } + + /** + * parseNumber + */ + static QVariant parseNumber(const QString &json, int &index) { + eatWhitespace(json, index); + + int lastIndex = lastIndexOfNumber(json, index); + int charLength = (lastIndex - index) + 1; + QString numberStr; + + numberStr = json.mid(index, charLength); + + index = lastIndex + 1; + bool ok; + + if (numberStr.contains('.')) { + return QVariant(numberStr.toDouble(NULL)); + } else if (numberStr.startsWith('-')) { + int i = numberStr.toInt(&ok); + if (!ok) { + qlonglong ll = numberStr.toLongLong(&ok); + return ok ? ll : QVariant(numberStr); + } + return i; + } else { + uint u = numberStr.toUInt(&ok); + if (!ok) { + qulonglong ull = numberStr.toULongLong(&ok); + return ok ? ull : QVariant(numberStr); + } + return u; + } + } + + /** + * lastIndexOfNumber + */ + static int lastIndexOfNumber(const QString &json, int index) { + int lastIndex; + + for(lastIndex = index; lastIndex < json.size(); lastIndex++) { + if (QString("0123456789+-.eE").indexOf(json[lastIndex]) == -1) { + break; + } + } + + return lastIndex -1; + } + + /** + * eatWhitespace + */ + static void eatWhitespace(const QString &json, int &index) { + for(; index < json.size(); index++) { + if (QString(" \t\n\r").indexOf(json[index]) == -1) { + break; + } + } + } + + /** + * lookAhead + */ + static int lookAhead(const QString &json, int index) { + int saveIndex = index; + return nextToken(json, saveIndex); + } + + /** + * nextToken + */ + static int nextToken(const QString &json, int &index) { + eatWhitespace(json, index); + + if (index == json.size()) { + return JsonTokenNone; + } + + QChar c = json[index]; + index++; + switch(c.toLatin1()) { + case '{': return JsonTokenCurlyOpen; + case '}': return JsonTokenCurlyClose; + case '[': return JsonTokenSquaredOpen; + case ']': return JsonTokenSquaredClose; + case ',': return JsonTokenComma; + case '"': return JsonTokenString; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '-': return JsonTokenNumber; + case ':': return JsonTokenColon; + } + index--; // ^ WTF? + + int remainingLength = json.size() - index; + + // True + if (remainingLength >= 4) { + if (json[index] == 't' && json[index + 1] == 'r' && + json[index + 2] == 'u' && json[index + 3] == 'e') { + index += 4; + return JsonTokenTrue; + } + } + + // False + if (remainingLength >= 5) { + if (json[index] == 'f' && json[index + 1] == 'a' && + json[index + 2] == 'l' && json[index + 3] == 's' && + json[index + 4] == 'e') { + index += 5; + return JsonTokenFalse; + } + } + + // Null + if (remainingLength >= 4) { + if (json[index] == 'n' && json[index + 1] == 'u' && + json[index + 2] == 'l' && json[index + 3] == 'l') { + index += 4; + return JsonTokenNull; + } + } + + return JsonTokenNone; + } + + void setDateTimeFormat(const QString &format) { + dateTimeFormat = format; + } + + void setDateFormat(const QString &format) { + dateFormat = format; + } + + QString getDateTimeFormat() { + return dateTimeFormat; + } + + QString getDateFormat() { + return dateFormat; + } + + void setPrettySerialize(bool enabled) { + prettySerialize = enabled; + } + + bool isPrettySerialize() { + return prettySerialize; + } + + + + QQueue BuilderJsonObject::created_list; + + BuilderJsonObject::BuilderJsonObject() { + // clean objects previous "created" + while (!BuilderJsonObject::created_list.isEmpty()) { + delete BuilderJsonObject::created_list.dequeue(); + } + } + + BuilderJsonObject::BuilderJsonObject(JsonObject &json) { + BuilderJsonObject(); + + obj = json; + } + + BuilderJsonObject *BuilderJsonObject::set(const QString &key, const QVariant &value) { + obj[key] = value; + + return this; + } + + BuilderJsonObject *BuilderJsonObject::set(const QString &key, BuilderJsonObject *builder) { + return set(key, builder->create()); + } + + BuilderJsonObject *BuilderJsonObject::set(const QString &key, BuilderJsonArray *builder) { + return set(key, builder->create()); + } + + JsonObject BuilderJsonObject::create() { + BuilderJsonObject::created_list.enqueue(this); + + return obj; + } + + + QQueue BuilderJsonArray::created_list; + + BuilderJsonArray::BuilderJsonArray() { + // clean objects previous "created" + while (!BuilderJsonArray::created_list.isEmpty()) { + delete BuilderJsonArray::created_list.dequeue(); + } + } + + BuilderJsonArray::BuilderJsonArray(JsonArray &json) { + BuilderJsonArray(); + + array = json; + } + + BuilderJsonArray *BuilderJsonArray::add(const QVariant &element) { + array.append(element); + + return this; + } + + BuilderJsonArray *BuilderJsonArray::add(BuilderJsonObject *builder) { + return add(builder->create()); + } + + BuilderJsonArray *BuilderJsonArray::add(BuilderJsonArray *builder) { + return add(builder->create()); + } + + JsonArray BuilderJsonArray::create() { + BuilderJsonArray::created_list.enqueue(this); + + return array; + } + + + + + BuilderJsonObject *objectBuilder() { + return new BuilderJsonObject(); + } + + BuilderJsonObject *objectBuilder(JsonObject &json) { + return new BuilderJsonObject(json); + } + + BuilderJsonArray *arrayBuilder() { + return new BuilderJsonArray(); + } + + BuilderJsonArray *arrayBuilder(JsonArray &json) { + return new BuilderJsonArray(json); + } + +} //end namespace diff --git a/client/platforms/ios/json.h b/client/platforms/ios/json.h new file mode 100644 index 00000000..bd37c381 --- /dev/null +++ b/client/platforms/ios/json.h @@ -0,0 +1,265 @@ +/** + * QtJson - A simple class for parsing JSON data into a QVariant hierarchies and vice-versa. + * Copyright (C) 2011 Eeli Reilin + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +/** + * \file json.h + */ + +#ifndef JSON_H +#define JSON_H + +#include +#include +#include + + +/** + * \namespace QtJson + * \brief A JSON data parser + * + * Json parses a JSON data into a QVariant hierarchy. + */ +namespace QtJson { + typedef QVariantMap JsonObject; + typedef QVariantList JsonArray; + + /** + * Clone a JSON object (makes a deep copy) + * + * \param data The JSON object + */ + QVariant clone(const QVariant &data); + + /** + * Insert value to JSON object (QVariantMap) + * + * \param v The JSON object + * \param key The key + * \param value The value + */ + void insert(QVariant &v, const QString &key, const QVariant &value); + + /** + * Append value to JSON array (QVariantList) + * + * \param v The JSON array + * \param value The value + */ + void append(QVariant &v, const QVariant &value); + + /** + * Parse a JSON string + * + * \param json The JSON data + */ + QVariant parse(const QString &json); + + /** + * Parse a JSON string + * + * \param json The JSON data + * \param success The success of the parsing + */ + QVariant parse(const QString &json, bool &success); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * + * \return QByteArray Textual JSON representation in UTF-8 + */ + QByteArray serialize(const QVariant &data); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * \param success The success of the serialization + * + * \return QByteArray Textual JSON representation in UTF-8 + */ + QByteArray serialize(const QVariant &data, bool &success, int _level = 0); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * + * \return QString Textual JSON representation + */ + QString serializeStr(const QVariant &data); + + /** + * This method generates a textual JSON representation + * + * \param data The JSON data generated by the parser. + * \param success The success of the serialization + * + * \return QString Textual JSON representation + */ + QString serializeStr(const QVariant &data, bool &success, int _level = 0); + + /** + * This method sets date(time) format to be used for QDateTime::toString + * If QString is empty, Qt::TextDate is used. + * + * \param format The JSON data generated by the parser. + */ + void setDateTimeFormat(const QString& format); + void setDateFormat(const QString& format); + + /** + * This method gets date(time) format to be used for QDateTime::toString + * If QString is empty, Qt::TextDate is used. + */ + QString getDateTimeFormat(); + QString getDateFormat(); + + /** + * @brief setPrettySerialize enable/disabled pretty-print when serialize() a json + * @param enabled + */ + void setPrettySerialize(bool enabled); + + /** + * @brief isPrettySerialize check if is enabled pretty-print when serialize() a json + * @return + */ + bool isPrettySerialize(); + + + + + /** + * QVariant based Json object + */ + class Object : public QVariant { + template + Object& insertKey(Object* ptr, const QString& key) { + T* p = (T*)ptr->data(); + if (!p->contains(key)) p->insert(key, QVariant()); + return *reinterpret_cast(&p->operator[](key)); + } + template + void removeKey(Object *ptr, const QString& key) { + T* p = (T*)ptr->data(); + p->remove(key); + } + public: + Object() : QVariant() {} + Object(const Object& ref) : QVariant(ref) {} + + Object& operator=(const QVariant& rhs) { + /** It maybe more robust when running under Qt versions below 4.7 */ + QObject * obj = qvariant_cast(rhs); + // setValue(rhs); + setValue(obj); + return *this; + } + Object& operator[](const QString& key) { + if (type() == QVariant::Map) + return insertKey(this, key); + else if (type() == QVariant::Hash) + return insertKey(this, key); + + setValue(QVariantMap()); + + return insertKey(this, key); + } + const Object& operator[](const QString& key) const { + return const_cast(this)->operator[](key); + } + void remove(const QString& key) { + if (type() == QVariant::Map) + removeKey(this, key); + else if (type() == QVariant::Hash) + removeKey(this, key); + } + }; + + + class BuilderJsonArray; + + /** + * @brief The BuilderJsonObject class + */ + class BuilderJsonObject { + + public: + BuilderJsonObject(); + BuilderJsonObject(JsonObject &json); + + BuilderJsonObject *set(const QString &key, const QVariant &value); + BuilderJsonObject *set(const QString &key, BuilderJsonObject *builder); + BuilderJsonObject *set(const QString &key, BuilderJsonArray *builder); + JsonObject create(); + + private: + static QQueue created_list; + + JsonObject obj; + }; + + /** + * @brief The BuilderJsonArray class + */ + class BuilderJsonArray { + + public: + BuilderJsonArray(); + BuilderJsonArray(JsonArray &json); + + BuilderJsonArray *add(const QVariant &element); + BuilderJsonArray *add(BuilderJsonObject *builder); + BuilderJsonArray *add(BuilderJsonArray *builder); + JsonArray create(); + + private: + static QQueue created_list; + + JsonArray array; + }; + + + /** + * @brief Create a BuilderJsonObject + * @return + */ + BuilderJsonObject *objectBuilder(); + + /** + * @brief Create a BuilderJsonObject starting from copy of another json + * @return + */ + BuilderJsonObject *objectBuilder(JsonObject &json); + + /** + * @brief Create a BuilderJsonArray + * @return + */ + BuilderJsonArray *arrayBuilder(); + + /** + * @brief Create a BuilderJsonArray starting from copy of another json + * @return + */ + BuilderJsonArray *arrayBuilder(JsonArray &json); +} + +#endif //JSON_H \ No newline at end of file diff --git a/client/protocols/android_vpnprotocol.cpp b/client/protocols/android_vpnprotocol.cpp index 0b986ab5..3f8141b0 100644 --- a/client/protocols/android_vpnprotocol.cpp +++ b/client/protocols/android_vpnprotocol.cpp @@ -46,7 +46,7 @@ constexpr auto PERMISSIONHELPER_CLASS = } // namespace -AndroidVpnProtocol::AndroidVpnProtocol(Protocol protocol, const QJsonObject &configuration, QObject* parent) +AndroidVpnProtocol::AndroidVpnProtocol(Proto protocol, const QJsonObject &configuration, QObject* parent) : VpnProtocol(configuration, parent), m_protocol(protocol), m_binder(this) diff --git a/client/protocols/android_vpnprotocol.h b/client/protocols/android_vpnprotocol.h index 23365eee..55ac0fdd 100644 --- a/client/protocols/android_vpnprotocol.h +++ b/client/protocols/android_vpnprotocol.h @@ -17,7 +17,7 @@ class AndroidVpnProtocol : public VpnProtocol, Q_OBJECT public: - explicit AndroidVpnProtocol(Protocol protocol, const QJsonObject& configuration, QObject* parent = nullptr); + explicit AndroidVpnProtocol(Proto protocol, const QJsonObject& configuration, QObject* parent = nullptr); static AndroidVpnProtocol* instance(); virtual ~AndroidVpnProtocol() override = default; @@ -53,7 +53,7 @@ protected: private: - Protocol m_protocol; + Proto m_protocol; bool m_serviceConnected = false; std::function m_logCallback; diff --git a/client/protocols/ios_vpnprotocol.h b/client/protocols/ios_vpnprotocol.h new file mode 100644 index 00000000..d66a89a7 --- /dev/null +++ b/client/protocols/ios_vpnprotocol.h @@ -0,0 +1,51 @@ +#ifndef IOS_VPNPROTOCOL_H +#define IOS_VPNPROTOCOL_H + +#include "vpnprotocol.h" +#include "protocols/protocols_defs.h" + +using namespace amnezia; + + +class IOSVpnProtocol : public VpnProtocol +{ + Q_OBJECT + +public: + explicit IOSVpnProtocol(amnezia::Proto proto, const QJsonObject& configuration, QObject* parent = nullptr); + static IOSVpnProtocol* instance(); + + virtual ~IOSVpnProtocol() = default; + + bool initialize(); + + virtual ErrorCode start() override; + virtual void stop() override; + + void resume_start(); + + void checkStatus(); + + void setNotificationText(const QString& title, const QString& message, + int timerSec); + void setFallbackConnectedNotification(); + + void getBackendLogs(std::function&& callback); + + void cleanupBackendLogs(); + +signals: + +protected slots: + +protected: + +private: + Proto m_protocol; + bool m_serviceConnected = false; + bool m_checkingStatus = false; + std::function m_logCallback; +}; + + +#endif // IOS_VPNPROTOCOL_H diff --git a/client/protocols/ios_vpnprotocol.mm b/client/protocols/ios_vpnprotocol.mm new file mode 100644 index 00000000..4dcc600b --- /dev/null +++ b/client/protocols/ios_vpnprotocol.mm @@ -0,0 +1,298 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "json.h" + +#include "ipaddressrange.h" +#include "ios_vpnprotocol.h" +#include "core/errorstrings.h" +#include "AmneziaVPN-Swift.h" + +namespace +{ +IOSVpnProtocol* s_instance = nullptr; +IOSVpnProtocolImpl* m_controller = nullptr; +} + +IOSVpnProtocol::IOSVpnProtocol(Proto proto, const QJsonObject &configuration, QObject* parent) +: VpnProtocol(configuration, parent), +m_protocol(proto) {} + +IOSVpnProtocol* IOSVpnProtocol::instance() { + return s_instance; +} + +bool IOSVpnProtocol::initialize() +{ + qDebug() << "Initializing Swift Controller"; + + static bool creating = false; + // No nested creation! + Q_ASSERT(creating == false); + creating = true; + + if (!m_controller) { + bool ok; + QtJson::JsonObject result = QtJson::parse(QJsonDocument(m_rawConfig).toJson(), ok).toMap(); + + if(!ok) { + qDebug() << QString("An error occurred during parsing"); + return false; + } + + QString vpnProto = result["protocol"].toString(); + qDebug() << "protocol: " << vpnProto; + qDebug() << "config data => "; + QtJson::JsonObject config = result["wireguard_config_data"].toMap(); + + QString privateKey = config["client_priv_key"].toString(); + QByteArray key = QByteArray::fromBase64(privateKey.toLocal8Bit()); + + qDebug() << " - " << "client_priv_key: " << config["client_priv_key"].toString(); + qDebug() << " - " << "client_pub_key: " << config["client_pub_key"].toString(); + qDebug() << " - " << "interface config: " << config["config"].toString(); + + QString addr = config["config"].toString().split("\n").takeAt(1).split(" = ").takeLast(); + QString dns = config["config"].toString().split("\n").takeAt(2).split(" = ").takeLast(); + QString privkey = config["config"].toString().split("\n").takeAt(3).split(" = ").takeLast(); + QString pubkey = config["config"].toString().split("\n").takeAt(6).split(" = ").takeLast(); + QString presharedkey = config["config"].toString().split("\n").takeAt(7).split(" = ").takeLast(); + QString allowedips = config["config"].toString().split("\n").takeAt(8).split(" = ").takeLast(); + QString endpoint = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast(); + QString keepalive = config["config"].toString().split("\n").takeAt(10).split(" = ").takeLast(); + qDebug() << " - " << "[Interface] address: " << addr; + qDebug() << " - " << "[Interface] dns: " << dns; + qDebug() << " - " << "[Interface] private key: " << privkey; + qDebug() << " - " << "[Peer] public key: " << pubkey; + qDebug() << " - " << "[Peer] preshared key: " << presharedkey; + qDebug() << " - " << "[Peer] allowed ips: " << allowedips; + qDebug() << " - " << "[Peer] endpoint: " << endpoint; + qDebug() << " - " << "[Peer] keepalive: " << keepalive; + + qDebug() << " - " << "hostName: " << config["hostName"].toString(); + qDebug() << " - " << "psk_key: " << config["psk_key"].toString(); + qDebug() << " - " << "server_pub_key: " << config["server_pub_key"].toString(); + + + + m_controller = [[IOSVpnProtocolImpl alloc] initWithBundleID:@VPN_NE_BUNDLEID + privateKey:key.toNSData() + deviceIpv4Address:addr.toNSString() + deviceIpv6Address:@"::/0" + closure:^(ConnectionState state, NSDate* date) { + qDebug() << "Creation completed with connection state:" << state; + creating = false; + + switch (state) { + case ConnectionStateError: { + [m_controller dealloc]; + m_controller = nullptr; + emit initialized(false, false, QDateTime()); + return; + } + case ConnectionStateConnected: { + Q_ASSERT(date); + QDateTime qtDate(QDateTime::fromNSDate(date)); + emit initialized(true, true, qtDate); + return; + } + case ConnectionStateDisconnected: + // Just in case we are connecting, let's call disconnect. + [m_controller disconnect]; + emit initialized(true, false, QDateTime()); + return; + } + } + callback:^(BOOL a_connected) { + qDebug() << "State changed: " << a_connected; + if (a_connected) { + emit isConnected(); + return; + } + + emit isDisconnected(); + }]; + } + return true; +} + +ErrorCode IOSVpnProtocol::start() +{ + bool ok; + QtJson::JsonObject result = QtJson::parse(QJsonDocument(m_rawConfig).toJson(), ok).toMap(); + + if(!ok) { + qDebug() << QString("An error occurred during config parsing"); + return InternalError; + } + QString protocol = result["protocol"].toString(); + QtJson::JsonObject config = result["wireguard_config_data"].toMap(); + + QString clientPrivateKey = config["client_priv_key"].toString(); + QByteArray key = QByteArray::fromBase64(clientPrivateKey.toLocal8Bit()); + QString clientPubKey = config["client_pub_key"].toString(); + + QString addr = config["config"].toString().split("\n").takeAt(1).split(" = ").takeLast(); + QStringList dnsServersList = config["config"].toString().split("\n").takeAt(2).split(" = ").takeLast().split(", "); + QString privkey = config["config"].toString().split("\n").takeAt(3).split(" = ").takeLast(); + QString pubkey = config["config"].toString().split("\n").takeAt(6).split(" = ").takeLast(); + QString presharedkey = config["config"].toString().split("\n").takeAt(7).split(" = ").takeLast(); + QStringList allowedIPList = config["config"].toString().split("\n").takeAt(8).split(" = ").takeLast().split(", "); + QString endpoint = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast(); + QString serverAddr = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast().split(":").takeFirst(); + QString port = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast().split(":").takeLast(); + QString keepalive = config["config"].toString().split("\n").takeAt(10).split(" = ").takeLast(); + + QString hostname = config["hostName"].toString(); + QString pskKey = config["psk_key"].toString(); + QString serverPubKey = config["server_pub_key"].toString(); + + qDebug() << "IOSVPNProtocol starts for" << hostname; + qDebug() << "DNS:" << dnsServersList.takeFirst().toNSString(); + qDebug() << "serverPublicKey:" << serverPubKey.toNSString(); + qDebug() << "serverIpv4AddrIn:" << serverAddr.toNSString(); + qDebug() << "serverPort:" << (uint32_t)port.toInt(); + qDebug() << "allowed ip list" << allowedIPList; + + NSMutableArray* allowedIPAddressRangesNS = + [NSMutableArray arrayWithCapacity:allowedIPList.length()]; + for (const IPAddressRange item : allowedIPList) { + VPNIPAddressRange* range = + [[VPNIPAddressRange alloc] initWithAddress:item.ipAddress().toNSString() + networkPrefixLength:item.range() + isIpv6:item.type() == IPAddressRange::IPv6]; + [allowedIPAddressRangesNS addObject:[range autorelease]]; + } + + [m_controller connectWithDnsServer:dnsServersList.takeFirst().toNSString() + serverIpv6Gateway:@"FE80::1" + serverPublicKey:serverPubKey.toNSString() + serverIpv4AddrIn:serverAddr.toNSString() + serverPort:port.toInt() + allowedIPAddressRanges:allowedIPAddressRangesNS + ipv6Enabled:NO + reason:0 + failureCallback:^() { + qDebug() << "IOSVPNProtocol - connection failed"; + emit isDisconnected(); + }]; + return NoError; +} + +void IOSVpnProtocol::stop() +{ + if (!m_controller) { + qDebug() << "Not correctly initialized"; + emit isDisconnected(); + return; + } + + [m_controller disconnect]; +} + +void IOSVpnProtocol::resume_start() +{ + +} + +void IOSVpnProtocol::checkStatus() +{ + qDebug() << "Checking status"; + + if (m_checkingStatus) { + qDebug() << "We are still waiting for the previous status."; + return; + } + + if (!m_controller) { + qDebug() << "Not correctly initialized"; + return; + } + + m_checkingStatus = true; + + [m_controller checkStatusWithCallback:^(NSString* serverIpv4Gateway, NSString* deviceIpv4Address, + NSString* configString) { + QString config = QString::fromNSString(configString); + + m_checkingStatus = false; + + if (config.isEmpty()) { + return; + } + + uint64_t txBytes = 0; + uint64_t rxBytes = 0; + + QStringList lines = config.split("\n"); + for (const QString& line : lines) { + if (line.startsWith("tx_bytes=")) { + txBytes = line.split("=")[1].toULongLong(); + } else if (line.startsWith("rx_bytes=")) { + rxBytes = line.split("=")[1].toULongLong(); + } + + if (txBytes && rxBytes) { + break; + } + } + + qDebug() << "ServerIpv4Gateway:" << QString::fromNSString(serverIpv4Gateway) + << "DeviceIpv4Address:" << QString::fromNSString(deviceIpv4Address) + << "RxBytes:" << rxBytes << "TxBytes:" << txBytes; + emit bytesChanged(rxBytes, txBytes); + + }]; +} + +void IOSVpnProtocol::setNotificationText(const QString &title, const QString &message, int timerSec) +{ + // TODO: add user notifications? +} + +void IOSVpnProtocol::setFallbackConnectedNotification() +{ + // TODO: add default user notifications? +} + +void IOSVpnProtocol::getBackendLogs(std::function &&callback) +{ + std::function a_callback = std::move(callback); + + QString groupId(GROUP_ID); + NSURL* groupPath = [[NSFileManager defaultManager] + containerURLForSecurityApplicationGroupIdentifier:groupId.toNSString()]; + + NSURL* path = [groupPath URLByAppendingPathComponent:@"networkextension.log"]; + + QFile file(QString::fromNSString([path path])); + if (!file.open(QIODevice::ReadOnly)) { + a_callback("Network extension log file missing or unreadable."); + return; + } + + QByteArray content = file.readAll(); + a_callback(content); +} + +void IOSVpnProtocol::cleanupBackendLogs() +{ + QString groupId(GROUP_ID); + NSURL* groupPath = [[NSFileManager defaultManager] + containerURLForSecurityApplicationGroupIdentifier:groupId.toNSString()]; + + NSURL* path = [groupPath URLByAppendingPathComponent:@"networkextension.log"]; + + QFile file(QString::fromNSString([path path])); + file.remove(); +} + diff --git a/client/protocols/protocols_defs.cpp b/client/protocols/protocols_defs.cpp index 30f1a2f1..93578d12 100644 --- a/client/protocols/protocols_defs.cpp +++ b/client/protocols/protocols_defs.cpp @@ -2,7 +2,7 @@ using namespace amnezia; -QDebug operator<<(QDebug debug, const amnezia::ProtocolEnumNS::Protocol &p) +QDebug operator<<(QDebug debug, const amnezia::ProtocolEnumNS::Proto &p) { QDebugStateSaver saver(debug); debug.nospace() << ProtocolProps::protoToString(p); @@ -10,29 +10,29 @@ QDebug operator<<(QDebug debug, const amnezia::ProtocolEnumNS::Protocol &p) return debug; } -amnezia::Protocol ProtocolProps::protoFromString(QString proto){ - QMetaEnum metaEnum = QMetaEnum::fromType(); +amnezia::Proto ProtocolProps::protoFromString(QString proto){ + QMetaEnum metaEnum = QMetaEnum::fromType(); for (int i = 0; i < metaEnum.keyCount(); ++i) { - Protocol p = static_cast(i); + Proto p = static_cast(i); if (proto == protoToString(p)) return p; } - return Protocol::Any; + return Proto::Any; } -QString ProtocolProps::protoToString(amnezia::Protocol p){ - if (p == Protocol::Any) return ""; +QString ProtocolProps::protoToString(amnezia::Proto p){ + if (p == Proto::Any) return ""; - QMetaEnum metaEnum = QMetaEnum::fromType(); + QMetaEnum metaEnum = QMetaEnum::fromType(); QString protoKey = metaEnum.valueToKey(static_cast(p)); return protoKey.toLower(); } -QList ProtocolProps::allProtocols() +QList ProtocolProps::allProtocols() { - QMetaEnum metaEnum = QMetaEnum::fromType(); - QList all; + QMetaEnum metaEnum = QMetaEnum::fromType(); + QList all; for (int i = 0; i < metaEnum.keyCount(); ++i) { - all.append(static_cast(i)); + all.append(static_cast(i)); } return all; @@ -48,7 +48,7 @@ TransportProto ProtocolProps::transportProtoFromString(QString p) return TransportProto::Udp; } -QString ProtocolProps::transportProtoToString(TransportProto proto, Protocol p) +QString ProtocolProps::transportProtoToString(TransportProto proto, Proto p) { QMetaEnum metaEnum = QMetaEnum::fromType(); QString protoKey = metaEnum.valueToKey(static_cast(proto)); @@ -66,124 +66,124 @@ QString ProtocolProps::transportProtoToString(TransportProto proto, Protocol p) } -QMap ProtocolProps::protocolHumanNames() +QMap ProtocolProps::protocolHumanNames() { return { - {Protocol::OpenVpn, "OpenVPN"}, - {Protocol::ShadowSocks, "ShadowSocks"}, - {Protocol::Cloak, "Cloak"}, - {Protocol::WireGuard, "WireGuard"}, - {Protocol::Ikev2, "IKEv2"}, - {Protocol::L2tp, "L2TP"}, + {Proto::OpenVpn, "OpenVPN"}, + {Proto::ShadowSocks, "ShadowSocks"}, + {Proto::Cloak, "Cloak"}, + {Proto::WireGuard, "WireGuard"}, + {Proto::Ikev2, "IKEv2"}, + {Proto::L2tp, "L2TP"}, - {Protocol::TorWebSite, "Web site in TOR network"}, - {Protocol::Dns, "DNS Service"}, - {Protocol::FileShare, "File Sharing Service"}, - {Protocol::Sftp, QObject::tr("Sftp service")} + {Proto::TorWebSite, "Web site in TOR network"}, + {Proto::Dns, "DNS Service"}, + {Proto::FileShare, "File Sharing Service"}, + {Proto::Sftp, QObject::tr("Sftp service")} }; } -QMap ProtocolProps::protocolDescriptions() +QMap ProtocolProps::protocolDescriptions() { return {}; } -amnezia::ServiceType ProtocolProps::protocolService(Protocol p) +amnezia::ServiceType ProtocolProps::protocolService(Proto p) { switch (p) { - case Protocol::Any : return ServiceType::None; - case Protocol::OpenVpn : return ServiceType::Vpn; - case Protocol::Cloak : return ServiceType::Vpn; - case Protocol::ShadowSocks : return ServiceType::Vpn; - case Protocol::WireGuard : return ServiceType::Vpn; - case Protocol::TorWebSite : return ServiceType::Other; - case Protocol::Dns : return ServiceType::Other; - case Protocol::FileShare : return ServiceType::Other; + case Proto::Any : return ServiceType::None; + case Proto::OpenVpn : return ServiceType::Vpn; + case Proto::Cloak : return ServiceType::Vpn; + case Proto::ShadowSocks : return ServiceType::Vpn; + case Proto::WireGuard : return ServiceType::Vpn; + case Proto::TorWebSite : return ServiceType::Other; + case Proto::Dns : return ServiceType::Other; + case Proto::FileShare : return ServiceType::Other; default: return ServiceType::Other; } } -int ProtocolProps::defaultPort(Protocol p) +int ProtocolProps::defaultPort(Proto p) { switch (p) { - case Protocol::Any : return -1; - case Protocol::OpenVpn : return 1194; - case Protocol::Cloak : return 443; - case Protocol::ShadowSocks : return 6789; - case Protocol::WireGuard : return 51820; - case Protocol::Ikev2 : return -1; - case Protocol::L2tp : return -1; + case Proto::Any : return -1; + case Proto::OpenVpn : return 1194; + case Proto::Cloak : return 443; + case Proto::ShadowSocks : return 6789; + case Proto::WireGuard : return 51820; + case Proto::Ikev2 : return -1; + case Proto::L2tp : return -1; - case Protocol::TorWebSite : return -1; - case Protocol::Dns : return 53; - case Protocol::FileShare : return 139; - case Protocol::Sftp : return 222; + case Proto::TorWebSite : return -1; + case Proto::Dns : return 53; + case Proto::FileShare : return 139; + case Proto::Sftp : return 222; default: return -1; } } -bool ProtocolProps::defaultPortChangeable(Protocol p) +bool ProtocolProps::defaultPortChangeable(Proto p) { switch (p) { - case Protocol::Any : return false; - case Protocol::OpenVpn : return true; - case Protocol::Cloak : return true; - case Protocol::ShadowSocks : return true; - case Protocol::WireGuard : return true; - case Protocol::Ikev2 : return false; - case Protocol::L2tp : return false; + case Proto::Any : return false; + case Proto::OpenVpn : return true; + case Proto::Cloak : return true; + case Proto::ShadowSocks : return true; + case Proto::WireGuard : return true; + case Proto::Ikev2 : return false; + case Proto::L2tp : return false; - case Protocol::TorWebSite : return true; - case Protocol::Dns : return false; - case Protocol::FileShare : return false; + case Proto::TorWebSite : return true; + case Proto::Dns : return false; + case Proto::FileShare : return false; default: return -1; } } -TransportProto ProtocolProps::defaultTransportProto(Protocol p) +TransportProto ProtocolProps::defaultTransportProto(Proto p) { switch (p) { - case Protocol::Any : return TransportProto::Udp; - case Protocol::OpenVpn : return TransportProto::Udp; - case Protocol::Cloak : return TransportProto::Tcp; - case Protocol::ShadowSocks : return TransportProto::Tcp; - case Protocol::WireGuard : return TransportProto::Udp; - case Protocol::Ikev2 : return TransportProto::Udp; - case Protocol::L2tp : return TransportProto::Udp; + case Proto::Any : return TransportProto::Udp; + case Proto::OpenVpn : return TransportProto::Udp; + case Proto::Cloak : return TransportProto::Tcp; + case Proto::ShadowSocks : return TransportProto::Tcp; + case Proto::WireGuard : return TransportProto::Udp; + case Proto::Ikev2 : return TransportProto::Udp; + case Proto::L2tp : return TransportProto::Udp; // non-vpn - case Protocol::TorWebSite : return TransportProto::Tcp; - case Protocol::Dns : return TransportProto::Udp; - case Protocol::FileShare : return TransportProto::Udp; - case Protocol::Sftp : return TransportProto::Tcp; + case Proto::TorWebSite : return TransportProto::Tcp; + case Proto::Dns : return TransportProto::Udp; + case Proto::FileShare : return TransportProto::Udp; + case Proto::Sftp : return TransportProto::Tcp; } } -bool ProtocolProps::defaultTransportProtoChangeable(Protocol p) +bool ProtocolProps::defaultTransportProtoChangeable(Proto p) { switch (p) { - case Protocol::Any : return false; - case Protocol::OpenVpn : return true; - case Protocol::Cloak : return false; - case Protocol::ShadowSocks : return false; - case Protocol::WireGuard : return false; - case Protocol::Ikev2 : return false; - case Protocol::L2tp : return false; + case Proto::Any : return false; + case Proto::OpenVpn : return true; + case Proto::Cloak : return false; + case Proto::ShadowSocks : return false; + case Proto::WireGuard : return false; + case Proto::Ikev2 : return false; + case Proto::L2tp : return false; // non-vpn - case Protocol::TorWebSite : return false; - case Protocol::Dns : return false; - case Protocol::FileShare : return false; - case Protocol::Sftp : return false; + case Proto::TorWebSite : return false; + case Proto::Dns : return false; + case Proto::FileShare : return false; + case Proto::Sftp : return false; default: return false; } } -QString ProtocolProps::key_proto_config_data(Protocol p) +QString ProtocolProps::key_proto_config_data(Proto p) { return protoToString(p) + "_config_data"; } -QString ProtocolProps::key_proto_config_path(Protocol p) +QString ProtocolProps::key_proto_config_path(Proto p) { return protoToString(p) + "_config_path"; } diff --git a/client/protocols/protocols_defs.h b/client/protocols/protocols_defs.h index 6715604d..6f8a201e 100644 --- a/client/protocols/protocols_defs.h +++ b/client/protocols/protocols_defs.h @@ -24,7 +24,7 @@ constexpr char containers[] = "containers"; constexpr char container[] = "container"; constexpr char defaultContainer[] = "defaultContainer"; -constexpr char protocol[] = "protocol"; +constexpr char vpnproto[] = "protocol"; constexpr char protocols[] = "protocols"; constexpr char remote[] = "remote"; @@ -121,7 +121,7 @@ enum TransportProto { }; Q_ENUM_NS(TransportProto) -enum Protocol { +enum Proto { Any = 0, OpenVpn, ShadowSocks, @@ -136,7 +136,7 @@ enum Protocol { FileShare, Sftp }; -Q_ENUM_NS(Protocol) +Q_ENUM_NS(Proto) enum ServiceType { None = 0, @@ -153,29 +153,29 @@ class ProtocolProps : public QObject Q_OBJECT public: - Q_INVOKABLE static QList allProtocols(); + Q_INVOKABLE static QList allProtocols(); // spelling may differ for various protocols - TCP for OpenVPN, tcp for others Q_INVOKABLE static TransportProto transportProtoFromString(QString p); - Q_INVOKABLE static QString transportProtoToString(TransportProto proto, Protocol p = Protocol::Any); + Q_INVOKABLE static QString transportProtoToString(TransportProto proto, Proto p = Proto::Any); - Q_INVOKABLE static Protocol protoFromString(QString p); - Q_INVOKABLE static QString protoToString(Protocol p); + Q_INVOKABLE static Proto protoFromString(QString p); + Q_INVOKABLE static QString protoToString(Proto p); - Q_INVOKABLE static QMap protocolHumanNames(); - Q_INVOKABLE static QMap protocolDescriptions(); + Q_INVOKABLE static QMap protocolHumanNames(); + Q_INVOKABLE static QMap protocolDescriptions(); - Q_INVOKABLE static ServiceType protocolService(Protocol p); + Q_INVOKABLE static ServiceType protocolService(Proto p); - Q_INVOKABLE static int defaultPort(Protocol p); - Q_INVOKABLE static bool defaultPortChangeable(Protocol p); + Q_INVOKABLE static int defaultPort(Proto p); + Q_INVOKABLE static bool defaultPortChangeable(Proto p); - Q_INVOKABLE static TransportProto defaultTransportProto(Protocol p); - Q_INVOKABLE static bool defaultTransportProtoChangeable(Protocol p); + Q_INVOKABLE static TransportProto defaultTransportProto(Proto p); + Q_INVOKABLE static bool defaultTransportProtoChangeable(Proto p); - Q_INVOKABLE static QString key_proto_config_data(Protocol p); - Q_INVOKABLE static QString key_proto_config_path(Protocol p); + Q_INVOKABLE static QString key_proto_config_data(Proto p); + Q_INVOKABLE static QString key_proto_config_path(Proto p); }; @@ -207,6 +207,6 @@ static void declareQmlProtocolEnum() { } // namespace amnezia -QDebug operator<<(QDebug debug, const amnezia::Protocol &p); +QDebug operator<<(QDebug debug, const amnezia::Proto &p); #endif // PROTOCOLS_DEFS_H diff --git a/client/protocols/vpnprotocol.cpp b/client/protocols/vpnprotocol.cpp index 34589e3c..5cea3a3b 100644 --- a/client/protocols/vpnprotocol.cpp +++ b/client/protocols/vpnprotocol.cpp @@ -15,7 +15,7 @@ VpnProtocol::VpnProtocol(const QJsonObject &configuration, QObject* parent) : QObject(parent), - m_connectionState(ConnectionState::Unknown), + m_connectionState(VpnConnectionState::Unknown), m_rawConfig(configuration), m_timeoutTimer(new QTimer(this)), m_receivedBytes(0), @@ -29,7 +29,7 @@ void VpnProtocol::setLastError(ErrorCode lastError) { m_lastError = lastError; if (lastError){ - setConnectionState(ConnectionState::Error); + setConnectionState(VpnConnectionState::Error); } qCritical().noquote() << "VpnProtocol error, code" << m_lastError << errorString(m_lastError); } @@ -57,7 +57,7 @@ void VpnProtocol::stopTimeoutTimer() m_timeoutTimer->stop(); } -VpnProtocol::ConnectionState VpnProtocol::connectionState() const +VpnProtocol::VpnConnectionState VpnProtocol::connectionState() const { return m_connectionState; } @@ -70,19 +70,19 @@ void VpnProtocol::setBytesChanged(quint64 receivedBytes, quint64 sentBytes) m_sentBytes = sentBytes; } -void VpnProtocol::setConnectionState(VpnProtocol::ConnectionState state) +void VpnProtocol::setConnectionState(VpnProtocol::VpnConnectionState state) { qDebug() << "VpnProtocol::setConnectionState" << textConnectionState(state); if (m_connectionState == state) { return; } - if (m_connectionState == ConnectionState::Disconnected && state == ConnectionState::Disconnecting) { + if (m_connectionState == VpnConnectionState::Disconnected && state == VpnConnectionState::Disconnecting) { return; } m_connectionState = state; - if (m_connectionState == ConnectionState::Disconnected) { + if (m_connectionState == VpnConnectionState::Disconnected) { m_receivedBytes = 0; m_sentBytes = 0; } @@ -116,17 +116,17 @@ QString VpnProtocol::routeGateway() const return m_routeGateway; } -QString VpnProtocol::textConnectionState(ConnectionState connectionState) +QString VpnProtocol::textConnectionState(VpnConnectionState connectionState) { switch (connectionState) { - case ConnectionState::Unknown: return tr("Unknown"); - case ConnectionState::Disconnected: return tr("Disconnected"); - case ConnectionState::Preparing: return tr("Preparing"); - case ConnectionState::Connecting: return tr("Connecting..."); - case ConnectionState::Connected: return tr("Connected"); - case ConnectionState::Disconnecting: return tr("Disconnecting..."); - case ConnectionState::Reconnecting: return tr("Reconnecting..."); - case ConnectionState::Error: return tr("Error"); + case VpnConnectionState::Unknown: return tr("Unknown"); + case VpnConnectionState::Disconnected: return tr("Disconnected"); + case VpnConnectionState::Preparing: return tr("Preparing"); + case VpnConnectionState::Connecting: return tr("Connecting..."); + case VpnConnectionState::Connected: return tr("Connected"); + case VpnConnectionState::Disconnecting: return tr("Disconnecting..."); + case VpnConnectionState::Reconnecting: return tr("Reconnecting..."); + case VpnConnectionState::Error: return tr("Error"); default: ; } @@ -141,10 +141,10 @@ QString VpnProtocol::textConnectionState() const bool VpnProtocol::isConnected() const { - return m_connectionState == ConnectionState::Connected; + return m_connectionState == VpnConnectionState::Connected; } bool VpnProtocol::isDisconnected() const { - return m_connectionState == ConnectionState::Disconnected; + return m_connectionState == VpnConnectionState::Disconnected; } diff --git a/client/protocols/vpnprotocol.h b/client/protocols/vpnprotocol.h index c6bfc3b2..8310d825 100644 --- a/client/protocols/vpnprotocol.h +++ b/client/protocols/vpnprotocol.h @@ -20,10 +20,10 @@ public: explicit VpnProtocol(const QJsonObject& configuration, QObject* parent = nullptr); virtual ~VpnProtocol() override = default; - enum ConnectionState {Unknown, Disconnected, Preparing, Connecting, Connected, Disconnecting, Reconnecting, Error}; - Q_ENUM(ConnectionState) + enum VpnConnectionState {Unknown, Disconnected, Preparing, Connecting, Connected, Disconnecting, Reconnecting, Error}; + Q_ENUM(VpnConnectionState) - static QString textConnectionState(ConnectionState connectionState); + static QString textConnectionState(VpnConnectionState connectionState); virtual ErrorCode prepare() { return ErrorCode::NoError; } @@ -32,7 +32,7 @@ public: virtual ErrorCode start() = 0; virtual void stop() = 0; - ConnectionState connectionState() const; + VpnConnectionState connectionState() const; ErrorCode lastError() const; QString textConnectionState() const; void setLastError(ErrorCode lastError); @@ -44,7 +44,7 @@ public: signals: void bytesChanged(quint64 receivedBytes, quint64 sentBytes); - void connectionStateChanged(VpnProtocol::ConnectionState state); + void connectionStateChanged(VpnProtocol::VpnConnectionState state); void timeoutTimerEvent(); void protocolError(amnezia::ErrorCode e); @@ -63,9 +63,9 @@ protected: void stopTimeoutTimer(); virtual void setBytesChanged(quint64 receivedBytes, quint64 sentBytes); - virtual void setConnectionState(VpnProtocol::ConnectionState state); + virtual void setConnectionState(VpnProtocol::VpnConnectionState state); - ConnectionState m_connectionState; + VpnConnectionState m_connectionState; QString m_routeGateway; QString m_vpnLocalAddress; QString m_vpnGateway; diff --git a/client/scripts/commons.sh b/client/scripts/commons.sh new file mode 100644 index 00000000..13ff5703 --- /dev/null +++ b/client/scripts/commons.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +printv() { + if [ -t 1 ]; then + NCOLORS=$(tput colors) + + if test -n "$NCOLORS" && test "$NCOLORS" -ge 8; then + NORMAL="$(tput sgr0)" + RED="$(tput setaf 1)" + GREEN="$(tput setaf 2)" + YELLOW="$(tput setaf 3)" + fi + fi + + if [[ $2 = 'G' ]]; then + # shellcheck disable=SC2086 + echo $1 -e "${GREEN}$3${NORMAL}" + elif [[ $2 = 'Y' ]]; then + # shellcheck disable=SC2086 + echo $1 -e "${YELLOW}$3${NORMAL}" + elif [[ $2 = 'N' ]]; then + # shellcheck disable=SC2086 + echo $1 -e "$3" + else + # shellcheck disable=SC2086 + echo $1 -e "${RED}$3${NORMAL}" + fi +} + +print() { + printv '' "$1" "$2" +} + +printn() { + printv "-n" "$1" "$2" +} + +error() { + printv '' R "$1" +} + +die() { + if [[ "$1" ]]; then + error "$1" + else + error Failed + fi + + exit 1 +} + diff --git a/client/scripts/ios_compile.sh b/client/scripts/ios_compile.sh new file mode 100755 index 00000000..11c2fb7c --- /dev/null +++ b/client/scripts/ios_compile.sh @@ -0,0 +1,227 @@ +#!/bin/bash + + +. $(dirname $0)/commons.sh + +if [ -f .env ]; then + . .env +fi + +RELEASE=1 +OS= +NETWORKEXTENSION= +ADJUST_SDK_TOKEN= +ADJUST="CONFIG-=adjust" + +helpFunction() { + print G "Usage:" + print N "\t$0 [-d|--debug] [-n|--networkextension] [-a|--adjusttoken ]" + print N "" + print N "By default, the project is compiled in release mode. Use -d or --debug for a debug build." + print N "Use -n or --networkextension to force the network-extension component for MacOS too." + print N "" + print N "If MVPN_IOS_ADJUST_TOKEN env is found, this will be used at compilation time." + print N "" + print G "Config variables:" + print N "\tQT_MACOS_BIN=" + print N "\tQT_IOS_BIN=" + print N "\tMVPN_IOS_ADJUST_TOKEN=" + print N "" + exit 0 +} + +print N "This script compiles AmneziaVPN for MacOS/iOS" +print N "" + +while [[ $# -gt 0 ]]; do + key="$1" + + case $key in + -a | --adjusttoken) + ADJUST_SDK_TOKEN="$2" + shift + shift + ;; + -d | --debug) + RELEASE= + shift + ;; + -n | --networkextension) + NETWORKEXTENSION=1 + shift + ;; + -h | --help) + helpFunction + ;; + *) + if [[ "$OS" ]]; then + helpFunction + fi + + OS=$1 + shift + ;; + esac +done + +fetch() { + if command -v "wget" &>/dev/null; then + wget -nc -O "$2" "$1" + return + fi + + if command -v "curl" &>/dev/null; then + curl "$1" -o "$2" -s -L + return + fi + + die "You must have 'wget' or 'curl' installed." +} + +sha256() { + if command -v "sha256sum" &>/dev/null; then + sha256sum "$1" + return 0 + fi + + if command -v "openssl" &>/dev/null; then + openssl dgst -sha256 "$1" + return 0 + fi + + die "You must have 'sha256sum' or 'openssl' installed." +} + +if [[ "$OS" != "macos" ]] && [[ "$OS" != "ios" ]] && [[ "$OS" != "macostest" ]]; then + helpFunction +fi + +if ! [[ "$ADJUST_SDK_TOKEN" ]] && [[ "$MVPN_IOS_ADJUST_TOKEN" ]]; then + print Y "Using the MVPN_IOS_ADJUST_TOKEN value for the adjust token" + ADJUST_SDK_TOKEN=$MVPN_IOS_ADJUST_TOKEN +fi + +if [[ "$OS" == "ios" ]]; then + # Network-extension is the default for IOS + NETWORKEXTENSION=1 +fi + +if ! [ -d "ios" ] || ! [ -d "macos" ]; then + die "This script must be executed at the root of the repository." +fi + +QMAKE=qmake +if [ "$OS" = "macos" ] && ! [ "$QT_MACOS_BIN" = "" ]; then + QMAKE=$QT_MACOS_BIN/qmake +elif [ "$OS" = "macostest" ] && ! [ "$QT_MACOS_BIN" = "" ]; then + QMAKE=$QT_MACOS_BIN/qmake +elif [ "$OS" = "ios" ] && ! [ "$QT_IOS_BIN" = "" ]; then + QMAKE=$QT_IOS_BIN/qmake +fi + +$QMAKE -v &>/dev/null || die "qmake doesn't exist or it fails" + +printn Y "Retrieve the wireguard-go version... " +(cd macos/gobridge && go list -m golang.zx2c4.com/wireguard | sed -n 's/.*v\([0-9.]*\).*/#define WIREGUARD_GO_VERSION "\1"/p') > macos/gobridge/wireguard-go-version.h +print G "done." + +printn Y "Cleaning the existing project... " +rm -rf mozillavpn.xcodeproj/ || die "Failed to remove things" +print G "done." + +#print Y "Importing translation files..." +#git submodule update --remote --depth 1 i18n || die "Failed to fetch newest translation files" +#python scripts/importLanguages.py $([[ "$OS" = "macos" ]] && echo "-m" || echo "") || die "Failed to import languages" +# +#print Y "Generating glean samples..." +#python scripts/generate_glean.py || die "Failed to generate glean samples" + +printn Y "Extract the project version... " +SHORTVERSION=$(cat version.pri | grep VERSION | grep defined | cut -d= -f2 | tr -d \ ) +FULLVERSION=$(echo $SHORTVERSION | cut -d. -f1).$(date +"%Y%m%d%H%M") +print G "$SHORTVERSION - $FULLVERSION" + +MACOS_FLAGS=" + QTPLUGIN+=qsvg + CONFIG-=static + CONFIG+=balrog + MVPN_MACOS=1 +" + +MACOSTEST_FLAGS=" + QTPLUGIN+=qsvg + CONFIG-=static + CONFIG+=DUMMY +" + +IOS_FLAGS=" + MVPN_IOS=1 + Q_OS_IOS=1 +" + +#printn Y "Mode: " +#if [[ "$RELEASE" ]]; then +# print G "release" +# MODE="CONFIG-=debug CONFIG+=release CONFIG-=debug_and_release" +#else +# print G "debug" +# MODE="CONFIG+=debug CONFIG-=release CONFIG-=debug_and_release" +#fi +MODE="CONFIG-=debug CONFIG+=release CONFIG-=debug_and_release" + +OSRUBY=$OS +printn Y "OS: " +print G "$OS" +if [ "$OS" = "macos" ]; then + PLATFORM=$MACOS_FLAGS +elif [ "$OS" = "macostest" ]; then + OSRUBY=macos + PLATFORM=$MACOSTEST_FLAGS +elif [ "$OS" = "ios" ]; then + PLATFORM=$IOS_FLAGS + if [[ "$ADJUST_SDK_TOKEN" ]]; then + printn Y "ADJUST_SDK_TOKEN: " + print G "$ADJUST_SDK_TOKEN" + ADJUST="CONFIG+=adjust" + fi +else + die "Why we are here?" +fi + +VPNMODE= +printn Y "VPN mode: " +if [[ "$NETWORKEXTENSION" ]]; then + print G network-extension + VPNMODE="CONFIG+=networkextension" +else + print G daemon +fi + +printn Y "Web-Extension: " +WEMODE= +if [ "$OS" = "macos" ]; then + print G web-extension + WEMODE="CONFIG+=webextension" +else + print G none +fi + +print Y "Creating the xcode project via qmake..." +$QMAKE \ + VERSION=$SHORTVERSION \ + BUILD_ID=$FULLVERSION \ + -spec macx-xcode \ + $MODE \ + $VPNMODE \ + $WEMODE \ + $PLATFORM \ + $ADJUST \ + ./client.pro || die "Compilation failed" + +print Y "Patching the xcode project..." +ruby scripts/xcode_patcher.rb "AmneziaVPN.xcodeproj" "$SHORTVERSION" "$FULLVERSION" "$OSRUBY" "$NETWORKEXTENSION" "$ADJUST_SDK_TOKEN" || die "Failed to merge xcode with wireguard" +print G "done." + +print Y "Opening in XCode..." +open AmneziaVPN.xcodeproj +print G "All done!" diff --git a/client/scripts/xcode_patcher.rb b/client/scripts/xcode_patcher.rb new file mode 100644 index 00000000..d950c886 --- /dev/null +++ b/client/scripts/xcode_patcher.rb @@ -0,0 +1,664 @@ +# 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/. + +require 'xcodeproj' + +class XCodeprojPatcher + attr :project + attr :target_main + attr :target_extension + + def run(file, shortVersion, fullVersion, platform, networkExtension, configHash, adjust_sdk_token) + open_project file + open_target_main + + die 'IOS requires networkExtension mode' if not networkExtension and platform == 'ios' + + group = @project.main_group.new_group('Configuration') + @configFile = group.new_file('xcode.xconfig') + + setup_target_main shortVersion, fullVersion, platform, networkExtension, configHash, adjust_sdk_token + +# if platform == 'macos' +# setup_target_loginitem shortVersion, fullVersion, configHash +# setup_target_nativemessaging shortVersion, fullVersion, configHash +# end + + + if networkExtension + setup_target_extension shortVersion, fullVersion, platform, configHash + setup_target_gobridge + else + setup_target_wireguardgo + setup_target_wireguardtools + end + + setup_target_balrog if platform == 'macos' + + @project.save + end + + def open_project(file) + @project = Xcodeproj::Project.open(file) + die 'Failed to open the project file: ' + file if @project.nil? + end + + def open_target_main + @target_main = @project.targets.find { |target| target.to_s == 'AmneziaVPN' } + return @target_main if not @target_main.nil? + + die 'Unable to open AmneziaVPN target' + end + + def setup_target_main(shortVersion, fullVersion, platform, networkExtension, configHash, adjust_sdk_token) + @target_main.build_configurations.each do |config| + config.base_configuration_reference = @configFile + + config.build_settings['LD_RUNPATH_SEARCH_PATHS'] ||= '"$(inherited) @executable_path/../Frameworks"' + config.build_settings['SWIFT_VERSION'] ||= '5.0' + config.build_settings['CLANG_ENABLE_MODULES'] ||= 'YES' + config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] ||= 'macos/app/WireGuard-Bridging-Header.h' + config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= [ + "$(inherited)", + "$(PROJECT_DIR)/3rd" + ] + + # Versions and names + config.build_settings['MARKETING_VERSION'] ||= shortVersion + config.build_settings['CURRENT_PROJECT_VERSION'] ||= fullVersion + config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = configHash['APP_ID_MACOS'] if platform == 'macos' + config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] = configHash['APP_ID_IOS'] if platform == 'ios' + config.build_settings['PRODUCT_NAME'] = 'AmneziaVPN' + + # other config + config.build_settings['INFOPLIST_FILE'] ||= platform + '/app/Info.plist' + if platform == 'ios' + config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'ios/app/main.entitlements' + if adjust_sdk_token != "" + config.build_settings['ADJUST_SDK_TOKEN'] = adjust_sdk_token + end + elsif networkExtension + config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'macos/app/app.entitlements' + else + config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'macos/app/daemon.entitlements' + end + + config.build_settings['CODE_SIGN_IDENTITY'] ||= 'Apple Development' + config.build_settings['ENABLE_BITCODE'] ||= 'NO' if platform == 'ios' + config.build_settings['SDKROOT'] = 'iphoneos' if platform == 'ios' + config.build_settings['SWIFT_PRECOMPILE_BRIDGING_HEADER'] = 'NO' if platform == 'ios' + + groupId = ""; + if (platform == 'macos') + groupId = configHash['DEVELOPMENT_TEAM'] + "." + configHash['GROUP_ID_MACOS'] + config.build_settings['APP_ID_MACOS'] ||= configHash['APP_ID_MACOS'] + else + groupId = configHash['GROUP_ID_IOS'] + config.build_settings['GROUP_ID_IOS'] ||= configHash['GROUP_ID_IOS'] + # Force xcode to not set QT_LIBRARY_SUFFIX to "_debug", which causes crash + config.build_settings['QT_LIBRARY_SUFFIX'] = "" + end + + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + 'GROUP_ID=\"' + groupId + '\"', + "VPN_NE_BUNDLEID=\\\"" + (platform == 'macos' ? configHash['NETEXT_ID_MACOS'] : configHash['NETEXT_ID_IOS']) + "\\\"", + ] + + if config.name == 'Release' + config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] ||= '-Onone' + end + end + + if networkExtension + # WireGuard group + group = @project.main_group.new_group('WireGuard') + + [ + 'macos/gobridge/wireguard-go-version.h', + '3rd/wireguard-apple/Sources/Shared/Keychain.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift', + '3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift', + '3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift', + '3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift', + '3rd/wireguard-apple/Sources/WireGuardApp/LocalizationHelper.swift', + '3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift', + '3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c', + '3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift', + ].each { |filename| + file = group.new_file(filename) + @target_main.add_file_references([file]) + } + + # @target_main + swift integration + group = @project.main_group.new_group('SwiftMainIntegration') + + [ + 'platforms/ios/iosvpnprotocol.swift', + 'platforms/ios/ioslogger.swift', + ].each { |filename| + file = group.new_file(filename) + @target_main.add_file_references([file]) + } + end + + if (platform == 'ios' && adjust_sdk_token != "") + frameworks_group = @project.groups.find { |group| group.display_name == 'Frameworks' } + frameworks_build_phase = @target_main.build_phases.find { |build_phase| build_phase.to_s == 'FrameworksBuildPhase' } + + framework_ref = frameworks_group.new_file('AdServices.framework') + build_file = frameworks_build_phase.add_file_reference(framework_ref) + build_file.settings = { 'ATTRIBUTES' => ['Weak'] } + + framework_ref = frameworks_group.new_file('iAd.framework') + frameworks_build_phase.add_file_reference(framework_ref) + + # Adjust SDK + group = @project.main_group.new_group('AdjustSDK') + + [ + '3rd/adjust-ios-sdk/Adjust/ADJActivityHandler.h', + '3rd/adjust-ios-sdk/Adjust/ADJActivityKind.h', + '3rd/adjust-ios-sdk/Adjust/ADJActivityPackage.h', + '3rd/adjust-ios-sdk/Adjust/ADJActivityState.h', + '3rd/adjust-ios-sdk/Adjust/ADJAdjustFactory.h', + '3rd/adjust-ios-sdk/Adjust/ADJAdRevenue.h', + '3rd/adjust-ios-sdk/Adjust/ADJAttribution.h', + '3rd/adjust-ios-sdk/Adjust/ADJAttributionHandler.h', + '3rd/adjust-ios-sdk/Adjust/ADJBackoffStrategy.h', + '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSData+ADJAdditions.h', + '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSNumber+ADJAdditions.h', + '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSString+ADJAdditions.h', + '3rd/adjust-ios-sdk/Adjust/ADJConfig.h', + '3rd/adjust-ios-sdk/Adjust/ADJEvent.h', + '3rd/adjust-ios-sdk/Adjust/ADJEventFailure.h', + '3rd/adjust-ios-sdk/Adjust/ADJEventSuccess.h', + '3rd/adjust-ios-sdk/Adjust/ADJLinkResolution.h', + '3rd/adjust-ios-sdk/Adjust/ADJLogger.h', + '3rd/adjust-ios-sdk/Adjust/ADJPackageBuilder.h', + '3rd/adjust-ios-sdk/Adjust/ADJPackageHandler.h', + '3rd/adjust-ios-sdk/Adjust/ADJPackageParams.h', + '3rd/adjust-ios-sdk/Adjust/ADJRequestHandler.h', + '3rd/adjust-ios-sdk/Adjust/ADJResponseData.h', + '3rd/adjust-ios-sdk/Adjust/ADJSdkClickHandler.h', + '3rd/adjust-ios-sdk/Adjust/ADJSessionFailure.h', + '3rd/adjust-ios-sdk/Adjust/ADJSessionParameters.h', + '3rd/adjust-ios-sdk/Adjust/ADJSessionSuccess.h', + '3rd/adjust-ios-sdk/Adjust/ADJSubscription.h', + '3rd/adjust-ios-sdk/Adjust/ADJThirdPartySharing.h', + '3rd/adjust-ios-sdk/Adjust/ADJTimerCycle.h', + '3rd/adjust-ios-sdk/Adjust/ADJTimerOnce.h', + '3rd/adjust-ios-sdk/Adjust/ADJUrlStrategy.h', + '3rd/adjust-ios-sdk/Adjust/ADJUserDefaults.h', + '3rd/adjust-ios-sdk/Adjust/Adjust.h', + '3rd/adjust-ios-sdk/Adjust/ADJUtil.h', + '3rd/adjust-ios-sdk/Adjust/ADJActivityHandler.m', + '3rd/adjust-ios-sdk/Adjust/ADJActivityKind.m', + '3rd/adjust-ios-sdk/Adjust/ADJActivityPackage.m', + '3rd/adjust-ios-sdk/Adjust/ADJActivityState.m', + '3rd/adjust-ios-sdk/Adjust/ADJAdjustFactory.m', + '3rd/adjust-ios-sdk/Adjust/ADJAdRevenue.m', + '3rd/adjust-ios-sdk/Adjust/ADJAttribution.m', + '3rd/adjust-ios-sdk/Adjust/ADJAttributionHandler.m', + '3rd/adjust-ios-sdk/Adjust/ADJBackoffStrategy.m', + '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSData+ADJAdditions.m', + '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSNumber+ADJAdditions.m', + '3rd/adjust-ios-sdk/Adjust/ADJAdditions/NSString+ADJAdditions.m', + '3rd/adjust-ios-sdk/Adjust/ADJConfig.m', + '3rd/adjust-ios-sdk/Adjust/ADJEvent.m', + '3rd/adjust-ios-sdk/Adjust/ADJEventFailure.m', + '3rd/adjust-ios-sdk/Adjust/ADJEventSuccess.m', + '3rd/adjust-ios-sdk/Adjust/ADJLinkResolution.m', + '3rd/adjust-ios-sdk/Adjust/ADJLogger.m', + '3rd/adjust-ios-sdk/Adjust/ADJPackageBuilder.m', + '3rd/adjust-ios-sdk/Adjust/ADJPackageHandler.m', + '3rd/adjust-ios-sdk/Adjust/ADJPackageParams.m', + '3rd/adjust-ios-sdk/Adjust/ADJRequestHandler.m', + '3rd/adjust-ios-sdk/Adjust/ADJResponseData.m', + '3rd/adjust-ios-sdk/Adjust/ADJSdkClickHandler.m', + '3rd/adjust-ios-sdk/Adjust/ADJSessionFailure.m', + '3rd/adjust-ios-sdk/Adjust/ADJSessionParameters.m', + '3rd/adjust-ios-sdk/Adjust/ADJSessionSuccess.m', + '3rd/adjust-ios-sdk/Adjust/ADJSubscription.m', + '3rd/adjust-ios-sdk/Adjust/ADJThirdPartySharing.m', + '3rd/adjust-ios-sdk/Adjust/ADJTimerCycle.m', + '3rd/adjust-ios-sdk/Adjust/ADJTimerOnce.m', + '3rd/adjust-ios-sdk/Adjust/ADJUrlStrategy.m', + '3rd/adjust-ios-sdk/Adjust/ADJUserDefaults.m', + '3rd/adjust-ios-sdk/Adjust/Adjust.m', + '3rd/adjust-ios-sdk/Adjust/ADJUtil.m', + ].each { |filename| + file = group.new_file(filename) + file_reference = @target_main.add_file_references([file], '-fobjc-arc') + } + end + end + + def setup_target_extension(shortVersion, fullVersion, platform, configHash) + @target_extension = @project.new_target(:app_extension, 'WireGuardNetworkExtension', platform == 'macos' ? :osx : :ios) + + @target_extension.build_configurations.each do |config| + config.base_configuration_reference = @configFile + + config.build_settings['LD_RUNPATH_SEARCH_PATHS'] ||= '"$(inherited) @executable_path/../Frameworks"' + config.build_settings['SWIFT_VERSION'] ||= '5.0' + config.build_settings['CLANG_ENABLE_MODULES'] ||= 'YES' + config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] ||= 'macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h' + config.build_settings['SWIFT_PRECOMPILE_BRIDGING_HEADER'] = 'NO' + config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'YES' + + # Versions and names + config.build_settings['MARKETING_VERSION'] ||= shortVersion + config.build_settings['CURRENT_PROJECT_VERSION'] ||= fullVersion + config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] ||= configHash['NETEXT_ID_MACOS'] if platform == 'macos' + config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] ||= configHash['NETEXT_ID_IOS'] if platform == 'ios' + config.build_settings['PRODUCT_NAME'] = 'WireGuardNetworkExtension' + + # other configs + config.build_settings['INFOPLIST_FILE'] ||= 'macos/networkextension/Info.plist' + config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= platform + '/networkextension/AmneziaVPNNetworkExtension.entitlements' + config.build_settings['CODE_SIGN_IDENTITY'] = 'Apple Development' + + if platform == 'ios' + config.build_settings['ENABLE_BITCODE'] ||= 'NO' + config.build_settings['SDKROOT'] = 'iphoneos' + + config.build_settings['OTHER_LDFLAGS'] ||= [ + "-stdlib=libc++", + "-Wl,-rpath,@executable_path/Frameworks", + "-framework", + "AssetsLibrary", + "-framework", + "MobileCoreServices", + "-lm", + "-framework", + "UIKit", + "-lz", + "-framework", + "OpenGLES", + ] + end + + groupId = ""; + if (platform == 'macos') + groupId = configHash['DEVELOPMENT_TEAM'] + "." + configHash['GROUP_ID_MACOS'] + else + groupId = configHash['GROUP_ID_IOS'] + end + + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + # This is needed to compile the iosglue without Qt. + 'NETWORK_EXTENSION=1', + 'GROUP_ID=\"' + groupId + '\"', + ] + + if config.name == 'Release' + config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] ||= '-Onone' + end + + end + + group = @project.main_group.new_group('WireGuardExtension') + [ + '3rd/wireguard-apple/Sources/WireGuardKit/WireGuardAdapter.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/PacketTunnelSettingsGenerator.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/DNSResolver.swift', + '3rd/wireguard-apple/Sources/WireGuardNetworkExtension/ErrorNotifier.swift', + '3rd/wireguard-apple/Sources/Shared/Keychain.swift', + '3rd/wireguard-apple/Sources/Shared/Model/TunnelConfiguration+WgQuickConfig.swift', + '3rd/wireguard-apple/Sources/Shared/Model/NETunnelProviderProtocol+Extension.swift', + '3rd/wireguard-apple/Sources/Shared/Model/String+ArrayConversion.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/TunnelConfiguration.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/IPAddressRange.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/Endpoint.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/DNSServer.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/InterfaceConfiguration.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/PeerConfiguration.swift', + '3rd/wireguard-apple/Sources/Shared/FileManager+Extension.swift', + '3rd/wireguard-apple/Sources/WireGuardKitC/x25519.c', + '3rd/wireguard-apple/Sources/WireGuardKit/Array+ConcurrentMap.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/IPAddress+AddrInfo.swift', + '3rd/wireguard-apple/Sources/WireGuardKit/PrivateKey.swift', + ].each { |filename| + file = group.new_file(filename) + @target_extension.add_file_references([file]) + } + # @target_extension + swift integration + group = @project.main_group.new_group('SwiftExtensionIntegration') + + [ + 'platforms/ios/iostunnel.swift', + 'platforms/ios/iosglue.mm', + 'platforms/ios/ioslogger.swift', + ].each { |filename| + file = group.new_file(filename) + @target_extension.add_file_references([file]) + } + + frameworks_group = @project.groups.find { |group| group.display_name == 'Frameworks' } + frameworks_build_phase = @target_extension.build_phases.find { |build_phase| build_phase.to_s == 'FrameworksBuildPhase' } + + frameworks_build_phase.clear + + framework_ref = frameworks_group.new_file('libwg-go.a') + frameworks_build_phase.add_file_reference(framework_ref) + + framework_ref = frameworks_group.new_file('NetworkExtension.framework') + frameworks_build_phase.add_file_reference(framework_ref) + + # This fails: @target_main.add_dependency @target_extension + container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) + container_proxy.container_portal = @project.root_object.uuid + container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target] + container_proxy.remote_global_id_string = @target_extension.uuid + container_proxy.remote_info = @target_extension.name + + dependency = @project.new(Xcodeproj::Project::PBXTargetDependency) + dependency.name = @target_extension.name + dependency.target = @target_main + dependency.target_proxy = container_proxy + + @target_main.dependencies << dependency + + copy_appex = @target_main.new_copy_files_build_phase + copy_appex.name = 'Copy Network-Extension plugin' + copy_appex.symbol_dst_subfolder_spec = :plug_ins + + appex_file = copy_appex.add_file_reference @target_extension.product_reference + appex_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] } + end + + def setup_target_gobridge + target_gobridge = legacy_target = @project.new(Xcodeproj::Project::PBXLegacyTarget) + + target_gobridge.build_working_directory = 'macos/gobridge' + target_gobridge.build_tool_path = 'make' + target_gobridge.pass_build_settings_in_environment = '1' + target_gobridge.build_arguments_string = '$(ACTION)' + target_gobridge.name = 'WireGuardGoBridge' + target_gobridge.product_name = 'WireGuardGoBridge' + + @project.targets << target_gobridge + @target_extension.add_dependency target_gobridge + end + + def setup_target_balrog + target_balrog = legacy_target = @project.new(Xcodeproj::Project::PBXLegacyTarget) + + target_balrog.build_working_directory = 'balrog' + target_balrog.build_tool_path = 'make' + target_balrog.pass_build_settings_in_environment = '1' + target_balrog.build_arguments_string = '$(ACTION)' + target_balrog.name = 'WireGuardBalrog' + target_balrog.product_name = 'WireGuardBalrog' + + @project.targets << target_balrog + + frameworks_group = @project.groups.find { |group| group.display_name == 'Frameworks' } + frameworks_build_phase = @target_main.build_phases.find { |build_phase| build_phase.to_s == 'FrameworksBuildPhase' } + + framework_ref = frameworks_group.new_file('balrog/balrog.a') + frameworks_build_phase.add_file_reference(framework_ref) + + # This fails: @target_main.add_dependency target_balrog + container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) + container_proxy.container_portal = @project.root_object.uuid + container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target] + container_proxy.remote_global_id_string = target_balrog.uuid + container_proxy.remote_info = target_balrog.name + + dependency = @project.new(Xcodeproj::Project::PBXTargetDependency) + dependency.name = target_balrog.name + dependency.target = @target_main + dependency.target_proxy = container_proxy + + @target_main.dependencies << dependency + end + + def setup_target_wireguardtools + target_wireguardtools = legacy_target = @project.new(Xcodeproj::Project::PBXLegacyTarget) + + target_wireguardtools.build_working_directory = '3rd/wireguard-tools/src' + target_wireguardtools.build_tool_path = 'make' + target_wireguardtools.pass_build_settings_in_environment = '1' + target_wireguardtools.build_arguments_string = '$(ACTION)' + target_wireguardtools.name = 'WireGuardTools' + target_wireguardtools.product_name = 'WireGuardTools' + + @project.targets << target_wireguardtools + + # This fails: @target_main.add_dependency target_wireguardtools + container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) + container_proxy.container_portal = @project.root_object.uuid + container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target] + container_proxy.remote_global_id_string = target_wireguardtools.uuid + container_proxy.remote_info = target_wireguardtools.name + + dependency = @project.new(Xcodeproj::Project::PBXTargetDependency) + dependency.name = target_wireguardtools.name + dependency.target = @target_main + dependency.target_proxy = container_proxy + + @target_main.dependencies << dependency + + copy_wireguardTools = @target_main.new_copy_files_build_phase + copy_wireguardTools.name = 'Copy wireguard-tools' + copy_wireguardTools.symbol_dst_subfolder_spec = :wrapper + copy_wireguardTools.dst_path = 'Contents/Resources/utils' + + group = @project.main_group.new_group('WireGuardTools') + file = group.new_file '3rd/wireguard-tools/src/wg' + + wireguardTools_file = copy_wireguardTools.add_file_reference file + wireguardTools_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] } + end + + def setup_target_wireguardgo + target_wireguardgo = legacy_target = @project.new(Xcodeproj::Project::PBXLegacyTarget) + + target_wireguardgo.build_working_directory = '3rd/wireguard-go' + target_wireguardgo.build_tool_path = 'make' + target_wireguardgo.pass_build_settings_in_environment = '1' + target_wireguardgo.build_arguments_string = '$(ACTION)' + target_wireguardgo.name = 'WireGuardGo' + target_wireguardgo.product_name = 'WireGuardGo' + + @project.targets << target_wireguardgo + + # This fails: @target_main.add_dependency target_wireguardgo + container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) + container_proxy.container_portal = @project.root_object.uuid + container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target] + container_proxy.remote_global_id_string = target_wireguardgo.uuid + container_proxy.remote_info = target_wireguardgo.name + + dependency = @project.new(Xcodeproj::Project::PBXTargetDependency) + dependency.name = target_wireguardgo.name + dependency.target = @target_main + dependency.target_proxy = container_proxy + + @target_main.dependencies << dependency + + copy_wireguardGo = @target_main.new_copy_files_build_phase + copy_wireguardGo.name = 'Copy wireguard-go' + copy_wireguardGo.symbol_dst_subfolder_spec = :wrapper + copy_wireguardGo.dst_path = 'Contents/Resources/utils' + + group = @project.main_group.new_group('WireGuardGo') + file = group.new_file '3rd/wireguard-go/wireguard-go' + + wireguardGo_file = copy_wireguardGo.add_file_reference file + wireguardGo_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] } + end + + def setup_target_loginitem(shortVersion, fullVersion, configHash) + @target_loginitem = @project.new_target(:application, 'AmneziaVPNLoginItem', :osx) + + @target_loginitem.build_configurations.each do |config| + config.base_configuration_reference = @configFile + + config.build_settings['LD_RUNPATH_SEARCH_PATHS'] ||= '"$(inherited) @executable_path/../Frameworks"' + + # Versions and names + config.build_settings['MARKETING_VERSION'] ||= shortVersion + config.build_settings['CURRENT_PROJECT_VERSION'] ||= fullVersion + config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] ||= configHash['LOGIN_ID_MACOS'] + config.build_settings['PRODUCT_NAME'] = 'AmneziaVPNLoginItem' + + # other configs + config.build_settings['INFOPLIST_FILE'] ||= 'macos/loginitem/Info.plist' + config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'macos/loginitem/MozillaVPNLoginItem.entitlements' + config.build_settings['CODE_SIGN_IDENTITY'] = 'Apple Development' + config.build_settings['SKIP_INSTALL'] = 'YES' + + config.build_settings['GCC_PREPROCESSOR_DEFINITIONS'] ||= [ + 'APP_ID=\"' + configHash['APP_ID_MACOS'] + '\"', + ] + + if config.name == 'Release' + config.build_settings['SWIFT_OPTIMIZATION_LEVEL'] ||= '-Onone' + end + end + + group = @project.main_group.new_group('LoginItem') + [ + 'macos/loginitem/main.m', + ].each { |filename| + file = group.new_file(filename) + @target_loginitem.add_file_references([file]) + } + + # This fails: @target_main.add_dependency @target_loginitem + container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) + container_proxy.container_portal = @project.root_object.uuid + container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target] + container_proxy.remote_global_id_string = @target_loginitem.uuid + container_proxy.remote_info = @target_loginitem.name + + dependency = @project.new(Xcodeproj::Project::PBXTargetDependency) + dependency.name = @target_loginitem.name + dependency.target = @target_main + dependency.target_proxy = container_proxy + + @target_main.dependencies << dependency + + copy_app = @target_main.new_copy_files_build_phase + copy_app.name = 'Copy LoginItem' + copy_app.symbol_dst_subfolder_spec = :wrapper + copy_app.dst_path = 'Contents/Library/LoginItems' + + app_file = copy_app.add_file_reference @target_loginitem.product_reference + app_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] } + end + + def setup_target_nativemessaging(shortVersion, fullVersion, configHash) + @target_nativemessaging = @project.new_target(:application, 'AmneziaVPNNativeMessaging', :osx) + + @target_nativemessaging.build_configurations.each do |config| + config.base_configuration_reference = @configFile + + config.build_settings['LD_RUNPATH_SEARCH_PATHS'] ||= '"$(inherited) @executable_path/../Frameworks"' + + # Versions and names + config.build_settings['MARKETING_VERSION'] ||= shortVersion + config.build_settings['CURRENT_PROJECT_VERSION'] ||= fullVersion + config.build_settings['PRODUCT_BUNDLE_IDENTIFIER'] ||= configHash['NATIVEMESSAGING_ID_MACOS'] + config.build_settings['PRODUCT_NAME'] = 'AmneziaVPNNativeMessaging' + + # other configs + config.build_settings['INFOPLIST_FILE'] ||= 'macos/nativeMessaging/Info.plist' + config.build_settings['CODE_SIGN_ENTITLEMENTS'] ||= 'macos/nativeMessaging/MozillaVPNNativeMessaging.entitlements' + config.build_settings['CODE_SIGN_IDENTITY'] = 'Apple Development' + config.build_settings['SKIP_INSTALL'] = 'YES' + end + + group = @project.main_group.new_group('NativeMessaging') + [ + 'extension/app/constants.h', + 'extension/app/handler.cpp', + 'extension/app/handler.h', + 'extension/app/json.hpp', + 'extension/app/logger.cpp', + 'extension/app/logger.h', + 'extension/app/main.cpp', + 'extension/app/vpnconnection.cpp', + 'extension/app/vpnconnection.h', + ].each { |filename| + file = group.new_file(filename) + @target_nativemessaging.add_file_references([file]) + } + + # This fails: @target_main.add_dependency @target_nativemessaging + container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy) + container_proxy.container_portal = @project.root_object.uuid + container_proxy.proxy_type = Xcodeproj::Constants::PROXY_TYPES[:native_target] + container_proxy.remote_global_id_string = @target_nativemessaging.uuid + container_proxy.remote_info = @target_nativemessaging.name + + dependency = @project.new(Xcodeproj::Project::PBXTargetDependency) + dependency.name = @target_nativemessaging.name + dependency.target = @target_main + dependency.target_proxy = container_proxy + + @target_main.dependencies << dependency + + copy_app = @target_main.new_copy_files_build_phase + copy_app.name = 'Copy LoginItem' + copy_app.symbol_dst_subfolder_spec = :wrapper + copy_app.dst_path = 'Contents/Library/NativeMessaging' + + app_file = copy_app.add_file_reference @target_nativemessaging.product_reference + app_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] } + + copy_nativeMessagingManifest = @target_main.new_copy_files_build_phase + copy_nativeMessagingManifest.name = 'Copy native messaging manifest' + copy_nativeMessagingManifest.symbol_dst_subfolder_spec = :wrapper + copy_nativeMessagingManifest.dst_path = 'Contents/Resources/utils' + + group = @project.main_group.new_group('WireGuardHelper') + file = group.new_file 'extension/app/manifests/macos/mozillavpn.json' + + nativeMessagingManifest_file = copy_nativeMessagingManifest.add_file_reference file + nativeMessagingManifest_file.settings = { "ATTRIBUTES" => ['RemoveHeadersOnCopy'] } + end + + def die(msg) + print $msg + exit 1 + end +end + +if ARGV.length < 4 || (ARGV[3] != "ios" && ARGV[3] != "macos") + puts "Usage: