first commit

This commit is contained in:
Stossy11
2025-03-31 13:40:08 +11:00
commit 3ac7a53322
23 changed files with 1759 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# StosVPN
A VPN for SideStore and StikJIT that is much stabler and supports offline JIT Enabling.

View 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 */;
}

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View 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>

View 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>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "E9FBFA8B-D737-454C-9876-DFFB697E8C2D"
type = "1"
version = "2.0">
</Bucket>

View File

@@ -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

Binary file not shown.

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View 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
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

732
StosVPN/ContentView.swift Normal file
View 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
View 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>

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,4 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//

View 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
View 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
View 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>

View 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
}
}

View 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>