TrojanVPN based on tun2socks library immediately disconnected
Tags: swift, vpn, ios, programming
Published: 2023-02-26 09:07:50

We've developed trojanVPN based on tun2socks and used tun2socks library.

This is source code of VPNManager.swift

`import Foundation
import NetworkExtension

extension NEVPNStatus: CustomStringConvertible {
    public var description: String {
        switch self {
        case .disconnected: return "Disconnected"
        case .invalid: return "Invalid"
        case .connected: return "Connected"
        case .connecting: return "Connecting"
        case .disconnecting: return "Disconnecting"
        case .reasserting: return "Reasserting"
        default: return "Error"
        }
    }
}

public class VPNManager {
    public var manager = NEVPNManager.shared()
    
    private static var sharedVPNManager: VPNManager = {
        return VPNManager()
    }()
    
    public class func shared() -> VPNManager {
        return sharedVPNManager
    }
    
    public init() {}
    
    public func loadVPNPreference(completion: @escaping (Error?) -> Void) {
        NETunnelProviderManager.loadAllFromPreferences() { managers, error in
            guard let managers = managers, error == nil else {
                completion(error)
                return
            }
            
            if managers.count == 0 {
                let newManager = NETunnelProviderManager()
                let tunnelProtocol = NETunnelProviderProtocol()
                tunnelProtocol.serverAddress = "127.0.0.1"
                tunnelProtocol.providerBundleIdentifier = "io.trojanGFW.library.PacketTunnel"
                tunnelProtocol.disconnectOnSleep = false
                newManager.protocolConfiguration = tunnelProtocol
                newManager.localizedDescription = "trojan-vpn"
                newManager.isEnabled = true
                newManager.saveToPreferences { error in
                    guard error == nil else {
                        completion(error)
                        return
                    }
                    newManager.loadFromPreferences { error in
                        self.manager = newManager
                        completion(nil)
                    }
                }
            } else {
                self.manager = managers[0]
                completion(nil)
            }
        }
    }
    
    public func enableVPNManager(completion: @escaping (Error?) -> Void) {
        manager.isEnabled = true
        manager.saveToPreferences { error in
            guard error == nil else {
                completion(error)
                return
            }
            self.manager.loadFromPreferences { error in
                completion(error)
            }
        }
    }
    
    public func toggleVPNConnection(completion: @escaping (Error?) -> Void) {
        if self.manager.connection.status == .disconnected || self.manager.connection.status == .invalid {
            do {
                try self.manager.connection.startVPNTunnel()
            } catch {
                completion(error)
            }
        } else {
            self.manager.connection.stopVPNTunnel()
        }
    }
}`


This is PacketTunnelProvider.swift

`import NetworkExtension
import ProxyConfig

class PacketTunnelProvider: NEPacketTunnelProvider {
    var proxyClient: TrojanProxy!
    override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
        let tunnelNetworkSettings = createTunnelSettings()
        setTunnelNetworkSettings(tunnelNetworkSettings) { [weak self] error in
            let tunFd = self?.packetFlow.value(forKeyPath: "socket.fileDescriptor") as! Int32
            switch ProxyConfig.preferHandler {
            case .Socks5:
                let host = ProxyConfig.getStringConfig(name: ProxyConfig.ConfigKey.Host.rawValue)!
                let port = ProxyConfig.getIntConfig(name: ProxyConfig.ConfigKey.Port.rawValue)!
                let password = ProxyConfig.getStringConfig(name: ProxyConfig.ConfigKey.Password.rawValue)!
                let proxyServer = "127.0.0.1:1080"
                
                self?.proxyClient = TrojanProxy(local_host: "127.0.0.1", local_port: 1080, remote_host: host, remote_port: UInt16(port), password: password)
                self?.proxyClient.start()
                NSLog("proxy server \(proxyServer)")
                DispatchQueue.global(qos: .default).async {
                    run(tunFd, "socks", proxyServer, "", "")
                }
                break
            }
            NSLog("tunnel start")
            completionHandler(nil)
        }
    }
    
    override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
        NSLog("tunnel stop")
        completionHandler()
        if (proxyClient != nil) {
            proxyClient.stop()
        }
    }
    
    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) {
        // Add code here to get ready to sleep.
        completionHandler()
    }
    
    override func wake() {
        // Add code here to wake up.
    }
    
    func createTunnelSettings() -> NEPacketTunnelNetworkSettings  {
        let newSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "240.0.0.10")
        newSettings.ipv4Settings = NEIPv4Settings(addresses: ["240.0.0.1"], subnetMasks: ["255.255.255.0"])
        newSettings.ipv4Settings?.includedRoutes = [NEIPv4Route.`default`()]
        // newSettings.ipv6Settings?.includedRoutes = [NEIPv6Route.`default`()]
        newSettings.proxySettings = nil
        newSettings.dnsSettings = NEDNSSettings(servers: ["8.8.8.8", "8.8.4.4"])
        newSettings.mtu = 1500
        return newSettings
    }
}`

Input host, port and password then click connect but it showing "connecting" then immediately "Disconnected".

What's the problem?

Please let me know who has best solution to connect.