From f5c85fcb12a6e27580c515d08cdad108d8eea7dd Mon Sep 17 00:00:00 2001 From: Jonathan Downing Date: Thu, 12 Oct 2017 13:06:25 +0100 Subject: [PATCH] Add OpenVPNNetworkSettingsBuilder Add OpenVPNPacketFlowAdapter --- OpenVPN Adapter.xcodeproj/project.pbxproj | 48 +- OpenVPN Adapter/OpenVPNAdapter.mm | 493 ++++-------------- .../OpenVPNNetworkSettingsBuilder.h | 52 ++ .../OpenVPNNetworkSettingsBuilder.m | 152 ++++++ OpenVPN Adapter/OpenVPNPacketFlowAdapter.h | 24 + OpenVPN Adapter/OpenVPNPacketFlowAdapter.mm | 179 +++++++ 6 files changed, 549 insertions(+), 399 deletions(-) create mode 100644 OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.h create mode 100644 OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.m create mode 100644 OpenVPN Adapter/OpenVPNPacketFlowAdapter.h create mode 100644 OpenVPN Adapter/OpenVPNPacketFlowAdapter.mm diff --git a/OpenVPN Adapter.xcodeproj/project.pbxproj b/OpenVPN Adapter.xcodeproj/project.pbxproj index d37ec18..3bc9bd6 100644 --- a/OpenVPN Adapter.xcodeproj/project.pbxproj +++ b/OpenVPN Adapter.xcodeproj/project.pbxproj @@ -7,6 +7,18 @@ objects = { /* Begin PBXBuildFile section */ + ABD6EF091F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = ABD6EF071F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h */; }; + ABD6EF0A1F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = ABD6EF071F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h */; }; + ABD6EF0B1F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = ABD6EF081F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m */; }; + ABD6EF0C1F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = ABD6EF081F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m */; }; + ABD6EF101F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = ABD6EF0E1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h */; }; + ABD6EF111F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = ABD6EF0E1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h */; }; + ABD6EF121F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABD6EF0F1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm */; }; + ABD6EF131F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABD6EF0F1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm */; }; + ABD6EF171F8F9C38007D3D90 /* OpenVPNAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = ABD6EF151F8F9C37007D3D90 /* OpenVPNAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ABD6EF181F8F9C38007D3D90 /* OpenVPNAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABD6EF161F8F9C38007D3D90 /* OpenVPNAdapter.mm */; }; + ABD6EF191F8F9C3B007D3D90 /* OpenVPNAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = ABD6EF151F8F9C37007D3D90 /* OpenVPNAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; }; + ABD6EF1A1F8F9C3B007D3D90 /* OpenVPNAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = ABD6EF161F8F9C38007D3D90 /* OpenVPNAdapter.mm */; }; C90BAD311E73FF6C00DEFB32 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C90BAD301E73FF6C00DEFB32 /* SystemConfiguration.framework */; }; C912BB251E7C3339002B9414 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C912BB241E7C3339002B9414 /* NetworkExtension.framework */; }; C915F1F41F612F3300B3DF23 /* OpenVPNPrivateKey.h in Headers */ = {isa = PBXBuildFile; fileRef = C915F1F21F612F3300B3DF23 /* OpenVPNPrivateKey.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -99,8 +111,6 @@ C9BB47601E71663A00F3F98C /* Umbrella-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB475E1E71663A00F3F98C /* Umbrella-Header.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9BB47711E7171A100F3F98C /* OpenVPNError.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB476F1E7171A100F3F98C /* OpenVPNError.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9BB47721E7171A100F3F98C /* OpenVPNAdapterEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB47701E7171A100F3F98C /* OpenVPNAdapterEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C9BB477F1E7173C700F3F98C /* OpenVPNAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB477B1E7173C700F3F98C /* OpenVPNAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C9BB47821E7173C700F3F98C /* OpenVPNAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = C9BB477E1E7173C700F3F98C /* OpenVPNAdapter.mm */; }; C9BB47911E71821A00F3F98C /* OpenVPNAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BB47901E71821A00F3F98C /* OpenVPNAdapterTests.swift */; }; C9BB47931E71821A00F3F98C /* OpenVPNAdapter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C9BB475C1E71663A00F3F98C /* OpenVPNAdapter.framework */; }; C9BB47A21E7183DB00F3F98C /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9BB47A11E7183DB00F3F98C /* Bundle.swift */; }; @@ -116,12 +126,10 @@ C9CA4DD61F602F7B00C4F184 /* OpenVPNCertificate.m in Sources */ = {isa = PBXBuildFile; fileRef = C9CA4DD21F602F7B00C4F184 /* OpenVPNCertificate.m */; }; C9CA4DE11F603A5300C4F184 /* OpenVPNCertificateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9CA4DE01F603A5300C4F184 /* OpenVPNCertificateTests.swift */; }; C9CA4DE21F603A5300C4F184 /* OpenVPNCertificateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C9CA4DE01F603A5300C4F184 /* OpenVPNCertificateTests.swift */; }; - C9D2ABDB1EA20F99007EDF9D /* OpenVPNAdapter.mm in Sources */ = {isa = PBXBuildFile; fileRef = C9BB477E1E7173C700F3F98C /* OpenVPNAdapter.mm */; }; C9D2ABDE1EA20F99007EDF9D /* ovpncli.cpp in Sources */ = {isa = PBXBuildFile; fileRef = C9FD92191E9A667600374FC4 /* ovpncli.cpp */; }; C9D2ABE01EA20F99007EDF9D /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C912BB241E7C3339002B9414 /* NetworkExtension.framework */; }; C9D2ABE11EA20F99007EDF9D /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C90BAD301E73FF6C00DEFB32 /* SystemConfiguration.framework */; }; C9D2ABE41EA20F99007EDF9D /* OpenVPNAdapterEvent.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB47701E7171A100F3F98C /* OpenVPNAdapterEvent.h */; settings = {ATTRIBUTES = (Public, ); }; }; - C9D2ABE51EA20F99007EDF9D /* OpenVPNAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB477B1E7173C700F3F98C /* OpenVPNAdapter.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9D2ABE61EA20F99007EDF9D /* Umbrella-Header.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB475E1E71663A00F3F98C /* Umbrella-Header.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9D2ABE81EA20F99007EDF9D /* OpenVPNError.h in Headers */ = {isa = PBXBuildFile; fileRef = C9BB476F1E7171A100F3F98C /* OpenVPNError.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9D2ABEA1EA20F99007EDF9D /* ovpncli.hpp in Headers */ = {isa = PBXBuildFile; fileRef = C9FD92181E9A667600374FC4 /* ovpncli.hpp */; }; @@ -154,6 +162,12 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + ABD6EF071F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNNetworkSettingsBuilder.h; sourceTree = ""; }; + ABD6EF081F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OpenVPNNetworkSettingsBuilder.m; sourceTree = ""; }; + ABD6EF0E1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNPacketFlowAdapter.h; sourceTree = ""; }; + ABD6EF0F1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNPacketFlowAdapter.mm; sourceTree = ""; }; + ABD6EF151F8F9C37007D3D90 /* OpenVPNAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNAdapter.h; sourceTree = ""; }; + ABD6EF161F8F9C38007D3D90 /* OpenVPNAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNAdapter.mm; sourceTree = ""; }; C90BAD261E73F47E00DEFB32 /* Info-Framework.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-Framework.plist"; sourceTree = ""; }; C90BAD271E73F47E00DEFB32 /* Info-Tests.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-Tests.plist"; sourceTree = ""; }; C90BAD291E73F56800DEFB32 /* Framework.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Framework.xcconfig; sourceTree = ""; }; @@ -211,8 +225,6 @@ C9BB475E1E71663A00F3F98C /* Umbrella-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Umbrella-Header.h"; sourceTree = ""; }; C9BB476F1E7171A100F3F98C /* OpenVPNError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = OpenVPNError.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; C9BB47701E7171A100F3F98C /* OpenVPNAdapterEvent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNAdapterEvent.h; sourceTree = ""; }; - C9BB477B1E7173C700F3F98C /* OpenVPNAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNAdapter.h; sourceTree = ""; }; - C9BB477E1E7173C700F3F98C /* OpenVPNAdapter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = OpenVPNAdapter.mm; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; C9BB478E1E71821A00F3F98C /* OpenVPNAdapterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenVPNAdapterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; C9BB47901E71821A00F3F98C /* OpenVPNAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenVPNAdapterTests.swift; sourceTree = ""; }; C9BB47A11E7183DB00F3F98C /* Bundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = ""; }; @@ -414,8 +426,12 @@ C9BB47671E7169F000F3F98C /* Adapter */ = { isa = PBXGroup; children = ( - C9BB477B1E7173C700F3F98C /* OpenVPNAdapter.h */, - C9BB477E1E7173C700F3F98C /* OpenVPNAdapter.mm */, + ABD6EF151F8F9C37007D3D90 /* OpenVPNAdapter.h */, + ABD6EF161F8F9C38007D3D90 /* OpenVPNAdapter.mm */, + ABD6EF071F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h */, + ABD6EF081F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m */, + ABD6EF0E1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h */, + ABD6EF0F1F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm */, ); name = Adapter; sourceTree = ""; @@ -536,7 +552,7 @@ C9354F451F1E4A4500F4C935 /* OpenVPNReachabilityStatus.h in Headers */, C9BCE25E1EB3C201009D6AC1 /* OpenVPNSessionToken+Internal.h in Headers */, C9BB47721E7171A100F3F98C /* OpenVPNAdapterEvent.h in Headers */, - C9BB477F1E7173C700F3F98C /* OpenVPNAdapter.h in Headers */, + ABD6EF171F8F9C38007D3D90 /* OpenVPNAdapter.h in Headers */, C915F1FE1F6164CF00B3DF23 /* OpenVPNKeyType.h in Headers */, C9657A4C1EB0CD6C00EFF210 /* OpenVPNProperties.h in Headers */, C9657A571EB0CDFB00EFF210 /* OpenVPNProperties+Internal.h in Headers */, @@ -546,6 +562,7 @@ C98467AB1EAA5BE100272A9A /* OpenVPNConfiguration+Internal.h in Headers */, C98467A61EAA5B7700272A9A /* OpenVPNConfiguration.h in Headers */, C9B7956B1F1D26C900CF35FE /* OpenVPNReachability+Internal.h in Headers */, + ABD6EF101F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h in Headers */, C9657A2B1EB0B6FA00EFF210 /* OpenVPNTransportStats+Internal.h in Headers */, C9BB47601E71663A00F3F98C /* Umbrella-Header.h in Headers */, C9657A5E1EB0D60700EFF210 /* OpenVPNTransportProtocol.h in Headers */, @@ -556,6 +573,7 @@ C9BB47711E7171A100F3F98C /* OpenVPNError.h in Headers */, C9B795641F1D182500CF35FE /* OpenVPNReachabilityTracker.h in Headers */, C9E4401D1F6086A1001D7C41 /* NSError+Message.h in Headers */, + ABD6EF091F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h in Headers */, C9657A611EB0D64E00EFF210 /* OpenVPNIPv6Preference.h in Headers */, C9657A671EB0D73200EFF210 /* OpenVPNMinTLSVersion.h in Headers */, C93779D51EAE32670030A362 /* OpenVPNCredentials.h in Headers */, @@ -577,7 +595,7 @@ C9354F461F1E4A4600F4C935 /* OpenVPNReachabilityStatus.h in Headers */, C9BCE25F1EB3C201009D6AC1 /* OpenVPNSessionToken+Internal.h in Headers */, C9D2ABE41EA20F99007EDF9D /* OpenVPNAdapterEvent.h in Headers */, - C9D2ABE51EA20F99007EDF9D /* OpenVPNAdapter.h in Headers */, + ABD6EF191F8F9C3B007D3D90 /* OpenVPNAdapter.h in Headers */, C915F1FF1F6164CF00B3DF23 /* OpenVPNKeyType.h in Headers */, C9657A4D1EB0CD6C00EFF210 /* OpenVPNProperties.h in Headers */, C9657A561EB0CDFA00EFF210 /* OpenVPNProperties+Internal.h in Headers */, @@ -587,6 +605,7 @@ C98467AC1EAA5BE200272A9A /* OpenVPNConfiguration+Internal.h in Headers */, C98467A71EAA5B7700272A9A /* OpenVPNConfiguration.h in Headers */, C9B7956C1F1D26C900CF35FE /* OpenVPNReachability+Internal.h in Headers */, + ABD6EF111F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.h in Headers */, C9657A2F1EB0B79500EFF210 /* OpenVPNTransportStats+Internal.h in Headers */, C9D2ABE61EA20F99007EDF9D /* Umbrella-Header.h in Headers */, C9657A5F1EB0D60700EFF210 /* OpenVPNTransportProtocol.h in Headers */, @@ -597,6 +616,7 @@ C9D2ABE81EA20F99007EDF9D /* OpenVPNError.h in Headers */, C9B795651F1D182500CF35FE /* OpenVPNReachabilityTracker.h in Headers */, C9E4401E1F6086A1001D7C41 /* NSError+Message.h in Headers */, + ABD6EF0A1F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.h in Headers */, C9657A621EB0D64E00EFF210 /* OpenVPNIPv6Preference.h in Headers */, C9657A681EB0D73200EFF210 /* OpenVPNMinTLSVersion.h in Headers */, C93779D61EAE32670030A362 /* OpenVPNCredentials.h in Headers */, @@ -811,10 +831,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ABD6EF181F8F9C38007D3D90 /* OpenVPNAdapter.mm in Sources */, C9657A421EB0CAC200EFF210 /* OpenVPNServerEntry.mm in Sources */, C9BCE25A1EB3C0D9009D6AC1 /* OpenVPNSessionToken.mm in Sources */, - C9BB47821E7173C700F3F98C /* OpenVPNAdapter.mm in Sources */, + ABD6EF0B1F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m in Sources */, C98467A81EAA5B7700272A9A /* OpenVPNConfiguration.mm in Sources */, + ABD6EF121F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm in Sources */, C9E4401F1F6086A1001D7C41 /* NSError+Message.m in Sources */, C9657A311EB0B7A900EFF210 /* OpenVPNTransportStats.mm in Sources */, C9B795661F1D182500CF35FE /* OpenVPNReachabilityTracker.mm in Sources */, @@ -847,10 +869,12 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + ABD6EF1A1F8F9C3B007D3D90 /* OpenVPNAdapter.mm in Sources */, C9657A431EB0CAC200EFF210 /* OpenVPNServerEntry.mm in Sources */, C9BCE25B1EB3C0D9009D6AC1 /* OpenVPNSessionToken.mm in Sources */, - C9D2ABDB1EA20F99007EDF9D /* OpenVPNAdapter.mm in Sources */, + ABD6EF0C1F8F8CCD007D3D90 /* OpenVPNNetworkSettingsBuilder.m in Sources */, C98467A91EAA5B7700272A9A /* OpenVPNConfiguration.mm in Sources */, + ABD6EF131F8F93AB007D3D90 /* OpenVPNPacketFlowAdapter.mm in Sources */, C9E440201F6086A1001D7C41 /* NSError+Message.m in Sources */, C9657A301EB0B7A600EFF210 /* OpenVPNTransportStats.mm in Sources */, C9B795671F1D182500CF35FE /* OpenVPNReachabilityTracker.mm in Sources */, diff --git a/OpenVPN Adapter/OpenVPNAdapter.mm b/OpenVPN Adapter/OpenVPNAdapter.mm index dab10fc..5003260 100644 --- a/OpenVPN Adapter/OpenVPNAdapter.mm +++ b/OpenVPN Adapter/OpenVPNAdapter.mm @@ -12,8 +12,6 @@ #import #import -#import -#import #import #import "OpenVPNAdapterEvent.h" #import "OpenVPNCredentials+Internal.h" @@ -21,56 +19,23 @@ #import "OpenVPNConnectionInfo+Internal.h" #import "OpenVPNError.h" #import "OpenVPNInterfaceStats+Internal.h" +#import "OpenVPNNetworkSettingsBuilder.h" +#import "OpenVPNPacketFlowAdapter.h" #import "OpenVPNProperties+Internal.h" #import "OpenVPNSessionToken+Internal.h" #import "OpenVPNTransportStats+Internal.h" class Client; -@interface OpenVPNAdapter () { - CFSocketRef _tunSocket; - CFSocketRef _vpnSocket; -} +@interface OpenVPNAdapter () @property (nonatomic) Client *client; -@property (nonatomic) NEPacketTunnelFlow *packetFlow; - -@property (nonatomic) NSString *remoteAddress; - -@property (nonatomic) NSString *ipv4DefaultGateway; -@property (nonatomic) NSString *ipv6DefaultGateway; - -@property (nonatomic) NSNumber *mtu; - -@property (nonatomic) NSMutableArray *ipv4LocalAddresses; -@property (nonatomic) NSMutableArray *ipv4SubnetMasks; -@property (nonatomic) NSMutableArray *ipv4IncludedRoutes; -@property (nonatomic) NSMutableArray *ipv4ExcludedRoutes; - -@property (nonatomic) NSMutableArray *ipv6LocalAddresses; -@property (nonatomic) NSMutableArray *ipv6NetworkPrefixLengths; -@property (nonatomic) NSMutableArray *ipv6IncludedRoutes; -@property (nonatomic) NSMutableArray *ipv6ExcludedRoutes; - -@property (nonatomic) NSMutableArray *dnsServers; -@property (nonatomic) NSMutableArray *searchDomains; -@property (nonatomic) NSMutableArray *proxyExceptionList; @property (nonatomic) NSString *sessionName; -@property (nonatomic) BOOL autoProxyConfigurationEnabled; -@property (nonatomic) NSURL *proxyAutoConfigurationURL; -@property (nonatomic) BOOL httpProxyServerEnabled; -@property (nonatomic) NEProxyServer *httpProxyServer; -@property (nonatomic) BOOL httpsProxyServerEnabled; -@property (nonatomic) NEProxyServer *httpsProxyServer; -@property (nonatomic, readonly) NEPacketTunnelNetworkSettings *networkSettings; +@property (nonatomic) OpenVPNNetworkSettingsBuilder *networkSettingsBuilder; -- (CFSocketNativeHandle)configureSockets; - -- (void)readTUNPackets; - -- (void)teardownTunnel:(BOOL)disconnect; +@property (nonatomic) OpenVPNPacketFlowAdapter *packetFlowAdapter; - (OpenVPNAdapterError)errorByName:(NSString *)errorName; - (OpenVPNAdapterEvent)eventByName:(NSString *)errorName; @@ -88,7 +53,7 @@ public: bool tun_builder_set_remote_address(const std::string& address, bool ipv6) override { NSString *remoteAddress = [[NSString alloc] initWithUTF8String:address.c_str()]; - client.remoteAddress = remoteAddress; + client.networkSettingsBuilder.remoteAddress = remoteAddress; return true; } @@ -97,14 +62,14 @@ public: NSString *gatewayAddress = [[NSString alloc] initWithUTF8String:gateway.c_str()]; NSString *defaultGateway = gatewayAddress.length == 0 || [gatewayAddress isEqualToString:@"UNSPEC"] ? nil : gatewayAddress; if (ipv6) { - client.ipv6DefaultGateway = defaultGateway; - [client.ipv6LocalAddresses addObject:localAddress]; - [client.ipv6NetworkPrefixLengths addObject:@(prefix_length)]; + client.networkSettingsBuilder.ipv6DefaultGateway = defaultGateway; + [client.networkSettingsBuilder.ipv6LocalAddresses addObject:localAddress]; + [client.networkSettingsBuilder.ipv6NetworkPrefixLengths addObject:@(prefix_length)]; } else { NSString *subnetMask = [[NSString alloc] initWithUTF8String:IPv4::Addr::netmask_from_prefix_len(prefix_length).to_string().c_str()]; - client.ipv4DefaultGateway = defaultGateway; - [client.ipv4LocalAddresses addObject:localAddress]; - [client.ipv4SubnetMasks addObject:subnetMask]; + client.networkSettingsBuilder.ipv4DefaultGateway = defaultGateway; + [client.networkSettingsBuilder.ipv4LocalAddresses addObject:localAddress]; + [client.networkSettingsBuilder.ipv4SubnetMasks addObject:subnetMask]; } return true; } @@ -112,13 +77,13 @@ public: bool tun_builder_reroute_gw(bool ipv4, bool ipv6, unsigned int flags) override { if (ipv4) { NEIPv4Route *includedRoute = [NEIPv4Route defaultRoute]; - includedRoute.gatewayAddress = client.ipv4DefaultGateway; - [client.ipv4IncludedRoutes addObject:includedRoute]; + includedRoute.gatewayAddress = client.networkSettingsBuilder.ipv4DefaultGateway; + [client.networkSettingsBuilder.ipv4IncludedRoutes addObject:includedRoute]; } if (ipv6) { NEIPv6Route *includedRoute = [NEIPv6Route defaultRoute]; - includedRoute.gatewayAddress = client.ipv6DefaultGateway; - [client.ipv6IncludedRoutes addObject:includedRoute]; + includedRoute.gatewayAddress = client.networkSettingsBuilder.ipv6DefaultGateway; + [client.networkSettingsBuilder.ipv6IncludedRoutes addObject:includedRoute]; } return true; } @@ -127,13 +92,13 @@ public: NSString *route = [[NSString alloc] initWithUTF8String:address.c_str()]; if (ipv6) { NEIPv6Route *includedRoute = [[NEIPv6Route alloc] initWithDestinationAddress:route networkPrefixLength:@(prefix_length)]; - includedRoute.gatewayAddress = client.ipv6DefaultGateway; - [client.ipv6IncludedRoutes addObject:includedRoute]; + includedRoute.gatewayAddress = client.networkSettingsBuilder.ipv6DefaultGateway; + [client.networkSettingsBuilder.ipv6IncludedRoutes addObject:includedRoute]; } else { NSString *subnetMask = [[NSString alloc] initWithUTF8String:IPv4::Addr::netmask_from_prefix_len(prefix_length).to_string().c_str()]; NEIPv4Route *includedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnetMask]; - includedRoute.gatewayAddress = client.ipv4DefaultGateway; - [client.ipv4IncludedRoutes addObject:includedRoute]; + includedRoute.gatewayAddress = client.networkSettingsBuilder.ipv4DefaultGateway; + [client.networkSettingsBuilder.ipv4IncludedRoutes addObject:includedRoute]; } return true; } @@ -142,29 +107,29 @@ public: NSString *route = [[NSString alloc] initWithUTF8String:address.c_str()]; if (ipv6) { NEIPv6Route *excludedRoute = [[NEIPv6Route alloc] initWithDestinationAddress:route networkPrefixLength:@(prefix_length)]; - [client.ipv6ExcludedRoutes addObject:excludedRoute]; + [client.networkSettingsBuilder.ipv6ExcludedRoutes addObject:excludedRoute]; } else { NSString *subnetMask = [[NSString alloc] initWithUTF8String:IPv4::Addr::netmask_from_prefix_len(prefix_length).to_string().c_str()]; NEIPv4Route *excludedRoute = [[NEIPv4Route alloc] initWithDestinationAddress:route subnetMask:subnetMask]; - [client.ipv4ExcludedRoutes addObject:excludedRoute]; + [client.networkSettingsBuilder.ipv4ExcludedRoutes addObject:excludedRoute]; } return true; } bool tun_builder_add_dns_server(const std::string& address, bool ipv6) override { NSString *dnsAddress = [[NSString alloc] initWithUTF8String:address.c_str()]; - [client.dnsServers addObject:dnsAddress]; + [client.networkSettingsBuilder.dnsServers addObject:dnsAddress]; return true; } bool tun_builder_add_search_domain(const std::string& domain) override { NSString *searchDomain = [[NSString alloc] initWithUTF8String:domain.c_str()]; - [client.searchDomains addObject:searchDomain]; + [client.networkSettingsBuilder.searchDomains addObject:searchDomain]; return true; } bool tun_builder_set_mtu(int mtu) override { - client.mtu = @(mtu); + client.networkSettingsBuilder.mtu = @(mtu); return true; } @@ -175,28 +140,28 @@ public: bool tun_builder_add_proxy_bypass(const std::string& bypass_host) override { NSString *bypassHost = [[NSString alloc] initWithUTF8String:bypass_host.c_str()]; - [client.proxyExceptionList addObject:bypassHost]; + [client.networkSettingsBuilder.proxyExceptionList addObject:bypassHost]; return true; } bool tun_builder_set_proxy_auto_config_url(const std::string& urlString) override { NSURL *url = [[NSURL alloc] initWithString:[[NSString alloc] initWithUTF8String:urlString.c_str()]]; - client.autoProxyConfigurationEnabled = url != nil; - client.proxyAutoConfigurationURL = url; + client.networkSettingsBuilder.autoProxyConfigurationEnabled = url != nil; + client.networkSettingsBuilder.proxyAutoConfigurationURL = url; return true; } bool tun_builder_set_proxy_http(const std::string& host, int port) override { NSString *address = [[NSString alloc] initWithUTF8String:host.c_str()]; - client.httpProxyServerEnabled = YES; - client.httpProxyServer = [[NEProxyServer alloc] initWithAddress:address port:port]; + client.networkSettingsBuilder.httpProxyServerEnabled = YES; + client.networkSettingsBuilder.httpProxyServer = [[NEProxyServer alloc] initWithAddress:address port:port]; return true; } bool tun_builder_set_proxy_https(const std::string& host, int port) override { NSString *address = [[NSString alloc] initWithUTF8String:host.c_str()]; - client.httpsProxyServerEnabled = YES; - client.httpsProxyServer = [[NEProxyServer alloc] initWithAddress:address port:port]; + client.networkSettingsBuilder.httpsProxyServerEnabled = YES; + client.networkSettingsBuilder.httpsProxyServer = [[NEProxyServer alloc] initWithAddress:address port:port]; return true; } @@ -205,29 +170,38 @@ public: } bool tun_builder_new() override { - reset_network_parameters(); + reset_tun(); return true; } int tun_builder_establish() override { + NEPacketTunnelNetworkSettings *networkSettings = client.networkSettingsBuilder.networkSettings; + if (!networkSettings) { + return -1; + } + + __block NEPacketTunnelFlow *packetFlow; + dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - [client.delegate openVPNAdapter:client configureTunnelWithNetworkSettings:client.networkSettings completionHandler:^(NEPacketTunnelFlow * _Nullable packetFlow) { - client.packetFlow = packetFlow; + [client.delegate openVPNAdapter:client configureTunnelWithNetworkSettings:networkSettings completionHandler:^(NEPacketTunnelFlow * _Nullable flow) { + packetFlow = flow; dispatch_semaphore_signal(semaphore); }]; dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC)); - if (client.packetFlow) { - [client readTUNPackets]; - return [client configureSockets]; + if (packetFlow) { + client.packetFlowAdapter = [[OpenVPNPacketFlowAdapter alloc] initWithPacketFlow:packetFlow]; + } + + if (client.packetFlowAdapter) { + return client.packetFlowAdapter.socketHandle; } else { return -1; } } void tun_builder_teardown(bool disconnect) override { - reset_network_parameters(); - [client teardownTunnel:disconnect]; + reset_tun(); } bool tun_builder_persist() override { @@ -277,29 +251,10 @@ public: } } - void reset_network_parameters() { - client.remoteAddress = nil; - client.ipv4DefaultGateway = nil; - client.ipv6DefaultGateway = nil; - client.mtu = nil; - client.ipv4LocalAddresses = nil; - client.ipv4SubnetMasks = nil; - client.ipv4IncludedRoutes = nil; - client.ipv4ExcludedRoutes = nil; - client.ipv6LocalAddresses = nil; - client.ipv6NetworkPrefixLengths = nil; - client.ipv6IncludedRoutes = nil; - client.ipv6ExcludedRoutes = nil; - client.dnsServers = nil; - client.searchDomains = nil; + void reset_tun() { + client.packetFlowAdapter = nil; + client.networkSettingsBuilder = nil; client.sessionName = nil; - client.proxyExceptionList = nil; - client.autoProxyConfigurationEnabled = NO; - client.proxyAutoConfigurationURL = nil; - client.httpProxyServerEnabled = NO; - client.httpProxyServer = nil; - client.httpsProxyServerEnabled = NO; - client.httpsProxyServer = nil; } private: OpenVPNAdapter *client; @@ -307,6 +262,8 @@ private: @implementation OpenVPNAdapter +#pragma mark - Initialization + - (instancetype)init { if ((self = [super init])) { self.client = new Client(self); @@ -314,216 +271,11 @@ private: return self; } -static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { - if (type == kCFSocketDataCallBack) { - [(__bridge OpenVPNAdapter *)info readVPNPacket:(__bridge NSData *)data]; - } -} - -- (CFSocketNativeHandle)configureSockets { - [self invalidateSockets]; - - int sockets[2]; - if (socketpair(PF_LOCAL, SOCK_DGRAM, IPPROTO_IP, sockets) == -1) { - NSLog(@"Failed to create a pair of connected sockets: %@", [NSString stringWithUTF8String:strerror(errno)]); - return NO; - } - - if (![self configureBufferSizeForSocket: sockets[0]] || ![self configureBufferSizeForSocket: sockets[1]]) { - NSLog(@"Failed to configure buffer size of the sockets"); - return NO; - } - - CFSocketContext socketCtxt = {0, (__bridge void *)self, NULL, NULL, NULL}; - - _vpnSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[0], kCFSocketDataCallBack, SocketCallback, &socketCtxt); - _tunSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[1], kCFSocketNoCallBack, NULL, NULL); - - if (!_vpnSocket || !_tunSocket) { - [self invalidateSockets]; - NSLog(@"Failed to create core foundation sockets from native sockets"); - return NO; - } - - CFRunLoopSourceRef tunSocketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _vpnSocket, 0); - CFRunLoopAddSource(CFRunLoopGetMain(), tunSocketSource, kCFRunLoopDefaultMode); - CFRelease(tunSocketSource); - - return CFSocketGetNative(_tunSocket); -} - -- (void)teardownTunnel:(BOOL)disconnect { - [self invalidateSockets]; -} - -- (void)invalidateSockets { - if (_vpnSocket) { - CFSocketInvalidate(_vpnSocket); - CFRelease(_vpnSocket); - _vpnSocket = nil; - } - - if (_tunSocket) { - CFSocketInvalidate(_tunSocket); - CFRelease(_tunSocket); - _tunSocket = nil; - } -} - -- (BOOL)configureBufferSizeForSocket:(int)socket { - int buf_value = 65536; - socklen_t buf_len = sizeof(buf_value); - - if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_value, buf_len) == -1) { - NSLog(@"Failed to setup buffer size for input: %@", [NSString stringWithUTF8String:strerror(errno)]); - return NO; - } - - if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_value, buf_len) == -1) { - NSLog(@"Failed to setup buffer size for output: %@", [NSString stringWithUTF8String:strerror(errno)]); - return NO; - } - - return YES; -} - -- (void)readTUNPackets { - [self.packetFlow readPacketsWithCompletionHandler:^(NSArray * _Nonnull packets, NSArray * _Nonnull protocols) { - [self writeVPNPackets:packets protocols:protocols]; - [self readTUNPackets]; - }]; -} - -- (void)writeVPNPackets:(NSArray *)packets protocols:(NSArray *)protocols { - [packets enumerateObjectsUsingBlock:^(NSData *data, NSUInteger idx, BOOL *stop) { - if (!_vpnSocket) { - *stop = YES; - return; - } - - // Prepare data for sending - NSData *packet = [self prepareVPNPacket:data protocol:protocols[idx]]; - - // Send data to the VPN server - CFSocketSendData(_vpnSocket, NULL, (CFDataRef)packet, 0.05); - }]; -} - -- (NSData *)prepareVPNPacket:(NSData *)packet protocol:(NSNumber *)protocol { - NSMutableData *data = [NSMutableData new]; - -#if TARGET_OS_IPHONE - // Prepend data with network protocol. It should be done because OpenVPN on iOS uses uint32_t prefixes containing network protocol. - uint32_t prefix = CFSwapInt32HostToBig((uint32_t)[protocol unsignedIntegerValue]); - [data appendBytes:&prefix length:sizeof(prefix)]; -#endif - - [data appendData:packet]; - - return [data copy]; -} - -- (void)readVPNPacket:(NSData *)packet { -#if TARGET_OS_IPHONE - // Get network protocol from prefix - NSUInteger prefixSize = sizeof(uint32_t); - - if (packet.length < prefixSize) { - NSLog(@"Incorrect OpenVPN packet size"); - return; - } - - uint32_t protocol = PF_UNSPEC; - [packet getBytes:&protocol length:prefixSize]; - protocol = CFSwapInt32BigToHost(protocol); - - NSRange range = NSMakeRange(prefixSize, packet.length - prefixSize); - NSData *data = [packet subdataWithRange:range]; -#else - // Get network protocol from header - uint8_t header = 0; - [packet getBytes:&header length:1]; - - uint32_t version = openvpn::IPHeader::version(header); - uint8_t protocol = [self protocolFamilyForVersion:version]; - - NSData *data = packet; -#endif - - // Send the packet to the TUN interface - if (![self.packetFlow writePackets:@[data] withProtocols:@[@(protocol)]]) { - NSLog(@"Failed to send OpenVPN packet to the TUN interface"); - } -} - -- (uint8_t)protocolFamilyForVersion:(uint32_t)version { - switch (version) { - case 4: return PF_INET; - case 6: return PF_INET6; - default: return PF_UNSPEC; - } -} - -- (NEPacketTunnelNetworkSettings *)networkSettings { - NEPacketTunnelNetworkSettings *networkSettings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:self.remoteAddress]; - - NEIPv4Settings *ipv4Settings = [[NEIPv4Settings alloc] initWithAddresses:self.ipv4LocalAddresses subnetMasks:self.ipv4SubnetMasks]; - ipv4Settings.includedRoutes = self.ipv4IncludedRoutes; - ipv4Settings.excludedRoutes = self.ipv4ExcludedRoutes; - networkSettings.IPv4Settings = ipv4Settings; - - NEIPv6Settings *ipv6Settings = [[NEIPv6Settings alloc] initWithAddresses:self.ipv6LocalAddresses networkPrefixLengths:self.ipv6NetworkPrefixLengths]; - ipv6Settings.includedRoutes = self.ipv6IncludedRoutes; - ipv6Settings.excludedRoutes = self.ipv6ExcludedRoutes; - networkSettings.IPv6Settings = ipv6Settings; - - NEDNSSettings *dnsSettings = [[NEDNSSettings alloc] initWithServers:self.dnsServers]; - dnsSettings.searchDomains = self.searchDomains; - networkSettings.DNSSettings = dnsSettings; - - NEProxySettings *proxySettings = [[NEProxySettings alloc] init]; - proxySettings.autoProxyConfigurationEnabled = self.autoProxyConfigurationEnabled; - proxySettings.proxyAutoConfigurationURL = self.proxyAutoConfigurationURL; - proxySettings.exceptionList = self.proxyExceptionList; - proxySettings.HTTPServer = self.httpProxyServer; - proxySettings.HTTPEnabled = self.httpProxyServerEnabled; - proxySettings.HTTPSServer = self.httpsProxyServer; - proxySettings.HTTPSEnabled = self.httpsProxyServerEnabled; - networkSettings.proxySettings = proxySettings; - - networkSettings.MTU = self.mtu; - - return networkSettings; -} - -+ (NSString *)copyright { - return [[NSString alloc] initWithUTF8String:Client::copyright().c_str()]; -} - -+ (NSString *)platform { - return [[NSString alloc] initWithUTF8String:Client::platform().c_str()]; -} - -- (OpenVPNConnectionInfo *)connectionInformation { - ClientAPI::ConnectionInfo information = self.client->connection_info(); - return information.defined ? [[OpenVPNConnectionInfo alloc] initWithConnectionInfo:information] : nil; -} - -- (OpenVPNInterfaceStats *)interfaceStatistics { - return [[OpenVPNInterfaceStats alloc] initWithInterfaceStats:self.client->tun_stats()]; -} - -- (OpenVPNSessionToken *)sessionToken { - ClientAPI::SessionToken token; - return self.client->session_token(token) ? [[OpenVPNSessionToken alloc] initWithSessionToken:token] : nil; -} - -- (OpenVPNTransportStats *)transportStatistics { - return [[OpenVPNTransportStats alloc] initWithTransportStats:self.client->transport_stats()]; -} +#pragma mark - OpenVPNClient Lifecycle - (OpenVPNProperties *)applyConfiguration:(OpenVPNConfiguration *)configuration error:(NSError * _Nullable __autoreleasing *)error { ClientAPI::EvalConfig eval = self.client->eval_config(configuration.config); + if (eval.error) { if (error) { NSString *errorReason = [self reasonForError:OpenVPNAdapterErrorConfigurationFailure]; @@ -556,19 +308,11 @@ static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, dispatch_async(connectQueue, ^{ Client::init_process(); ClientAPI::Status status = self.client->connect(); - [self handleStatus:status]; + [self handleConnectionStatus:status]; Client::uninit_process(); }); } -- (void)pauseWithReason:(NSString *)reason { - self.client->pause(std::string(reason.UTF8String)); -} - -- (void)resume { - self.client->resume(); -} - - (void)reconnectAfterTimeInterval:(NSTimeInterval)timeInterval { self.client->reconnect(timeInterval); } @@ -577,7 +321,15 @@ static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, self.client->stop(); } -- (void)handleStatus:(ClientAPI::Status)status { +- (void)pauseWithReason:(NSString *)reason { + self.client->pause(std::string(reason.UTF8String)); +} + +- (void)resume { + self.client->resume(); +} + +- (void)handleConnectionStatus:(ClientAPI::Status)status { if (!status.error) { return; } @@ -588,6 +340,36 @@ static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, [self.delegate openVPNAdapter:self handleError:error]; } +#pragma mark - OpenVPNClient Information + ++ (NSString *)copyright { + return [[NSString alloc] initWithUTF8String:Client::copyright().c_str()]; +} + ++ (NSString *)platform { + return [[NSString alloc] initWithUTF8String:Client::platform().c_str()]; +} + +- (OpenVPNConnectionInfo *)connectionInformation { + ClientAPI::ConnectionInfo information = self.client->connection_info(); + return information.defined ? [[OpenVPNConnectionInfo alloc] initWithConnectionInfo:information] : nil; +} + +- (OpenVPNInterfaceStats *)interfaceStatistics { + return [[OpenVPNInterfaceStats alloc] initWithInterfaceStats:self.client->tun_stats()]; +} + +- (OpenVPNSessionToken *)sessionToken { + ClientAPI::SessionToken token; + return self.client->session_token(token) ? [[OpenVPNSessionToken alloc] initWithSessionToken:token] : nil; +} + +- (OpenVPNTransportStats *)transportStatistics { + return [[OpenVPNTransportStats alloc] initWithTransportStats:self.client->transport_stats()]; +} + +#pragma mark - OpenVPNAdapterEvent Helpers + - (OpenVPNAdapterEvent)eventByName:(NSString *)eventName { NSDictionary *events = @{@"DISCONNECTED": @(OpenVPNAdapterEventDisconnected), @"CONNECTED": @(OpenVPNAdapterEventConnected), @@ -609,6 +391,8 @@ static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, return event; } +#pragma mark - OpenVPNAdapterError Helpers + - (OpenVPNAdapterError)errorByName:(NSString *)errorName { NSDictionary *errors = @{@"NETWORK_RECV_ERROR": @(OpenVPNAdapterErrorNetworkRecvError), @"NETWORK_EOF_ERROR": @(OpenVPNAdapterErrorNetworkEOFError), @@ -679,6 +463,7 @@ static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, } - (NSString *)reasonForError:(OpenVPNAdapterError)error { + // TODO: Add missing error reasons switch (error) { case OpenVPNAdapterErrorConfigurationFailure: return @"See OpenVPN error message for more details."; case OpenVPNAdapterErrorCredentialsFailure: return @"See OpenVPN error message for more details."; @@ -749,82 +534,16 @@ static inline void SocketCallback(CFSocketRef socket, CFSocketCallBackType type, } } -- (NSMutableArray *)ipv4LocalAddresses { - if (!_ipv4LocalAddresses) { - _ipv4LocalAddresses = [[NSMutableArray alloc] init]; +#pragma mark - Lazy Initialization + +- (OpenVPNNetworkSettingsBuilder *)networkSettingsBuilder { + if (!_networkSettingsBuilder) { + _networkSettingsBuilder = [[OpenVPNNetworkSettingsBuilder alloc] init]; } - return _ipv4LocalAddresses; + return _networkSettingsBuilder; } -- (NSMutableArray *)ipv4SubnetMasks { - if (!_ipv4SubnetMasks) { - _ipv4SubnetMasks = [[NSMutableArray alloc] init]; - } - return _ipv4SubnetMasks; -} - -- (NSMutableArray *)ipv4IncludedRoutes { - if (!_ipv4IncludedRoutes) { - _ipv4IncludedRoutes = [[NSMutableArray alloc] init]; - } - return _ipv4IncludedRoutes; -} - -- (NSMutableArray *)ipv4ExcludedRoutes { - if (!_ipv4ExcludedRoutes) { - _ipv4ExcludedRoutes = [[NSMutableArray alloc] init]; - } - return _ipv4ExcludedRoutes; -} - -- (NSMutableArray *)ipv6LocalAddresses { - if (!_ipv6LocalAddresses) { - _ipv6LocalAddresses = [[NSMutableArray alloc] init]; - } - return _ipv6LocalAddresses; -} - -- (NSMutableArray *)ipv6NetworkPrefixLengths { - if (!_ipv6NetworkPrefixLengths) { - _ipv6NetworkPrefixLengths = [[NSMutableArray alloc] init]; - } - return _ipv6NetworkPrefixLengths; -} - -- (NSMutableArray *)ipv6IncludedRoutes { - if (!_ipv6IncludedRoutes) { - _ipv6IncludedRoutes = [[NSMutableArray alloc] init]; - } - return _ipv6IncludedRoutes; -} - -- (NSMutableArray *)ipv6ExcludedRoutes { - if (!_ipv6ExcludedRoutes) { - _ipv6ExcludedRoutes = [[NSMutableArray alloc] init]; - } - return _ipv6ExcludedRoutes; -} - -- (NSMutableArray *)dnsServers { - if (!_dnsServers) { - _dnsServers = [[NSMutableArray alloc] init]; - } - return _dnsServers; -} - -- (NSMutableArray *)searchDomains { - if (!_searchDomains) { - _searchDomains = [[NSMutableArray alloc] init]; - } - return _searchDomains; -} - -- (NSMutableArray *)proxyExceptionList { - if (!_proxyExceptionList) { - _proxyExceptionList = [[NSMutableArray alloc] init]; - } - return _proxyExceptionList; -} +#pragma mark - Dealloc - (void)dealloc { delete _client; diff --git a/OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.h b/OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.h new file mode 100644 index 0000000..afe083d --- /dev/null +++ b/OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.h @@ -0,0 +1,52 @@ +// +// OpenVPNNetworkSettingsBuilder.h +// OpenVPN Adapter +// +// Created by Jonathan Downing on 12/10/2017. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class NEIPv4Route; +@class NEIPv6Route; +@class NEPacketTunnelNetworkSettings; +@class NEProxyServer; + +@interface OpenVPNNetworkSettingsBuilder : NSObject + +@property (nonatomic, copy, nullable) NSString *remoteAddress; + +@property (nonatomic, copy, nullable) NSString *ipv4DefaultGateway; +@property (nonatomic, copy, nullable) NSString *ipv6DefaultGateway; + +@property (nonatomic, copy, nullable) NSNumber *mtu; + +@property (nonatomic, readonly) NSMutableArray *ipv4LocalAddresses; +@property (nonatomic, readonly) NSMutableArray *ipv4SubnetMasks; +@property (nonatomic, readonly) NSMutableArray *ipv4IncludedRoutes; +@property (nonatomic, readonly) NSMutableArray *ipv4ExcludedRoutes; + +@property (nonatomic, readonly) NSMutableArray *ipv6LocalAddresses; +@property (nonatomic, readonly) NSMutableArray *ipv6NetworkPrefixLengths; +@property (nonatomic, readonly) NSMutableArray *ipv6IncludedRoutes; +@property (nonatomic, readonly) NSMutableArray *ipv6ExcludedRoutes; + +@property (nonatomic, readonly) NSMutableArray *dnsServers; +@property (nonatomic, readonly) NSMutableArray *searchDomains; + +@property (nonatomic, readonly) NSMutableArray *proxyExceptionList; + +@property (nonatomic) BOOL autoProxyConfigurationEnabled; +@property (nonatomic, copy, nullable) NSURL *proxyAutoConfigurationURL; +@property (nonatomic) BOOL httpProxyServerEnabled; +@property (nonatomic, copy, nullable) NEProxyServer *httpProxyServer; +@property (nonatomic) BOOL httpsProxyServerEnabled; +@property (nonatomic, copy, nullable) NEProxyServer *httpsProxyServer; + +@property (nonatomic, readonly, nullable) NEPacketTunnelNetworkSettings *networkSettings; + +@end + +NS_ASSUME_NONNULL_END diff --git a/OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.m b/OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.m new file mode 100644 index 0000000..7f34051 --- /dev/null +++ b/OpenVPN Adapter/OpenVPNNetworkSettingsBuilder.m @@ -0,0 +1,152 @@ +// +// OpenVPNNetworkSettingsBuilder.m +// OpenVPN Adapter +// +// Created by Jonathan Downing on 12/10/2017. +// + +#import "OpenVPNNetworkSettingsBuilder.h" +#import + +@interface OpenVPNNetworkSettingsBuilder () +@property (nonatomic) NSMutableArray *ipv4LocalAddresses; +@property (nonatomic) NSMutableArray *ipv4SubnetMasks; +@property (nonatomic) NSMutableArray *ipv4IncludedRoutes; +@property (nonatomic) NSMutableArray *ipv4ExcludedRoutes; +@property (nonatomic) NSMutableArray *ipv6LocalAddresses; +@property (nonatomic) NSMutableArray *ipv6NetworkPrefixLengths; +@property (nonatomic) NSMutableArray *ipv6IncludedRoutes; +@property (nonatomic) NSMutableArray *ipv6ExcludedRoutes; +@property (nonatomic) NSMutableArray *dnsServers; +@property (nonatomic) NSMutableArray *searchDomains; +@property (nonatomic) NSMutableArray *proxyExceptionList; +@end + +@implementation OpenVPNNetworkSettingsBuilder + +#pragma mark - NEPacketTunnelNetworkSettings Generation + +- (NEPacketTunnelNetworkSettings *)networkSettings { + if (!self.remoteAddress.length) { + return nil; + } + + NEPacketTunnelNetworkSettings *networkSettings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:self.remoteAddress]; + + if (self.ipv4LocalAddresses.count && (self.ipv4LocalAddresses.count == self.ipv4SubnetMasks.count)) { + NEIPv4Settings *ipv4Settings = [[NEIPv4Settings alloc] initWithAddresses:self.ipv4LocalAddresses subnetMasks:self.ipv4SubnetMasks]; + ipv4Settings.includedRoutes = self.ipv4IncludedRoutes; + ipv4Settings.excludedRoutes = self.ipv4ExcludedRoutes; + networkSettings.IPv4Settings = ipv4Settings; + } + + if (self.ipv6LocalAddresses.count && (self.ipv6LocalAddresses.count == self.ipv6NetworkPrefixLengths.count)) { + NEIPv6Settings *ipv6Settings = [[NEIPv6Settings alloc] initWithAddresses:self.ipv6LocalAddresses networkPrefixLengths:self.ipv6NetworkPrefixLengths]; + ipv6Settings.includedRoutes = self.ipv6IncludedRoutes; + ipv6Settings.excludedRoutes = self.ipv6ExcludedRoutes; + networkSettings.IPv6Settings = ipv6Settings; + } + + if (self.dnsServers.count) { + NEDNSSettings *dnsSettings = [[NEDNSSettings alloc] initWithServers:self.dnsServers]; + dnsSettings.searchDomains = self.searchDomains; + networkSettings.DNSSettings = dnsSettings; + } + + if (self.autoProxyConfigurationEnabled || self.httpProxyServerEnabled || self.httpsProxyServerEnabled) { + NEProxySettings *proxySettings = [[NEProxySettings alloc] init]; + proxySettings.autoProxyConfigurationEnabled = self.autoProxyConfigurationEnabled; + proxySettings.proxyAutoConfigurationURL = self.proxyAutoConfigurationURL; + proxySettings.exceptionList = self.proxyExceptionList; + proxySettings.HTTPServer = self.httpProxyServer; + proxySettings.HTTPEnabled = self.httpProxyServerEnabled; + proxySettings.HTTPSServer = self.httpsProxyServer; + proxySettings.HTTPSEnabled = self.httpsProxyServerEnabled; + networkSettings.proxySettings = proxySettings; + } + + networkSettings.MTU = self.mtu; + + return networkSettings; +} + +#pragma mark - Lazy Initializers + +- (NSMutableArray *)ipv4LocalAddresses { + if (!_ipv4LocalAddresses) { + _ipv4LocalAddresses = [[NSMutableArray alloc] init]; + } + return _ipv4LocalAddresses; +} + +- (NSMutableArray *)ipv4SubnetMasks { + if (!_ipv4SubnetMasks) { + _ipv4SubnetMasks = [[NSMutableArray alloc] init]; + } + return _ipv4SubnetMasks; +} + +- (NSMutableArray *)ipv4IncludedRoutes { + if (!_ipv4IncludedRoutes) { + _ipv4IncludedRoutes = [[NSMutableArray alloc] init]; + } + return _ipv4IncludedRoutes; +} + +- (NSMutableArray *)ipv4ExcludedRoutes { + if (!_ipv4ExcludedRoutes) { + _ipv4ExcludedRoutes = [[NSMutableArray alloc] init]; + } + return _ipv4ExcludedRoutes; +} + +- (NSMutableArray *)ipv6LocalAddresses { + if (!_ipv6LocalAddresses) { + _ipv6LocalAddresses = [[NSMutableArray alloc] init]; + } + return _ipv6LocalAddresses; +} + +- (NSMutableArray *)ipv6NetworkPrefixLengths { + if (!_ipv6NetworkPrefixLengths) { + _ipv6NetworkPrefixLengths = [[NSMutableArray alloc] init]; + } + return _ipv6NetworkPrefixLengths; +} + +- (NSMutableArray *)ipv6IncludedRoutes { + if (!_ipv6IncludedRoutes) { + _ipv6IncludedRoutes = [[NSMutableArray alloc] init]; + } + return _ipv6IncludedRoutes; +} + +- (NSMutableArray *)ipv6ExcludedRoutes { + if (!_ipv6ExcludedRoutes) { + _ipv6ExcludedRoutes = [[NSMutableArray alloc] init]; + } + return _ipv6ExcludedRoutes; +} + +- (NSMutableArray *)dnsServers { + if (!_dnsServers) { + _dnsServers = [[NSMutableArray alloc] init]; + } + return _dnsServers; +} + +- (NSMutableArray *)searchDomains { + if (!_searchDomains) { + _searchDomains = [[NSMutableArray alloc] init]; + } + return _searchDomains; +} + +- (NSMutableArray *)proxyExceptionList { + if (!_proxyExceptionList) { + _proxyExceptionList = [[NSMutableArray alloc] init]; + } + return _proxyExceptionList; +} + +@end diff --git a/OpenVPN Adapter/OpenVPNPacketFlowAdapter.h b/OpenVPN Adapter/OpenVPNPacketFlowAdapter.h new file mode 100644 index 0000000..c09603e --- /dev/null +++ b/OpenVPN Adapter/OpenVPNPacketFlowAdapter.h @@ -0,0 +1,24 @@ +// +// OpenVPNPacketFlowAdapter.h +// OpenVPN Adapter +// +// Created by Jonathan Downing on 12/10/2017. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class NEPacketTunnelFlow; + +@interface OpenVPNPacketFlowAdapter : NSObject + +@property (nonatomic, readonly) CFSocketNativeHandle socketHandle; + +- (instancetype)init NS_UNAVAILABLE; + +- (nullable instancetype)initWithPacketFlow:(NEPacketTunnelFlow *)packetFlow NS_DESIGNATED_INITIALIZER; + +@end + +NS_ASSUME_NONNULL_END diff --git a/OpenVPN Adapter/OpenVPNPacketFlowAdapter.mm b/OpenVPN Adapter/OpenVPNPacketFlowAdapter.mm new file mode 100644 index 0000000..918cd64 --- /dev/null +++ b/OpenVPN Adapter/OpenVPNPacketFlowAdapter.mm @@ -0,0 +1,179 @@ +// +// OpenVPNPacketFlowAdapter.mm +// OpenVPN Adapter +// +// Created by Jonathan Downing on 12/10/2017. +// + +#import "OpenVPNPacketFlowAdapter.h" +#import +#import + +@interface OpenVPNPacketFlowAdapter () { + CFSocketRef _openVPNClientSocket; + CFSocketRef _packetFlowSocket; +} +@property (nonatomic) NEPacketTunnelFlow *packetFlow; +@end + +@implementation OpenVPNPacketFlowAdapter + +- (instancetype)initWithPacketFlow:(NEPacketTunnelFlow *)packetFlow { + if ((self = [super init])) { + self.packetFlow = packetFlow; + + if (![self configureSockets]) { + return nil; + } + + [self readPacketFlowPackets]; + } + return self; +} + +- (CFSocketNativeHandle)socketHandle { + return CFSocketGetNative(_openVPNClientSocket); +} + +static inline void PacketFlowSocketCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { + if (type == kCFSocketDataCallBack) { + [(__bridge OpenVPNPacketFlowAdapter *)info writeDataToPacketFlow:(__bridge NSData *)data]; + } +} + +- (BOOL)configureSockets { + int sockets[2]; + if (socketpair(PF_LOCAL, SOCK_DGRAM, IPPROTO_IP, sockets) == -1) { + NSLog(@"Failed to create a pair of connected sockets: %@", [NSString stringWithUTF8String:strerror(errno)]); + return NO; + } + + if (![self configureBufferSizeForSocket:sockets[0]] || ![self configureBufferSizeForSocket:sockets[1]]) { + NSLog(@"Failed to configure buffer size of the sockets"); + return NO; + } + + CFSocketContext socketCtxt = {0, (__bridge void *)self, NULL, NULL, NULL}; + + _packetFlowSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[0], kCFSocketDataCallBack, PacketFlowSocketCallback, &socketCtxt); + _openVPNClientSocket = CFSocketCreateWithNative(kCFAllocatorDefault, sockets[1], kCFSocketNoCallBack, NULL, NULL); + + if (!_packetFlowSocket || !_openVPNClientSocket) { + NSLog(@"Failed to create core foundation sockets from native sockets"); + return NO; + } + + CFRunLoopSourceRef packetFlowSocketSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _packetFlowSocket, 0); + CFRunLoopAddSource(CFRunLoopGetMain(), packetFlowSocketSource, kCFRunLoopDefaultMode); + CFRelease(packetFlowSocketSource); + + return YES; +} + +- (BOOL)configureBufferSizeForSocket:(int)socket { + int buf_value = 65536; + socklen_t buf_len = sizeof(buf_value); + + if (setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &buf_value, buf_len) == -1) { + NSLog(@"Failed to setup buffer size for input: %@", [NSString stringWithUTF8String:strerror(errno)]); + return NO; + } + + if (setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &buf_value, buf_len) == -1) { + NSLog(@"Failed to setup buffer size for output: %@", [NSString stringWithUTF8String:strerror(errno)]); + return NO; + } + + return YES; +} + +- (void)readPacketFlowPackets { + [self.packetFlow readPacketsWithCompletionHandler:^(NSArray * _Nonnull packets, NSArray * _Nonnull protocols) { + [self writeVPNPackets:packets protocols:protocols]; + [self readPacketFlowPackets]; + }]; +} + +- (void)writeVPNPackets:(NSArray *)packets protocols:(NSArray *)protocols { + [packets enumerateObjectsUsingBlock:^(NSData *data, NSUInteger idx, BOOL *stop) { + if (!_packetFlowSocket) { + *stop = YES; + return; + } + + // Prepare data for sending + NSData *packet = [self prepareVPNPacket:data protocol:protocols[idx]]; + + // Send data to the VPN server + CFSocketSendData(_packetFlowSocket, NULL, (CFDataRef)packet, 0.05); + }]; +} + +- (NSData *)prepareVPNPacket:(NSData *)packet protocol:(NSNumber *)protocol { + NSMutableData *data = [NSMutableData new]; + +#if TARGET_OS_IPHONE + // Prepend data with network protocol. It should be done because OpenVPN on iOS uses uint32_t prefixes containing network protocol. + uint32_t prefix = CFSwapInt32HostToBig((uint32_t)[protocol unsignedIntegerValue]); + [data appendBytes:&prefix length:sizeof(prefix)]; +#endif + + [data appendData:packet]; + + return [data copy]; +} + +- (void)writeDataToPacketFlow:(NSData *)packet { +#if TARGET_OS_IPHONE + // Get network protocol from prefix + NSUInteger prefixSize = sizeof(uint32_t); + + if (packet.length < prefixSize) { + NSLog(@"Incorrect OpenVPN packet size"); + return; + } + + uint32_t protocol = PF_UNSPEC; + [packet getBytes:&protocol length:prefixSize]; + protocol = CFSwapInt32BigToHost(protocol); + + NSRange range = NSMakeRange(prefixSize, packet.length - prefixSize); + NSData *data = [packet subdataWithRange:range]; +#else + // Get network protocol from header + uint8_t header = 0; + [packet getBytes:&header length:1]; + + uint32_t version = openvpn::IPHeader::version(header); + uint8_t protocol = [self protocolFamilyForVersion:version]; + + NSData *data = packet; +#endif + + // Send the packet to the TUN interface + if (![self.packetFlow writePackets:@[data] withProtocols:@[@(protocol)]]) { + NSLog(@"Failed to send OpenVPN packet to the TUN interface"); + } +} + +- (uint8_t)protocolFamilyForVersion:(uint32_t)version { + switch (version) { + case 4: return PF_INET; + case 6: return PF_INET6; + default: return PF_UNSPEC; + } +} + +- (void)dealloc { + if (_packetFlowSocket) { + CFSocketInvalidate(_packetFlowSocket); + CFRelease(_packetFlowSocket); + } + + if (_openVPNClientSocket) { + CFSocketInvalidate(_openVPNClientSocket); + CFRelease(_openVPNClientSocket); + } +} + +@end