-
-
Save hossinasaadi/fe773e608fff717a97d3c7fde5faa257 to your computer and use it in GitHub Desktop.
PacketTunnel Provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// PacketTunnelProvider.swift | |
// Shadowsocks | |
// | |
// Created by Taher Ismail on 19/03/19. | |
// Copyright © 2019 Taher Ismail. All rights reserved. | |
// | |
import NetworkExtension | |
import NEKit | |
import tun2socks | |
import CocoaLumberjackSwift | |
import PacketProcessor | |
import CocoaAsyncSocket | |
import SocksKit | |
let AVAILABLE_INTERFACES = [ | |
"en0", | |
"en2", | |
"en3", | |
"en4", | |
"pdp_ip0", | |
"pdp_ip1", | |
"pdp_ip2", | |
"pdp_ip3", | |
] | |
enum VPNProtocol { | |
case shadowsocks | |
case socks5 | |
case https | |
case http | |
case http_auth | |
case https_auth | |
static func getProtocol(method: String) -> VPNProtocol { | |
switch method { | |
case "HTTPS": | |
return .https | |
case "HTTP": | |
return .http | |
case "SOCKS5": | |
return .socks5 | |
case "HTTPSAUTH": | |
return .https_auth | |
case "HTTPAUTH": | |
return .http_auth | |
default: | |
return .shadowsocks | |
} | |
} | |
} | |
class PacketTunnelProvider: NEPacketTunnelProvider { | |
let SharedDefaults = UserDefaults(suiteName: "group.com.simplicityinapps.Stealth-VPN") | |
var chosenProtocol: VPNProtocol! | |
var shadowsocks: ShadowsocksProxy? | |
var http: HTTPProxy? | |
var https: HTTPSProxy? | |
var httpAuth: HTTPAuthProxy? | |
var httpsAuth: HTTPSAuthProxy? | |
var socks5: SOCKS5Proxy? | |
var proxyPort: UInt16! = 9090 | |
override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { | |
// MARK: - Config | |
let host = SharedDefaults?.string(forKey: Defaults.ss_host) ?? "" | |
let port = SharedDefaults?.integer(forKey: Defaults.ss_port) ?? 443 | |
let method = SharedDefaults?.string(forKey: Defaults.ss_method) ?? "" | |
let username = SharedDefaults?.string(forKey: Defaults.ss_username) ?? "" | |
let password = SharedDefaults?.string(forKey: Defaults.ss_password) ?? "" | |
let sharedProxy = SharedDefaults?.bool(forKey: Defaults.shared_proxy) ?? false | |
let localIP = SharedDefaults?.string(forKey: Defaults.shared_proxy) ?? "" | |
let dns = SharedDefaults?.stringArray(forKey: Defaults.ss_dns) ?? ["1.1.1.1"] | |
let vpnProtocol = VPNProtocol.getProtocol(method: method) | |
chosenProtocol = vpnProtocol | |
var address = "127.0.0.1" | |
if sharedProxy { | |
address = localIP | |
} | |
proxyPort = 9090 | |
RawSocketFactory.TunnelProvider = self | |
// MARK: - General Settings | |
let proxySettings = NEProxySettings() | |
proxySettings.autoProxyConfigurationEnabled = true | |
if vpnProtocol != .shadowsocks && vpnProtocol != .socks5 { | |
proxySettings.proxyAutoConfigurationJavaScript = """ | |
function FindProxyForURL(url, host) { | |
return "PROXY \(address):\(proxyPort ?? 9090)"; | |
} | |
""" | |
} else { | |
proxySettings.proxyAutoConfigurationJavaScript = """ | |
function FindProxyForURL(url, host) { | |
return "SOCKS \(address):\(proxyPort ?? 9090)"; | |
} | |
""" | |
} | |
proxySettings.excludeSimpleHostnames = true | |
proxySettings.matchDomains = [""] | |
proxySettings.exceptionList = ["api.smoot.apple.com","configuration.apple.com","xp.apple.com","smp-device-content.apple.com","guzzoni.apple.com","captive.apple.com","*.ess.apple.com","*.push.apple.com","*.push-apple.com.akadns.net"] | |
let networkSettings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: "1.1.1.1") | |
networkSettings.mtu = 1500 | |
networkSettings.proxySettings = proxySettings | |
networkSettings.dnsSettings = NEDNSSettings(servers: dns) | |
let ipv4Settings = NEIPv4Settings(addresses: ["192.169.89.1"], subnetMasks: ["255.255.255.0"]) | |
ipv4Settings.includedRoutes = [NEIPv4Route.default()] | |
ipv4Settings.excludedRoutes = [ | |
NEIPv4Route(destinationAddress: "10.0.0.0", subnetMask: "255.0.0.0"), | |
NEIPv4Route(destinationAddress: "100.64.0.0", subnetMask: "255.192.0.0"), | |
NEIPv4Route(destinationAddress: "127.0.0.0", subnetMask: "255.0.0.0"), | |
NEIPv4Route(destinationAddress: "169.254.0.0", subnetMask: "255.255.0.0"), | |
NEIPv4Route(destinationAddress: "172.16.0.0", subnetMask: "255.240.0.0"), | |
NEIPv4Route(destinationAddress: "192.168.0.0", subnetMask: "255.255.0.0"), | |
NEIPv4Route(destinationAddress: "17.0.0.0", subnetMask: "255.0.0.0"), | |
] | |
networkSettings.ipv4Settings = ipv4Settings | |
networkSettings.ipv4Settings = NEIPv4Settings(addresses: [getIPAddress()], subnetMasks: ["255.255.255.255"]) | |
switch vpnProtocol { | |
case .shadowsocks: | |
shadowsocks = ShadowsocksProxy( | |
serverAddress: host, | |
serverPort: UInt16(port), | |
localAddress: address, | |
localPort: proxyPort, | |
password: password, | |
method: method | |
) | |
case .http: | |
http = HTTPProxy( | |
serverAddress: host, | |
serverPort: UInt16(port), | |
localAddress: address, | |
localPort: proxyPort | |
) | |
case .https: | |
https = HTTPSProxy( | |
serverAddress: host, | |
serverPort: UInt16(port), | |
localAddress: address, | |
localPort: proxyPort | |
) | |
case .http_auth: | |
httpAuth = HTTPAuthProxy( | |
serverAddress: host, | |
serverPort: UInt16(port), | |
localAddress: address, | |
localPort: proxyPort, | |
username: username, | |
password: password | |
) | |
case .https_auth: | |
httpsAuth = HTTPSAuthProxy( | |
serverAddress: host, | |
serverPort: UInt16(port), | |
localAddress: address, | |
localPort: proxyPort, | |
username: username, | |
password: password | |
) | |
case .socks5: | |
socks5 = SOCKS5Proxy( | |
serverAddress: host, | |
serverPort: UInt16(port), | |
localAddress: address, | |
localPort: proxyPort | |
) | |
} | |
if !(SharedDefaults?.bool(forKey: "StartInApp"))! { | |
self.displayMessage("Please use Stealth VPN to connect") { (done) in | |
self.cancelTunnelWithError(nil) | |
} | |
} | |
setTunnelNetworkSettings(networkSettings) { error in | |
if error == nil { | |
do { | |
try self.shadowsocks?.start() | |
try self.http?.start() | |
try self.https?.start() | |
try self.httpAuth?.start() | |
try self.httpsAuth?.start() | |
try self.socks5?.start() | |
TunnelInterface.setup(with: self.packetFlow) | |
TunnelInterface.startTun2Socks(Int32(self.proxyPort)) | |
DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { | |
TunnelInterface.processPackets() | |
}) | |
} catch let error { | |
completionHandler(error) | |
} | |
completionHandler(nil) | |
} else { | |
completionHandler(error) | |
} | |
} | |
} | |
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { | |
if reason != .none { | |
shadowsocks?.stop() | |
http?.stop() | |
https?.stop() | |
httpAuth?.stop() | |
httpsAuth?.stop() | |
socks5?.stop() | |
} | |
completionHandler() | |
exit(EXIT_SUCCESS) | |
} | |
func getIPAddress() -> String { | |
var address: String? | |
var ifaddr: UnsafeMutablePointer<ifaddrs>? = nil | |
if getifaddrs(&ifaddr) == 0 { | |
var ptr = ifaddr | |
while ptr != nil { | |
defer { ptr = ptr?.pointee.ifa_next } | |
let interface = ptr?.pointee | |
let addrFamily = interface?.ifa_addr.pointee.sa_family | |
if addrFamily == UInt8(AF_INET) || addrFamily == UInt8(AF_INET6) { | |
let name: String = String(cString: (interface!.ifa_name)) | |
if AVAILABLE_INTERFACES.contains(name) { | |
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 ?? "" | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment