diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj index 11bf854..d3727c3 100644 --- a/StikDebug.xcodeproj/project.pbxproj +++ b/StikDebug.xcodeproj/project.pbxproj @@ -9,6 +9,10 @@ /* Begin PBXBuildFile section */ 68D569BE2E1B415700A5BA36 /* CodeEditorView in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BD2E1B415700A5BA36 /* CodeEditorView */; }; 68D569C02E1B415700A5BA36 /* LanguageSupport in Frameworks */ = {isa = PBXBuildFile; productRef = 68D569BF2E1B415700A5BA36 /* LanguageSupport */; }; + A1B2C3D40123456789ABC001 /* StikDebugTunnelConfiguration.swift in StikDebugTunnel Sources */ = {isa = PBXBuildFile; fileRef = A3B2C3D40123456789ABC003 /* StikDebugTunnelConfiguration.swift */; }; + A1B2C3D40123456789ABC002 /* IPv4PacketRewriter.swift in StikDebugTunnel Sources */ = {isa = PBXBuildFile; fileRef = A3B2C3D40123456789ABC004 /* IPv4PacketRewriter.swift */; }; + A1B2C3D40123456789ABC003 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A3B2C3D40123456789ABC002 /* NetworkExtension.framework */; }; + A1B2C3D40123456789ABC004 /* StikDebugTunnel.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = A3B2C3D40123456789ABC001 /* StikDebugTunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -26,6 +30,13 @@ remoteGlobalIDString = DC6F1D362D94EADD0071B2B6; remoteInfo = StikJIT; }; + A2B2C3D40123456789ABC001 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = DC6F1D2F2D94EADD0071B2B6 /* Project object */; + proxyType = 1; + remoteGlobalIDString = A5B2C3D40123456789ABC001; + remoteInfo = StikDebugTunnel; + }; /* End PBXContainerItemProxy section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -35,6 +46,7 @@ dstPath = ""; dstSubfolderSpec = 13; files = ( + A1B2C3D40123456789ABC004 /* StikDebugTunnel.appex in Embed Foundation Extensions */, ); name = "Embed Foundation Extensions"; runOnlyForDeploymentPostprocessing = 0; @@ -47,6 +59,10 @@ DC6F1D372D94EADD0071B2B6 /* StikDebug.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = StikDebug.app; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D482D94EADF0071B2B6 /* StikDebugTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StikDebugUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + A3B2C3D40123456789ABC001 /* StikDebugTunnel.appex */ = {isa = PBXFileReference; explicitFileType = wrapper.app-extension; includeInIndex = 0; path = StikDebugTunnel.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + A3B2C3D40123456789ABC002 /* NetworkExtension.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = NetworkExtension.framework; path = System/Library/Frameworks/NetworkExtension.framework; sourceTree = SDKROOT; }; + A3B2C3D40123456789ABC003 /* StikDebugTunnelConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StikJIT/Tunnel/StikDebugTunnelConfiguration.swift; sourceTree = ""; }; + A3B2C3D40123456789ABC004 /* IPv4PacketRewriter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StikJIT/Tunnel/IPv4PacketRewriter.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ @@ -78,6 +94,11 @@ path = StikJITUITests; sourceTree = ""; }; + A4B2C3D40123456789ABC001 /* StikDebugTunnel */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = StikDebugTunnel; + sourceTree = ""; + }; /* End PBXFileSystemSynchronizedRootGroup section */ /* Begin PBXFrameworksBuildPhase section */ @@ -101,6 +122,14 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + A6B2C3D40123456789ABC002 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A1B2C3D40123456789ABC003 /* NetworkExtension.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -111,6 +140,9 @@ isa = PBXGroup; children = ( DC6F1D392D94EADD0071B2B6 /* StikJIT */, + A4B2C3D40123456789ABC001 /* StikDebugTunnel */, + A3B2C3D40123456789ABC003 /* StikDebugTunnelConfiguration.swift */, + A3B2C3D40123456789ABC004 /* IPv4PacketRewriter.swift */, DC6F1D4B2D94EADF0071B2B6 /* StikJITTests */, DC6F1D552D94EADF0071B2B6 /* StikJITUITests */, DC6F1D752D94EB620071B2B6 /* Frameworks */, @@ -124,6 +156,7 @@ DC6F1D372D94EADD0071B2B6 /* StikDebug.app */, DC6F1D482D94EADF0071B2B6 /* StikDebugTests.xctest */, DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */, + A3B2C3D40123456789ABC001 /* StikDebugTunnel.appex */, ); name = Products; sourceTree = ""; @@ -133,6 +166,7 @@ children = ( DC139F6D2DE97EA400F63846 /* WidgetKit.framework */, DC139F6F2DE97EA400F63846 /* SwiftUI.framework */, + A3B2C3D40123456789ABC002 /* NetworkExtension.framework */, ); name = Frameworks; sourceTree = ""; @@ -152,6 +186,7 @@ buildRules = ( ); dependencies = ( + A7B2C3D40123456789ABC001 /* PBXTargetDependency */, ); fileSystemSynchronizedGroups = ( DC6F1D392D94EADD0071B2B6 /* StikJIT */, @@ -211,6 +246,26 @@ productReference = DC6F1D522D94EADF0071B2B6 /* StikDebugUITests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + A5B2C3D40123456789ABC001 /* StikDebugTunnel */ = { + isa = PBXNativeTarget; + buildConfigurationList = A9B2C3D40123456789ABC001 /* Build configuration list for PBXNativeTarget "StikDebugTunnel" */; + buildPhases = ( + A6B2C3D40123456789ABC001 /* Sources */, + A6B2C3D40123456789ABC002 /* Frameworks */, + A6B2C3D40123456789ABC003 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + A4B2C3D40123456789ABC001 /* StikDebugTunnel */, + ); + name = StikDebugTunnel; + productName = StikDebugTunnel; + productReference = A3B2C3D40123456789ABC001 /* StikDebugTunnel.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -233,6 +288,9 @@ CreatedOnToolsVersion = 16.2; TestTargetID = DC6F1D362D94EADD0071B2B6; }; + A5B2C3D40123456789ABC001 = { + CreatedOnToolsVersion = 16.2; + }; }; }; buildConfigurationList = DC6F1D322D94EADD0071B2B6 /* Build configuration list for PBXProject "StikDebug" */; @@ -257,6 +315,7 @@ DC6F1D362D94EADD0071B2B6 /* StikDebug */, DC6F1D472D94EADF0071B2B6 /* StikDebugTests */, DC6F1D512D94EADF0071B2B6 /* StikDebugUITests */, + A5B2C3D40123456789ABC001 /* StikDebugTunnel */, ); }; /* End PBXProject section */ @@ -283,6 +342,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A6B2C3D40123456789ABC003 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -307,6 +373,15 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + A6B2C3D40123456789ABC001 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1B2C3D40123456789ABC001 /* StikDebugTunnelConfiguration.swift in StikDebugTunnel Sources */, + A1B2C3D40123456789ABC002 /* IPv4PacketRewriter.swift in StikDebugTunnel Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -320,9 +395,58 @@ target = DC6F1D362D94EADD0071B2B6 /* StikDebug */; targetProxy = DC6F1D532D94EADF0071B2B6 /* PBXContainerItemProxy */; }; + A7B2C3D40123456789ABC001 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = A5B2C3D40123456789ABC001 /* StikDebugTunnel */; + targetProxy = A2B2C3D40123456789ABC001 /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ + A8B2C3D40123456789ABC001 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = StikDebugTunnel/StikDebugTunnel.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = StikDebugTunnel/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 17.4; + PRODUCT_BUNDLE_IDENTIFIER = com.stik.stikdebug.tunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + A8B2C3D40123456789ABC002 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + APPLICATION_EXTENSION_API_ONLY = YES; + CODE_SIGN_ENTITLEMENTS = StikDebugTunnel/StikDebugTunnel.entitlements; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + GENERATE_INFOPLIST_FILE = NO; + INFOPLIST_FILE = StikDebugTunnel/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 17.4; + PRODUCT_BUNDLE_IDENTIFIER = com.stik.stikdebug.tunnel; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; DC6F1D5A2D94EADF0071B2B6 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -707,6 +831,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + A9B2C3D40123456789ABC001 /* Build configuration list for PBXNativeTarget "StikDebugTunnel" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A8B2C3D40123456789ABC001 /* Debug */, + A8B2C3D40123456789ABC002 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ diff --git a/StikDebugTunnel/Info.plist b/StikDebugTunnel/Info.plist new file mode 100644 index 0000000..3059459 --- /dev/null +++ b/StikDebugTunnel/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + diff --git a/StikDebugTunnel/PacketTunnelProvider.swift b/StikDebugTunnel/PacketTunnelProvider.swift new file mode 100644 index 0000000..7407468 --- /dev/null +++ b/StikDebugTunnel/PacketTunnelProvider.swift @@ -0,0 +1,97 @@ +// +// PacketTunnelProvider.swift +// StikDebugTunnel +// + +import NetworkExtension + +final class PacketTunnelProvider: NEPacketTunnelProvider { + private var interfaceIP = StikDebugTunnelConfiguration.default.interfaceIP + private var peerIP = StikDebugTunnelConfiguration.default.peerIP + private var packetLoopActive = false + + override func startTunnel( + options: [String: NSObject]?, + completionHandler: @escaping (Error?) -> Void + ) { + let providerConfiguration = + (protocolConfiguration as? NETunnelProviderProtocol)?.providerConfiguration + + if let value = options?[StikDebugTunnelConfiguration.interfaceIPKey] as? String + ?? providerConfiguration?[StikDebugTunnelConfiguration.interfaceIPKey] as? String { + interfaceIP = value + } + if let value = options?[StikDebugTunnelConfiguration.peerIPKey] as? String + ?? providerConfiguration?[StikDebugTunnelConfiguration.peerIPKey] as? String { + peerIP = value + } + + let ipv4Settings = NEIPv4Settings( + addresses: [interfaceIP], + subnetMasks: ["255.255.255.0"] + ) + ipv4Settings.includedRoutes = [ + NEIPv4Route( + destinationAddress: peerIP, + subnetMask: "255.255.255.255" + ) + ] + ipv4Settings.excludedRoutes = [.default()] + + let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: peerIP) + settings.ipv4Settings = ipv4Settings + + setTunnelNetworkSettings(settings) { [weak self] error in + guard let self else { + completionHandler(error) + return + } + + if let error { + completionHandler(error) + return + } + + packetLoopActive = true + readPackets() + completionHandler(nil) + } + } + + override func stopTunnel( + with reason: NEProviderStopReason, + completionHandler: @escaping () -> Void + ) { + packetLoopActive = false + completionHandler() + } + + private func readPackets() { + guard packetLoopActive else { return } + + packetFlow.readPackets { [weak self] packets, protocols in + guard let self, self.packetLoopActive else { return } + + var rewrittenPackets = packets + for index in rewrittenPackets.indices { + guard protocols[index].int32Value == AF_INET else { continue } + rewrittenPackets[index].withUnsafeMutableBytes { rawBuffer in + guard let baseAddress = rawBuffer.baseAddress else { return } + var packet = Array( + UnsafeRawBufferPointer( + start: baseAddress, + count: rawBuffer.count + ) + ) + IPv4PacketRewriter.swapEndpoints(in: &packet) + packet.withUnsafeBytes { rewrittenBuffer in + rawBuffer.copyBytes(from: rewrittenBuffer) + } + } + } + + self.packetFlow.writePackets(rewrittenPackets, withProtocols: protocols) + self.readPackets() + } + } +} diff --git a/StikDebugTunnel/StikDebugTunnel.entitlements b/StikDebugTunnel/StikDebugTunnel.entitlements new file mode 100644 index 0000000..f70316e --- /dev/null +++ b/StikDebugTunnel/StikDebugTunnel.entitlements @@ -0,0 +1,14 @@ + + + + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.security.application-groups + + group.com.stik.stikdebug + + +