具有本地设备内服务器的iOS数据包隧道提供程序

问题描述

我正在使用Apple提供的网络扩展框架来构建类似于iOS的Charles Proxy和Surge 4的数据包嗅探/监视应用程序。

到目前为止,我已经启动了项目的基本结构并运行了该应用程序,该应用程序触发了PacketTunnelProvider Extension,在其中,我可以看到通过packetFlow.readPackets(completionHandler :)方法转发的数据包。我的背景不是网络方面的,因此我对这类应用程序的基本结构感到困惑。他们是否在设备上托管了充当代理的服务器,以拦截网络请求?谁能提供网络请求的一般流程示意图?即数据包隧道提供程序,代理服务器,虚拟接口和隧道之间有什么关系?

如果这些应用程序确实使用本地设备服务器,如何配置NEPacketTunnelNetworkSettings以允许连接?我尝试合并本地GCDWebServer这样的本地设备服务器,但在两者之间建立链接没有运气。

例如,如果可以通过 192.168.1.231:8080 访问GCDWebServer,我将如何更改下面的代码以使客户端与服务器通信?

主应用程序:

    let proxyServer = NEProxyServer(address: "192.168.1.231",port: 8080)
    
    let proxySettings = NEProxySettings()
    proxySettings.exceptionList = []
    proxySettings.httpEnabled = true
    proxySettings.httpServer = proxyServer
    
    let providerProtocol = NETunnelProviderProtocol()
    providerProtocol.providerBundleIdentifier = self.tunnelBundleId
    providerProtocol.serverAddress = "My Server"
    providerProtocol.providerConfiguration = [:]
    providerProtocol.proxySettings = proxySettings
    
    let newManager = NETunnelProviderManager()
    newManager.localizedDescription = "Custom VPN"
    newManager.protocolConfiguration = providerProtocol
    newManager.isEnabled = true
    saveLoadManager()
    self.vpnManager = newManager

PacketTunnelProviderExtension:

func startTunnel(options: [String : NSObject]?,completionHandler: @escaping (Error?) -> Void) {
  ...
        let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.143")
        settings.ipv4Settings = NEIPv4Settings(addresses: ["198.17.203.2"],subnetMasks: ["255.255.255.255"])
        settings.ipv4Settings?.includedRoutes = [NEIPv4Route.default()]
        settings.ipv4Settings?.excludedRoutes = []
        settings.dnsSettings = NednSSettings(servers: ["8.8.8.8","8.8.4.4"])

        settings.dnsSettings?.matchDomains = [""]
        self.setTunnelNetworkSettings(settings) { error in
            if let e = error {
                NSLog("Settings error %@",e.localizedDescription)
            } else {
                completionHandler(error)
                self.readPackets()
            }
        }
  ...
}

解决方法

我正在开发Proxyman的iOS版本,我的经验可以为您提供帮助:

他们是否在充当代理的设备上托管服务器来拦截网络请求?

是的,您必须在网络扩展(不是主应用程序)上启动侦听器以充当代理服务器。您可以使用Swift NIOCocoaAsyncSocket编写一个简单的代理服务器。

要拦截HTTPS流量,这是一个很大的挑战,但是由于它不在范围之内,所以在此我不予赘述。

任何人都可以提供网络请求的一般流程图吗?

由于Network Extension和Main应用程序是两个不同的过程,因此它们无法像普通应用程序那样直接通信。

因此,流程可能类似于:

Internet-> iPhone->您的网络扩展(VPN)->转发到本地代理服务器->拦截或监视->保存到本地数据库(在共享容器组中)->再次转发到目标服务器

从主应用程序中,您可以通过读取本地数据库来接收数据。

如何配置NEPacketTunnelNetworkSettings以允许连接?

在“网络”扩展中,让我们在Host:Port上启动代理服务器,然后像示例中一样初始化NetworkSetting:

    private func initTunnelSettings(proxyHost: String,proxyPort: Int) -> NEPacketTunnelNetworkSettings {
    let settings: NEPacketTunnelNetworkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "127.0.0.1")

    /* proxy settings */
    let proxySettings: NEProxySettings = NEProxySettings()
    proxySettings.httpServer = NEProxyServer(
        address: proxyHost,port: proxyPort
    )
    proxySettings.httpsServer = NEProxyServer(
        address: proxyHost,port: proxyPort
    )
    proxySettings.autoProxyConfigurationEnabled = false
    proxySettings.httpEnabled = true
    proxySettings.httpsEnabled = true
    proxySettings.excludeSimpleHostnames = true
    proxySettings.exceptionList = [
        "192.168.0.0/16","10.0.0.0/8","172.16.0.0/12","127.0.0.1","localhost","*.local"
    ]
    settings.proxySettings = proxySettings

    /* ipv4 settings */
    let ipv4Settings: NEIPv4Settings = NEIPv4Settings(
        addresses: [settings.tunnelRemoteAddress],subnetMasks: ["255.255.255.255"]
    )
    ipv4Settings.includedRoutes = [NEIPv4Route.default()]
    ipv4Settings.excludedRoutes = [
        NEIPv4Route(destinationAddress: "192.168.0.0",subnetMask: "255.255.0.0"),NEIPv4Route(destinationAddress: "10.0.0.0",subnetMask: "255.0.0.0"),NEIPv4Route(destinationAddress: "172.16.0.0",subnetMask: "255.240.0.0")
    ]
    settings.ipv4Settings = ipv4Settings

    /* MTU */
    settings.mtu = 1500

    return settings
}

然后启动VPN,

let networkSettings = initTunnelSettings(proxyHost: ip,proxyPort: port)

// Start
setTunnelNetworkSettings(networkSettings) { // Handle success }

然后将包转发到本地代理服务器:

let endpoint = NWHostEndpoint(hostname: proxyIP,port: proxyPort)
self.connection = self.createTCPConnection(to: endpoint,enableTLS: false,tlsParameters: nil,delegate: nil)

    packetFlow.readPackets {[weak self] (packets,protocols) in
        guard let strongSelf = self else { return }
        for packet in packets {
            strongSelf.connection.write(packet,completionHandler: { (error) in
            })
        }

        // Repeat
        strongSelf.readPackets()
    }

由此,您的本地服务器可以接收软件包,然后转发到目标服务器。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...