Merge branch 'feature/improve-testing' into develop

This commit is contained in:
Sergey Abramchuk
2020-08-18 13:42:14 +03:00
18 changed files with 588 additions and 269 deletions
+43 -20
View File
@@ -7,6 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
C910EAE524EBB1DA0081AF13 /* OpenVPNReachabilityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97E5FD624123F48005C2EBC /* OpenVPNReachabilityTests.swift */; };
C910EAE724EBB5210081AF13 /* OpenVPNConfigurationEvaluation.h in Headers */ = {isa = PBXBuildFile; fileRef = C910EAE624EBB5210081AF13 /* OpenVPNConfigurationEvaluation.h */; settings = {ATTRIBUTES = (Public, ); }; };
C910EAEE24EBBFB60081AF13 /* client.crt in Resources */ = {isa = PBXBuildFile; fileRef = C910EAEB24EBBFB60081AF13 /* client.crt */; };
C910EAEF24EBBFB60081AF13 /* ca.crt in Resources */ = {isa = PBXBuildFile; fileRef = C910EAEC24EBBFB60081AF13 /* ca.crt */; };
C910EAF024EBBFB60081AF13 /* client.key in Resources */ = {isa = PBXBuildFile; fileRef = C910EAED24EBBFB60081AF13 /* client.key */; };
C910EAF224EBC6F90081AF13 /* client.ovpn in Resources */ = {isa = PBXBuildFile; fileRef = C910EAF124EBC6F90081AF13 /* client.ovpn */; };
C97E5F6E24122F12005C2EBC /* NSArray+OpenVPNAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F6D24122F12005C2EBC /* NSArray+OpenVPNAdditions.h */; };
C97E5F7024122F22005C2EBC /* NSError+OpenVPNError.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F6F24122F22005C2EBC /* NSError+OpenVPNError.h */; };
C97E5F7224122F5C005C2EBC /* OpenVPNClient.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F7124122F5C005C2EBC /* OpenVPNClient.h */; };
@@ -31,8 +37,8 @@
C97E5F98241230F7005C2EBC /* OpenVPNPacket.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F97241230F7005C2EBC /* OpenVPNPacket.h */; };
C97E5F9A24123135005C2EBC /* OpenVPNPacketFlowBridge.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F9924123135005C2EBC /* OpenVPNPacketFlowBridge.h */; };
C97E5F9C2412313F005C2EBC /* OpenVPNPrivateKey.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F9B2412313F005C2EBC /* OpenVPNPrivateKey.h */; };
C97E5F9E2412314A005C2EBC /* OpenVPNProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F9D24123149005C2EBC /* OpenVPNProperties.h */; };
C97E5FA024123152005C2EBC /* OpenVPNProperties+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F9F24123151005C2EBC /* OpenVPNProperties+Internal.h */; };
C97E5F9E2412314A005C2EBC /* OpenVPNConfigurationEvaluation.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F9D24123149005C2EBC /* OpenVPNConfigurationEvaluation.h */; };
C97E5FA024123152005C2EBC /* OpenVPNConfigurationEvaluation+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5F9F24123151005C2EBC /* OpenVPNConfigurationEvaluation+Internal.h */; };
C97E5FA22412315D005C2EBC /* OpenVPNReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5FA12412315C005C2EBC /* OpenVPNReachability.h */; };
C97E5FA424123168005C2EBC /* OpenVPNReachability+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5FA324123168005C2EBC /* OpenVPNReachability+Internal.h */; };
C97E5FA62412317B005C2EBC /* OpenVPNReachabilityStatus.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5FA52412317B005C2EBC /* OpenVPNReachabilityStatus.h */; };
@@ -46,7 +52,6 @@
C97E5FB6241231FF005C2EBC /* OpenVPNTransportStats+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5FB5241231FF005C2EBC /* OpenVPNTransportStats+Internal.h */; };
C97E5FB824123211005C2EBC /* OpenVPNTransportProtocol.h in Headers */ = {isa = PBXBuildFile; fileRef = C97E5FB724123211005C2EBC /* OpenVPNTransportProtocol.h */; };
C97E5FDD24123F48005C2EBC /* OpenVPNConfigurationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97E5FD724123F48005C2EBC /* OpenVPNConfigurationTests.swift */; };
C97E5FDE24123F48005C2EBC /* CustomFlow.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97E5FD824123F48005C2EBC /* CustomFlow.swift */; };
C97E5FDF24123F48005C2EBC /* OpenVPNAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97E5FD924123F48005C2EBC /* OpenVPNAdapterTests.swift */; };
C97E5FE024123F48005C2EBC /* VPNProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97E5FDA24123F48005C2EBC /* VPNProfile.swift */; };
C97E5FE124123F48005C2EBC /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = C97E5FDB24123F48005C2EBC /* Bundle.swift */; };
@@ -73,7 +78,7 @@
OBJ_271 /* OpenVPNPacket.mm in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_39 /* OpenVPNPacket.mm */; };
OBJ_272 /* OpenVPNPacketFlowBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_40 /* OpenVPNPacketFlowBridge.mm */; };
OBJ_273 /* OpenVPNPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_41 /* OpenVPNPrivateKey.m */; };
OBJ_274 /* OpenVPNProperties.mm in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_42 /* OpenVPNProperties.mm */; };
OBJ_274 /* OpenVPNConfigurationEvaluation.mm in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_42 /* OpenVPNConfigurationEvaluation.mm */; };
OBJ_275 /* OpenVPNReachability.mm in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_43 /* OpenVPNReachability.mm */; };
OBJ_276 /* OpenVPNReachabilityTracker.mm in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_44 /* OpenVPNReachabilityTracker.mm */; };
OBJ_277 /* OpenVPNServerEntry.mm in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_45 /* OpenVPNServerEntry.mm */; };
@@ -87,7 +92,6 @@
OBJ_286 /* OpenVPNTLSCertProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_54 /* OpenVPNTLSCertProfile.h */; settings = {ATTRIBUTES = (Public, ); }; };
OBJ_287 /* OpenVPNPrivateKey.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_55 /* OpenVPNPrivateKey.h */; settings = {ATTRIBUTES = (Public, ); }; };
OBJ_288 /* OpenVPNInterfaceStats.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_56 /* OpenVPNInterfaceStats.h */; settings = {ATTRIBUTES = (Public, ); }; };
OBJ_289 /* OpenVPNProperties.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_57 /* OpenVPNProperties.h */; settings = {ATTRIBUTES = (Public, ); }; };
OBJ_290 /* OpenVPNReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_58 /* OpenVPNReachability.h */; settings = {ATTRIBUTES = (Public, ); }; };
OBJ_291 /* OpenVPNIPv6Preference.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_59 /* OpenVPNIPv6Preference.h */; settings = {ATTRIBUTES = (Public, ); }; };
OBJ_292 /* OpenVPNAdapterPacketFlow.h in Headers */ = {isa = PBXBuildFile; fileRef = OBJ_60 /* OpenVPNAdapterPacketFlow.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -225,6 +229,11 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
C910EAE624EBB5210081AF13 /* OpenVPNConfigurationEvaluation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNConfigurationEvaluation.h; sourceTree = "<group>"; };
C910EAEB24EBBFB60081AF13 /* client.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = client.crt; sourceTree = "<group>"; };
C910EAEC24EBBFB60081AF13 /* ca.crt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ca.crt; sourceTree = "<group>"; };
C910EAED24EBBFB60081AF13 /* client.key */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = client.key; sourceTree = "<group>"; };
C910EAF124EBC6F90081AF13 /* client.ovpn */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = client.ovpn; sourceTree = "<group>"; };
C97E5F6D24122F12005C2EBC /* NSArray+OpenVPNAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSArray+OpenVPNAdditions.h"; sourceTree = "<group>"; };
C97E5F6F24122F22005C2EBC /* NSError+OpenVPNError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+OpenVPNError.h"; sourceTree = "<group>"; };
C97E5F7124122F5C005C2EBC /* OpenVPNClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNClient.h; sourceTree = "<group>"; };
@@ -249,8 +258,8 @@
C97E5F97241230F7005C2EBC /* OpenVPNPacket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNPacket.h; sourceTree = "<group>"; };
C97E5F9924123135005C2EBC /* OpenVPNPacketFlowBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNPacketFlowBridge.h; sourceTree = "<group>"; };
C97E5F9B2412313F005C2EBC /* OpenVPNPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNPrivateKey.h; sourceTree = "<group>"; };
C97E5F9D24123149005C2EBC /* OpenVPNProperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNProperties.h; sourceTree = "<group>"; };
C97E5F9F24123151005C2EBC /* OpenVPNProperties+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OpenVPNProperties+Internal.h"; sourceTree = "<group>"; };
C97E5F9D24123149005C2EBC /* OpenVPNConfigurationEvaluation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNConfigurationEvaluation.h; sourceTree = "<group>"; };
C97E5F9F24123151005C2EBC /* OpenVPNConfigurationEvaluation+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OpenVPNConfigurationEvaluation+Internal.h"; sourceTree = "<group>"; };
C97E5FA12412315C005C2EBC /* OpenVPNReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNReachability.h; sourceTree = "<group>"; };
C97E5FA324123168005C2EBC /* OpenVPNReachability+Internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "OpenVPNReachability+Internal.h"; sourceTree = "<group>"; };
C97E5FA52412317B005C2EBC /* OpenVPNReachabilityStatus.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OpenVPNReachabilityStatus.h; sourceTree = "<group>"; };
@@ -268,7 +277,6 @@
C97E5FCD24123F2E005C2EBC /* OpenVPNAdapterTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenVPNAdapterTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
C97E5FD624123F48005C2EBC /* OpenVPNReachabilityTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenVPNReachabilityTests.swift; sourceTree = "<group>"; };
C97E5FD724123F48005C2EBC /* OpenVPNConfigurationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenVPNConfigurationTests.swift; sourceTree = "<group>"; };
C97E5FD824123F48005C2EBC /* CustomFlow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomFlow.swift; sourceTree = "<group>"; };
C97E5FD924123F48005C2EBC /* OpenVPNAdapterTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenVPNAdapterTests.swift; sourceTree = "<group>"; };
C97E5FDA24123F48005C2EBC /* VPNProfile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VPNProfile.swift; sourceTree = "<group>"; };
C97E5FDB24123F48005C2EBC /* Bundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Bundle.swift; sourceTree = "<group>"; };
@@ -431,7 +439,7 @@
OBJ_39 /* OpenVPNPacket.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNPacket.mm; sourceTree = "<group>"; };
OBJ_40 /* OpenVPNPacketFlowBridge.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNPacketFlowBridge.mm; sourceTree = "<group>"; };
OBJ_41 /* OpenVPNPrivateKey.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OpenVPNPrivateKey.m; sourceTree = "<group>"; };
OBJ_42 /* OpenVPNProperties.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNProperties.mm; sourceTree = "<group>"; };
OBJ_42 /* OpenVPNConfigurationEvaluation.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNConfigurationEvaluation.mm; sourceTree = "<group>"; };
OBJ_43 /* OpenVPNReachability.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNReachability.mm; sourceTree = "<group>"; };
OBJ_44 /* OpenVPNReachabilityTracker.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNReachabilityTracker.mm; sourceTree = "<group>"; };
OBJ_45 /* OpenVPNServerEntry.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = OpenVPNServerEntry.mm; sourceTree = "<group>"; };
@@ -445,7 +453,6 @@
OBJ_54 /* OpenVPNTLSCertProfile.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNTLSCertProfile.h; sourceTree = "<group>"; };
OBJ_55 /* OpenVPNPrivateKey.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNPrivateKey.h; sourceTree = "<group>"; };
OBJ_56 /* OpenVPNInterfaceStats.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNInterfaceStats.h; sourceTree = "<group>"; };
OBJ_57 /* OpenVPNProperties.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNProperties.h; sourceTree = "<group>"; };
OBJ_58 /* OpenVPNReachability.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNReachability.h; sourceTree = "<group>"; };
OBJ_59 /* OpenVPNIPv6Preference.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNIPv6Preference.h; sourceTree = "<group>"; };
OBJ_60 /* OpenVPNAdapterPacketFlow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenVPNAdapterPacketFlow.h; sourceTree = "<group>"; };
@@ -527,12 +534,23 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
C910EAEA24EBBFB60081AF13 /* Resources */ = {
isa = PBXGroup;
children = (
C910EAF124EBC6F90081AF13 /* client.ovpn */,
C910EAEB24EBBFB60081AF13 /* client.crt */,
C910EAEC24EBBFB60081AF13 /* ca.crt */,
C910EAED24EBBFB60081AF13 /* client.key */,
);
name = Resources;
path = Tests/Resources;
sourceTree = "<group>";
};
C97E5FD524123F48005C2EBC /* OpenVPNAdapter */ = {
isa = PBXGroup;
children = (
C97E5FDB24123F48005C2EBC /* Bundle.swift */,
C97E5FDA24123F48005C2EBC /* VPNProfile.swift */,
C97E5FD824123F48005C2EBC /* CustomFlow.swift */,
C97E5FD624123F48005C2EBC /* OpenVPNReachabilityTests.swift */,
C97E5FD724123F48005C2EBC /* OpenVPNConfigurationTests.swift */,
C97E5FD924123F48005C2EBC /* OpenVPNAdapterTests.swift */,
@@ -673,6 +691,7 @@
OBJ_231 /* Tests */ = {
isa = PBXGroup;
children = (
C910EAEA24EBBFB60081AF13 /* Resources */,
C97E5FD524123F48005C2EBC /* OpenVPNAdapter */,
);
name = Tests;
@@ -746,9 +765,9 @@
OBJ_40 /* OpenVPNPacketFlowBridge.mm */,
C97E5F9B2412313F005C2EBC /* OpenVPNPrivateKey.h */,
OBJ_41 /* OpenVPNPrivateKey.m */,
C97E5F9D24123149005C2EBC /* OpenVPNProperties.h */,
C97E5F9F24123151005C2EBC /* OpenVPNProperties+Internal.h */,
OBJ_42 /* OpenVPNProperties.mm */,
C97E5F9D24123149005C2EBC /* OpenVPNConfigurationEvaluation.h */,
C97E5F9F24123151005C2EBC /* OpenVPNConfigurationEvaluation+Internal.h */,
OBJ_42 /* OpenVPNConfigurationEvaluation.mm */,
C97E5FA12412315C005C2EBC /* OpenVPNReachability.h */,
C97E5FA324123168005C2EBC /* OpenVPNReachability+Internal.h */,
OBJ_43 /* OpenVPNReachability.mm */,
@@ -778,7 +797,7 @@
OBJ_54 /* OpenVPNTLSCertProfile.h */,
OBJ_55 /* OpenVPNPrivateKey.h */,
OBJ_56 /* OpenVPNInterfaceStats.h */,
OBJ_57 /* OpenVPNProperties.h */,
C910EAE624EBB5210081AF13 /* OpenVPNConfigurationEvaluation.h */,
OBJ_58 /* OpenVPNReachability.h */,
OBJ_59 /* OpenVPNIPv6Preference.h */,
OBJ_60 /* OpenVPNAdapterPacketFlow.h */,
@@ -974,12 +993,11 @@
C97E5F6E24122F12005C2EBC /* NSArray+OpenVPNAdditions.h in Headers */,
OBJ_286 /* OpenVPNTLSCertProfile.h in Headers */,
C97E5FAE241231B3005C2EBC /* OpenVPNSessionToken.h in Headers */,
C97E5F9E2412314A005C2EBC /* OpenVPNProperties.h in Headers */,
C97E5F9E2412314A005C2EBC /* OpenVPNConfigurationEvaluation.h in Headers */,
C97E5FA62412317B005C2EBC /* OpenVPNReachabilityStatus.h in Headers */,
OBJ_287 /* OpenVPNPrivateKey.h in Headers */,
C97E5F7424122FE6005C2EBC /* OpenVPNAdapter.h in Headers */,
OBJ_288 /* OpenVPNInterfaceStats.h in Headers */,
OBJ_289 /* OpenVPNProperties.h in Headers */,
OBJ_290 /* OpenVPNReachability.h in Headers */,
OBJ_291 /* OpenVPNIPv6Preference.h in Headers */,
C97E5F8C24123091005C2EBC /* OpenVPNInterfaceStats.h in Headers */,
@@ -987,7 +1005,7 @@
C97E5F92241230C6005C2EBC /* OpenVPNKeyType.h in Headers */,
C97E5F94241230D9005C2EBC /* OpenVPNMinTLSVersion.h in Headers */,
C97E5F7E24123046005C2EBC /* OpenVPNConfiguration.h in Headers */,
C97E5FA024123152005C2EBC /* OpenVPNProperties+Internal.h in Headers */,
C97E5FA024123152005C2EBC /* OpenVPNConfigurationEvaluation+Internal.h in Headers */,
C97E5FB2241231DA005C2EBC /* OpenVPNTLSCertProfile.h in Headers */,
OBJ_292 /* OpenVPNAdapterPacketFlow.h in Headers */,
C97E5FA824123186005C2EBC /* OpenVPNReachabilityTracker.h in Headers */,
@@ -1009,6 +1027,7 @@
OBJ_301 /* OpenVPNSessionToken.h in Headers */,
C97E5FA424123168005C2EBC /* OpenVPNReachability+Internal.h in Headers */,
OBJ_302 /* OpenVPNCertificate.h in Headers */,
C910EAE724EBB5210081AF13 /* OpenVPNConfigurationEvaluation.h in Headers */,
C97E5FAC241231AA005C2EBC /* OpenVPNServerEntry+Internal.h in Headers */,
C97E5F90241230B3005C2EBC /* OpenVPNIPv6Preference.h in Headers */,
C97E5FE624124B0E005C2EBC /* Umbrella-Header.h in Headers */,
@@ -1147,6 +1166,10 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C910EAEE24EBBFB60081AF13 /* client.crt in Resources */,
C910EAF024EBBFB60081AF13 /* client.key in Resources */,
C910EAEF24EBBFB60081AF13 /* ca.crt in Resources */,
C910EAF224EBC6F90081AF13 /* client.ovpn in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -1160,7 +1183,7 @@
C97E5FE124123F48005C2EBC /* Bundle.swift in Sources */,
C97E5FDD24123F48005C2EBC /* OpenVPNConfigurationTests.swift in Sources */,
C97E5FDF24123F48005C2EBC /* OpenVPNAdapterTests.swift in Sources */,
C97E5FDE24123F48005C2EBC /* CustomFlow.swift in Sources */,
C910EAE524EBB1DA0081AF13 /* OpenVPNReachabilityTests.swift in Sources */,
C97E5FE024123F48005C2EBC /* VPNProfile.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -1194,7 +1217,7 @@
OBJ_271 /* OpenVPNPacket.mm in Sources */,
OBJ_272 /* OpenVPNPacketFlowBridge.mm in Sources */,
OBJ_273 /* OpenVPNPrivateKey.m in Sources */,
OBJ_274 /* OpenVPNProperties.mm in Sources */,
OBJ_274 /* OpenVPNConfigurationEvaluation.mm in Sources */,
OBJ_275 /* OpenVPNReachability.mm in Sources */,
OBJ_276 /* OpenVPNReachabilityTracker.mm in Sources */,
OBJ_277 /* OpenVPNServerEntry.mm in Sources */,
+1 -1
View File
@@ -26,7 +26,7 @@ FOUNDATION_EXPORT const unsigned char OpenVPNAdapterVersionString[];
#import <OpenVPNAdapter/OpenVPNConfiguration.h>
#import <OpenVPNAdapter/OpenVPNCredentials.h>
#import <OpenVPNAdapter/OpenVPNServerEntry.h>
#import <OpenVPNAdapter/OpenVPNProperties.h>
#import <OpenVPNAdapter/OpenVPNConfigurationEvaluation.h>
#import <OpenVPNAdapter/OpenVPNConnectionInfo.h>
#import <OpenVPNAdapter/OpenVPNSessionToken.h>
#import <OpenVPNAdapter/OpenVPNTransportStats.h>
@@ -0,0 +1 @@
../library/OpenVPNConfigurationEvaluation.h
@@ -1 +0,0 @@
../library/OpenVPNProperties.h
@@ -22,7 +22,7 @@ typedef NS_ENUM(NSInteger, OpenVPNAdapterEvent);
@class OpenVPNConnectionInfo;
@class OpenVPNCredentials;
@class OpenVPNInterfaceStats;
@class OpenVPNProperties;
@class OpenVPNConfigurationEvaluation;
@class OpenVPNTransportStats;
@class OpenVPNSessionToken;
@@ -126,15 +126,26 @@ NS_SWIFT_NAME(openVPNAdapter(_:handleEvent:message:));
*/
@property (nonatomic, readonly) OpenVPNTransportStats *transportStatistics;
/**
Evaluate the given configuration object and determine needed credentials.
@param configuration The configuration object.
@param error If there is an error applying the configuration, upon return contains an error object that describes the problem.
@return An object describing the configuration which has been evaluated.
*/
+ (nullable OpenVPNConfigurationEvaluation *)evaluateConfiguration:(OpenVPNConfiguration *)configuration
error:(NSError **)error
NS_SWIFT_NAME(evaluate(configuration:));
/**
Applies the given configuration object.
Call this method prior to connecting, this method has no effect after calling connect.
@param configuration The configuration object.
@param error If there is an error applying the configuration, upon return contains an error object that describes the problem.
@return A properties object describing the configuration which has been applied.
@return An object describing the configuration which has been applied.
*/
- (nullable OpenVPNProperties *)applyConfiguration:(OpenVPNConfiguration *)configuration
- (nullable OpenVPNConfigurationEvaluation *)applyConfiguration:(OpenVPNConfiguration *)configuration
error:(NSError **)error
NS_SWIFT_NAME(apply(configuration:));
@@ -24,7 +24,7 @@
#import "OpenVPNConfiguration+Internal.h"
#import "OpenVPNConnectionInfo+Internal.h"
#import "OpenVPNInterfaceStats+Internal.h"
#import "OpenVPNProperties+Internal.h"
#import "OpenVPNConfigurationEvaluation+Internal.h"
#import "OpenVPNSessionToken+Internal.h"
#import "OpenVPNTransportStats+Internal.h"
#import "NSError+OpenVPNError.h"
@@ -50,22 +50,40 @@
#pragma mark - OpenVPNClient Lifecycle
- (OpenVPNProperties *)applyConfiguration:(OpenVPNConfiguration *)configuration error:(NSError * __autoreleasing *)error {
+ (nullable OpenVPNConfigurationEvaluation *)evaluateConfiguration:(OpenVPNConfiguration *)configuration error:(NSError **)error {
ClientAPI::EvalConfig eval = OpenVPNClient::eval_config_static(configuration.config);
if (eval.error) {
if (error) {
NSString *message = [NSString stringWithUTF8String:eval.message.c_str()];
*error = [NSError ovpn_errorObjectForAdapterError:OpenVPNAdapterErrorConfigurationFailure
description:@"Failed to evaluate OpenVPN configuration."
message:message
fatal:YES];
}
return nil;
}
return [[OpenVPNConfigurationEvaluation alloc] initWithEvalConfig:eval];
}
- (OpenVPNConfigurationEvaluation *)applyConfiguration:(OpenVPNConfiguration *)configuration error:(NSError * __autoreleasing *)error {
ClientAPI::EvalConfig eval = self.vpnClient->apply_config(configuration.config);
if (eval.error) {
if (error) {
NSString *message = [NSString stringWithUTF8String:eval.message.c_str()];
*error = [NSError ovpn_errorObjectForAdapterError:OpenVPNAdapterErrorConfigurationFailure
description:@"Failed to apply OpenVPN configuration."
message:message
fatal:YES];
description:@"Failed to apply OpenVPN configuration."
message:message
fatal:YES];
}
return nil;
}
return [[OpenVPNProperties alloc] initWithEvalConfig:eval];
return [[OpenVPNConfigurationEvaluation alloc] initWithEvalConfig:eval];
}
- (BOOL)provideCredentials:(OpenVPNCredentials *)credentials error:(NSError * __autoreleasing *)error {
@@ -1,18 +1,18 @@
//
// OpenVPNProperties+Internal.h
// OpenVPNConfigurationEvaluation+Internal.h
// OpenVPN Adapter
//
// Created by Sergey Abramchuk on 26.04.17.
//
//
#import "OpenVPNProperties.h"
#import "OpenVPNConfigurationEvaluation.h"
#include <ovpnapi.hpp>
using namespace openvpn;
@interface OpenVPNProperties (Internal)
@interface OpenVPNConfigurationEvaluation (Internal)
- (instancetype)initWithEvalConfig:(ClientAPI::EvalConfig)eval;
@@ -1,5 +1,5 @@
//
// OpenVPNProperties.h
// OpenVPNConfigurationEvaluation.h
// OpenVPN Adapter
//
// Created by Sergey Abramchuk on 26.04.17.
@@ -11,7 +11,7 @@
typedef NS_ENUM(NSInteger, OpenVPNTransportProtocol);
@class OpenVPNServerEntry;
@interface OpenVPNProperties : NSObject
@interface OpenVPNConfigurationEvaluation : NSObject
/**
This username must be used with profile
@@ -33,6 +33,11 @@ typedef NS_ENUM(NSInteger, OpenVPNTransportProtocol);
*/
@property (readonly, nonatomic) BOOL autologin;
/**
If YES this is an External PKI profile (no cert or key directives)
*/
@property (readonly, nonatomic) BOOL externalPki;
/**
Static challenge, may be empty, ignored if autologin
*/
@@ -1,13 +1,13 @@
//
// OpenVPNProperties.m
// OpenVPNConfigurationEvaluation.m
// OpenVPN Adapter
//
// Created by Sergey Abramchuk on 26.04.17.
//
//
#import "OpenVPNProperties.h"
#import "OpenVPNProperties+Internal.h"
#import "OpenVPNConfigurationEvaluation.h"
#import "OpenVPNConfigurationEvaluation+Internal.h"
#include <openvpn/common/number.hpp>
@@ -16,7 +16,7 @@
using namespace openvpn;
@implementation OpenVPNProperties
@implementation OpenVPNConfigurationEvaluation
- (instancetype)initWithEvalConfig:(ClientAPI::EvalConfig)eval {
if (self = [super init]) {
@@ -26,6 +26,7 @@ using namespace openvpn;
_friendlyName = !eval.friendlyName.empty() ? [NSString stringWithUTF8String:eval.friendlyName.c_str()] : nil;
_autologin = eval.autologin;
_externalPki = eval.externalPki;
_staticChallenge = !eval.staticChallenge.empty() ? [NSString stringWithUTF8String:eval.staticChallenge.c_str()] : nil;
_staticChallengeEcho = eval.staticChallengeEcho;
-21
View File
@@ -1,21 +0,0 @@
//
// CustomFlow.swift
// OpenVPN Adapter
//
// Created by Sergey Abramchuk on 28.10.2017.
//
import NetworkExtension
@testable import OpenVPNAdapter
class CustomFlow: NSObject, OpenVPNAdapterPacketFlow {
func readPackets(completionHandler: @escaping ([Data], [NSNumber]) -> Void) {
}
func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool {
return true
}
}
+115 -93
View File
@@ -12,82 +12,124 @@ import NetworkExtension
class OpenVPNAdapterTests: XCTestCase {
enum ExpectationsType {
case connection
private class CustomFlow: NSObject, OpenVPNAdapterPacketFlow {
func readPackets(completionHandler: @escaping ([Data], [NSNumber]) -> Void) {
}
func writePackets(_ packets: [Data], withProtocols protocols: [NSNumber]) -> Bool {
return true
}
}
let customFlow = CustomFlow()
private class AdapterDelegate: NSObject, OpenVPNAdapterDelegate {
let settingsHandler: (NEPacketTunnelNetworkSettings?) -> Void
let errorHandler: (Error) -> Void
let eventHandler: (OpenVPNAdapterEvent) -> Void
init(
settingsHandler: @escaping (NEPacketTunnelNetworkSettings?) -> Void,
errorHandler: @escaping (Error) -> Void,
eventHandler: @escaping (OpenVPNAdapterEvent) -> Void
) {
self.settingsHandler = settingsHandler
self.errorHandler = errorHandler
self.eventHandler = eventHandler
super.init()
}
func openVPNAdapter(
_ openVPNAdapter: OpenVPNAdapter,
configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?,
completionHandler: @escaping (Error?) -> Void
) {
settingsHandler(networkSettings)
completionHandler(nil)
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
errorHandler(error)
}
func openVPNAdapter(
_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?
) {
eventHandler(event)
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
print(logMessage)
}
}
var expectations = [ExpectationsType : XCTestExpectation]()
private let customFlow = CustomFlow()
override func setUp() {
super.setUp()
expectations.removeAll()
}
override func tearDown() {
super.tearDown()
}
func testApplyConfiguration() {
guard let vpnConfiguration = VPNProfile.configuration.data(using: .utf8) else { fatalError() }
let adapter = OpenVPNAdapter()
func testVPNConnection() {
let configuration = OpenVPNConfiguration()
configuration.fileContent = vpnConfiguration
configuration.settings = ["auth-user-pass": ""]
let result: OpenVPNProperties
configuration.fileContent = VPNProfile.live.configuration.data(using: .utf8)
var settings = VPNProfile.live.settings ?? [:]
if let key = VPNProfile.live.key, let cert = VPNProfile.live.cert {
settings["cert"] = cert.replacingOccurrences(of: "\n", with: "\\n")
settings["key"] = key.replacingOccurrences(of: "\n", with: "\\n")
}
configuration.settings = settings
let preEvaluation: OpenVPNConfigurationEvaluation
do {
result = try adapter.apply(configuration: configuration)
preEvaluation = try OpenVPNAdapter.evaluate(configuration: configuration)
} catch {
XCTFail("Failed to configure OpenVPN adapted due to error: \(error)")
XCTFail("Evaluation failed due to error: \(error)")
return
}
XCTAssert(result.remoteHost == VPNProfile.remoteHost)
XCTAssert(result.remotePort == VPNProfile.remotePort)
XCTAssert(result.autologin == false)
}
func testProvideCredentials() {
guard !preEvaluation.externalPki else {
XCTFail("Currently profile cannot be External PKI as it is not imlemented yet.")
return
}
if preEvaluation.isPrivateKeyPasswordRequired {
guard let keyPassword = VPNProfile.live.keyPassword else {
XCTFail("Private key password required.")
return
}
configuration.privateKeyPassword = keyPassword
}
let adapter = OpenVPNAdapter()
let credentials = OpenVPNCredentials()
credentials.username = "username"
credentials.password = "password"
let finalEvaluation: OpenVPNConfigurationEvaluation
do {
try adapter.provide(credentials: credentials)
finalEvaluation = try adapter.apply(configuration: configuration)
} catch {
XCTFail("Failed to provide credentials. \(error)")
return
}
}
// Test connection to the VPN server
func testConnection() {
guard let vpnConfiguration = VPNProfile.configuration.data(using: .utf8) else { fatalError() }
let adapter = OpenVPNAdapter()
let configuration = OpenVPNConfiguration()
configuration.fileContent = vpnConfiguration
let result: OpenVPNProperties
do {
result = try adapter.apply(configuration: configuration)
} catch {
XCTFail("Failed to configure OpenVPN adapted due to error: \(error)")
XCTFail("Failed to apply OpenVPN configuration due to error: \(error)")
return
}
if !result.autologin {
if !finalEvaluation.autologin {
guard let username = VPNProfile.live.username, let password = VPNProfile.live.password else {
XCTFail("If unable to autologin, username and password must be provided.")
return
}
let credentials = OpenVPNCredentials()
credentials.username = VPNProfile.username
credentials.password = VPNProfile.password
credentials.username = username
credentials.password = password
do {
try adapter.provide(credentials: credentials)
@@ -97,48 +139,28 @@ class OpenVPNAdapterTests: XCTestCase {
}
}
expectations[.connection] = expectation(description: "me.ss-abramchuk.openvpn-adapter.connection")
adapter.delegate = self
adapter.connect(using: customFlow)
waitForExpectations(timeout: 30.0) { (error) in
adapter.disconnect()
}
}
}
extension OpenVPNAdapterTests: OpenVPNAdapterDelegate {
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, configureTunnelWithNetworkSettings networkSettings: NEPacketTunnelNetworkSettings?, completionHandler: @escaping (Error?) -> Void) {
completionHandler(nil)
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleEvent event: OpenVPNAdapterEvent, message: String?) {
switch event {
case .connected:
guard let connectionExpectation = expectations[.connection] else { return }
connectionExpectation.fulfill()
case .disconnected:
break
default:
break
}
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleError error: Error) {
if let connectionExpectation = expectations[.connection] {
XCTFail("Failed to establish conection.")
connectionExpectation.fulfill()
}
let connectionExpectation = XCTestExpectation(
description: "Establish connection using \(VPNProfile.live.profileName)"
)
dump(error)
let adapterDelegate = AdapterDelegate(
settingsHandler: { (settings) in
},
errorHandler: { (error) in
XCTFail("Failed to establish conection due to error: \(error)")
},
eventHandler: { (event) in
guard event == .connected else { return }
connectionExpectation.fulfill()
adapter.disconnect()
}
)
adapter.delegate = adapterDelegate
adapter.connect(using: customFlow)
wait(for: [connectionExpectation], timeout: 15.0)
}
func openVPNAdapter(_ openVPNAdapter: OpenVPNAdapter, handleLogMessage logMessage: String) {
print(logMessage)
}
}
@@ -9,8 +9,6 @@
import XCTest
@testable import OpenVPNAdapter
// TODO: Test getting/setting of all properties of OpenVPNConfiguration
class OpenVPNConfigurationTests: XCTestCase {
override func setUp() {
@@ -23,135 +21,148 @@ class OpenVPNConfigurationTests: XCTestCase {
super.tearDown()
}
func testGetSetProfile() {
guard let originalProfile = VPNProfile.configuration.data(using: .utf8) else { fatalError() }
func testEvaluateEmptyConfig() {
let configuration = OpenVPNConfiguration()
guard configuration.fileContent == nil else {
XCTFail("Empty file content should return nil")
return
do {
let _ = try OpenVPNAdapter.evaluate(configuration: configuration)
XCTFail("We shouldn't be here, evaluation should fail.")
} catch {
guard let fatal = (error as NSError).userInfo[OpenVPNAdapterErrorFatalKey] as? Bool else {
XCTFail("Error should contain OpenVPNAdapterErrorFatalKey.")
return
}
XCTAssert(fatal)
}
configuration.fileContent = originalProfile
guard let returnedProfile = configuration.fileContent else {
XCTFail("Returned file content should not be nil")
return
}
XCTAssert(originalProfile.elementsEqual(returnedProfile))
configuration.fileContent = nil
XCTAssert(configuration.fileContent == nil, "Empty file content should return nil")
configuration.fileContent = Data()
XCTAssert(configuration.fileContent == nil, "Empty file content should return nil")
}
func testGetSetSettings() {
let originalSettings = [
func testEvaluateManuallyPopulatedConfig() {
let configuration = OpenVPNConfiguration()
guard let caURL = Bundle.current.url(forResource: "ca", withExtension: "crt"),
let caContent = try? String(contentsOf: caURL, encoding: .utf8)
else {
fatalError("Failed to get ca.crt, check its existance in the Resources folder.")
}
guard let certURL = Bundle.current.url(forResource: "client", withExtension: "crt"),
let certContent = try? String(contentsOf: certURL, encoding: .utf8)
else {
fatalError("Failed to get client.crt, check its existance in the Resources folder.")
}
guard let keyURL = Bundle.current.url(forResource: "client", withExtension: "key"),
let keyContent = try? String(contentsOf: keyURL, encoding: .utf8)
else {
fatalError("Failed to get client.key, check its existance in the Resources folder.")
}
configuration.settings = [
"client": "",
"dev": "tun",
"remote-cert-tls" : "server"
"proto": "udp",
"remote": "my-server.com 1194",
"resolv-retry": "infinite",
"nobind": "",
"auth-user-pass": "",
"cipher": "AES-256-CBC",
"comp-lzo": "",
"verb": "3",
"ca": caContent.replacingOccurrences(of: "\n", with: "\\n"),
"cert": certContent.replacingOccurrences(of: "\n", with: "\\n"),
"key": keyContent.replacingOccurrences(of: "\n", with: "\\n")
]
let evaluation: OpenVPNConfigurationEvaluation
do {
evaluation = try OpenVPNAdapter.evaluate(configuration: configuration)
} catch {
XCTFail("Evaluation failed due to error: \(error)")
return
}
XCTAssert(evaluation.remoteHost == "my-server.com")
XCTAssert(evaluation.remotePort == 1194)
XCTAssert(
!evaluation.autologin, "Username and password are required so autologin should be false."
)
XCTAssert(
!evaluation.externalPki,
"Key and cert were provided to the configuration so it shouldn't be External PKI profile."
)
}
func testEvaluateConfigFromFile() {
let configuration = OpenVPNConfiguration()
guard configuration.settings == nil else {
XCTFail("Empty settings should return nil")
guard let configURL = Bundle.current.url(forResource: "client", withExtension: "ovpn"),
let configContent = try? Data(contentsOf: configURL)
else {
fatalError("Failed to get client.ovpn, check its existance in the Resources folder.")
}
configuration.fileContent = configContent
let evaluation: OpenVPNConfigurationEvaluation
do {
evaluation = try OpenVPNAdapter.evaluate(configuration: configuration)
} catch {
XCTFail("Evaluation failed due to error: \(error)")
return
}
configuration.settings = originalSettings
XCTAssert(evaluation.remoteHost == "my-server.com")
XCTAssert(evaluation.remotePort == 1194)
guard let returnedSettings = configuration.settings else {
XCTFail("Returned settings should not be nil")
return
}
XCTAssert(
!evaluation.autologin, "Username and password required so autologin should be false."
)
let equals = originalSettings.allSatisfy { (key, value) in
returnedSettings[key] == value
}
XCTAssert(equals)
configuration.settings = [:]
XCTAssert(configuration.settings == nil, "Empty settings should return nil")
configuration.settings = nil
XCTAssert(configuration.settings == nil, "Empty settings should return nil")
XCTAssert(
evaluation.externalPki,
"Key and cert were not provided to the configuration so it should be External PKI profile."
)
}
func testGetSetRemote() {
guard let originalProfile = VPNProfile.configuration.data(using: .utf8) else { fatalError() }
let originalServer = "192.168.1.200"
let originalPort: UInt = 12000
let configuration = OpenVPNConfiguration()
configuration.fileContent = originalProfile
XCTAssertNil(configuration.server)
XCTAssertEqual(configuration.port, 0)
configuration.server = originalServer
configuration.port = originalPort
XCTAssertNotNil(configuration.server)
XCTAssertEqual(configuration.server, originalServer)
XCTAssertEqual(configuration.port, originalPort)
}
func testGetSetProto() {
let originalOption: OpenVPNTransportProtocol = .UDP
func testSetConfigurationProperties() {
let configuration = OpenVPNConfiguration()
guard configuration.proto == .default else {
XCTFail("proto option should return default value")
return
guard let configURL = Bundle.current.url(forResource: "client", withExtension: "ovpn"),
let configContent = try? Data(contentsOf: configURL)
else {
fatalError("Failed to get client.ovpn, check its existance in the Resources folder.")
}
configuration.proto = originalOption
guard configuration.proto == originalOption else {
XCTFail("proto option should be equal to original value (enabled)")
return
}
configuration.fileContent = configContent
configuration.settings = [
"persist-key": "",
"persist-tun": ""
]
configuration.server = "another-server.com"
configuration.port = 5000
XCTAssert(configuration.proto == .default)
configuration.proto = .adaptive
configuration.tlsCertProfile = .preferred
XCTAssert(configuration.fileContent?.elementsEqual(configContent) == true)
XCTAssert(configuration.settings?["persist-key"] != nil)
XCTAssert(configuration.settings?["persist-tun"] != nil)
XCTAssert(configuration.server == "another-server.com")
XCTAssert(configuration.port == 5000)
XCTAssert(configuration.proto == .adaptive)
XCTAssert(configuration.tlsCertProfile == .preferred)
XCTAssert(configuration.ipv6 == .default)
configuration.ipv6 = .enabled
XCTAssert(configuration.ipv6 == .enabled)
}
func testGetSetIPv6() {
let originalOption: OpenVPNIPv6Preference = .enabled
let configuration = OpenVPNConfiguration()
guard configuration.ipv6 == .default else {
XCTFail("IPv6 option should return default value")
return
}
configuration.ipv6 = originalOption
guard configuration.ipv6 == originalOption else {
XCTFail("IPv6 option should be equal to original value (enabled)")
return
}
}
func testGetSetTLSCertProfile() {
let originalOption: OpenVPNTLSCertProfile = .preferred
let configuration = OpenVPNConfiguration()
guard configuration.tlsCertProfile == .default else {
XCTFail("TLS Cert Profile option should return default value")
return
}
configuration.tlsCertProfile = originalOption
guard configuration.tlsCertProfile == originalOption else {
XCTFail("TLS Cert Profile option should be equal to original value (preferred)")
return
}
}
}
@@ -7,7 +7,11 @@
//
import XCTest
#if os(macOS)
import CoreWLAN
#endif
@testable import OpenVPNAdapter
class OpenVPNReachabilityTests: XCTestCase {
@@ -22,6 +26,7 @@ class OpenVPNReachabilityTests: XCTestCase {
super.tearDown()
}
#if os(macOS)
func testReachability() {
let wifiClient = CWWiFiClient.shared()
guard let interface = wifiClient.interface() else {
@@ -62,4 +67,5 @@ class OpenVPNReachabilityTests: XCTestCase {
waitForExpectations(timeout: 30.0, handler: nil)
}
#endif
}
+35 -5
View File
@@ -9,11 +9,41 @@
import Foundation
struct VPNProfile {
static let username: String = <#OPENVPN_USERNAME#>
static let password: String = <#OPENVPN_PASSWORD#>
let profileName: String
static let configuration: String = <#OPENVPN_CONFIGURATION#>
let configuration: String
static let remoteHost: String = <#OPENVPN_REMOTE_HOST#>
static let remotePort: Int = <#OPENVPN_REMOTE_PORT#>
let cert: String?
let key: String?
let keyPassword: String?
let username: String?
let password: String?
let settings: [String: String]?
}
extension VPNProfile {
static let live: VPNProfile = {
let profileName: String = <#OPENVPN_PROFILE_NAME#>
let configuration: String = <#OPENVPN_CONFIGURATION#>
let cert: String? = <#OPENVPN_CERT#>
let key: String? = <#OPENVPN_KEY#>
let keyPassword: String? = <#PRIVATE_KEY_PASSWORD#>
let username: String? = <#OPENVPN_USERNAME#>
let password: String? = <#OPENVPN_PASSWORD#>
let settings: [String: String]? = <#OPENVPN_ADDITIONAL_SETTINGS#>
return VPNProfile(
profileName: profileName, configuration: configuration, cert: cert, key: key, keyPassword: keyPassword,
username: username, password: password, settings: settings
)
}()
}
+35
View File
@@ -0,0 +1,35 @@
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
+103
View File
@@ -0,0 +1,103 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 2 (0x2)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=KG, ST=NA, L=BISHKEK, O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
Validity
Not Before: Oct 22 21:59:53 2014 GMT
Not After : Oct 19 21:59:53 2024 GMT
Subject: C=KG, ST=NA, O=OpenVPN-TEST, CN=Test-Client/emailAddress=me@myhost.mydomain
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:ec:65:8f:e9:12:c2:1a:5b:e6:56:2a:08:a9:82:
3a:2d:44:78:a3:00:3b:b0:9f:e7:27:10:40:93:ef:
f1:cc:3e:a0:aa:04:a2:80:1b:13:a9:e6:fe:81:d6:
70:90:a8:d8:d4:de:30:d8:35:00:d2:be:62:f0:48:
da:fc:15:8d:c4:c6:6d:0b:99:f1:2b:83:00:0a:d3:
2a:23:0b:e5:cd:f9:35:df:43:61:15:72:ad:95:98:
f6:73:21:41:5e:a0:dd:47:27:a0:d5:9a:d4:41:a8:
1c:1d:57:20:71:17:8f:f7:28:9e:3e:07:ce:ec:d5:
0e:42:4f:1e:74:47:8e:47:9d:d2:14:28:27:2c:14:
10:f5:d1:96:b5:93:74:84:ef:f9:04:de:8d:4a:6f:
df:77:ab:ea:d1:58:d3:44:fe:5a:04:01:ff:06:7a:
97:f7:fd:e3:57:48:e1:f0:df:40:13:9f:66:23:5a:
e3:55:54:3d:54:39:ee:00:f9:12:f1:d2:df:74:2e:
ba:d7:f0:8d:c6:dd:18:58:1c:93:22:0b:75:fa:a8:
d6:e0:b5:2f:2d:b9:d4:fe:b9:4f:86:e2:75:48:16:
60:fb:3f:c9:b4:30:42:29:fb:3b:b3:2b:b9:59:81:
6a:46:f3:45:83:bf:fd:d5:1a:ff:37:0c:6f:5b:fd:
61:f1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints:
CA:FALSE
X509v3 Subject Key Identifier:
D2:B4:36:0F:B1:FC:DD:A5:EA:2A:F7:C7:23:89:FA:E3:FA:7A:44:1D
X509v3 Authority Key Identifier:
keyid:2B:40:E5:C9:7D:F5:F4:96:38:E9:2F:E3:2F:D9:40:64:C9:8E:05:9B
DirName:/C=KG/ST=NA/L=BISHKEK/O=OpenVPN-TEST/emailAddress=me@myhost.mydomain
serial:A1:4E:DE:FA:90:F2:AE:81
Signature Algorithm: sha256WithRSAEncryption
7f:e0:fe:84:a7:ec:df:62:a5:cd:3c:c1:e6:42:b1:31:12:f0:
b9:da:a7:9e:3f:bd:96:52:b6:fc:55:74:64:3e:e4:ff:7e:aa:
f7:3e:06:18:5f:73:85:f8:c8:e0:67:1b:4d:97:ca:05:d0:37:
07:33:64:9b:e6:78:77:14:9a:55:bb:2a:ac:c3:7f:c9:15:08:
83:5c:c8:c2:61:d3:71:4c:05:0b:2b:cb:a3:87:6d:a0:32:ed:
b0:b3:27:97:4a:55:8d:01:2a:30:56:68:ab:f2:da:5c:10:73:
c9:aa:0a:9c:4b:4c:a0:5b:51:6e:0a:7e:6c:53:80:b0:00:e1:
1e:9a:4c:0a:37:9e:20:89:bc:c5:e5:79:58:b7:45:ff:d3:c4:
a1:fd:d9:78:3d:45:16:74:df:82:44:1d:1d:81:50:5a:b9:32:
4c:e2:4f:3f:0e:3a:65:5a:64:83:3b:29:31:c4:99:88:bc:c5:
84:39:f2:19:12:e1:66:d0:ea:fb:75:b1:d2:27:be:91:59:a3:
2b:09:d5:5c:bf:46:8e:d6:67:d6:0b:ec:da:ab:f0:80:19:87:
64:07:a9:77:b1:5e:0c:e2:c5:1d:6a:ac:5d:23:f3:30:75:36:
4e:ca:c3:4e:b0:4d:8c:2c:ce:52:61:63:de:d5:f5:ef:ef:0a:
6b:23:25:26:3c:3a:f2:c3:c2:16:19:3f:a9:32:ba:68:f9:c9:
12:3c:3e:c6:1f:ff:9b:4e:f4:90:b0:63:f5:d1:33:00:30:5a:
e8:24:fa:35:44:9b:6a:80:f3:a6:cc:7b:3c:73:5f:50:c4:30:
71:d8:74:90:27:0a:01:4e:a5:5e:b1:f8:da:c2:61:81:11:ae:
29:a3:8f:fa:7e:4c:4e:62:b1:00:de:92:e3:8f:6a:2e:da:d9:
38:5d:6b:7c:0d:e4:01:aa:c8:c6:6d:8b:cd:c0:c8:6e:e4:57:
21:8a:f6:46:30:d9:ad:51:a1:87:96:a6:53:c9:1e:c6:bb:c3:
eb:55:fe:8c:d6:5c:d5:c6:f3:ca:b0:60:d2:d4:2a:1f:88:94:
d3:4c:1a:da:0c:94:fe:c1:5d:0d:2a:db:99:29:5d:f6:dd:16:
c4:c8:4d:74:9e:80:d9:d0:aa:ed:7b:e3:30:e4:47:d8:f5:15:
c1:71:b8:c6:fd:ee:fc:9e:b2:5f:b5:b7:92:ed:ff:ca:37:f6:
c7:82:b4:54:13:9b:83:cd:87:8b:7e:64:f6:2e:54:3a:22:b1:
c5:c1:f4:a5:25:53:9a:4d:a8:0f:e7:35:4b:89:df:19:83:66:
64:d9:db:d1:61:2b:24:1b:1d:44:44:fb:49:30:87:b7:49:23:
08:02:8a:e0:25:f3:f4:43
-----BEGIN CERTIFICATE-----
MIIFFDCCAvygAwIBAgIBAjANBgkqhkiG9w0BAQsFADBmMQswCQYDVQQGEwJLRzEL
MAkGA1UECBMCTkExEDAOBgNVBAcTB0JJU0hLRUsxFTATBgNVBAoTDE9wZW5WUE4t
VEVTVDEhMB8GCSqGSIb3DQEJARYSbWVAbXlob3N0Lm15ZG9tYWluMB4XDTE0MTAy
MjIxNTk1M1oXDTI0MTAxOTIxNTk1M1owajELMAkGA1UEBhMCS0cxCzAJBgNVBAgT
Ak5BMRUwEwYDVQQKEwxPcGVuVlBOLVRFU1QxFDASBgNVBAMTC1Rlc3QtQ2xpZW50
MSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW4wggEiMA0GCSqGSIb3
DQEBAQUAA4IBDwAwggEKAoIBAQDsZY/pEsIaW+ZWKgipgjotRHijADuwn+cnEECT
7/HMPqCqBKKAGxOp5v6B1nCQqNjU3jDYNQDSvmLwSNr8FY3Exm0LmfErgwAK0yoj
C+XN+TXfQ2EVcq2VmPZzIUFeoN1HJ6DVmtRBqBwdVyBxF4/3KJ4+B87s1Q5CTx50
R45HndIUKCcsFBD10Za1k3SE7/kE3o1Kb993q+rRWNNE/loEAf8Gepf3/eNXSOHw
30ATn2YjWuNVVD1UOe4A+RLx0t90LrrX8I3G3RhYHJMiC3X6qNbgtS8tudT+uU+G
4nVIFmD7P8m0MEIp+zuzK7lZgWpG80WDv/3VGv83DG9b/WHxAgMBAAGjgcgwgcUw
CQYDVR0TBAIwADAdBgNVHQ4EFgQU0rQ2D7H83aXqKvfHI4n64/p6RB0wgZgGA1Ud
IwSBkDCBjYAUK0DlyX319JY46S/jL9lAZMmOBZuhaqRoMGYxCzAJBgNVBAYTAktH
MQswCQYDVQQIEwJOQTEQMA4GA1UEBxMHQklTSEtFSzEVMBMGA1UEChMMT3BlblZQ
Ti1URVNUMSEwHwYJKoZIhvcNAQkBFhJtZUBteWhvc3QubXlkb21haW6CCQChTt76
kPKugTANBgkqhkiG9w0BAQsFAAOCAgEAf+D+hKfs32KlzTzB5kKxMRLwudqnnj+9
llK2/FV0ZD7k/36q9z4GGF9zhfjI4GcbTZfKBdA3BzNkm+Z4dxSaVbsqrMN/yRUI
g1zIwmHTcUwFCyvLo4dtoDLtsLMnl0pVjQEqMFZoq/LaXBBzyaoKnEtMoFtRbgp+
bFOAsADhHppMCjeeIIm8xeV5WLdF/9PEof3ZeD1FFnTfgkQdHYFQWrkyTOJPPw46
ZVpkgzspMcSZiLzFhDnyGRLhZtDq+3Wx0ie+kVmjKwnVXL9GjtZn1gvs2qvwgBmH
ZAepd7FeDOLFHWqsXSPzMHU2TsrDTrBNjCzOUmFj3tX17+8KayMlJjw68sPCFhk/
qTK6aPnJEjw+xh//m070kLBj9dEzADBa6CT6NUSbaoDzpsx7PHNfUMQwcdh0kCcK
AU6lXrH42sJhgRGuKaOP+n5MTmKxAN6S449qLtrZOF1rfA3kAarIxm2LzcDIbuRX
IYr2RjDZrVGhh5amU8kexrvD61X+jNZc1cbzyrBg0tQqH4iU00wa2gyU/sFdDSrb
mSld9t0WxMhNdJ6A2dCq7XvjMORH2PUVwXG4xv3u/J6yX7W3ku3/yjf2x4K0VBOb
g82Hi35k9i5UOiKxxcH0pSVTmk2oD+c1S4nfGYNmZNnb0WErJBsdRET7STCHt0kj
CAKK4CXz9EM=
-----END CERTIFICATE-----
+28
View File
@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDsZY/pEsIaW+ZW
KgipgjotRHijADuwn+cnEECT7/HMPqCqBKKAGxOp5v6B1nCQqNjU3jDYNQDSvmLw
SNr8FY3Exm0LmfErgwAK0yojC+XN+TXfQ2EVcq2VmPZzIUFeoN1HJ6DVmtRBqBwd
VyBxF4/3KJ4+B87s1Q5CTx50R45HndIUKCcsFBD10Za1k3SE7/kE3o1Kb993q+rR
WNNE/loEAf8Gepf3/eNXSOHw30ATn2YjWuNVVD1UOe4A+RLx0t90LrrX8I3G3RhY
HJMiC3X6qNbgtS8tudT+uU+G4nVIFmD7P8m0MEIp+zuzK7lZgWpG80WDv/3VGv83
DG9b/WHxAgMBAAECggEBAIOdaCpUD02trOh8LqZxowJhBOl7z7/ex0uweMPk67LT
i5AdVHwOlzwZJ8oSIknoOBEMRBWcLQEojt1JMuL2/R95emzjIKshHHzqZKNulFvB
TIUpdnwChTKtH0mqUkLlPU3Ienty4IpNlpmfUKimfbkWHERdBJBHbtDsTABhdo3X
9pCF/yRKqJS2Fy/Mkl3gv1y/NB1OL4Jhl7vQbf+kmgfQN2qdOVe2BOKQ8NlPUDmE
/1XNIDaE3s6uvUaoFfwowzsCCwN2/8QrRMMKkjvV+lEVtNmQdYxj5Xj5IwS0vkK0
6icsngW87cpZxxc1zsRWcSTloy5ohub4FgKhlolmigECgYEA+cBlxzLvaMzMlBQY
kCac9KQMvVL+DIFHlZA5i5L/9pRVp4JJwj3GUoehFJoFhsxnKr8HZyLwBKlCmUVm
VxnshRWiAU18emUmeAtSGawlAS3QXhikVZDdd/L20YusLT+DXV81wlKR97/r9+17
klQOLkSdPm9wcMDOWMNHX8bUg8kCgYEA8k+hQv6+TR/+Beao2IIctFtw/EauaJiJ
wW5ql1cpCLPMAOQUvjs0Km3zqctfBF8mUjdkcyJ4uhL9FZtfywY22EtRIXOJ/8VR
we65mVo6RLR8YVM54sihanuFOnlyF9LIBWB+9pUfh1/Y7DSebh7W73uxhAxQhi3Y
QwfIQIFd8OkCgYBalH4VXhLYhpaYCiXSej6ot6rrK2N6c5Tb2MAWMA1nh+r84tMP
gMoh+pDgYPAqMI4mQbxUmqZEeoLuBe6VHpDav7rPECRaW781AJ4ZM4cEQ3Jz/inz
4qOAMn10CF081/Ez9ykPPlU0bsYNWHNd4eB2xWnmUBKOwk7UgJatVPaUiQKBgQCI
f18CVGpzG9CHFnaK8FCnMNOm6VIaTcNcGY0mD81nv5Dt943P054BQMsAHTY7SjZW
HioRyZtkhonXAB2oSqnekh7zzxgv4sG5k3ct8evdBCcE1FNJc2eqikZ0uDETRoOy
s7cRxNNr+QxDkyikM+80HOPU1PMPgwfOSrX90GJQ8QKBgEBKohGMV/sNa4t14Iau
qO8aagoqh/68K9GFXljsl3/iCSa964HIEREtW09Qz1w3dotEgp2w8bsDa+OwWrLy
0SY7T5jRViM3cDWRlUBLrGGiL0FiwsfqiRiji60y19erJgrgyGVIb1kIgIBRkgFM
2MMweASzTmZcri4PA/5C0HYb
-----END PRIVATE KEY-----
+47
View File
@@ -0,0 +1,47 @@
client
dev tun
proto udp
remote my-server.com 1194
resolv-retry infinite
nobind
auth-user-pass
cipher AES-256-CBC
comp-lzo
verb 3
<ca>
-----BEGIN CERTIFICATE-----
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
-----END CERTIFICATE-----
</ca>