Merge branch 'ios-wireguard' into dev
This commit is contained in:
commit
2380875cbf
13 changed files with 884 additions and 231 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -47,6 +47,7 @@ client/amneziavpn_qml_plugin_import.cpp
|
||||||
client/qmlcache_loader.cpp
|
client/qmlcache_loader.cpp
|
||||||
client/rep_ipc_interface_replica.h
|
client/rep_ipc_interface_replica.h
|
||||||
client/resources_qmlcache.qrc
|
client/resources_qmlcache.qrc
|
||||||
|
client/3rd/OpenVPNAdpter/build/
|
||||||
|
|
||||||
# QtCreator
|
# QtCreator
|
||||||
|
|
||||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -7,3 +7,6 @@
|
||||||
[submodule "client/3rd/wireguard-apple"]
|
[submodule "client/3rd/wireguard-apple"]
|
||||||
path = client/3rd/wireguard-apple
|
path = client/3rd/wireguard-apple
|
||||||
url = https://github.com/WireGuard/wireguard-apple
|
url = https://github.com/WireGuard/wireguard-apple
|
||||||
|
[submodule "client/3rd/OpenVPNAdapter"]
|
||||||
|
path = client/3rd/OpenVPNAdapter
|
||||||
|
url = https://github.com/ss-abramchuk/OpenVPNAdapter.git
|
||||||
|
|
1
client/3rd/OpenVPNAdapter
Submodule
1
client/3rd/OpenVPNAdapter
Submodule
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 0e2db0baa0d66029cbb18d74b78bc7a5c9013fba
|
|
@ -217,7 +217,9 @@ enum ConnectionState : NSInteger;
|
||||||
SWIFT_CLASS("_TtC10AmneziaVPN18IOSVpnProtocolImpl")
|
SWIFT_CLASS("_TtC10AmneziaVPN18IOSVpnProtocolImpl")
|
||||||
@interface IOSVpnProtocolImpl : NSObject
|
@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;
|
- (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;
|
||||||
|
- (nonnull instancetype)initWithBundleID:(NSString * _Nonnull)bundleID config:(NSString * _Nonnull)config closure:(void (^ _Nonnull)(enum ConnectionState, NSDate * _Nullable))closure callback:(void (^ _Nonnull)(BOOL))callback;
|
||||||
- (void)connectWithDnsServer:(NSString * _Nonnull)dnsServer serverIpv6Gateway:(NSString * _Nonnull)serverIpv6Gateway serverPublicKey:(NSString * _Nonnull)serverPublicKey presharedKey:(NSString * _Nonnull)presharedKey serverIpv4AddrIn:(NSString * _Nonnull)serverIpv4AddrIn serverPort:(NSInteger)serverPort allowedIPAddressRanges:(NSArray<VPNIPAddressRange *> * _Nonnull)allowedIPAddressRanges ipv6Enabled:(Boolean)enabled reason:(NSInteger)reason failureCallback:(void (^ _Nonnull)(void))failureCallback;
|
- (void)connectWithDnsServer:(NSString * _Nonnull)dnsServer serverIpv6Gateway:(NSString * _Nonnull)serverIpv6Gateway serverPublicKey:(NSString * _Nonnull)serverPublicKey presharedKey:(NSString * _Nonnull)presharedKey serverIpv4AddrIn:(NSString * _Nonnull)serverIpv4AddrIn serverPort:(NSInteger)serverPort allowedIPAddressRanges:(NSArray<VPNIPAddressRange *> * _Nonnull)allowedIPAddressRanges ipv6Enabled:(Boolean)enabled reason:(NSInteger)reason failureCallback:(void (^ _Nonnull)(void))failureCallback;
|
||||||
|
- (void)connectWithOvpnConfig:(NSString * _Nonnull)ovpnConfig failureCallback:(void (^ _Nonnull)(void))failureCallback;
|
||||||
- (void)disconnect;
|
- (void)disconnect;
|
||||||
- (void)checkStatusWithCallback:(void (^ _Nonnull)(NSString * _Nonnull, NSString * _Nonnull, NSString * _Nonnull))callback;
|
- (void)checkStatusWithCallback:(void (^ _Nonnull)(NSString * _Nonnull, NSString * _Nonnull, NSString * _Nonnull))callback;
|
||||||
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
|
- (nonnull instancetype)init SWIFT_UNAVAILABLE;
|
||||||
|
@ -230,8 +232,6 @@ typedef SWIFT_ENUM(NSInteger, ConnectionState, closed) {
|
||||||
ConnectionStateDisconnected = 2,
|
ConnectionStateDisconnected = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SWIFT_CLASS("_TtC10AmneziaVPN17VPNIPAddressRange")
|
SWIFT_CLASS("_TtC10AmneziaVPN17VPNIPAddressRange")
|
||||||
@interface VPNIPAddressRange : NSObject
|
@interface VPNIPAddressRange : NSObject
|
||||||
- (nonnull instancetype)initWithAddress:(NSString * _Nonnull)address networkPrefixLength:(uint8_t)networkPrefixLength isIpv6:(BOOL)isIpv6 OBJC_DESIGNATED_INITIALIZER;
|
- (nonnull instancetype)initWithAddress:(NSString * _Nonnull)address networkPrefixLength:(uint8_t)networkPrefixLength isIpv6:(BOOL)isIpv6 OBJC_DESIGNATED_INITIALIZER;
|
||||||
|
|
|
@ -276,6 +276,11 @@ ios {
|
||||||
LIBS += -framework StoreKit
|
LIBS += -framework StoreKit
|
||||||
LIBS += -framework UserNotifications
|
LIBS += -framework UserNotifications
|
||||||
|
|
||||||
|
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/LZ4.framework
|
||||||
|
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/mbedTLS.framework
|
||||||
|
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/OpenVPNClient.framework
|
||||||
|
# LIBS += $$PWD/3rd/OpenVPNAdapter/build/Debug-iphoneos/OpenVPNAdapter.framework
|
||||||
|
|
||||||
DEFINES += MVPN_IOS
|
DEFINES += MVPN_IOS
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
|
@ -309,7 +314,7 @@ ios {
|
||||||
QMAKE_DEVELOPMENT_TEAM = X7UJ388FXK
|
QMAKE_DEVELOPMENT_TEAM = X7UJ388FXK
|
||||||
QMAKE_PROVISIONING_PROFILE = f2fefb59-14aa-4aa9-ac14-1d5531b06dcc
|
QMAKE_PROVISIONING_PROFILE = f2fefb59-14aa-4aa9-ac14-1d5531b06dcc
|
||||||
QMAKE_XCODE_CODE_SIGN_IDENTITY = "Apple Distribution"
|
QMAKE_XCODE_CODE_SIGN_IDENTITY = "Apple Distribution"
|
||||||
QMAKE_INFO_PLIST= $$PWD/ios/app/Info.plist
|
QMAKE_INFO_PLIST = $$PWD/ios/app/Info.plist
|
||||||
|
|
||||||
XCODEBUILD_FLAGS += -allowProvisioningUpdates
|
XCODEBUILD_FLAGS += -allowProvisioningUpdates
|
||||||
|
|
||||||
|
|
|
@ -4,94 +4,72 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import os
|
import os
|
||||||
|
import OpenVPNAdapter
|
||||||
|
|
||||||
|
enum TunnelProtoType: String {
|
||||||
|
case wireguard, openvpn, none
|
||||||
|
}
|
||||||
|
|
||||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
|
|
||||||
private lazy var wgAdapter: WireGuardAdapter = {
|
private lazy var wgAdapter: WireGuardAdapter = {
|
||||||
return WireGuardAdapter(with: self) { logLevel, message in
|
return WireGuardAdapter(with: self) { logLevel, message in
|
||||||
wg_log(logLevel.osLogLevel, message: message)
|
wg_log(logLevel.osLogLevel, message: message)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
private lazy var ovpnAdapter: OpenVPNAdapter = {
|
||||||
|
let adapter = OpenVPNAdapter()
|
||||||
|
adapter.delegate = self
|
||||||
|
return adapter
|
||||||
|
}()
|
||||||
|
|
||||||
|
let vpnReachability = OpenVPNReachability()
|
||||||
|
|
||||||
|
var startHandler: ((Error?) -> Void)?
|
||||||
|
var stopHandler: (() -> Void)?
|
||||||
|
var protoType: TunnelProtoType = .wireguard
|
||||||
|
|
||||||
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||||
let activationAttemptId = options?["activationAttemptId"] as? String
|
let activationAttemptId = options?["activationAttemptId"] as? String
|
||||||
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
let errorNotifier = ErrorNotifier(activationAttemptId: activationAttemptId)
|
||||||
|
|
||||||
Logger.configureGlobal(tagged: "NET", withFilePath: FileManager.logFileURL?.path)
|
Logger.configureGlobal(tagged: "NET", withFilePath: FileManager.logFileURL?.path)
|
||||||
|
|
||||||
wg_log(.info, message: "Starting tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
if let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||||
guard let tunnelProviderProtocol = self.protocolConfiguration as? NETunnelProviderProtocol,
|
let _: Data = providerConfiguration["ovpn"] as? Data {
|
||||||
let tunnelConfiguration = tunnelProviderProtocol.asTunnelConfiguration() else {
|
protoType = .openvpn
|
||||||
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
|
} else {
|
||||||
completionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
|
protoType = .wireguard
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the tunnel
|
switch protoType {
|
||||||
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
case .wireguard:
|
||||||
guard let adapterError = adapterError else {
|
startWireguard(activationAttemptId: activationAttemptId,
|
||||||
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
|
errorNotifier: errorNotifier,
|
||||||
|
completionHandler: completionHandler)
|
||||||
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
case .openvpn:
|
||||||
|
startOpenVPN(completionHandler: completionHandler)
|
||||||
completionHandler(nil)
|
case .none:
|
||||||
return
|
break
|
||||||
}
|
|
||||||
|
|
||||||
switch adapterError {
|
|
||||||
case .cannotLocateTunnelFileDescriptor:
|
|
||||||
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
|
||||||
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
|
||||||
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
|
||||||
|
|
||||||
case .dnsResolution(let dnsErrors):
|
|
||||||
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
|
||||||
.joined(separator: ", ")
|
|
||||||
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
|
||||||
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
|
|
||||||
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
|
|
||||||
|
|
||||||
case .setNetworkSettings(let error):
|
|
||||||
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
|
||||||
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
|
||||||
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
|
||||||
|
|
||||||
case .startWireGuardBackend(let errorCode):
|
|
||||||
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
|
||||||
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
|
|
||||||
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
|
|
||||||
|
|
||||||
case .invalidState:
|
|
||||||
// Must never happen
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||||
wg_log(.info, staticMessage: "Stopping tunnel")
|
switch protoType {
|
||||||
|
case .wireguard:
|
||||||
wgAdapter.stop { error in
|
stopWireguard(with: reason, completionHandler: completionHandler)
|
||||||
ErrorNotifier.removeLastErrorFile()
|
case .openvpn:
|
||||||
|
stopOpenVPN(with: reason, completionHandler: completionHandler)
|
||||||
if let error = error {
|
case .none:
|
||||||
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
|
break
|
||||||
}
|
|
||||||
completionHandler()
|
|
||||||
|
|
||||||
#if os(macOS)
|
|
||||||
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
|
|
||||||
// Remove it when they finally fix this upstream and the fix has been rolled out to
|
|
||||||
// sufficient quantities of users.
|
|
||||||
exit(0)
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)? = nil) {
|
||||||
guard let completionHandler = completionHandler else { return }
|
guard let completionHandler = completionHandler else { return }
|
||||||
|
|
||||||
if messageData.count == 1 && messageData[0] == 0 {
|
if messageData.count == 1 && messageData[0] == 0 {
|
||||||
wgAdapter.getRuntimeConfiguration { settings in
|
wgAdapter.getRuntimeConfiguration { settings in
|
||||||
var data: Data?
|
var data: Data?
|
||||||
|
@ -108,31 +86,144 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
do {
|
do {
|
||||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
|
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: configString)
|
||||||
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
|
wgAdapter.update(tunnelConfiguration: tunnelConfiguration) { error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
|
wg_log(.error, message: "Failed to switch tunnel configuration: \(error.localizedDescription)")
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
return
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
self.wgAdapter.getRuntimeConfiguration { settings in
|
||||||
|
var data: Data?
|
||||||
|
if let settings = settings {
|
||||||
|
data = settings.data(using: .utf8)!
|
||||||
|
}
|
||||||
|
completionHandler(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.wgAdapter.getRuntimeConfiguration { settings in
|
|
||||||
var data: Data?
|
|
||||||
if let settings = settings {
|
|
||||||
data = settings.data(using: .utf8)!
|
|
||||||
}
|
|
||||||
completionHandler(data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: Private methods
|
||||||
|
private func startWireguard(activationAttemptId: String?,
|
||||||
|
errorNotifier: ErrorNotifier,
|
||||||
|
completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
guard let tunnelProviderProtocol = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
|
let tunnelConfiguration = tunnelProviderProtocol.asTunnelConfiguration() else {
|
||||||
|
errorNotifier.notify(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
|
||||||
|
completionHandler(PacketTunnelProviderError.savedProtocolConfigurationIsInvalid)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wg_log(.info, message: "Starting wireguard tunnel from the " + (activationAttemptId == nil ? "OS directly, rather than the app" : "app"))
|
||||||
|
|
||||||
|
// Start the tunnel
|
||||||
|
wgAdapter.start(tunnelConfiguration: tunnelConfiguration) { adapterError in
|
||||||
|
guard let adapterError = adapterError else {
|
||||||
|
let interfaceName = self.wgAdapter.interfaceName ?? "unknown"
|
||||||
|
|
||||||
|
wg_log(.info, message: "Tunnel interface is \(interfaceName)")
|
||||||
|
|
||||||
|
completionHandler(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch adapterError {
|
||||||
|
case .cannotLocateTunnelFileDescriptor:
|
||||||
|
wg_log(.error, staticMessage: "Starting tunnel failed: could not determine file descriptor")
|
||||||
|
errorNotifier.notify(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||||
|
completionHandler(PacketTunnelProviderError.couldNotDetermineFileDescriptor)
|
||||||
|
|
||||||
|
case .dnsResolution(let dnsErrors):
|
||||||
|
let hostnamesWithDnsResolutionFailure = dnsErrors.map { $0.address }
|
||||||
|
.joined(separator: ", ")
|
||||||
|
wg_log(.error, message: "DNS resolution failed for the following hostnames: \(hostnamesWithDnsResolutionFailure)")
|
||||||
|
errorNotifier.notify(PacketTunnelProviderError.dnsResolutionFailure)
|
||||||
|
completionHandler(PacketTunnelProviderError.dnsResolutionFailure)
|
||||||
|
|
||||||
|
case .setNetworkSettings(let error):
|
||||||
|
wg_log(.error, message: "Starting tunnel failed with setTunnelNetworkSettings returning \(error.localizedDescription)")
|
||||||
|
errorNotifier.notify(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||||
|
completionHandler(PacketTunnelProviderError.couldNotSetNetworkSettings)
|
||||||
|
|
||||||
|
case .startWireGuardBackend(let errorCode):
|
||||||
|
wg_log(.error, message: "Starting tunnel failed with wgTurnOn returning \(errorCode)")
|
||||||
|
errorNotifier.notify(PacketTunnelProviderError.couldNotStartBackend)
|
||||||
|
completionHandler(PacketTunnelProviderError.couldNotStartBackend)
|
||||||
|
|
||||||
|
case .invalidState:
|
||||||
|
// Must never happen
|
||||||
|
fatalError()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func startOpenVPN(completionHandler: @escaping (Error?) -> Void) {
|
||||||
|
guard let protocolConfiguration = self.protocolConfiguration as? NETunnelProviderProtocol,
|
||||||
|
let providerConfiguration = protocolConfiguration.providerConfiguration,
|
||||||
|
let ovpnConfiguration: Data = providerConfiguration["ovpn"] as? Data else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let configuration = OpenVPNConfiguration()
|
||||||
|
configuration.fileContent = ovpnConfiguration
|
||||||
|
// configuration.settings = [] // Additional setting if needed any
|
||||||
|
// configuration.tunPersist = true // keep tun active during pauses/reconections
|
||||||
|
let evaluation: OpenVPNConfigurationEvaluation
|
||||||
|
do {
|
||||||
|
evaluation = try ovpnAdapter.apply(configuration: configuration)
|
||||||
|
} catch {
|
||||||
|
completionHandler(error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !evaluation.autologin {
|
||||||
|
print("Implement login with user credentials")
|
||||||
|
}
|
||||||
|
|
||||||
|
vpnReachability.startTracking { [weak self] status in
|
||||||
|
guard status == .reachableViaWiFi else { return }
|
||||||
|
self?.ovpnAdapter.reconnect(afterTimeInterval: 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
startHandler = completionHandler
|
||||||
|
ovpnAdapter.connect(using: packetFlow)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopWireguard(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||||
|
wg_log(.info, staticMessage: "Stopping tunnel")
|
||||||
|
|
||||||
|
wgAdapter.stop { error in
|
||||||
|
ErrorNotifier.removeLastErrorFile()
|
||||||
|
|
||||||
|
if let error = error {
|
||||||
|
wg_log(.error, message: "Failed to stop WireGuard adapter: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
completionHandler()
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
// HACK: This is a filthy hack to work around Apple bug 32073323 (dup'd by us as 47526107).
|
||||||
|
// Remove it when they finally fix this upstream and the fix has been rolled out to
|
||||||
|
// sufficient quantities of users.
|
||||||
|
exit(0)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func stopOpenVPN(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||||
|
stopHandler = completionHandler
|
||||||
|
if vpnReachability.isTracking {
|
||||||
|
vpnReachability.stopTracking()
|
||||||
|
}
|
||||||
|
ovpnAdapter.disconnect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension WireGuardLogLevel {
|
extension WireGuardLogLevel {
|
||||||
|
@ -145,3 +236,87 @@ extension WireGuardLogLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NEPacketTunnelFlow: OpenVPNAdapterPacketFlow {}
|
||||||
|
|
||||||
|
extension PacketTunnelProvider: OpenVPNAdapterDelegate {
|
||||||
|
|
||||||
|
// OpenVPNAdapter calls this delegate method to configure a VPN tunnel.
|
||||||
|
// `completionHandler` callback requires an object conforming to `OpenVPNAdapterPacketFlow`
|
||||||
|
// protocol if the tunnel is configured without errors. Otherwise send nil.
|
||||||
|
// `OpenVPNAdapterPacketFlow` method signatures are similar to `NEPacketTunnelFlow` so
|
||||||
|
// you can just extend that class to adopt `OpenVPNAdapterPacketFlow` protocol and
|
||||||
|
// send `self.packetFlow` to `completionHandler` callback.
|
||||||
|
func openVPNAdapter(
|
||||||
|
_ openVPNAdapter: OpenVPNAdapter,
|
||||||
|
configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?,
|
||||||
|
completionHandler: @escaping (Error?) -> Void
|
||||||
|
) {
|
||||||
|
// In order to direct all DNS queries first to the VPN DNS servers before the primary DNS servers
|
||||||
|
// send empty string to NEDNSSettings.matchDomains
|
||||||
|
networkSettings?.dnsSettings?.matchDomains = [""]
|
||||||
|
|
||||||
|
// Set the network settings for the current tunneling session.
|
||||||
|
setTunnelNetworkSettings(networkSettings, completionHandler: completionHandler)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process events returned by the OpenVPN library
|
||||||
|
func openVPNAdapter(
|
||||||
|
_ openVPNAdapter: OpenVPNAdapter,
|
||||||
|
handleEvent event:
|
||||||
|
OpenVPNAdapterEvent, message: String?
|
||||||
|
) {
|
||||||
|
switch event {
|
||||||
|
case .connected:
|
||||||
|
if reasserting {
|
||||||
|
reasserting = false
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let startHandler = startHandler else { return }
|
||||||
|
|
||||||
|
startHandler(nil)
|
||||||
|
self.startHandler = nil
|
||||||
|
|
||||||
|
case .disconnected:
|
||||||
|
guard let stopHandler = stopHandler else { return }
|
||||||
|
|
||||||
|
if vpnReachability.isTracking {
|
||||||
|
vpnReachability.stopTracking()
|
||||||
|
}
|
||||||
|
|
||||||
|
stopHandler()
|
||||||
|
self.stopHandler = nil
|
||||||
|
|
||||||
|
case .reconnecting:
|
||||||
|
reasserting = true
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle errors thrown by the OpenVPN library
|
||||||
|
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
|
||||||
|
// Handle only fatal errors
|
||||||
|
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool,
|
||||||
|
fatal == true else { return }
|
||||||
|
|
||||||
|
if vpnReachability.isTracking {
|
||||||
|
vpnReachability.stopTracking()
|
||||||
|
}
|
||||||
|
|
||||||
|
if let startHandler = startHandler {
|
||||||
|
startHandler(error)
|
||||||
|
self.startHandler = nil
|
||||||
|
} else {
|
||||||
|
cancelTunnelWithError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use this method to process any log message returned by OpenVPN library.
|
||||||
|
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
|
||||||
|
// Handle log messages
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
let vpnName = "AmneziaVPN"
|
let vpnName = "Amnezia WireguardVPN"
|
||||||
var vpnBundleID = "";
|
var vpnBundleID = "";
|
||||||
|
|
||||||
@objc class VPNIPAddressRange : NSObject {
|
@objc class VPNIPAddressRange : NSObject {
|
||||||
|
@ -30,27 +30,32 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||||
private var deviceIpv4Address: String? = nil
|
private var deviceIpv4Address: String? = nil
|
||||||
private var deviceIpv6Address: String? = nil
|
private var deviceIpv6Address: String? = nil
|
||||||
|
|
||||||
@objc enum IOSConnectionState: Int { case Error, Connected, Disconnected }
|
@objc enum ConnectionState: Int { case Error, Connected, Disconnected }
|
||||||
|
|
||||||
@objc init(bundleID: String, privateKey: Data, deviceIpv4Address: String, deviceIpv6Address: String, closure: @escaping (IOSConnectionState, Date?) -> Void, callback: @escaping (Bool) -> Void) {
|
@objc init(bundleID: String,
|
||||||
|
config: String,
|
||||||
|
closure: @escaping (ConnectionState, Date?) -> Void,
|
||||||
|
callback: @escaping (Bool) -> Void) {
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
Logger.configureGlobal(tagged: "APP", withFilePath: "")
|
Logger.configureGlobal(tagged: "APP", withFilePath: "")
|
||||||
|
|
||||||
|
print("Config from caller: \(config)")
|
||||||
|
|
||||||
vpnBundleID = bundleID;
|
vpnBundleID = bundleID;
|
||||||
precondition(!vpnBundleID.isEmpty)
|
precondition(!vpnBundleID.isEmpty)
|
||||||
|
|
||||||
stateChangeCallback = callback
|
stateChangeCallback = callback
|
||||||
self.privateKey = PrivateKey(rawValue: privateKey)
|
|
||||||
self.deviceIpv4Address = deviceIpv4Address
|
NotificationCenter.default.removeObserver(self)
|
||||||
self.deviceIpv6Address = deviceIpv6Address
|
NotificationCenter.default.addObserver(self,
|
||||||
|
selector: #selector(self.vpnStatusDidChange(notification:)),
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(self.vpnStatusDidChange(notification:)), name: Notification.Name.NEVPNStatusDidChange, object: nil)
|
name: Notification.Name.NEVPNStatusDidChange,
|
||||||
|
object: nil)
|
||||||
|
|
||||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
Logger.global?.log(message: "Loading from preference failed: \(error)")
|
Logger.global?.log(message: "Loading from preference failed: \(error)")
|
||||||
closure(IOSConnectionState.Error, nil)
|
closure(ConnectionState.Error, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,11 +69,22 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||||
print("We have received \(nsManagers.count) managers.")
|
print("We have received \(nsManagers.count) managers.")
|
||||||
|
|
||||||
let tunnel = nsManagers.first(where: IOSVpnProtocolImpl.isOurManager(_:))
|
let tunnel = nsManagers.first(where: IOSVpnProtocolImpl.isOurManager(_:))
|
||||||
|
|
||||||
|
// if let name = tunnel?.localizedDescription, name == vpnName {
|
||||||
|
// tunnel?.removeFromPreferences(completionHandler: { removeError in
|
||||||
|
// if let error = removeError {
|
||||||
|
// Logger.global?.log(message: "WireguardVPN Tunnel Remove from Prefs Error: \(error)")
|
||||||
|
// closure(ConnectionState.Error, nil)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
if tunnel == nil {
|
if tunnel == nil {
|
||||||
Logger.global?.log(message: "Creating the tunnel")
|
Logger.global?.log(message: "Creating the tunnel")
|
||||||
print("Creating the tunnel")
|
print("Creating the tunnel")
|
||||||
self!.tunnel = NETunnelProviderManager()
|
self!.tunnel = NETunnelProviderManager()
|
||||||
closure(IOSConnectionState.Disconnected, nil)
|
closure(ConnectionState.Disconnected, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,9 +93,80 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||||
|
|
||||||
self!.tunnel = tunnel
|
self!.tunnel = tunnel
|
||||||
if tunnel?.connection.status == .connected {
|
if tunnel?.connection.status == .connected {
|
||||||
closure(IOSConnectionState.Connected, tunnel?.connection.connectedDate)
|
closure(ConnectionState.Connected, tunnel?.connection.connectedDate)
|
||||||
} else {
|
} else {
|
||||||
closure(IOSConnectionState.Disconnected, nil)
|
closure(ConnectionState.Disconnected, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc init(bundleID: String,
|
||||||
|
privateKey: Data,
|
||||||
|
deviceIpv4Address: String,
|
||||||
|
deviceIpv6Address: String,
|
||||||
|
closure: @escaping (ConnectionState, Date?) -> Void,
|
||||||
|
callback: @escaping (Bool) -> Void) {
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
Logger.configureGlobal(tagged: "APP", withFilePath: "")
|
||||||
|
|
||||||
|
vpnBundleID = bundleID;
|
||||||
|
precondition(!vpnBundleID.isEmpty)
|
||||||
|
|
||||||
|
stateChangeCallback = callback
|
||||||
|
self.privateKey = PrivateKey(rawValue: privateKey)
|
||||||
|
self.deviceIpv4Address = deviceIpv4Address
|
||||||
|
self.deviceIpv6Address = deviceIpv6Address
|
||||||
|
|
||||||
|
NotificationCenter.default.removeObserver(self)
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(self.vpnStatusDidChange(notification:)), name: Notification.Name.NEVPNStatusDidChange, object: nil)
|
||||||
|
|
||||||
|
NETunnelProviderManager.loadAllFromPreferences { [weak self] managers, error in
|
||||||
|
if let error = error {
|
||||||
|
Logger.global?.log(message: "Loading from preference failed: \(error)")
|
||||||
|
closure(ConnectionState.Error, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if self == nil {
|
||||||
|
Logger.global?.log(message: "We are shutting down.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let nsManagers = managers ?? []
|
||||||
|
Logger.global?.log(message: "We have received \(nsManagers.count) managers.")
|
||||||
|
print("We have received \(nsManagers.count) managers.")
|
||||||
|
|
||||||
|
let tunnel = nsManagers.first(where: IOSVpnProtocolImpl.isOurManager(_:))
|
||||||
|
|
||||||
|
// if let name = tunnel?.localizedDescription, name != vpnName {
|
||||||
|
// tunnel?.removeFromPreferences(completionHandler: { removeError in
|
||||||
|
// if let error = removeError {
|
||||||
|
// Logger.global?.log(message: "OpenVpn Tunnel Remove from Prefs Error: \(error)")
|
||||||
|
// closure(ConnectionState.Error, nil)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
|
if tunnel == nil {
|
||||||
|
Logger.global?.log(message: "Creating the tunnel")
|
||||||
|
print("Creating the tunnel")
|
||||||
|
self!.tunnel = NETunnelProviderManager()
|
||||||
|
closure(ConnectionState.Disconnected, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.global?.log(message: "Tunnel already exists")
|
||||||
|
print("Tunnel already exists")
|
||||||
|
|
||||||
|
self!.tunnel = tunnel
|
||||||
|
|
||||||
|
if tunnel?.connection.status == .connected {
|
||||||
|
closure(ConnectionState.Connected, tunnel?.connection.connectedDate)
|
||||||
|
} else {
|
||||||
|
closure(ConnectionState.Disconnected, nil)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -142,6 +229,22 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||||
print("Found the manager with the correct bundle identifier: \(tunnelProto.providerBundleIdentifier!)")
|
print("Found the manager with the correct bundle identifier: \(tunnelProto.providerBundleIdentifier!)")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func connect(ovpnConfig: String, failureCallback: @escaping () -> Void) {
|
||||||
|
Logger.global?.log(message: "Connecting")
|
||||||
|
assert(tunnel != nil)
|
||||||
|
|
||||||
|
let addr: String = ovpnConfig
|
||||||
|
.splitToArray(separator: "\n", trimmingCharacters: nil)
|
||||||
|
.first { $0.starts(with: "remote ") }
|
||||||
|
.splitToArray(separator: " ", trimmingCharacters: nil)[1]
|
||||||
|
print("server: \(addr)")
|
||||||
|
|
||||||
|
// Let's remove the previous config if it exists.
|
||||||
|
(tunnel?.protocolConfiguration as? NETunnelProviderProtocol)?.destroyConfigurationReference()
|
||||||
|
|
||||||
|
self.configureOpenVPNTunnel(serverAddress: addr, config: ovpnConfig, failureCallback: failureCallback)
|
||||||
|
}
|
||||||
|
|
||||||
@objc func connect(dnsServer: String, serverIpv6Gateway: String, serverPublicKey: String, presharedKey: String, serverIpv4AddrIn: String, serverPort: Int, allowedIPAddressRanges: Array<VPNIPAddressRange>, ipv6Enabled: Bool, reason: Int, failureCallback: @escaping () -> Void) {
|
@objc func connect(dnsServer: String, serverIpv6Gateway: String, serverPublicKey: String, presharedKey: String, serverIpv4AddrIn: String, serverPort: Int, allowedIPAddressRanges: Array<VPNIPAddressRange>, ipv6Enabled: Bool, reason: Int, failureCallback: @escaping () -> Void) {
|
||||||
Logger.global?.log(message: "Connecting")
|
Logger.global?.log(message: "Connecting")
|
||||||
|
@ -247,6 +350,46 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func configureOpenVPNTunnel(serverAddress: String, config: String, failureCallback: @escaping () -> Void) {
|
||||||
|
let tunnelProtocol = NETunnelProviderProtocol()
|
||||||
|
tunnelProtocol.serverAddress = serverAddress
|
||||||
|
tunnelProtocol.providerBundleIdentifier = vpnBundleID
|
||||||
|
tunnelProtocol.providerConfiguration = ["ovpn": Data(config.utf8)]
|
||||||
|
tunnel?.protocolConfiguration = tunnelProtocol
|
||||||
|
tunnel?.localizedDescription = "Amnezia OpenVPN"
|
||||||
|
tunnel?.isEnabled = true
|
||||||
|
|
||||||
|
tunnel?.saveToPreferences { [unowned self] saveError in
|
||||||
|
if let error = saveError {
|
||||||
|
Logger.global?.log(message: "Connect OpenVPN Tunnel Save Error: \(error)")
|
||||||
|
failureCallback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.global?.log(message: "Saving the OpenVPN tunnel succeeded")
|
||||||
|
|
||||||
|
self.tunnel?.loadFromPreferences { error in
|
||||||
|
if let error = error {
|
||||||
|
Logger.global?.log(message: "Connect OpenVPN Tunnel Load Error: \(error)")
|
||||||
|
failureCallback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.global?.log(message: "Loading the OpenVPN tunnel succeeded")
|
||||||
|
print("Loading the openvpn tunnel succeeded")
|
||||||
|
|
||||||
|
do {
|
||||||
|
print("starting openvpn tunnel")
|
||||||
|
try self.tunnel?.connection.startVPNTunnel()
|
||||||
|
} catch let error {
|
||||||
|
Logger.global?.log(message: "Something went wrong: \(error)")
|
||||||
|
failureCallback()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func disconnect() {
|
@objc func disconnect() {
|
||||||
Logger.global?.log(message: "Disconnecting")
|
Logger.global?.log(message: "Disconnecting")
|
||||||
|
@ -257,7 +400,74 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||||
@objc func checkStatus(callback: @escaping (String, String, String) -> Void) {
|
@objc func checkStatus(callback: @escaping (String, String, String) -> Void) {
|
||||||
Logger.global?.log(message: "Check status")
|
Logger.global?.log(message: "Check status")
|
||||||
assert(tunnel != nil)
|
assert(tunnel != nil)
|
||||||
|
|
||||||
|
let protoType = (tunnel!.localizedDescription ?? "").toTunnelType
|
||||||
|
|
||||||
|
switch protoType {
|
||||||
|
case .wireguard:
|
||||||
|
checkWireguardStatus(callback: callback)
|
||||||
|
case .openvpn:
|
||||||
|
checkOVPNStatus(callback: callback)
|
||||||
|
case .empty:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func checkOVPNStatus(callback: @escaping (String, String, String) -> Void) {
|
||||||
|
Logger.global?.log(message: "Check OpenVPN")
|
||||||
|
guard let proto = tunnel?.protocolConfiguration as? NETunnelProviderProtocol else {
|
||||||
|
callback("", "", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let configData = proto.providerConfiguration?["ovpn"] as? Data,
|
||||||
|
let ovpnConfig = String(data: configData, encoding: .utf8) else {
|
||||||
|
callback("", "", "")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let serverIpv4Gateway: String = ovpnConfig
|
||||||
|
.splitToArray(separator: "\n", trimmingCharacters: nil)
|
||||||
|
.first { $0.starts(with: "remote ") }
|
||||||
|
.splitToArray(separator: " ", trimmingCharacters: nil)[1]
|
||||||
|
|
||||||
|
print("server IP: \(serverIpv4Gateway)")
|
||||||
|
|
||||||
|
|
||||||
|
let deviceIpv4Address = getTunIPAddress()
|
||||||
|
print("device IP: \(serverIpv4Gateway)")
|
||||||
|
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("", "", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private func checkWireguardStatus(callback: @escaping (String, String, String) -> Void) {
|
||||||
|
Logger.global?.log(message: "Check Wireguard")
|
||||||
let proto = tunnel!.protocolConfiguration as? NETunnelProviderProtocol
|
let proto = tunnel!.protocolConfiguration as? NETunnelProviderProtocol
|
||||||
if proto == nil {
|
if proto == nil {
|
||||||
callback("", "", "")
|
callback("", "", "")
|
||||||
|
@ -305,4 +515,50 @@ public class IOSVpnProtocolImpl : NSObject {
|
||||||
callback("", "", "")
|
callback("", "", "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func getTunIPAddress() -> String? {
|
||||||
|
var address: String? = nil
|
||||||
|
var interfaces: UnsafeMutablePointer<ifaddrs>? = nil
|
||||||
|
var temp_addr: UnsafeMutablePointer<ifaddrs>? = nil
|
||||||
|
var success: Int = 0
|
||||||
|
|
||||||
|
// retrieve the current interfaces - returns 0 on success
|
||||||
|
success = Int(getifaddrs(&interfaces))
|
||||||
|
if success == 0 {
|
||||||
|
// Loop through linked list of interfaces
|
||||||
|
temp_addr = interfaces
|
||||||
|
while temp_addr != nil {
|
||||||
|
if temp_addr?.pointee.ifa_addr == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if temp_addr?.pointee.ifa_addr.pointee.sa_family == UInt8(AF_INET) {
|
||||||
|
// Check if interface is en0 which is the wifi connection on the iPhone
|
||||||
|
if let name = temp_addr?.pointee.ifa_name, ((String(utf8String: name)?.contains("tun")) != nil) {
|
||||||
|
// Get NSString from C String
|
||||||
|
if let value = temp_addr?.pointee.ifa_addr as? sockaddr_in {
|
||||||
|
address = String(utf8String: inet_ntoa(value.sin_addr))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temp_addr = temp_addr?.pointee.ifa_next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
freeifaddrs(interfaces)
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TunnelType: String {
|
||||||
|
case wireguard, openvpn, empty
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
var toTunnelType: TunnelType {
|
||||||
|
switch self {
|
||||||
|
case "wireguard": return .wireguard
|
||||||
|
case "openvpn": return .openvpn
|
||||||
|
default:
|
||||||
|
return .empty
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
#include "protocols/protocols_defs.h"
|
#include "protocols/protocols_defs.h"
|
||||||
|
#include "json.h"
|
||||||
|
|
||||||
using namespace amnezia;
|
using namespace amnezia;
|
||||||
|
|
||||||
|
@ -45,6 +46,12 @@ private:
|
||||||
bool m_serviceConnected = false;
|
bool m_serviceConnected = false;
|
||||||
bool m_checkingStatus = false;
|
bool m_checkingStatus = false;
|
||||||
std::function<void(const QString&)> m_logCallback;
|
std::function<void(const QString&)> m_logCallback;
|
||||||
|
|
||||||
|
void setupWireguardProtocol(const QtJson::JsonObject& result);
|
||||||
|
void setupOpenVPNProtocol(const QtJson::JsonObject& result);
|
||||||
|
|
||||||
|
void launchWireguardTunnel(const QtJson::JsonObject &result);
|
||||||
|
void launchOpenVPNTunnel(const QtJson::JsonObject &result);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,6 @@
|
||||||
|
|
||||||
#include <QByteArray>
|
#include <QByteArray>
|
||||||
|
|
||||||
#include "json.h"
|
|
||||||
|
|
||||||
#include "ipaddressrange.h"
|
#include "ipaddressrange.h"
|
||||||
#include "ios_vpnprotocol.h"
|
#include "ios_vpnprotocol.h"
|
||||||
#include "core/errorstrings.h"
|
#include "core/errorstrings.h"
|
||||||
|
@ -21,6 +19,7 @@ namespace
|
||||||
{
|
{
|
||||||
IOSVpnProtocol* s_instance = nullptr;
|
IOSVpnProtocol* s_instance = nullptr;
|
||||||
IOSVpnProtocolImpl* m_controller = nullptr;
|
IOSVpnProtocolImpl* m_controller = nullptr;
|
||||||
|
Proto currentProto = amnezia::Proto::Any;
|
||||||
}
|
}
|
||||||
|
|
||||||
IOSVpnProtocol::IOSVpnProtocol(Proto proto, const QJsonObject &configuration, QObject* parent)
|
IOSVpnProtocol::IOSVpnProtocol(Proto proto, const QJsonObject &configuration, QObject* parent)
|
||||||
|
@ -35,10 +34,9 @@ bool IOSVpnProtocol::initialize()
|
||||||
{
|
{
|
||||||
qDebug() << "Initializing Swift Controller";
|
qDebug() << "Initializing Swift Controller";
|
||||||
|
|
||||||
static bool creating = false;
|
|
||||||
// No nested creation!
|
// qDebug() << "config =>";
|
||||||
Q_ASSERT(creating == false);
|
// qDebug() << QJsonDocument(m_rawConfig).toJson();
|
||||||
creating = true;
|
|
||||||
|
|
||||||
if (!m_controller) {
|
if (!m_controller) {
|
||||||
bool ok;
|
bool ok;
|
||||||
|
@ -49,142 +47,69 @@ bool IOSVpnProtocol::initialize()
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString vpnProto = result["protocol"].toString();
|
QString protoName = result["protocol"].toString();
|
||||||
qDebug() << "protocol: " << vpnProto;
|
qDebug() << "protocol: " << protoName;
|
||||||
qDebug() << "config data => ";
|
|
||||||
QtJson::JsonObject config = result["wireguard_config_data"].toMap();
|
if (protoName == "wireguard") {
|
||||||
|
setupWireguardProtocol(result);
|
||||||
QString privateKey = config["client_priv_key"].toString();
|
currentProto = amnezia::Proto::WireGuard;
|
||||||
QByteArray key = QByteArray::fromBase64(privateKey.toLocal8Bit());
|
} else if (protoName == "openvpn") {
|
||||||
|
setupOpenVPNProtocol(result);
|
||||||
qDebug() << " - " << "client_priv_key: " << config["client_priv_key"].toString();
|
currentProto = amnezia::Proto::OpenVpn;
|
||||||
qDebug() << " - " << "client_pub_key: " << config["client_pub_key"].toString();
|
} else {
|
||||||
qDebug() << " - " << "interface config: " << config["config"].toString();
|
return false;
|
||||||
|
|
||||||
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 connectionStateChanged(VpnConnectionState::Error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ConnectionStateConnected: {
|
|
||||||
Q_ASSERT(date);
|
|
||||||
QDateTime qtDate(QDateTime::fromNSDate(date));
|
|
||||||
emit connectionStateChanged(VpnConnectionState::Connected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
case ConnectionStateDisconnected:
|
|
||||||
// Just in case we are connecting, let's call disconnect.
|
|
||||||
[m_controller disconnect];
|
|
||||||
emit connectionStateChanged(VpnConnectionState::Disconnected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
callback:^(BOOL a_connected) {
|
|
||||||
qDebug() << "State changed: " << a_connected;
|
|
||||||
if (a_connected) {
|
|
||||||
emit connectionStateChanged(Connected);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// emit connectionStateChanged(Disconnected);
|
|
||||||
}];
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ErrorCode IOSVpnProtocol::start()
|
ErrorCode IOSVpnProtocol::start()
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
QtJson::JsonObject result = QtJson::parse(QJsonDocument(m_rawConfig).toJson(), ok).toMap();
|
QtJson::JsonObject result = QtJson::parse(QJsonDocument(m_rawConfig).toJson(), ok).toMap();
|
||||||
|
qDebug() << "current protocol: " << currentProto;
|
||||||
|
qDebug() << "new protocol: " << m_protocol;
|
||||||
|
qDebug() << "config: " << result;
|
||||||
|
|
||||||
if(!ok) {
|
if(!ok) {
|
||||||
qDebug() << QString("An error occurred during config parsing");
|
qDebug() << QString("An error occurred during config parsing");
|
||||||
return InternalError;
|
return InternalError;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString protocol = result["protocol"].toString();
|
QString protocol = result["protocol"].toString();
|
||||||
QtJson::JsonObject config = result["wireguard_config_data"].toMap();
|
|
||||||
|
|
||||||
QString clientPrivateKey = config["client_priv_key"].toString();
|
switch (m_protocol) {
|
||||||
QByteArray key = QByteArray::fromBase64(clientPrivateKey.toLocal8Bit());
|
case amnezia::Proto::OpenVpn:
|
||||||
QString clientPubKey = config["client_pub_key"].toString();
|
if (currentProto == amnezia::Proto::WireGuard) {
|
||||||
|
if (m_controller) {
|
||||||
QString addr = config["config"].toString().split("\n").takeAt(1).split(" = ").takeLast();
|
stop();
|
||||||
QStringList dnsServersList = config["config"].toString().split("\n").takeAt(2).split(" = ").takeLast().split(", ");
|
initialize();
|
||||||
QString privkey = config["config"].toString().split("\n").takeAt(3).split(" = ").takeLast();
|
}
|
||||||
QString pubkey = config["config"].toString().split("\n").takeAt(6).split(" = ").takeLast();
|
launchOpenVPNTunnel(result);
|
||||||
QString presharedkey = config["config"].toString().split("\n").takeAt(7).split(" = ").takeLast();
|
currentProto = amnezia::Proto::OpenVpn;
|
||||||
QStringList allowedIPList = config["config"].toString().split("\n").takeAt(8).split(" = ").takeLast().split(", ");
|
return NoError;
|
||||||
QString endpoint = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast();
|
}
|
||||||
QString serverAddr = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast().split(":").takeFirst();
|
initialize();
|
||||||
QString port = config["config"].toString().split("\n").takeAt(9).split(" = ").takeLast().split(":").takeLast();
|
launchOpenVPNTunnel(result);
|
||||||
QString keepalive = config["config"].toString().split("\n").takeAt(10).split(" = ").takeLast();
|
break;
|
||||||
|
case amnezia::Proto::WireGuard:
|
||||||
QString hostname = config["hostName"].toString();
|
if (currentProto == amnezia::Proto::OpenVpn) {
|
||||||
QString pskKey = config["psk_key"].toString();
|
if (m_controller) {
|
||||||
QString serverPubKey = config["server_pub_key"].toString();
|
stop();
|
||||||
|
initialize();
|
||||||
qDebug() << "IOSVPNProtocol starts for" << hostname;
|
}
|
||||||
qDebug() << "DNS:" << dnsServersList.takeFirst().toNSString();
|
launchWireguardTunnel(result);
|
||||||
qDebug() << "serverPublicKey:" << serverPubKey.toNSString();
|
currentProto = amnezia::Proto::WireGuard;
|
||||||
qDebug() << "serverIpv4AddrIn:" << serverAddr.toNSString();
|
return NoError;
|
||||||
qDebug() << "serverPort:" << (uint32_t)port.toInt();
|
}
|
||||||
qDebug() << "allowed ip list" << allowedIPList;
|
initialize();
|
||||||
|
launchWireguardTunnel(result);
|
||||||
NSMutableArray<VPNIPAddressRange*>* allowedIPAddressRangesNS =
|
break;
|
||||||
[NSMutableArray<VPNIPAddressRange*> arrayWithCapacity:allowedIPList.length()];
|
default:
|
||||||
for (const IPAddressRange item : allowedIPList) {
|
break;
|
||||||
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()
|
|
||||||
presharedKey:pskKey.toNSString()
|
|
||||||
serverIpv4AddrIn:serverAddr.toNSString()
|
|
||||||
serverPort:port.toInt()
|
|
||||||
allowedIPAddressRanges:allowedIPAddressRangesNS
|
|
||||||
ipv6Enabled:NO
|
|
||||||
reason:0
|
|
||||||
failureCallback:^() {
|
|
||||||
qDebug() << "IOSVPNProtocol - connection failed";
|
|
||||||
emit connectionStateChanged(Disconnected);
|
|
||||||
}];
|
|
||||||
return NoError;
|
return NoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,11 +117,21 @@ void IOSVpnProtocol::stop()
|
||||||
{
|
{
|
||||||
if (!m_controller) {
|
if (!m_controller) {
|
||||||
qDebug() << "Not correctly initialized";
|
qDebug() << "Not correctly initialized";
|
||||||
emit connectionStateChanged(Disconnected);
|
|
||||||
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
// emit connectionStateChanged(Disconnected);
|
||||||
|
// });
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
[m_controller disconnect];
|
[m_controller disconnect];
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(Disconnected);
|
||||||
|
});
|
||||||
|
|
||||||
|
[m_controller dealloc];
|
||||||
|
m_controller = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void IOSVpnProtocol::resume_start()
|
void IOSVpnProtocol::resume_start()
|
||||||
|
@ -296,3 +231,225 @@ void IOSVpnProtocol::cleanupBackendLogs()
|
||||||
file.remove();
|
file.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void IOSVpnProtocol::setupWireguardProtocol(const QtJson::JsonObject &result)
|
||||||
|
{
|
||||||
|
static bool creating = false;
|
||||||
|
// No nested creation!
|
||||||
|
Q_ASSERT(creating == false);
|
||||||
|
creating = true;
|
||||||
|
|
||||||
|
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;
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(VpnConnectionState::Error);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ConnectionStateConnected: {
|
||||||
|
Q_ASSERT(date);
|
||||||
|
QDateTime qtDate(QDateTime::fromNSDate(date));
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(VpnConnectionState::Connected);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ConnectionStateDisconnected:
|
||||||
|
// Just in case we are connecting, let's call disconnect.
|
||||||
|
[m_controller disconnect];
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(VpnConnectionState::Disconnected);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback:^(BOOL a_connected) {
|
||||||
|
if (currentProto != m_protocol) {
|
||||||
|
qDebug() << "Protocols switched: " << a_connected;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "State changed: " << a_connected;
|
||||||
|
if (a_connected) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(Connected);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
// emit connectionStateChanged(Disconnected);
|
||||||
|
// });
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOSVpnProtocol::setupOpenVPNProtocol(const QtJson::JsonObject &result)
|
||||||
|
{
|
||||||
|
static bool creating = false;
|
||||||
|
// No nested creation!
|
||||||
|
Q_ASSERT(creating == false);
|
||||||
|
creating = true;
|
||||||
|
|
||||||
|
QtJson::JsonObject ovpn = result["openvpn_config_data"].toMap();
|
||||||
|
QString ovpnConfig = ovpn["config"].toString();
|
||||||
|
// qDebug() << ovpn;
|
||||||
|
|
||||||
|
m_controller = [[IOSVpnProtocolImpl alloc] initWithBundleID:@VPN_NE_BUNDLEID
|
||||||
|
config:ovpnConfig.toNSString()
|
||||||
|
closure:^(ConnectionState state, NSDate* date) {
|
||||||
|
qDebug() << "OVPN Creation completed with connection state:" << state;
|
||||||
|
creating = false;
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case ConnectionStateError: {
|
||||||
|
[m_controller dealloc];
|
||||||
|
m_controller = nullptr;
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(VpnConnectionState::Error);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ConnectionStateConnected: {
|
||||||
|
Q_ASSERT(date);
|
||||||
|
QDateTime qtDate(QDateTime::fromNSDate(date));
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(VpnConnectionState::Connected);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case ConnectionStateDisconnected:
|
||||||
|
// Just in case we are connecting, let's call disconnect.
|
||||||
|
// [m_controller disconnect];
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(VpnConnectionState::Disconnected);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callback:^(BOOL a_connected) {
|
||||||
|
if (currentProto != m_protocol) {
|
||||||
|
qDebug() << "Protocols switched: " << a_connected;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
qDebug() << "OVPN State changed: " << a_connected;
|
||||||
|
if (a_connected) {
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(Connected);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
// emit connectionStateChanged(Disconnected);
|
||||||
|
// });
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOSVpnProtocol::launchWireguardTunnel(const QtJson::JsonObject &result)
|
||||||
|
{
|
||||||
|
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<VPNIPAddressRange*>* allowedIPAddressRangesNS =
|
||||||
|
[NSMutableArray<VPNIPAddressRange*> 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()
|
||||||
|
presharedKey:pskKey.toNSString()
|
||||||
|
serverIpv4AddrIn:serverAddr.toNSString()
|
||||||
|
serverPort:port.toInt()
|
||||||
|
allowedIPAddressRanges:allowedIPAddressRangesNS
|
||||||
|
ipv6Enabled:NO
|
||||||
|
reason:0
|
||||||
|
failureCallback:^() {
|
||||||
|
qDebug() << "Wireguard Protocol - connection failed";
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(Disconnected);
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
void IOSVpnProtocol::launchOpenVPNTunnel(const QtJson::JsonObject &result)
|
||||||
|
{
|
||||||
|
QtJson::JsonObject ovpn = result["openvpn_config_data"].toMap();
|
||||||
|
QString ovpnConfig = ovpn["config"].toString();
|
||||||
|
|
||||||
|
[m_controller connectWithOvpnConfig:ovpnConfig.toNSString()
|
||||||
|
failureCallback:^{
|
||||||
|
qDebug() << "IOSVPNProtocol - connection failed";
|
||||||
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
|
emit connectionStateChanged(Disconnected);
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
|
@ -39,6 +39,26 @@ error() {
|
||||||
printv '' R "$1"
|
printv '' R "$1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XCODEBUILD="/usr/bin/xcodebuild"
|
||||||
|
WORKINGDIR=`pwd`
|
||||||
|
|
||||||
|
compile_openvpn_adapter() {
|
||||||
|
cd 3rd/OpenVPNAdapter
|
||||||
|
|
||||||
|
$XCODEBUILD -scheme OpenVPNAdapter -configuration Debug -xcconfig Configuration/amnezia.xcconfig -sdk iphoneos -destination 'generic/platform=iOS' -project OpenVPNAdapter.xcodeproj
|
||||||
|
|
||||||
|
cd ../../
|
||||||
|
}
|
||||||
|
|
||||||
|
prepare_to_build_vpn() {
|
||||||
|
cat $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/Project.xcconfig > $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||||
|
cat << EOF >> $WORKINGDIR/3rd/OpenVPNAdapter/Configuration/amnezia.xcconfig
|
||||||
|
PROJECT_TEMP_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/OpenVPNAdapter.build
|
||||||
|
CONFIGURATION_BUILD_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Debug-iphoneos
|
||||||
|
BUILT_PRODUCTS_DIR = $WORKINGDIR/3rd/OpenVPNAdapter/build/Debug-iphoneos
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
die() {
|
die() {
|
||||||
if [[ "$1" ]]; then
|
if [[ "$1" ]]; then
|
||||||
error "$1"
|
error "$1"
|
||||||
|
|
|
@ -126,7 +126,7 @@ printn Y "Retrieve the wireguard-go version... "
|
||||||
print G "done."
|
print G "done."
|
||||||
|
|
||||||
printn Y "Cleaning the existing project... "
|
printn Y "Cleaning the existing project... "
|
||||||
rm -rf mozillavpn.xcodeproj/ || die "Failed to remove things"
|
rm -rf AmneziaVPN.xcodeproj/ || die "Failed to remove things"
|
||||||
print G "done."
|
print G "done."
|
||||||
|
|
||||||
#print Y "Importing translation files..."
|
#print Y "Importing translation files..."
|
||||||
|
@ -205,6 +205,16 @@ else
|
||||||
print G none
|
print G none
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ "$OS" = "ios" ]; then
|
||||||
|
print Y "Prepare to build OpenVPNAdapter..."
|
||||||
|
prepare_to_build_vpn
|
||||||
|
print Y "Building OpenVPNAdapter..."
|
||||||
|
compile_openvpn_adapter
|
||||||
|
else
|
||||||
|
print Y "No OpenVPNAdapter will be built"
|
||||||
|
|
||||||
|
fi
|
||||||
|
|
||||||
print Y "Creating the xcode project via qmake..."
|
print Y "Creating the xcode project via qmake..."
|
||||||
$QMAKE \
|
$QMAKE \
|
||||||
VERSION=$SHORTVERSION \
|
VERSION=$SHORTVERSION \
|
||||||
|
|
|
@ -61,7 +61,8 @@ class XCodeprojPatcher
|
||||||
config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] ||= 'macos/app/WireGuard-Bridging-Header.h'
|
config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] ||= 'macos/app/WireGuard-Bridging-Header.h'
|
||||||
config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= [
|
config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= [
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/3rd"
|
"$(PROJECT_DIR)/3rd",
|
||||||
|
"$(PROJECT_DIR)/3rd/OpenVPNAdapter/build/Debug-iphoneos"
|
||||||
]
|
]
|
||||||
|
|
||||||
# Versions and names
|
# Versions and names
|
||||||
|
@ -251,6 +252,11 @@ class XCodeprojPatcher
|
||||||
config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] ||= 'macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h'
|
config.build_settings['SWIFT_OBJC_BRIDGING_HEADER'] ||= 'macos/networkextension/WireGuardNetworkExtension-Bridging-Header.h'
|
||||||
config.build_settings['SWIFT_PRECOMPILE_BRIDGING_HEADER'] = 'NO'
|
config.build_settings['SWIFT_PRECOMPILE_BRIDGING_HEADER'] = 'NO'
|
||||||
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'YES'
|
config.build_settings['APPLICATION_EXTENSION_API_ONLY'] = 'YES'
|
||||||
|
config.build_settings['FRAMEWORK_SEARCH_PATHS'] ||= [
|
||||||
|
"$(inherited)",
|
||||||
|
"$(PROJECT_DIR)/3rd",
|
||||||
|
"$(PROJECT_DIR)/3rd/OpenVPNAdapter/build/Debug-iphoneos"
|
||||||
|
]
|
||||||
|
|
||||||
# Versions and names
|
# Versions and names
|
||||||
config.build_settings['MARKETING_VERSION'] ||= shortVersion
|
config.build_settings['MARKETING_VERSION'] ||= shortVersion
|
||||||
|
@ -350,6 +356,18 @@ class XCodeprojPatcher
|
||||||
|
|
||||||
framework_ref = frameworks_group.new_file('NetworkExtension.framework')
|
framework_ref = frameworks_group.new_file('NetworkExtension.framework')
|
||||||
frameworks_build_phase.add_file_reference(framework_ref)
|
frameworks_build_phase.add_file_reference(framework_ref)
|
||||||
|
|
||||||
|
framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Debug-iphoneos/LZ4.framework')
|
||||||
|
frameworks_build_phase.add_file_reference(framework_ref)
|
||||||
|
|
||||||
|
framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Debug-iphoneos/mbedTLS.framework')
|
||||||
|
frameworks_build_phase.add_file_reference(framework_ref)
|
||||||
|
|
||||||
|
framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Debug-iphoneos/OpenVPNClient.framework')
|
||||||
|
frameworks_build_phase.add_file_reference(framework_ref)
|
||||||
|
|
||||||
|
framework_ref = frameworks_group.new_file('3rd/OpenVPNAdapter/build/Debug-iphoneos/OpenVPNAdapter.framework')
|
||||||
|
frameworks_build_phase.add_file_reference(framework_ref)
|
||||||
|
|
||||||
# This fails: @target_main.add_dependency @target_extension
|
# This fails: @target_main.add_dependency @target_extension
|
||||||
container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy)
|
container_proxy = @project.new(Xcodeproj::Project::PBXContainerItemProxy)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
DEVELOPMENT_TEAM = X7UJ388FXK
|
DEVELOPMENT_TEAM = 7VAGZXD78P
|
||||||
|
|
||||||
GROUP_ID_IOS = group.ru.kotit.AmneziaVPN.udev
|
GROUP_ID_IOS = group.ru.kotit.AmneziaVPN.udev
|
||||||
APP_ID_IOS = ru.kotit.AmneziaVPN
|
APP_ID_IOS = ru.kotit.AmneziaVPN
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue