Skip to content

Instantly share code, notes, and snippets.

@Taher-17
Last active March 5, 2024 15:22
Show Gist options
  • Save Taher-17/0168803963cdf7b00c6ac9ad1a55c987 to your computer and use it in GitHub Desktop.
Save Taher-17/0168803963cdf7b00c6ac9ad1a55c987 to your computer and use it in GitHub Desktop.
PacketTunnel Provider
//
// 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