mirror of
https://github.com/jkcoxson/LocalDevVPN.git
synced 2026-03-02 06:26:16 +01:00
first commit
This commit is contained in:
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# StosVPN
|
||||
|
||||
A VPN for SideStore and StikJIT that is much stabler and supports offline JIT Enabling.
|
||||
521
StosVPN.xcodeproj/project.pbxproj
Normal file
521
StosVPN.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,521 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 70;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
4EB3C7712D96715400C1B22C /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EB3C7702D96715400C1B22C /* NetworkExtension.framework */; };
|
||||
4EB3C7792D96715400C1B22C /* TunnelProv.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 4EB3C76E2D96715400C1B22C /* TunnelProv.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
4EB3C7772D96715400C1B22C /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 4EB3C7502D96631A00C1B22C /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 4EB3C76D2D96715400C1B22C;
|
||||
remoteInfo = TunnelProv;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
4EB3C77E2D96715400C1B22C /* Embed Foundation Extensions */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "";
|
||||
dstSubfolderSpec = 13;
|
||||
files = (
|
||||
4EB3C7792D96715400C1B22C /* TunnelProv.appex in Embed Foundation Extensions */,
|
||||
);
|
||||
name = "Embed Foundation Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
4EB3C7582D96631A00C1B22C /* StosVPN.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StosVPN.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4EB3C76E2D96715400C1B22C /* TunnelProv.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TunnelProv.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
4EB3C7702D96715400C1B22C /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
4EB3C77A2D96715400C1B22C /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 4EB3C76D2D96715400C1B22C /* TunnelProv */;
|
||||
};
|
||||
4EB3C7802D9672DE00C1B22C /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
);
|
||||
target = 4EB3C7572D96631A00C1B22C /* StosVPN */;
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
4EB3C75A2D96631A00C1B22C /* StosVPN */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4EB3C7802D9672DE00C1B22C /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = StosVPN; sourceTree = "<group>"; };
|
||||
4EB3C7722D96715400C1B22C /* TunnelProv */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4EB3C77A2D96715400C1B22C /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = TunnelProv; sourceTree = "<group>"; };
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
4EB3C7552D96631A00C1B22C /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EB3C76B2D96715400C1B22C /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
4EB3C7712D96715400C1B22C /* NetworkExtension.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
4EB3C74F2D96631A00C1B22C = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EB3C75A2D96631A00C1B22C /* StosVPN */,
|
||||
4EB3C7722D96715400C1B22C /* TunnelProv */,
|
||||
4EB3C76F2D96715400C1B22C /* Frameworks */,
|
||||
4EB3C7592D96631A00C1B22C /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4EB3C7592D96631A00C1B22C /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EB3C7582D96631A00C1B22C /* StosVPN.app */,
|
||||
4EB3C76E2D96715400C1B22C /* TunnelProv.appex */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4EB3C76F2D96715400C1B22C /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4EB3C7702D96715400C1B22C /* NetworkExtension.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
4EB3C7572D96631A00C1B22C /* StosVPN */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 4EB3C7662D96631B00C1B22C /* Build configuration list for PBXNativeTarget "StosVPN" */;
|
||||
buildPhases = (
|
||||
4EB3C7542D96631A00C1B22C /* Sources */,
|
||||
4EB3C7552D96631A00C1B22C /* Frameworks */,
|
||||
4EB3C7562D96631A00C1B22C /* Resources */,
|
||||
4EB3C77E2D96715400C1B22C /* Embed Foundation Extensions */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
4EB3C7782D96715400C1B22C /* PBXTargetDependency */,
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
4EB3C75A2D96631A00C1B22C /* StosVPN */,
|
||||
);
|
||||
name = StosVPN;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = StosVPN;
|
||||
productReference = 4EB3C7582D96631A00C1B22C /* StosVPN.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
4EB3C76D2D96715400C1B22C /* TunnelProv */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 4EB3C77B2D96715400C1B22C /* Build configuration list for PBXNativeTarget "TunnelProv" */;
|
||||
buildPhases = (
|
||||
4EB3C76A2D96715400C1B22C /* Sources */,
|
||||
4EB3C76B2D96715400C1B22C /* Frameworks */,
|
||||
4EB3C76C2D96715400C1B22C /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
4EB3C7722D96715400C1B22C /* TunnelProv */,
|
||||
);
|
||||
name = TunnelProv;
|
||||
packageProductDependencies = (
|
||||
);
|
||||
productName = TunnelProv;
|
||||
productReference = 4EB3C76E2D96715400C1B22C /* TunnelProv.appex */;
|
||||
productType = "com.apple.product-type.app-extension";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
4EB3C7502D96631A00C1B22C /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
BuildIndependentTargetsInParallel = 1;
|
||||
LastSwiftUpdateCheck = 1620;
|
||||
LastUpgradeCheck = 1620;
|
||||
ORGANIZATIONNAME = stossy11;
|
||||
TargetAttributes = {
|
||||
4EB3C7572D96631A00C1B22C = {
|
||||
CreatedOnToolsVersion = 16.2;
|
||||
LastSwiftMigration = 1620;
|
||||
};
|
||||
4EB3C76D2D96715400C1B22C = {
|
||||
CreatedOnToolsVersion = 16.2;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 4EB3C7532D96631A00C1B22C /* Build configuration list for PBXProject "StosVPN" */;
|
||||
compatibilityVersion = "Xcode 14.0";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 4EB3C74F2D96631A00C1B22C;
|
||||
minimizedProjectReferenceProxies = 1;
|
||||
productRefGroup = 4EB3C7592D96631A00C1B22C /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
4EB3C7572D96631A00C1B22C /* StosVPN */,
|
||||
4EB3C76D2D96715400C1B22C /* TunnelProv */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
4EB3C7562D96631A00C1B22C /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EB3C76C2D96715400C1B22C /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
4EB3C7542D96631A00C1B22C /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
4EB3C76A2D96715400C1B22C /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
4EB3C7782D96715400C1B22C /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 4EB3C76D2D96715400C1B22C /* TunnelProv */;
|
||||
targetProxy = 4EB3C7772D96715400C1B22C /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
4EB3C7642D96631B00C1B22C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4EB3C7652D96631B00C1B22C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 18.2;
|
||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
4EB3C7672D96631B00C1B22C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = StosVPN/StosVPN.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"StosVPN/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = StosVPN/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4EB3C7682D96631B00C1B22C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = StosVPN/StosVPN.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_ASSET_PATHS = "\"StosVPN/Preview Content\"";
|
||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = StosVPN/Info.plist;
|
||||
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
|
||||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
4EB3C77C2D96715400C1B22C /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = TunnelProv/TunnelProv.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = TunnelProv/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = TunnelProv;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN.TunnelProv;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
4EB3C77D2D96715400C1B22C /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_ENTITLEMENTS = TunnelProv/TunnelProv.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 95J8WZ4TN8;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = TunnelProv/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = TunnelProv;
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.StosVPN.TunnelProv;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SKIP_INSTALL = YES;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "StosVPN/StosVPN-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
4EB3C7532D96631A00C1B22C /* Build configuration list for PBXProject "StosVPN" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4EB3C7642D96631B00C1B22C /* Debug */,
|
||||
4EB3C7652D96631B00C1B22C /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
4EB3C7662D96631B00C1B22C /* Build configuration list for PBXNativeTarget "StosVPN" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4EB3C7672D96631B00C1B22C /* Debug */,
|
||||
4EB3C7682D96631B00C1B22C /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
4EB3C77B2D96715400C1B22C /* Build configuration list for PBXNativeTarget "TunnelProv" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
4EB3C77C2D96715400C1B22C /* Debug */,
|
||||
4EB3C77D2D96715400C1B22C /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 4EB3C7502D96631A00C1B22C /* Project object */;
|
||||
}
|
||||
7
StosVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
7
StosVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
BIN
StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
BIN
StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate
generated
Normal file
Binary file not shown.
78
StosVPN.xcodeproj/xcshareddata/xcschemes/StosVPN.xcscheme
Normal file
78
StosVPN.xcodeproj/xcshareddata/xcschemes/StosVPN.xcscheme
Normal file
@@ -0,0 +1,78 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1620"
|
||||
version = "1.7">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB3C7572D96631A00C1B22C"
|
||||
BuildableName = "StosVPN.app"
|
||||
BlueprintName = "StosVPN"
|
||||
ReferencedContainer = "container:StosVPN.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB3C7572D96631A00C1B22C"
|
||||
BuildableName = "StosVPN.app"
|
||||
BlueprintName = "StosVPN"
|
||||
ReferencedContainer = "container:StosVPN.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB3C7572D96631A00C1B22C"
|
||||
BuildableName = "StosVPN.app"
|
||||
BlueprintName = "StosVPN"
|
||||
ReferencedContainer = "container:StosVPN.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
97
StosVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme
Normal file
97
StosVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme
Normal file
@@ -0,0 +1,97 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1620"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES"
|
||||
buildArchitectures = "Automatic">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB3C76D2D96715400C1B22C"
|
||||
BuildableName = "TunnelProv.appex"
|
||||
BlueprintName = "TunnelProv"
|
||||
ReferencedContainer = "container:StosVPN.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB3C7572D96631A00C1B22C"
|
||||
BuildableName = "StosVPN.app"
|
||||
BlueprintName = "StosVPN"
|
||||
ReferencedContainer = "container:StosVPN.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
shouldAutocreateTestPlan = "YES">
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB3C7572D96631A00C1B22C"
|
||||
BuildableName = "StosVPN.app"
|
||||
BlueprintName = "StosVPN"
|
||||
ReferencedContainer = "container:StosVPN.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
askForAppToLaunch = "Yes"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB3C7572D96631A00C1B22C"
|
||||
BuildableName = "StosVPN.app"
|
||||
BlueprintName = "StosVPN"
|
||||
ReferencedContainer = "container:StosVPN.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Bucket
|
||||
uuid = "E9FBFA8B-D737-454C-9876-DFFB697E8C2D"
|
||||
type = "1"
|
||||
version = "2.0">
|
||||
</Bucket>
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>StosVPN.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>TunnelProv.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>4EB3C7572D96631A00C1B22C</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>4EB3C76D2D96715400C1B22C</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
BIN
StosVPN/Assets.xcassets/.DS_Store
vendored
Normal file
BIN
StosVPN/Assets.xcassets/.DS_Store
vendored
Normal file
Binary file not shown.
11
StosVPN/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
11
StosVPN/Assets.xcassets/AccentColor.colorset/Contents.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
36
StosVPN/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
36
StosVPN/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "StosVPN-icon.jpg",
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "tinted"
|
||||
}
|
||||
],
|
||||
"idiom" : "universal",
|
||||
"platform" : "ios",
|
||||
"size" : "1024x1024"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
StosVPN/Assets.xcassets/AppIcon.appiconset/StosVPN-icon.jpg
Normal file
BIN
StosVPN/Assets.xcassets/AppIcon.appiconset/StosVPN-icon.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
6
StosVPN/Assets.xcassets/Contents.json
Normal file
6
StosVPN/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
732
StosVPN/ContentView.swift
Normal file
732
StosVPN/ContentView.swift
Normal file
@@ -0,0 +1,732 @@
|
||||
//
|
||||
// ContentView.swift
|
||||
// StosVPN
|
||||
//
|
||||
// Created by Stossy11 on 28/03/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import Foundation
|
||||
import NetworkExtension
|
||||
|
||||
|
||||
// MARK: - Logging Utility
|
||||
class VPNLogger: ObservableObject {
|
||||
@Published var logs: [String] = []
|
||||
|
||||
static var shared = VPNLogger()
|
||||
|
||||
private init() {}
|
||||
|
||||
func log(_ message: Any, file: String = #file, function: String = #function, line: Int = #line) {
|
||||
#if DEBUG
|
||||
let fileName = (file as NSString).lastPathComponent
|
||||
print("[\(fileName):\(line)] \(function): \(message)")
|
||||
#endif
|
||||
|
||||
logs.append("\(message)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Tunnel Manager
|
||||
class TunnelManager: ObservableObject {
|
||||
@Published var hasLocalDeviceSupport = false
|
||||
@Published var tunnelStatus: TunnelStatus = .disconnected
|
||||
|
||||
static var shared = TunnelManager()
|
||||
|
||||
private var vpnManager: NETunnelProviderManager?
|
||||
private var vpnObserver: NSObjectProtocol?
|
||||
|
||||
private var tunnelDeviceIp: String {
|
||||
UserDefaults.standard.string(forKey: "TunnelDeviceIP") ?? "10.7.0.0"
|
||||
}
|
||||
|
||||
private var tunnelFakeIp: String {
|
||||
UserDefaults.standard.string(forKey: "TunnelFakeIP") ?? "10.7.0.1"
|
||||
}
|
||||
|
||||
private var tunnelSubnetMask: String {
|
||||
UserDefaults.standard.string(forKey: "TunnelSubnetMask") ?? "255.255.255.0"
|
||||
}
|
||||
|
||||
private var tunnelBundleId: String {
|
||||
Bundle.main.bundleIdentifier!.appending(".TunnelProv")
|
||||
}
|
||||
|
||||
enum TunnelStatus: String {
|
||||
case disconnected = "Disconnected"
|
||||
case connecting = "Connecting"
|
||||
case connected = "Connected"
|
||||
case disconnecting = "Disconnecting"
|
||||
case error = "Error"
|
||||
|
||||
var color: Color {
|
||||
switch self {
|
||||
case .disconnected: return .gray
|
||||
case .connecting: return .orange
|
||||
case .connected: return .green
|
||||
case .disconnecting: return .orange
|
||||
case .error: return .red
|
||||
}
|
||||
}
|
||||
|
||||
var systemImage: String {
|
||||
switch self {
|
||||
case .disconnected: return "network.slash"
|
||||
case .connecting: return "network.badge.shield.half.filled"
|
||||
case .connected: return "checkmark.shield.fill"
|
||||
case .disconnecting: return "network.badge.shield.half.filled"
|
||||
case .error: return "exclamationmark.shield.fill"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private init() {
|
||||
loadTunnelPreferences()
|
||||
setupStatusObserver()
|
||||
}
|
||||
|
||||
// MARK: - Private Methods
|
||||
private func loadTunnelPreferences() {
|
||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] (managers, error) in
|
||||
guard let self = self else { return }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
|
||||
if let error = error {
|
||||
VPNLogger.shared.log("Error loading preferences: \(error.localizedDescription)")
|
||||
self.tunnelStatus = .error
|
||||
return
|
||||
}
|
||||
|
||||
self.hasLocalDeviceSupport = true
|
||||
|
||||
if let managers = managers, !managers.isEmpty {
|
||||
for manager in managers {
|
||||
if let proto = manager.protocolConfiguration as? NETunnelProviderProtocol,
|
||||
proto.providerBundleIdentifier == self.tunnelBundleId {
|
||||
self.vpnManager = manager
|
||||
self.updateTunnelStatus(from: manager.connection.status)
|
||||
VPNLogger.shared.log("Loaded existing tunnel configuration")
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't find a matching manager, use the first one
|
||||
if self.vpnManager == nil, let firstManager = managers.first {
|
||||
self.vpnManager = firstManager
|
||||
self.updateTunnelStatus(from: firstManager.connection.status)
|
||||
VPNLogger.shared.log("Using existing tunnel configuration")
|
||||
}
|
||||
} else {
|
||||
VPNLogger.shared.log("No existing tunnel configuration found")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func setupStatusObserver() {
|
||||
NotificationCenter.default.addObserver(
|
||||
forName: .NEVPNStatusDidChange,
|
||||
object: nil,
|
||||
queue: .main
|
||||
) { [weak self] notification in
|
||||
guard let self = self,
|
||||
let connection = notification.object as? NEVPNConnection else {
|
||||
return
|
||||
}
|
||||
|
||||
self.updateTunnelStatus(from: connection.status)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateTunnelStatus(from connectionStatus: NEVPNStatus) {
|
||||
DispatchQueue.main.async {
|
||||
switch connectionStatus {
|
||||
case .invalid, .disconnected:
|
||||
self.tunnelStatus = .disconnected
|
||||
case .connecting:
|
||||
self.tunnelStatus = .connecting
|
||||
case .connected:
|
||||
self.tunnelStatus = .connected
|
||||
case .disconnecting:
|
||||
self.tunnelStatus = .disconnecting
|
||||
case .reasserting:
|
||||
self.tunnelStatus = .connecting
|
||||
@unknown default:
|
||||
self.tunnelStatus = .error
|
||||
}
|
||||
|
||||
VPNLogger.shared.log("VPN status updated: \(self.tunnelStatus.rawValue)")
|
||||
}
|
||||
}
|
||||
|
||||
private func createOrUpdateTunnelConfiguration(completion: @escaping (Bool) -> Void) {
|
||||
// First check if we already have configurations
|
||||
NETunnelProviderManager.loadAllFromPreferences { [weak self] (managers, error) in
|
||||
guard let self = self else { return completion(false) }
|
||||
|
||||
if let error = error {
|
||||
VPNLogger.shared.log("Error loading preferences: \(error.localizedDescription)")
|
||||
return completion(false)
|
||||
}
|
||||
|
||||
let manager: NETunnelProviderManager
|
||||
if let existingManagers = managers, !existingManagers.isEmpty {
|
||||
if let matchingManager = existingManagers.first(where: {
|
||||
($0.protocolConfiguration as? NETunnelProviderProtocol)?.providerBundleIdentifier == self.tunnelBundleId
|
||||
}) {
|
||||
manager = matchingManager
|
||||
VPNLogger.shared.log("Updating existing tunnel configuration")
|
||||
} else {
|
||||
manager = existingManagers[0]
|
||||
VPNLogger.shared.log("Using first available tunnel configuration")
|
||||
}
|
||||
} else {
|
||||
// Create a new manager if none exists
|
||||
manager = NETunnelProviderManager()
|
||||
VPNLogger.shared.log("Creating new tunnel configuration")
|
||||
}
|
||||
|
||||
manager.localizedDescription = "StosVPN"
|
||||
|
||||
let proto = NETunnelProviderProtocol()
|
||||
proto.providerBundleIdentifier = self.tunnelBundleId
|
||||
proto.serverAddress = "StosVPN's Local Network Tunnel"
|
||||
manager.protocolConfiguration = proto
|
||||
|
||||
let onDemandRule = NEOnDemandRuleEvaluateConnection()
|
||||
onDemandRule.interfaceTypeMatch = .any
|
||||
onDemandRule.connectionRules = [NEEvaluateConnectionRule(
|
||||
matchDomains: ["localhost"],
|
||||
andAction: .connectIfNeeded
|
||||
)]
|
||||
|
||||
manager.onDemandRules = [onDemandRule]
|
||||
manager.isOnDemandEnabled = true
|
||||
manager.isEnabled = true
|
||||
|
||||
manager.saveToPreferences { [weak self] error in
|
||||
guard let self = self else { return completion(false) }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if let error = error {
|
||||
VPNLogger.shared.log("Error saving tunnel configuration: \(error.localizedDescription)")
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
|
||||
self.vpnManager = manager
|
||||
VPNLogger.shared.log("Tunnel configuration saved successfully")
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public Methods
|
||||
|
||||
func toggleVPNConnection() {
|
||||
if tunnelStatus == .connected || tunnelStatus == .connecting {
|
||||
stopVPN()
|
||||
} else {
|
||||
startVPN()
|
||||
}
|
||||
}
|
||||
|
||||
func startVPN() {
|
||||
if let manager = vpnManager {
|
||||
startExistingVPN(manager: manager)
|
||||
} else {
|
||||
createOrUpdateTunnelConfiguration { [weak self] success in
|
||||
guard let self = self, success else { return }
|
||||
self.loadTunnelPreferences()
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
if let manager = self.vpnManager {
|
||||
self.startExistingVPN(manager: manager)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func startExistingVPN(manager: NETunnelProviderManager) {
|
||||
guard tunnelStatus != .connected else {
|
||||
VPNLogger.shared.log("Network tunnel is already connected")
|
||||
return
|
||||
}
|
||||
|
||||
tunnelStatus = .connecting
|
||||
|
||||
let options: [String: NSObject] = [
|
||||
"TunnelDeviceIP": tunnelDeviceIp as NSObject,
|
||||
"TunnelFakeIP": tunnelFakeIp as NSObject,
|
||||
"TunnelSubnetMask": tunnelSubnetMask as NSObject
|
||||
]
|
||||
|
||||
do {
|
||||
try manager.connection.startVPNTunnel(options: options)
|
||||
VPNLogger.shared.log("Network tunnel start initiated")
|
||||
} catch {
|
||||
tunnelStatus = .error
|
||||
VPNLogger.shared.log("Failed to start tunnel: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
func stopVPN() {
|
||||
guard let manager = vpnManager else { return }
|
||||
|
||||
tunnelStatus = .disconnecting
|
||||
manager.connection.stopVPNTunnel()
|
||||
VPNLogger.shared.log("Network tunnel stop initiated")
|
||||
}
|
||||
|
||||
deinit {
|
||||
if let observer = vpnObserver {
|
||||
NotificationCenter.default.removeObserver(observer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Views
|
||||
|
||||
struct ContentView: View {
|
||||
@StateObject private var tunnelManager = TunnelManager.shared
|
||||
@State private var showSettings = false
|
||||
@State var tunnel = false
|
||||
@AppStorage("autoConnect") private var autoConnect = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
VStack(spacing: 30) {
|
||||
Spacer()
|
||||
|
||||
StatusIndicatorView()
|
||||
|
||||
ConnectionButton(
|
||||
action: {
|
||||
tunnelManager.tunnelStatus == .connected ? tunnelManager.stopVPN() : tunnelManager.startVPN()
|
||||
}
|
||||
)
|
||||
|
||||
Spacer()
|
||||
|
||||
if tunnelManager.tunnelStatus == .connected {
|
||||
ConnectionStatsView()
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("StosVPN")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
showSettings = true
|
||||
} label: {
|
||||
Image(systemName: "gear")
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear() {
|
||||
if tunnelManager.tunnelStatus != .connected && autoConnect {
|
||||
tunnelManager.startVPN()
|
||||
}
|
||||
}
|
||||
|
||||
.sheet(isPresented: $showSettings) {
|
||||
SettingsView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct StatusIndicatorView: View {
|
||||
@StateObject private var tunnelManager = TunnelManager.shared
|
||||
@State private var animationAmount = 1.0
|
||||
@State private var isAnimating = false
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 20) {
|
||||
ZStack {
|
||||
Circle()
|
||||
.stroke(tunnelManager.tunnelStatus.color.opacity(0.2), lineWidth: 20)
|
||||
.frame(width: 200, height: 200)
|
||||
|
||||
Circle()
|
||||
.stroke(tunnelManager.tunnelStatus.color, lineWidth: 10)
|
||||
.frame(width: 200, height: 200)
|
||||
.scaleEffect(animationAmount)
|
||||
.opacity(2 - animationAmount)
|
||||
.animation(isAnimating ? Animation.easeOut(duration: 1.5).repeatForever(autoreverses: false) : .default, value: animationAmount)
|
||||
|
||||
VStack(spacing: 10) {
|
||||
Image(systemName: tunnelManager.tunnelStatus.systemImage)
|
||||
.font(.system(size: 50))
|
||||
.foregroundColor(tunnelManager.tunnelStatus.color)
|
||||
|
||||
Text(tunnelManager.tunnelStatus.rawValue)
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
updateAnimation()
|
||||
}
|
||||
.onChange(of: tunnelManager.tunnelStatus) { _ in
|
||||
updateAnimation()
|
||||
}
|
||||
|
||||
Text(tunnelManager.tunnelStatus == .connected ? "Tunnel active" : "Tunnel inactive")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(tunnelManager.tunnelStatus == .connected ? .green : .secondary)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateAnimation() {
|
||||
switch tunnelManager.tunnelStatus {
|
||||
case .disconnecting:
|
||||
isAnimating = false
|
||||
withAnimation {
|
||||
animationAmount = 1.0
|
||||
}
|
||||
case .disconnected:
|
||||
isAnimating = false
|
||||
animationAmount = 1.0
|
||||
default:
|
||||
isAnimating = true
|
||||
animationAmount = 1.0
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
withAnimation {
|
||||
animationAmount = 2.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ConnectionButton: View {
|
||||
@StateObject private var tunnelManager = TunnelManager.shared
|
||||
let action: () -> Void
|
||||
|
||||
var body: some View {
|
||||
Button(action: action) {
|
||||
HStack {
|
||||
Text(buttonText)
|
||||
.font(.headline)
|
||||
.fontWeight(.semibold)
|
||||
|
||||
if tunnelManager.tunnelStatus == .connecting || tunnelManager.tunnelStatus == .disconnecting {
|
||||
ProgressView()
|
||||
.progressViewStyle(CircularProgressViewStyle())
|
||||
.padding(.leading, 5)
|
||||
}
|
||||
}
|
||||
.frame(width: 200, height: 50)
|
||||
.background(buttonBackground)
|
||||
.foregroundColor(.white)
|
||||
.clipShape(Capsule())
|
||||
.shadow(color: Color.black.opacity(0.15), radius: 10, x: 0, y: 5)
|
||||
}
|
||||
.disabled(tunnelManager.tunnelStatus == .connecting || tunnelManager.tunnelStatus == .disconnecting)
|
||||
}
|
||||
|
||||
private var buttonText: String {
|
||||
if tunnelManager.tunnelStatus == .connected {
|
||||
return "Disconnect"
|
||||
} else if tunnelManager.tunnelStatus == .connecting {
|
||||
return "Connecting..."
|
||||
} else if tunnelManager.tunnelStatus == .disconnecting {
|
||||
return "Disconnecting..."
|
||||
} else {
|
||||
return "Connect"
|
||||
}
|
||||
}
|
||||
|
||||
private var buttonBackground: some View {
|
||||
Group {
|
||||
if tunnelManager.tunnelStatus == .connected {
|
||||
LinearGradient(
|
||||
gradient: Gradient(colors: [Color.red.opacity(0.8), Color.red]),
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
} else {
|
||||
LinearGradient(
|
||||
gradient: Gradient(colors: [Color.blue.opacity(0.8), Color.blue]),
|
||||
startPoint: .leading,
|
||||
endPoint: .trailing
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionStatsView: View {
|
||||
@State private var time = 0
|
||||
let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 25) {
|
||||
Text("Connection Details")
|
||||
.font(.headline)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
HStack(spacing: 30) {
|
||||
StatItemView(
|
||||
title: "Time Connected",
|
||||
value: formattedTime,
|
||||
icon: "clock.fill"
|
||||
)
|
||||
|
||||
StatItemView(
|
||||
title: "Status",
|
||||
value: "Active",
|
||||
icon: "checkmark.circle.fill"
|
||||
)
|
||||
}
|
||||
|
||||
HStack(spacing: 30) {
|
||||
StatItemView(
|
||||
title: "Network Interface",
|
||||
value: "Local",
|
||||
icon: "network"
|
||||
)
|
||||
|
||||
StatItemView(
|
||||
title: "Assigned IP",
|
||||
value: "10.7.0.1",
|
||||
icon: "number"
|
||||
)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.fill(Color(UIColor.darkGray))
|
||||
.shadow(color: Color.black.opacity(0.05), radius: 10, x: 0, y: 5)
|
||||
)
|
||||
.onReceive(timer) { _ in
|
||||
time += 1
|
||||
}
|
||||
}
|
||||
|
||||
var formattedTime: String {
|
||||
let minutes = (time / 60) % 60
|
||||
let hours = time / 3600
|
||||
let seconds = time % 60
|
||||
|
||||
if hours > 0 {
|
||||
return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
|
||||
} else {
|
||||
return String(format: "%02d:%02d", minutes, seconds)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct StatItemView: View {
|
||||
let title: String
|
||||
let value: String
|
||||
let icon: String
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
HStack {
|
||||
Image(systemName: icon)
|
||||
.foregroundColor(.blue)
|
||||
|
||||
Text(title)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Text(value)
|
||||
.font(.system(size: 16, weight: .semibold))
|
||||
.foregroundColor(.primary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
struct SettingsView: View {
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@AppStorage("TunnelDeviceIP") private var deviceIP = "10.7.0.0"
|
||||
@AppStorage("TunnelFakeIP") private var fakeIP = "10.7.0.1"
|
||||
@AppStorage("TunnelSubnetMask") private var subnetMask = "255.255.255.0"
|
||||
@AppStorage("autoConnect") private var autoConnect = false
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List {
|
||||
Section(header: Text("Connection Settings")) {
|
||||
Toggle("Auto-connect on Launch", isOn: $autoConnect)
|
||||
|
||||
NavigationLink(destination: ConnectionLogView()) {
|
||||
Label("Connection Logs", systemImage: "doc.text")
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("Network Configuration")) {
|
||||
HStack {
|
||||
Text("Device IP")
|
||||
Spacer()
|
||||
TextField("Device IP", text: $deviceIP)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.foregroundColor(.secondary)
|
||||
.keyboardType(.numbersAndPunctuation)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Tunnel IP")
|
||||
Spacer()
|
||||
TextField("Tunnel IP", text: $fakeIP)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.foregroundColor(.secondary)
|
||||
.keyboardType(.numbersAndPunctuation)
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Subnet Mask")
|
||||
Spacer()
|
||||
TextField("Subnet Mask", text: $subnetMask)
|
||||
.multilineTextAlignment(.trailing)
|
||||
.foregroundColor(.secondary)
|
||||
.keyboardType(.numbersAndPunctuation)
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("About")) {
|
||||
HStack {
|
||||
Text("App Version")
|
||||
Spacer()
|
||||
Text("1.0.0")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
NavigationLink(destination: PrivacyPolicyView()) {
|
||||
Text("Privacy Policy")
|
||||
}
|
||||
|
||||
NavigationLink(destination: HelpView()) {
|
||||
Text("Help & Support")
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button("Done") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct ConnectionLogView: View {
|
||||
@StateObject var logger = VPNLogger.shared
|
||||
var body: some View {
|
||||
List(logger.logs, id: \.self) { log in
|
||||
Text(log)
|
||||
.font(.system(.body, design: .monospaced))
|
||||
}
|
||||
.navigationTitle("Logs")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
struct PrivacyPolicyView: View {
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 15) {
|
||||
Text("Privacy Policy")
|
||||
.font(.title)
|
||||
.fontWeight(.bold)
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Text("Your privacy is important to us. This application is designed to create a local network interface for development and testing purposes.")
|
||||
|
||||
Text("Data Collection")
|
||||
.font(.headline)
|
||||
.padding(.top, 10)
|
||||
|
||||
Text("This application does not collect any personal information. All traffic remains on your device.")
|
||||
|
||||
Text("Permissions")
|
||||
.font(.headline)
|
||||
.padding(.top, 10)
|
||||
|
||||
Text("This app requires network extension permissions to create a virtual network interface on your device.")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.navigationTitle("Privacy Policy")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
struct HelpView: View {
|
||||
var body: some View {
|
||||
List {
|
||||
Section(header: Text("Frequently Asked Questions")) {
|
||||
NavigationLink("What does this app do?") {
|
||||
VStack(alignment: .leading, spacing: 15) {
|
||||
Text("This app creates a local network interface that can be used for development and testing purposes.")
|
||||
.padding(.bottom, 10)
|
||||
|
||||
Text("Common use cases include:")
|
||||
.fontWeight(.medium)
|
||||
|
||||
Text("• Testing applications that require specific network configurations")
|
||||
Text("• Development of network-related features")
|
||||
Text("• Isolating network traffic for analysis or debugging")
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
|
||||
NavigationLink("Why does the connection fail?") {
|
||||
|
||||
Text("Why does the connection fail?")
|
||||
.fontWeight(.medium)
|
||||
|
||||
Text("Connection failures could be due to system permission issues, configuration errors, or iOS restrictions. Try restarting the app or checking your settings.")
|
||||
.padding()
|
||||
}
|
||||
|
||||
NavigationLink("What is this app for?") {
|
||||
|
||||
Text("What is this app for?")
|
||||
.fontWeight(.medium)
|
||||
|
||||
Text("This app is for connecting to local Servers on iOS devices to debug or test specific network applications, such as web-servers, or other network-related applications.")
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
Section(header: Text("App Information")) {
|
||||
HStack {
|
||||
Image(systemName: "exclamationmark.shield")
|
||||
Text("Requires iOS 16.0 or later")
|
||||
}
|
||||
|
||||
HStack {
|
||||
Image(systemName: "lock.shield")
|
||||
Text("Uses Apple's Network Extension API")
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Help & Support")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ContentView()
|
||||
}
|
||||
10
StosVPN/Info.plist
Normal file
10
StosVPN/Info.plist
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSBonjourServices</key>
|
||||
<array>
|
||||
<string>_apple-mobdev2._tcp</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
4
StosVPN/StosVPN-Bridging-Header.h
Normal file
4
StosVPN/StosVPN-Bridging-Header.h
Normal file
@@ -0,0 +1,4 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
14
StosVPN/StosVPN.entitlements
Normal file
14
StosVPN/StosVPN.entitlements
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.networking.vpn.api</key>
|
||||
<array>
|
||||
<string>allow-vpn</string>
|
||||
</array>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>packet-tunnel-provider</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
17
StosVPN/StosVPNApp.swift
Normal file
17
StosVPN/StosVPNApp.swift
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// StosVPNApp.swift
|
||||
// StosVPN
|
||||
//
|
||||
// Created by Stossy11 on 28/03/2025.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct StosVPNApp: App {
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
}
|
||||
}
|
||||
}
|
||||
13
TunnelProv/Info.plist
Normal file
13
TunnelProv/Info.plist
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSExtension</key>
|
||||
<dict>
|
||||
<key>NSExtensionPointIdentifier</key>
|
||||
<string>com.apple.networkextension.packet-tunnel</string>
|
||||
<key>NSExtensionPrincipalClass</key>
|
||||
<string>$(PRODUCT_MODULE_NAME).PacketTunnelProvider</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
156
TunnelProv/PacketTunnelProvider.swift
Normal file
156
TunnelProv/PacketTunnelProvider.swift
Normal file
@@ -0,0 +1,156 @@
|
||||
//
|
||||
// PacketTunnelProvider.swift
|
||||
// TunnelProv
|
||||
//
|
||||
// Created by Stossy11 on 28/03/2025.
|
||||
//
|
||||
|
||||
import NetworkExtension
|
||||
|
||||
class PacketTunnelProvider: NEPacketTunnelProvider {
|
||||
var tunnelDeviceIp: String = "10.7.0.0"
|
||||
var tunnelFakeIp: String = "10.7.0.1"
|
||||
var tunnelSubnetMask: String = "255.255.255.0"
|
||||
|
||||
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
|
||||
if let deviceIp = options?["TunnelDeviceIP"] as? String {
|
||||
tunnelDeviceIp = deviceIp
|
||||
}
|
||||
if let fakeIp = options?["TunnelFakeIP"] as? String {
|
||||
tunnelFakeIp = fakeIp
|
||||
}
|
||||
|
||||
let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: tunnelDeviceIp)
|
||||
let ipv4 = NEIPv4Settings(addresses: [tunnelDeviceIp], subnetMasks: [tunnelSubnetMask])
|
||||
ipv4.includedRoutes = [NEIPv4Route(destinationAddress: tunnelDeviceIp, subnetMask: tunnelSubnetMask)]
|
||||
ipv4.excludedRoutes = [.default()]
|
||||
settings.ipv4Settings = ipv4
|
||||
setTunnelNetworkSettings(settings) { error in
|
||||
if error == nil {
|
||||
self.readPackets()
|
||||
}
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
|
||||
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
||||
// Add code here to start the process of stopping the tunnel.
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
|
||||
// Add code here to handle the message.
|
||||
if let handler = completionHandler {
|
||||
handler(messageData)
|
||||
}
|
||||
}
|
||||
|
||||
override func sleep(completionHandler: @escaping () -> Void) {
|
||||
completionHandler()
|
||||
}
|
||||
|
||||
override func wake() {
|
||||
// Add code here to wake up.
|
||||
}
|
||||
|
||||
private func readPackets() {
|
||||
packetFlow.readPackets { packets, protocols in
|
||||
var output: [Data] = []
|
||||
for (i, packet) in packets.enumerated() {
|
||||
var modifiedPacket = packet
|
||||
if protocols[i].int32Value == AF_INET {
|
||||
modifiedPacket = self.packetReplaceIp(packet, self.tunnelDeviceIp, self.tunnelFakeIp, self.tunnelFakeIp, self.tunnelDeviceIp)
|
||||
}
|
||||
|
||||
NSLog("wow \(modifiedPacket)")
|
||||
|
||||
// Ensure packet has at least 20 bytes before swapping
|
||||
if modifiedPacket.count >= 20 {
|
||||
var mutableBytes = [UInt8](modifiedPacket)
|
||||
|
||||
// Swap bytes
|
||||
(mutableBytes[12], mutableBytes[16]) = (mutableBytes[16], mutableBytes[12])
|
||||
(mutableBytes[13], mutableBytes[17]) = (mutableBytes[17], mutableBytes[13])
|
||||
(mutableBytes[14], mutableBytes[18]) = (mutableBytes[18], mutableBytes[14])
|
||||
(mutableBytes[15], mutableBytes[19]) = (mutableBytes[19], mutableBytes[15])
|
||||
|
||||
modifiedPacket = Data(mutableBytes)
|
||||
}
|
||||
|
||||
output.append(modifiedPacket)
|
||||
}
|
||||
self.packetFlow.writePackets(output, withProtocols: protocols)
|
||||
self.readPackets()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func packetReplaceIp(_ data: Data, _ sourceSearch: String, _ sourceReplace: String, _ destSearch: String, _ destReplace: String) -> Data {
|
||||
// Check if packet is too small for IPv4 header
|
||||
if data.count < 20 {
|
||||
return data
|
||||
}
|
||||
|
||||
// Convert IP strings to Data with network byte order (big-endian)
|
||||
func ipToUInt32(_ ipString: String) -> UInt32 {
|
||||
let components = ipString.split(separator: ".")
|
||||
var result: UInt32 = 0
|
||||
|
||||
if components.count == 4,
|
||||
let byte1 = UInt32(components[0]),
|
||||
let byte2 = UInt32(components[1]),
|
||||
let byte3 = UInt32(components[2]),
|
||||
let byte4 = UInt32(components[3]) {
|
||||
result = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Convert IP strings to UInt32
|
||||
let sourceSearchIP = ipToUInt32(sourceSearch)
|
||||
let sourceReplaceIP = ipToUInt32(sourceReplace)
|
||||
let destSearchIP = ipToUInt32(destSearch)
|
||||
let destReplaceIP = ipToUInt32(destReplace)
|
||||
|
||||
// Extract source and destination IPs from packet
|
||||
var sourcePacketIP: UInt32 = 0
|
||||
var destPacketIP: UInt32 = 0
|
||||
|
||||
(data as NSData).getBytes(&sourcePacketIP, range: NSRange(location: 12, length: 4))
|
||||
(data as NSData).getBytes(&destPacketIP, range: NSRange(location: 16, length: 4))
|
||||
|
||||
if sourceSearchIP != sourcePacketIP && destSearchIP != destPacketIP {
|
||||
return data
|
||||
}
|
||||
|
||||
let mutableData = NSMutableData(data: data)
|
||||
|
||||
if sourceSearchIP == sourcePacketIP {
|
||||
var sourceIP = sourceReplaceIP
|
||||
mutableData.replaceBytes(in: NSRange(location: 12, length: 4), withBytes: &sourceIP)
|
||||
}
|
||||
|
||||
if destSearchIP == destPacketIP {
|
||||
var destIP = destReplaceIP
|
||||
mutableData.replaceBytes(in: NSRange(location: 16, length: 4), withBytes: &destIP)
|
||||
}
|
||||
|
||||
return mutableData as Data
|
||||
}
|
||||
|
||||
// Helper function to convert IP string to Data
|
||||
private func ipToData(_ ip: String) -> Data {
|
||||
let components = ip.split(separator: ".")
|
||||
var data = Data(capacity: 4)
|
||||
|
||||
for component in components {
|
||||
if let byte = UInt8(component) {
|
||||
data.append(byte)
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
10
TunnelProv/TunnelProv.entitlements
Normal file
10
TunnelProv/TunnelProv.entitlements
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.developer.networking.networkextension</key>
|
||||
<array>
|
||||
<string>packet-tunnel-provider</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
Reference in New Issue
Block a user