diff --git a/OpenVPNAdapter.xcodeproj/project.pbxproj b/OpenVPNAdapter.xcodeproj/project.pbxproj index 4908f0a..d1db83e 100644 --- a/OpenVPNAdapter.xcodeproj/project.pbxproj +++ b/OpenVPNAdapter.xcodeproj/project.pbxproj @@ -99,6 +99,14 @@ C98467A91EAA5B7700272A9A /* OpenVPNConfiguration.mm in Sources */ = {isa = PBXBuildFile; fileRef = C98467A51EAA5B7700272A9A /* OpenVPNConfiguration.mm */; }; C98467AB1EAA5BE100272A9A /* OpenVPNConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C98467AA1EAA5BB500272A9A /* OpenVPNConfiguration+Internal.h */; }; C98467AC1EAA5BE200272A9A /* OpenVPNConfiguration+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C98467AA1EAA5BB500272A9A /* OpenVPNConfiguration+Internal.h */; }; + C9A50F2D21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = C9A50F2B21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h */; }; + C9A50F2E21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = C9A50F2B21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h */; }; + C9A50F2F21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = C9A50F2C21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m */; }; + C9A50F3021763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = C9A50F2C21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m */; }; + C9A50F3321763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = C9A50F3121763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h */; }; + C9A50F3421763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = C9A50F3121763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h */; }; + C9A50F3521763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = C9A50F3221763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m */; }; + C9A50F3621763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = C9A50F3221763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m */; }; C9B7955E1F1D16AA00CF35FE /* OpenVPNReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = C9B7955C1F1D16AA00CF35FE /* OpenVPNReachability.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9B7955F1F1D16AA00CF35FE /* OpenVPNReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = C9B7955C1F1D16AA00CF35FE /* OpenVPNReachability.h */; settings = {ATTRIBUTES = (Public, ); }; }; C9B795601F1D16AA00CF35FE /* OpenVPNReachability.mm in Sources */ = {isa = PBXBuildFile; fileRef = C9B7955D1F1D16AA00CF35FE /* OpenVPNReachability.mm */; }; @@ -230,6 +238,10 @@ C98467A41EAA5B7700272A9A /* OpenVPNConfiguration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OpenVPNConfiguration.h; path = OpenVPNAdapter/OpenVPNConfiguration.h; sourceTree = ""; }; C98467A51EAA5B7700272A9A /* OpenVPNConfiguration.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OpenVPNConfiguration.mm; path = OpenVPNAdapter/OpenVPNConfiguration.mm; sourceTree = ""; }; C98467AA1EAA5BB500272A9A /* OpenVPNConfiguration+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "OpenVPNConfiguration+Internal.h"; path = "OpenVPNAdapter/OpenVPNConfiguration+Internal.h"; sourceTree = ""; }; + C9A50F2B21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSArray+OpenVPNAdditions.h"; path = "Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.h"; sourceTree = SOURCE_ROOT; }; + C9A50F2C21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "NSArray+OpenVPNAdditions.m"; path = "Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.m"; sourceTree = SOURCE_ROOT; }; + C9A50F3121763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "NSSet+OpenVPNAdditions.h"; path = "Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.h"; sourceTree = SOURCE_ROOT; }; + C9A50F3221763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = "NSSet+OpenVPNAdditions.m"; path = "Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.m"; sourceTree = SOURCE_ROOT; }; C9B7955C1F1D16AA00CF35FE /* OpenVPNReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OpenVPNReachability.h; path = OpenVPNAdapter/OpenVPNReachability.h; sourceTree = ""; }; C9B7955D1F1D16AA00CF35FE /* OpenVPNReachability.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OpenVPNReachability.mm; path = OpenVPNAdapter/OpenVPNReachability.mm; sourceTree = ""; }; C9B795621F1D182500CF35FE /* OpenVPNReachabilityTracker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OpenVPNReachabilityTracker.h; path = OpenVPNAdapter/OpenVPNReachabilityTracker.h; sourceTree = ""; }; @@ -572,6 +584,10 @@ children = ( C9E350BF200F6EC0000820D9 /* NSError+OpenVPNError.h */, C9E350C0200F6EC0000820D9 /* NSError+OpenVPNError.m */, + C9A50F2B21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h */, + C9A50F2C21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m */, + C9A50F3121763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h */, + C9A50F3221763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m */, ); name = Extensions; sourceTree = ""; @@ -612,6 +628,7 @@ C9657A5E1EB0D60700EFF210 /* OpenVPNTransportProtocol.h in Headers */, C9657A1D1EB0A8D800EFF210 /* OpenVPNConnectionInfo+Internal.h in Headers */, C9B7955E1F1D16AA00CF35FE /* OpenVPNReachability.h in Headers */, + C9A50F3321763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h in Headers */, C915F1F41F612F3300B3DF23 /* OpenVPNPrivateKey.h in Headers */, C9657A171EB0A7F800EFF210 /* OpenVPNConnectionInfo.h in Headers */, C9310BC120FF6E9700838910 /* Umbrella-Header.h in Headers */, @@ -630,6 +647,7 @@ C9657A6A1EB0D75700EFF210 /* OpenVPNTLSCertProfile.h in Headers */, C9657A461EB0CB5900EFF210 /* OpenVPNServerEntry+Internal.h in Headers */, C9657A251EB0B60200EFF210 /* OpenVPNTransportStats.h in Headers */, + C9A50F2D21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -658,6 +676,7 @@ C9657A5F1EB0D60700EFF210 /* OpenVPNTransportProtocol.h in Headers */, C9657A1E1EB0A8D800EFF210 /* OpenVPNConnectionInfo+Internal.h in Headers */, C9B7955F1F1D16AA00CF35FE /* OpenVPNReachability.h in Headers */, + C9A50F3421763CBC0010C0D4 /* NSSet+OpenVPNAdditions.h in Headers */, C915F1F51F612F3300B3DF23 /* OpenVPNPrivateKey.h in Headers */, C9657A181EB0A7F800EFF210 /* OpenVPNConnectionInfo.h in Headers */, C9310BC220FF6E9700838910 /* Umbrella-Header.h in Headers */, @@ -676,6 +695,7 @@ C9657A6B1EB0D75700EFF210 /* OpenVPNTLSCertProfile.h in Headers */, C9657A471EB0CB5900EFF210 /* OpenVPNServerEntry+Internal.h in Headers */, C9657A261EB0B60200EFF210 /* OpenVPNTransportStats.h in Headers */, + C9A50F2E21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -926,11 +946,13 @@ ABD6EF121F8F93AB007D3D90 /* OpenVPNPacketFlowBridge.mm in Sources */, C9657A311EB0B7A900EFF210 /* OpenVPNTransportStats.mm in Sources */, C9B795661F1D182500CF35FE /* OpenVPNReachabilityTracker.mm in Sources */, + C9A50F3521763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m in Sources */, C9657A581EB0CE1300EFF210 /* OpenVPNProperties.mm in Sources */, C9CA4DD51F602F7B00C4F184 /* OpenVPNCertificate.m in Sources */, C9CDFDDD200781AF00323B73 /* OpenVPNClient.mm in Sources */, C915F1F61F612F3300B3DF23 /* OpenVPNPrivateKey.m in Sources */, C9FD921B1E9A667600374FC4 /* ovpncli.cpp in Sources */, + C9A50F2F21763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m in Sources */, C9657A361EB0BA3900EFF210 /* OpenVPNInterfaceStats.mm in Sources */, C9657A211EB0ACAE00EFF210 /* OpenVPNConnectionInfo.mm in Sources */, C9C2B2BD200CC42A00CA0FF3 /* OpenVPNPacket.mm in Sources */, @@ -967,11 +989,13 @@ ABD6EF131F8F93AB007D3D90 /* OpenVPNPacketFlowBridge.mm in Sources */, C9657A301EB0B7A600EFF210 /* OpenVPNTransportStats.mm in Sources */, C9B795671F1D182500CF35FE /* OpenVPNReachabilityTracker.mm in Sources */, + C9A50F3621763CBC0010C0D4 /* NSSet+OpenVPNAdditions.m in Sources */, C9657A591EB0CE1400EFF210 /* OpenVPNProperties.mm in Sources */, C9CA4DD61F602F7B00C4F184 /* OpenVPNCertificate.m in Sources */, C9CDFDDE200781AF00323B73 /* OpenVPNClient.mm in Sources */, C915F1F71F612F3300B3DF23 /* OpenVPNPrivateKey.m in Sources */, C9D2ABDE1EA20F99007EDF9D /* ovpncli.cpp in Sources */, + C9A50F3021763A2C0010C0D4 /* NSArray+OpenVPNAdditions.m in Sources */, C9657A371EB0BA3900EFF210 /* OpenVPNInterfaceStats.mm in Sources */, C9657A221EB0ACAE00EFF210 /* OpenVPNConnectionInfo.mm in Sources */, C9C2B2BE200CC42A00CA0FF3 /* OpenVPNPacket.mm in Sources */, diff --git a/Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.h b/Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.h new file mode 100644 index 0000000..71ecf8c --- /dev/null +++ b/Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.h @@ -0,0 +1,18 @@ +// +// NSArray+OpenVPNAdditions.h +// OpenVPNAdapter +// +// Created by Sergey Abramchuk on 16/10/2018. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSArray (OpenVPNEmptyArray) + +@property (nonatomic, readonly) BOOL ovpn_isNotEmpty; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.m b/Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.m new file mode 100644 index 0000000..1ded853 --- /dev/null +++ b/Sources/OpenVPNAdapter/NSArray+OpenVPNAdditions.m @@ -0,0 +1,16 @@ +// +// NSArray+OpenVPNAdditions.m +// OpenVPNAdapter +// +// Created by Sergey Abramchuk on 16/10/2018. +// + +#import "NSArray+OpenVPNAdditions.h" + +@implementation NSArray (OpenVPNEmptyArray) + +- (BOOL)ovpn_isNotEmpty { + return (self.count > 0) ? YES : NO; +} + +@end diff --git a/Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.h b/Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.h new file mode 100644 index 0000000..f7e7c37 --- /dev/null +++ b/Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.h @@ -0,0 +1,18 @@ +// +// NSSet+OpenVPNAdditions.h +// OpenVPNAdapter +// +// Created by Sergey Abramchuk on 16/10/2018. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface NSSet (OpenVPNEmptySet) + +@property (nonatomic, readonly) BOOL ovpn_isNotEmpty; + +@end + +NS_ASSUME_NONNULL_END diff --git a/Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.m b/Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.m new file mode 100644 index 0000000..0af8b0f --- /dev/null +++ b/Sources/OpenVPNAdapter/NSSet+OpenVPNAdditions.m @@ -0,0 +1,16 @@ +// +// NSSet+OpenVPNAdditions.m +// OpenVPNAdapter +// +// Created by Sergey Abramchuk on 16/10/2018. +// + +#import "NSSet+OpenVPNAdditions.h" + +@implementation NSSet (OpenVPNEmptySet) + +- (BOOL)ovpn_isNotEmpty { + return (self.count > 0) ? YES : NO; +} + +@end diff --git a/Sources/OpenVPNAdapter/OpenVPNAdapter.mm b/Sources/OpenVPNAdapter/OpenVPNAdapter.mm index 58bdd17..8d4795c 100644 --- a/Sources/OpenVPNAdapter/OpenVPNAdapter.mm +++ b/Sources/OpenVPNAdapter/OpenVPNAdapter.mm @@ -172,6 +172,7 @@ - (BOOL)addIPV4Address:(NSString *)address subnetMask:(NSString *)subnetMask gateway:(NSString *)gateway { self.networkSettingsBuilder.ipv4DefaultGateway = gateway; + [self.networkSettingsBuilder.ipv4LocalAddresses addObject:address]; [self.networkSettingsBuilder.ipv4SubnetMasks addObject:subnetMask]; @@ -180,6 +181,7 @@ - (BOOL)addIPV6Address:(NSString *)address prefixLength:(NSNumber *)prefixLength gateway:(NSString *)gateway { self.networkSettingsBuilder.ipv6DefaultGateway = gateway; + [self.networkSettingsBuilder.ipv6LocalAddresses addObject:address]; [self.networkSettingsBuilder.ipv6NetworkPrefixLengths addObject:prefixLength]; @@ -188,34 +190,78 @@ - (BOOL)addIPV4Route:(NEIPv4Route *)route { route.gatewayAddress = self.networkSettingsBuilder.ipv4DefaultGateway; - [self.networkSettingsBuilder.ipv4IncludedRoutes addObject:route]; - return YES; + NSUInteger index = [self.networkSettingsBuilder.ipv4IncludedRoutes indexOfObjectPassingTest:^BOOL(NEIPv4Route *obj, NSUInteger idx, BOOL *stop) { + return [obj.destinationAddress isEqualToString:route.destinationAddress] && + [obj.destinationSubnetMask isEqualToString:route.destinationSubnetMask]; + }]; + + if (index == NSNotFound) { + [self.networkSettingsBuilder.ipv4IncludedRoutes addObject:route]; + return YES; + } else { + return NO; + } } - (BOOL)addIPV6Route:(NEIPv6Route *)route { route.gatewayAddress = self.networkSettingsBuilder.ipv6DefaultGateway; - [self.networkSettingsBuilder.ipv6IncludedRoutes addObject:route]; - return YES; + NSUInteger index = [self.networkSettingsBuilder.ipv6IncludedRoutes indexOfObjectPassingTest:^BOOL(NEIPv6Route *obj, NSUInteger idx, BOOL *stop) { + return [obj.destinationAddress isEqualToString:route.destinationAddress] && + obj.destinationNetworkPrefixLength == route.destinationNetworkPrefixLength; + }]; + + if (index == NSNotFound) { + [self.networkSettingsBuilder.ipv6IncludedRoutes addObject:route]; + return YES; + } else { + return NO; + } } - (BOOL)excludeIPV4Route:(NEIPv4Route *)route { - [self.networkSettingsBuilder.ipv4ExcludedRoutes addObject:route]; - return YES; + NSUInteger index = [self.networkSettingsBuilder.ipv4ExcludedRoutes indexOfObjectPassingTest:^BOOL(NEIPv4Route *obj, NSUInteger idx, BOOL *stop) { + return [obj.destinationAddress isEqualToString:route.destinationAddress] && + [obj.destinationSubnetMask isEqualToString:route.destinationSubnetMask]; + }]; + + if (index == NSNotFound) { + [self.networkSettingsBuilder.ipv4ExcludedRoutes addObject:route]; + return YES; + } else { + return NO; + } } - (BOOL)excludeIPV6Route:(NEIPv6Route *)route { - [self.networkSettingsBuilder.ipv6ExcludedRoutes addObject:route]; - return YES; + NSUInteger index = [self.networkSettingsBuilder.ipv6ExcludedRoutes indexOfObjectPassingTest:^BOOL(NEIPv6Route *obj, NSUInteger idx, BOOL *stop) { + return [obj.destinationAddress isEqualToString:route.destinationAddress] && + obj.destinationNetworkPrefixLength == route.destinationNetworkPrefixLength; + }]; + + if (index == NSNotFound) { + [self.networkSettingsBuilder.ipv6ExcludedRoutes addObject:route]; + return YES; + } else { + return NO; + } } - (BOOL)addDNS:(NSString *)dns { + if ([self.networkSettingsBuilder.dnsServers containsObject:dns]) { + return NO; + } + [self.networkSettingsBuilder.dnsServers addObject:dns]; return YES; } - (BOOL)addSearchDomain:(NSString *)domain { + if ([self.networkSettingsBuilder.searchDomains containsObject:domain]) { + return NO; + } + [self.networkSettingsBuilder.searchDomains addObject:domain]; return YES; } @@ -231,6 +277,10 @@ } - (BOOL)addProxyBypassHost:(NSString *)bypassHost { + if ([self.networkSettingsBuilder.proxyExceptionList containsObject:bypassHost]) { + return NO; + } + [self.networkSettingsBuilder.proxyExceptionList addObject:bypassHost]; return YES; } @@ -260,7 +310,6 @@ - (BOOL)establishTunnel { NEPacketTunnelNetworkSettings *networkSettings = [self.networkSettingsBuilder networkSettings]; - if (!networkSettings) { return NO; } dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); diff --git a/Sources/OpenVPNAdapter/OpenVPNNetworkSettingsBuilder.m b/Sources/OpenVPNAdapter/OpenVPNNetworkSettingsBuilder.m index 11a31f1..f95433c 100644 --- a/Sources/OpenVPNAdapter/OpenVPNNetworkSettingsBuilder.m +++ b/Sources/OpenVPNAdapter/OpenVPNNetworkSettingsBuilder.m @@ -9,6 +9,9 @@ #import +#import "NSArray+OpenVPNAdditions.h" +#import "NSSet+OpenVPNAdditions.h" + @interface OpenVPNNetworkSettingsBuilder () @property (nonatomic) NSMutableArray *ipv4LocalAddresses; @@ -33,11 +36,13 @@ #pragma mark - NEPacketTunnelNetworkSettings Generation - (NEPacketTunnelNetworkSettings *)networkSettings { - if (!self.remoteAddress.length) { return nil; } + NSAssert(self.remoteAddress != nil && self.remoteAddress.length > 0, @"Remote address is nil or empty."); NEPacketTunnelNetworkSettings *networkSettings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:self.remoteAddress]; - if (self.ipv4LocalAddresses.count && (self.ipv4LocalAddresses.count == self.ipv4SubnetMasks.count)) { + if (self.ipv4LocalAddresses.ovpn_isNotEmpty) { + NSAssert(self.ipv4LocalAddresses.count == self.ipv4SubnetMasks.count, @"Number of IPv4 addresses is not equal to number of IPv4 subnet masks."); + NEIPv4Settings *ipv4Settings = [[NEIPv4Settings alloc] initWithAddresses:self.ipv4LocalAddresses subnetMasks:self.ipv4SubnetMasks]; @@ -47,7 +52,9 @@ networkSettings.IPv4Settings = ipv4Settings; } - if (self.ipv6LocalAddresses.count && (self.ipv6LocalAddresses.count == self.ipv6NetworkPrefixLengths.count)) { + if (self.ipv6LocalAddresses.ovpn_isNotEmpty) { + NSAssert(self.ipv6LocalAddresses.count == self.ipv6NetworkPrefixLengths.count, @"Number of IPv6 addresses is not equal to number of IPv6 prefixes."); + NEIPv6Settings *ipv6Settings = [[NEIPv6Settings alloc] initWithAddresses:self.ipv6LocalAddresses networkPrefixLengths:self.ipv6NetworkPrefixLengths]; @@ -57,7 +64,7 @@ networkSettings.IPv6Settings = ipv6Settings; } - if (self.dnsServers.count) { + if (self.dnsServers.ovpn_isNotEmpty) { NEDNSSettings *dnsSettings = [[NEDNSSettings alloc] initWithServers:self.dnsServers]; dnsSettings.searchDomains = self.searchDomains; networkSettings.DNSSettings = dnsSettings;