From d183e2c90b0ddd4d1a70000516a74bd6ec16567a Mon Sep 17 00:00:00 2001 From: sh4tteredd <55893559+sh4tteredd@users.noreply.github.com> Date: Sun, 22 Feb 2026 16:41:02 +0100 Subject: [PATCH 1/4] iOS 26.4b1 patch --- LocalDevVPN.xcodeproj/project.pbxproj | 14 +-- .../UserInterfaceState.xcuserstate | Bin 0 -> 19506 bytes .../xcschemes/TunnelProv.xcscheme | 1 + LocalDevVPN/ContentView.swift | 20 +++- .../Localization/en.lproj/Localizable.strings | 1 + TunnelProv/PacketTunnelProvider.swift | 90 ++++++++++++++++-- 6 files changed, 108 insertions(+), 18 deletions(-) create mode 100644 LocalDevVPN.xcodeproj/project.xcworkspace/xcuserdata/lolo.xcuserdatad/UserInterfaceState.xcuserstate diff --git a/LocalDevVPN.xcodeproj/project.pbxproj b/LocalDevVPN.xcodeproj/project.pbxproj index 6856d8b..2667ec2 100644 --- a/LocalDevVPN.xcodeproj/project.pbxproj +++ b/LocalDevVPN.xcodeproj/project.pbxproj @@ -201,6 +201,8 @@ it, pl, ko, + "zh-Hant", + fr, ); mainGroup = 4EB3C74F2D96631A00C1B22C; minimizedProjectReferenceProxies = 1; @@ -418,7 +420,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"LocalDevVPN/Preview Content\""; - DEVELOPMENT_TEAM = 42Q7QX86GV; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; INFOPLIST_KEY_CFBundleDisplayName = LocalDevVPN; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; @@ -434,7 +436,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.1.5; - PRODUCT_BUNDLE_IDENTIFIER = com.jkcoxson.LocalDevVPN; + PRODUCT_BUNDLE_IDENTIFIER = com.lolo.LocalDevVPN; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -456,7 +458,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"LocalDevVPN/Preview Content\""; - DEVELOPMENT_TEAM = 42Q7QX86GV; + DEVELOPMENT_TEAM = ""; ENABLE_PREVIEWS = YES; INFOPLIST_KEY_CFBundleDisplayName = LocalDevVPN; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; @@ -472,7 +474,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.1.5; - PRODUCT_BUNDLE_IDENTIFIER = com.jkcoxson.LocalDevVPN; + PRODUCT_BUNDLE_IDENTIFIER = com.lolo.LocalDevVPN; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -486,7 +488,7 @@ 4EB3C77C2D96715400C1B22C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - DEVELOPMENT_TEAM = 42Q7QX86GV; + DEVELOPMENT_TEAM = ""; INFOPLIST_KEY_CFBundleDisplayName = TunnelProv; INFOPLIST_KEY_NSHumanReadableCopyright = Stossy11; INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; @@ -512,7 +514,7 @@ 4EB3C77D2D96715400C1B22C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - DEVELOPMENT_TEAM = 42Q7QX86GV; + DEVELOPMENT_TEAM = ""; INFOPLIST_KEY_CFBundleDisplayName = TunnelProv; INFOPLIST_KEY_NSHumanReadableCopyright = Stossy11; INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; diff --git a/LocalDevVPN.xcodeproj/project.xcworkspace/xcuserdata/lolo.xcuserdatad/UserInterfaceState.xcuserstate b/LocalDevVPN.xcodeproj/project.xcworkspace/xcuserdata/lolo.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000000000000000000000000000000000000..9c36bec6652dfc1184c5d7296e37437a19423e0e GIT binary patch literal 19506 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 Q#NX`g{n1JPuc9~q51dSO&j0`b literal 0 HcmV?d00001 diff --git a/LocalDevVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme b/LocalDevVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme index 8d51541..7c566e4 100644 --- a/LocalDevVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme +++ b/LocalDevVPN.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme @@ -74,6 +74,7 @@ savedToolIdentifier = "" useCustomWorkingDirectory = "NO" debugDocumentVersioning = "YES" + askForAppToLaunch = "Yes" launchAutomaticallySubstyle = "2"> diff --git a/LocalDevVPN/ContentView.swift b/LocalDevVPN/ContentView.swift index 34a41ba..4628327 100644 --- a/LocalDevVPN/ContentView.swift +++ b/LocalDevVPN/ContentView.swift @@ -66,7 +66,11 @@ class TunnelManager: ObservableObject { private var tunnelSubnetMask: String { UserDefaults.standard.string(forKey: "TunnelSubnetMask") ?? "255.255.255.0" } - + + private var useWiFiSubnet: Bool { + UserDefaults.standard.bool(forKey: "useWiFiSubnet") + } + private var tunnelBundleId: String { Bundle.main.bundleIdentifier!.appending(".TunnelProv") } @@ -503,6 +507,7 @@ class TunnelManager: ObservableObject { "TunnelDeviceIP": self.tunnelDeviceIp as NSObject, "TunnelFakeIP": self.tunnelFakeIp as NSObject, "TunnelSubnetMask": self.tunnelSubnetMask as NSObject, + "UseWiFiSubnet": self.useWiFiSubnet as NSObject, ] do { @@ -1087,6 +1092,7 @@ struct SettingsView: View { @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("useWiFiSubnet") private var useWiFiSubnet = false @AppStorage("autoConnect") private var autoConnect = false @AppStorage("shownTunnelAlert") private var shownTunnelAlert = false @StateObject private var tunnelManager = TunnelManager.shared @@ -1106,10 +1112,14 @@ struct SettingsView: View { } Section(header: Text("network_configuration")) { - Group { - networkConfigRow(label: "tunnel_ip", text: $deviceIP) - networkConfigRow(label: "device_ip", text: $fakeIP) - networkConfigRow(label: "subnet_mask", text: $subnetMask) + Toggle("match_wifi_subnet", isOn: $useWiFiSubnet) + + if !useWiFiSubnet { + Group { + networkConfigRow(label: "tunnel_ip", text: $deviceIP) + networkConfigRow(label: "device_ip", text: $fakeIP) + networkConfigRow(label: "subnet_mask", text: $subnetMask) + } } } diff --git a/LocalDevVPN/Localization/en.lproj/Localizable.strings b/LocalDevVPN/Localization/en.lproj/Localizable.strings index b08c6c0..c85e0a3 100644 --- a/LocalDevVPN/Localization/en.lproj/Localizable.strings +++ b/LocalDevVPN/Localization/en.lproj/Localizable.strings @@ -57,6 +57,7 @@ "auto_connect_on_launch" = "Auto Connect on Launch"; "connection_logs" = "Connection Logs"; "network_configuration" = "Network Configuration"; +"match_wifi_subnet" = "Match WiFi Subnet"; "device_ip" = "Device IP"; "tunnel_ip" = "Tunnel IP"; "subnet_mask" = "Subnet Mask"; diff --git a/TunnelProv/PacketTunnelProvider.swift b/TunnelProv/PacketTunnelProvider.swift index 8d7c352..a0ef6ba 100644 --- a/TunnelProv/PacketTunnelProvider.swift +++ b/TunnelProv/PacketTunnelProvider.swift @@ -6,6 +6,7 @@ // import NetworkExtension +import Darwin class PacketTunnelProvider: NEPacketTunnelProvider { var tunnelDeviceIp: String = "10.7.0.0" @@ -16,11 +17,27 @@ class PacketTunnelProvider: NEPacketTunnelProvider { private var fakeIpValue: UInt32 = 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 useWifiSubnet = options?["UseWiFiSubnet"] as? Bool ?? false + + if useWifiSubnet { + if let wifiInfo = getWiFiNetworkInfo() { + tunnelDeviceIp = wifiInfo.ipAddress + tunnelSubnetMask = wifiInfo.subnetMask + let fakeIpParts = wifiInfo.ipAddress.split(separator: ".") + if fakeIpParts.count == 4 { + tunnelFakeIp = "\(fakeIpParts[0]).\(fakeIpParts[1]).\(fakeIpParts[2]).\(Int(fakeIpParts[3])! + 1)" + } + } + } else { + if let deviceIp = options?["TunnelDeviceIP"] as? String { + tunnelDeviceIp = deviceIp + } + if let fakeIp = options?["TunnelFakeIP"] as? String { + tunnelFakeIp = fakeIp + } + if let subnetMask = options?["TunnelSubnetMask"] as? String { + tunnelSubnetMask = subnetMask + } } deviceIpValue = ipToUInt32(tunnelDeviceIp) @@ -28,8 +45,18 @@ class PacketTunnelProvider: NEPacketTunnelProvider { let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: tunnelDeviceIp) let ipv4 = NEIPv4Settings(addresses: [tunnelDeviceIp], subnetMasks: [tunnelSubnetMask]) - ipv4.includedRoutes = [NEIPv4Route(destinationAddress: tunnelDeviceIp, subnetMask: tunnelSubnetMask)] - ipv4.excludedRoutes = [.default()] + + let localSubnet = calculateNetworkAddress(ip: tunnelDeviceIp, mask: tunnelSubnetMask) + let localRoute = NEIPv4Route(destinationAddress: localSubnet, subnetMask: tunnelSubnetMask) + + if useWifiSubnet { + ipv4.includedRoutes = [localRoute] + ipv4.excludedRoutes = [.default()] + } else { + ipv4.includedRoutes = [NEIPv4Route(destinationAddress: tunnelDeviceIp, subnetMask: tunnelSubnetMask)] + ipv4.excludedRoutes = [.default()] + } + settings.ipv4Settings = ipv4 setTunnelNetworkSettings(settings) { error in @@ -69,4 +96,53 @@ class PacketTunnelProvider: NEPacketTunnelProvider { } return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4 } + + private func getWiFiNetworkInfo() -> (ipAddress: String, subnetMask: String)? { + var address: String? + var subnetMask: String? + + var ifaddr: UnsafeMutablePointer? + guard getifaddrs(&ifaddr) == 0 else { return nil } + guard let firstAddr = ifaddr else { return nil } + + for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) { + let interface = ifptr.pointee + let addrFamily = interface.ifa_addr.pointee.sa_family + + if addrFamily == UInt8(AF_INET) { + let name = String(cString: interface.ifa_name) + if name == "en0" { + var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len), + &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) + address = String(cString: hostname) + + var maskHostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + getnameinfo(interface.ifa_netmask, socklen_t(interface.ifa_netmask.pointee.sa_len), + &maskHostname, socklen_t(maskHostname.count), nil, socklen_t(0), NI_NUMERICHOST) + subnetMask = String(cString: maskHostname) + } + } + } + freeifaddrs(ifaddr) + + if let addr = address, let mask = subnetMask { + return (addr, mask) + } + return nil + } + + private func calculateNetworkAddress(ip: String, mask: String) -> String { + let ipParts = ip.split(separator: ".").compactMap { UInt32($0) } + let maskParts = mask.split(separator: ".").compactMap { UInt32($0) } + + guard ipParts.count == 4, maskParts.count == 4 else { return ip } + + let network = (ipParts[0] & maskParts[0]) | + (ipParts[1] & maskParts[1]) | + (ipParts[2] & maskParts[2]) | + (ipParts[3] & maskParts[3]) + + return "\(network >> 24 & 0xFF).\(network >> 16 & 0xFF).\(network >> 8 & 0xFF).\(network & 0xFF)" + } } From 408ec10e7902c750a2e62645d99d3efeed56811e Mon Sep 17 00:00:00 2001 From: sh4tteredd <55893559+sh4tteredd@users.noreply.github.com> Date: Sun, 22 Feb 2026 17:28:49 +0100 Subject: [PATCH 2/4] Update project.pbxproj --- LocalDevVPN.xcodeproj/project.pbxproj | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/LocalDevVPN.xcodeproj/project.pbxproj b/LocalDevVPN.xcodeproj/project.pbxproj index 2667ec2..6856d8b 100644 --- a/LocalDevVPN.xcodeproj/project.pbxproj +++ b/LocalDevVPN.xcodeproj/project.pbxproj @@ -201,8 +201,6 @@ it, pl, ko, - "zh-Hant", - fr, ); mainGroup = 4EB3C74F2D96631A00C1B22C; minimizedProjectReferenceProxies = 1; @@ -420,7 +418,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"LocalDevVPN/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 42Q7QX86GV; ENABLE_PREVIEWS = YES; INFOPLIST_KEY_CFBundleDisplayName = LocalDevVPN; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; @@ -436,7 +434,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.1.5; - PRODUCT_BUNDLE_IDENTIFIER = com.lolo.LocalDevVPN; + PRODUCT_BUNDLE_IDENTIFIER = com.jkcoxson.LocalDevVPN; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -458,7 +456,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; DEVELOPMENT_ASSET_PATHS = "\"LocalDevVPN/Preview Content\""; - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 42Q7QX86GV; ENABLE_PREVIEWS = YES; INFOPLIST_KEY_CFBundleDisplayName = LocalDevVPN; INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; @@ -474,7 +472,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.1.5; - PRODUCT_BUNDLE_IDENTIFIER = com.lolo.LocalDevVPN; + PRODUCT_BUNDLE_IDENTIFIER = com.jkcoxson.LocalDevVPN; PROVISIONING_PROFILE_SPECIFIER = ""; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; @@ -488,7 +486,7 @@ 4EB3C77C2D96715400C1B22C /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 42Q7QX86GV; INFOPLIST_KEY_CFBundleDisplayName = TunnelProv; INFOPLIST_KEY_NSHumanReadableCopyright = Stossy11; INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; @@ -514,7 +512,7 @@ 4EB3C77D2D96715400C1B22C /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - DEVELOPMENT_TEAM = ""; + DEVELOPMENT_TEAM = 42Q7QX86GV; INFOPLIST_KEY_CFBundleDisplayName = TunnelProv; INFOPLIST_KEY_NSHumanReadableCopyright = Stossy11; INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64; From bf4b2041e9726fa2f1cc6072fa22cb2252115532 Mon Sep 17 00:00:00 2001 From: sh4tteredd <55893559+sh4tteredd@users.noreply.github.com> Date: Mon, 23 Feb 2026 14:27:55 +0100 Subject: [PATCH 3/4] Update ContentView.swift --- LocalDevVPN/ContentView.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/LocalDevVPN/ContentView.swift b/LocalDevVPN/ContentView.swift index 4628327..d2fc957 100644 --- a/LocalDevVPN/ContentView.swift +++ b/LocalDevVPN/ContentView.swift @@ -788,6 +788,14 @@ extension View { struct StatusOverviewCard: View { @StateObject private var tunnelManager = TunnelManager.shared @AppStorage("TunnelDeviceIP") private var deviceIP = "10.7.0.0" + @AppStorage("useWiFiSubnet") private var useWiFiSubnet = false + + private var currentIP: String { + if useWiFiSubnet, let wifiIP = getCurrentWiFiIP() { + return wifiIP + } + return deviceIP + } var body: some View { DashboardCard { @@ -832,7 +840,7 @@ struct StatusOverviewCard: View { private var statusTip: String { switch tunnelManager.tunnelStatus { case .connected: - return String(format: NSLocalizedString("connected_to_ip", comment: ""), deviceIP) + return String(format: NSLocalizedString("connected_to_ip", comment: ""), currentIP) case .connecting: return NSLocalizedString("ios_might_ask_you_to_allow_the_vpn", comment: "") case .disconnecting: From b60a43e582c575649d67ca05f9552d5d70d4e460 Mon Sep 17 00:00:00 2001 From: sh4tteredd <55893559+sh4tteredd@users.noreply.github.com> Date: Mon, 23 Feb 2026 22:44:01 +0100 Subject: [PATCH 4/4] Update ContentView.swift --- LocalDevVPN/ContentView.swift | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/LocalDevVPN/ContentView.swift b/LocalDevVPN/ContentView.swift index d2fc957..bea6d5c 100644 --- a/LocalDevVPN/ContentView.swift +++ b/LocalDevVPN/ContentView.swift @@ -8,6 +8,7 @@ import Foundation import NetworkExtension import SwiftUI +import Darwin import NavigationBackport @@ -16,6 +17,30 @@ extension Bundle { var tunnelBundleID: String { bundleIdentifier!.appending(".TunnelProv") } } +private func getCurrentWiFiIP() -> String? { + var address: String? + var ifaddr: UnsafeMutablePointer? + guard getifaddrs(&ifaddr) == 0 else { return nil } + guard let firstAddr = ifaddr else { return nil } + + for ifptr in sequence(first: firstAddr, next: { $0.pointee.ifa_next }) { + let interface = ifptr.pointee + let addrFamily = interface.ifa_addr.pointee.sa_family + + if addrFamily == UInt8(AF_INET) { + let name = String(cString: interface.ifa_name) + if name == "en0" { + var hostname = [CChar](repeating: 0, count: Int(NI_MAXHOST)) + getnameinfo(interface.ifa_addr, socklen_t(interface.ifa_addr.pointee.sa_len), + &hostname, socklen_t(hostname.count), nil, socklen_t(0), NI_NUMERICHOST) + address = String(cString: hostname) + } + } + } + freeifaddrs(ifaddr) + return address +} + // MARK: - Logging Utility class VPNLogger: ObservableObject {