From 3ac7a5332293c43c51cc2fa7cba1b62a8020bfbd Mon Sep 17 00:00:00 2001 From: Stossy11 <69031796+stossy11@users.noreply.github.com> Date: Mon, 31 Mar 2025 13:40:08 +1100 Subject: [PATCH] first commit --- .DS_Store | Bin 0 -> 6148 bytes README.md | 3 + StosVPN.xcodeproj/project.pbxproj | 521 +++++++++++++ .../contents.xcworkspacedata | 7 + .../UserInterfaceState.xcuserstate | Bin 0 -> 41416 bytes .../xcshareddata/xcschemes/StosVPN.xcscheme | 78 ++ .../xcschemes/TunnelProv.xcscheme | 97 +++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 + .../xcschemes/xcschememanagement.plist | 32 + StosVPN/Assets.xcassets/.DS_Store | Bin 0 -> 6148 bytes .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 36 + .../AppIcon.appiconset/StosVPN-icon.jpg | Bin 0 -> 42948 bytes StosVPN/Assets.xcassets/Contents.json | 6 + StosVPN/ContentView.swift | 732 ++++++++++++++++++ StosVPN/Info.plist | 10 + .../Preview Assets.xcassets/Contents.json | 6 + StosVPN/StosVPN-Bridging-Header.h | 4 + StosVPN/StosVPN.entitlements | 14 + StosVPN/StosVPNApp.swift | 17 + TunnelProv/Info.plist | 13 + TunnelProv/PacketTunnelProvider.swift | 156 ++++ TunnelProv/TunnelProv.entitlements | 10 + 23 files changed, 1759 insertions(+) create mode 100644 .DS_Store create mode 100644 README.md create mode 100644 StosVPN.xcodeproj/project.pbxproj create mode 100644 StosVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 StosVPN.xcodeproj/xcshareddata/xcschemes/StosVPN.xcscheme create mode 100644 StosVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme create mode 100644 StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist create mode 100644 StosVPN/Assets.xcassets/.DS_Store create mode 100644 StosVPN/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 StosVPN/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 StosVPN/Assets.xcassets/AppIcon.appiconset/StosVPN-icon.jpg create mode 100644 StosVPN/Assets.xcassets/Contents.json create mode 100644 StosVPN/ContentView.swift create mode 100644 StosVPN/Info.plist create mode 100644 StosVPN/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 StosVPN/StosVPN-Bridging-Header.h create mode 100644 StosVPN/StosVPN.entitlements create mode 100644 StosVPN/StosVPNApp.swift create mode 100644 TunnelProv/Info.plist create mode 100644 TunnelProv/PacketTunnelProvider.swift create mode 100644 TunnelProv/TunnelProv.entitlements diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f91fe845c237f7337b8c5157724ddac371868f93 GIT binary patch literal 6148 zcmeHKO-sW-5PhRXLcP?R$HgBY^ir>}#H&;So~4Z_(vsi@c+CIs|9JC1`DSO81al}J zq{s~HyxE105Ud3QDZ=Kc+j<%f=5J&b6jJAOPt~rtAUAjaY)yG zgc8rF@r3UBYgD*JgGYKE)_2o#Rd1TO1sT6>m$j*jV!5uDjLv3fCszj_@8{cQR=><_ z?z<~p#09%@2AlzBz!`7`It=j6R>=+xT{{ELfHUyTfSwP5O)*c{8pfl8DwY65eMU#2 zt+j;agoJs*){q{GSSry{C6yRr=^PI!E>GARS~?^ZACkWORlG?2I_D2D9FiNlb_Se* zJ_GyKo#_34!Cz*u$nU3k&lzwA{uu)@D#~KcO{Kf_$M*ED4Qw}TDjL_SL80Ay1hAv$ g$Z>RtCC;CGm65`4k_yYz$0g&}MuK)l5 literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..9362e7e --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# StosVPN + +A VPN for SideStore and StikJIT that is much stabler and supports offline JIT Enabling. diff --git a/StosVPN.xcodeproj/project.pbxproj b/StosVPN.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b6c06a7 --- /dev/null +++ b/StosVPN.xcodeproj/project.pbxproj @@ -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 = ""; }; + 4EB3C7722D96715400C1B22C /* TunnelProv */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (4EB3C77A2D96715400C1B22C /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = TunnelProv; sourceTree = ""; }; +/* 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 = ""; + }; + 4EB3C7592D96631A00C1B22C /* Products */ = { + isa = PBXGroup; + children = ( + 4EB3C7582D96631A00C1B22C /* StosVPN.app */, + 4EB3C76E2D96715400C1B22C /* TunnelProv.appex */, + ); + name = Products; + sourceTree = ""; + }; + 4EB3C76F2D96715400C1B22C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4EB3C7702D96715400C1B22C /* NetworkExtension.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* 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 */; +} diff --git a/StosVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/StosVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/StosVPN.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate b/StosVPN.xcodeproj/project.xcworkspace/xcuserdata/stossy11.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..0d8ac2ecb29db363e06f24ff2d8fda443b10eb34 GIT binary patch literal 41416 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�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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StosVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme b/StosVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme new file mode 100644 index 0000000..31c9da5 --- /dev/null +++ b/StosVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..a97bd34 --- /dev/null +++ b/StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist b/StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..ee4d95c --- /dev/null +++ b/StosVPN.xcodeproj/xcuserdata/stossy11.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,32 @@ + + + + + SchemeUserState + + StosVPN.xcscheme_^#shared#^_ + + orderHint + 0 + + TunnelProv.xcscheme_^#shared#^_ + + orderHint + 1 + + + SuppressBuildableAutocreation + + 4EB3C7572D96631A00C1B22C + + primary + + + 4EB3C76D2D96715400C1B22C + + primary + + + + + diff --git a/StosVPN/Assets.xcassets/.DS_Store b/StosVPN/Assets.xcassets/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..194359239234354845def2bdd74ab41cb056041e GIT binary patch literal 6148 zcmeHK%}N6?5T3NvZYe?$1-%8l7OlS^Ue;O|I~T7x8(V zNm8*?!GnmD8JK*N$xK4NESU`eh-SaD2~Yz72bHi;!R8C0andO%SP!AlbKHW07}mju zWGb2+|B(SYyG1y}^Ge_ZKA&F#q3x&8g)A7PNf4<3<6na$3e&97coT(UX>Pvkl%1+` z?cJ+^m-$&gZTh`S>YXVS1!LO}&cp7YU0XU*aps3{x2F@rP8UP2F2cB@22ItEJE_j~ z%z#sID(%|paJad%yI+@kjqOog4)?bjb-A;>HyTx(<+Y82)AmF37^`Q)u)vQ|%Ywx* zyrA);%|3a(I8yN)#*gP3&%wwHFaylM3@~7?Hm5oRd*LlG1I)nhGC=o(L?v`B<_7iE zfermW(s+rG1Z{dt5K4=##oQo{pa_$SXi|lJVhEFtere-ei@8CQ4nnVt^VpSzeW3`w zI{Kv!2jLpzmKk6MCK)K2ZjI{y@%Qil$t3PE1I)loF(67UuhqmY+1fg_IjXf1^#+xM o;&Ou@DQM_YjImUTS5UQ}Uy_07TFeci2ZetGG!5J^1AofEJIW1Hod5s; literal 0 HcmV?d00001 diff --git a/StosVPN/Assets.xcassets/AccentColor.colorset/Contents.json b/StosVPN/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/StosVPN/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StosVPN/Assets.xcassets/AppIcon.appiconset/Contents.json b/StosVPN/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..2c36209 --- /dev/null +++ b/StosVPN/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -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 + } +} diff --git a/StosVPN/Assets.xcassets/AppIcon.appiconset/StosVPN-icon.jpg b/StosVPN/Assets.xcassets/AppIcon.appiconset/StosVPN-icon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9dc50d51d5f78978cf9a8e7e5990e9c8b47357ef GIT binary patch literal 42948 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 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() +} diff --git a/StosVPN/Info.plist b/StosVPN/Info.plist new file mode 100644 index 0000000..4f62b60 --- /dev/null +++ b/StosVPN/Info.plist @@ -0,0 +1,10 @@ + + + + + NSBonjourServices + + _apple-mobdev2._tcp + + + diff --git a/StosVPN/Preview Content/Preview Assets.xcassets/Contents.json b/StosVPN/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/StosVPN/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/StosVPN/StosVPN-Bridging-Header.h b/StosVPN/StosVPN-Bridging-Header.h new file mode 100644 index 0000000..1b2cb5d --- /dev/null +++ b/StosVPN/StosVPN-Bridging-Header.h @@ -0,0 +1,4 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// + diff --git a/StosVPN/StosVPN.entitlements b/StosVPN/StosVPN.entitlements new file mode 100644 index 0000000..dd735cf --- /dev/null +++ b/StosVPN/StosVPN.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.developer.networking.vpn.api + + allow-vpn + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + + diff --git a/StosVPN/StosVPNApp.swift b/StosVPN/StosVPNApp.swift new file mode 100644 index 0000000..507cc9c --- /dev/null +++ b/StosVPN/StosVPNApp.swift @@ -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() + } + } +} diff --git a/TunnelProv/Info.plist b/TunnelProv/Info.plist new file mode 100644 index 0000000..3059459 --- /dev/null +++ b/TunnelProv/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + diff --git a/TunnelProv/PacketTunnelProvider.swift b/TunnelProv/PacketTunnelProvider.swift new file mode 100644 index 0000000..afdd5f5 --- /dev/null +++ b/TunnelProv/PacketTunnelProvider.swift @@ -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 + } +} + diff --git a/TunnelProv/TunnelProv.entitlements b/TunnelProv/TunnelProv.entitlements new file mode 100644 index 0000000..ffab33e --- /dev/null +++ b/TunnelProv/TunnelProv.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + +