211 lines
7.1 KiB
Swift
211 lines
7.1 KiB
Swift
import NetworkExtension
|
|
import DNS
|
|
|
|
fileprivate var db: SQLiteDatabase?
|
|
|
|
class DNSProxyProvider: NEDNSProxyProvider {
|
|
|
|
override func startProxy(options:[String: Any]? = nil, completionHandler: @escaping (Error?) -> Void) {
|
|
// Add code here to start the DNS proxy.
|
|
simpleTunnelLog("startProxy")
|
|
do {
|
|
db = try SQLiteDatabase.open(path: DB_PATH)
|
|
try db!.createTable(table: DNSQuery.self)
|
|
} catch {
|
|
simpleTunnelLog("Error: \(error)")
|
|
completionHandler(error)
|
|
return
|
|
}
|
|
completionHandler(nil)
|
|
}
|
|
|
|
override func stopProxy(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
|
|
// Add code here to stop the DNS proxy.
|
|
simpleTunnelLog("stopProxy")
|
|
db = nil
|
|
completionHandler()
|
|
}
|
|
|
|
override func sleep(completionHandler: @escaping () -> Void) {
|
|
// Add code here to get ready to sleep.
|
|
simpleTunnelLog("sleep")
|
|
completionHandler()
|
|
}
|
|
|
|
override func wake() {
|
|
// Add code here to wake up.
|
|
simpleTunnelLog("wake")
|
|
}
|
|
|
|
override func handleNewUDPFlow(_ flow: NEAppProxyUDPFlow, initialRemoteEndpoint remoteEndpoint: NWEndpoint) -> Bool {
|
|
simpleTunnelLog("handleUDPFlow \(flow.metaData.sourceAppSigningIdentifier)")
|
|
simpleTunnelLog("handleUDPFlow \((remoteEndpoint as! NWHostEndpoint).hostname)")
|
|
|
|
let con = createUDPSession(to: remoteEndpoint, from: (flow.localEndpoint as! NWHostEndpoint))
|
|
let newConnection = ClientDNSProxy(newUDPFlow: flow)
|
|
newConnection.open(con)
|
|
return true
|
|
}
|
|
|
|
|
|
/*override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
|
|
// Add code here to handle the incoming flow.
|
|
NSLog("PSI: handleFlow %@", flow.metaData.sourceAppSigningIdentifier)
|
|
|
|
var newConnection: ClientAppProxyConnection?
|
|
|
|
guard let clientTunnel = tunnel else { return false }
|
|
|
|
|
|
if let tcpFlow = flow as? NEAppProxyTCPFlow {
|
|
let remoteHost = (tcpFlow.remoteEndpoint as! NWHostEndpoint).hostname
|
|
let remotePort = (tcpFlow.remoteEndpoint as! NWHostEndpoint).port
|
|
NSLog("PSI: TCP HOST : \(remoteHost)")
|
|
NSLog("PSI: TCP PORT : \(remotePort)")
|
|
newConnection = ClientAppProxyTCPConnection(tunnel: clientTunnel, newTCPFlow: tcpFlow)
|
|
} else if let udpFlow = flow as? NEAppProxyUDPFlow {
|
|
let localHost = (udpFlow.localEndpoint as! NWHostEndpoint).hostname
|
|
let localPort = (udpFlow.localEndpoint as! NWHostEndpoint).port
|
|
NSLog("PSI: UDP HOST : \(localHost)")
|
|
NSLog("PSI: UDP PORT : \(localPort)")
|
|
newConnection = ClientAppProxyUDPConnection(tunnel: clientTunnel, newUDPFlow: udpFlow)
|
|
}
|
|
|
|
guard newConnection != nil else { return false }
|
|
|
|
newConnection!.open()
|
|
|
|
return true
|
|
// return super.handleNewFlow(flow)
|
|
}*/
|
|
}
|
|
|
|
class ClientDNSProxy : NSObject {
|
|
public let identifier: Int
|
|
let appProxyFlow: NEAppProxyFlow
|
|
var datagramsOutstanding = 0
|
|
var conn: NWUDPSession?
|
|
|
|
/// The NEAppProxyUDPFlow object corresponding to this connection.
|
|
var UDPFlow: NEAppProxyUDPFlow {
|
|
return (appProxyFlow as! NEAppProxyUDPFlow)
|
|
}
|
|
|
|
init(newUDPFlow: NEAppProxyUDPFlow) {
|
|
appProxyFlow = newUDPFlow
|
|
identifier = newUDPFlow.hash
|
|
super.init()
|
|
}
|
|
|
|
/// Send an "Open" message to the SimpleTunnel server, to begin the process of establishing a flow of data in the SimpleTunnel protocol.
|
|
func open(_ connection: NWUDPSession) {
|
|
// open([
|
|
// TunnelMessageKey.TunnelType.rawValue: TunnelLayer.app.rawValue as AnyObject,
|
|
// TunnelMessageKey.AppProxyFlowType.rawValue: AppProxyFlowKind.udp.rawValue as AnyObject
|
|
// ])
|
|
connection.setReadHandler({ datas, error in
|
|
guard let datagrams = datas, error == nil else {
|
|
simpleTunnelLog("Failed to read UDP connection: \(String(describing: error))")
|
|
return
|
|
}
|
|
|
|
self.UDPFlow.writeDatagrams(datagrams, sentBy: [connection.endpoint]) { error in
|
|
if let error = error {
|
|
simpleTunnelLog("Failed to write datagrams to the UDP Flow: \(error)")
|
|
// self.tunnel?.sendCloseType(.read, forConnection: self.identifier)
|
|
self.UDPFlow.closeWriteWithError(nil)
|
|
}
|
|
}
|
|
}, maxDatagrams: 32)
|
|
self.conn = connection
|
|
UDPFlow.open(withLocalEndpoint: (UDPFlow.localEndpoint as! NWHostEndpoint)) { (e: Error?) in
|
|
self.handleSendResult(nil)
|
|
}
|
|
}
|
|
|
|
/// Handle the result of sending a "Data" message to the SimpleTunnel server.
|
|
func handleSendResult(_ error: NSError?) {
|
|
|
|
if let sendError = error {
|
|
simpleTunnelLog("Failed to send message to Tunnel Server. error = \(sendError)")
|
|
// handleErrorCondition(.hostUnreachable)
|
|
return
|
|
}
|
|
|
|
if datagramsOutstanding > 0 {
|
|
datagramsOutstanding -= 1
|
|
}
|
|
|
|
// Only read more datagrams from the source application if all outstanding datagrams have been sent on the network.
|
|
guard datagramsOutstanding == 0 else { return }
|
|
|
|
// Read a new set of datagrams from the source application.
|
|
UDPFlow.readDatagrams { datagrams, remoteEndPoints, readError in
|
|
|
|
guard let readDatagrams = datagrams,
|
|
let readEndpoints = remoteEndPoints
|
|
, readError == nil else
|
|
{
|
|
simpleTunnelLog("Failed to read data from the UDP flow. error = \(String(describing: readError))")
|
|
// self.handleErrorCondition(.peerReset)
|
|
return
|
|
}
|
|
|
|
guard !readDatagrams.isEmpty && readEndpoints.count == readDatagrams.count else {
|
|
simpleTunnelLog("\(self.identifier): Received EOF on the UDP flow. Closing the flow...")
|
|
// self.tunnel?.sendCloseType(.write, forConnection: self.identifier)
|
|
self.UDPFlow.closeReadWithError(nil)
|
|
return
|
|
}
|
|
|
|
self.datagramsOutstanding = readDatagrams.count
|
|
|
|
for (index, datagram) in readDatagrams.enumerated() {
|
|
guard let endpoint = readEndpoints[index] as? NWHostEndpoint else { continue }
|
|
|
|
let response = try! Message.init(deserialize: datagram)
|
|
for q in response.questions {
|
|
simpleTunnelLog("got name \(q.name)")
|
|
try? db?.insertDNSQuery(appId: self.UDPFlow.metaData.sourceAppSigningIdentifier as NSString,
|
|
dnsQuery: q.name as NSString)
|
|
}
|
|
|
|
simpleTunnelLog("(\(self.identifier)): Sending a \(datagram.count)-byte datagram to \(endpoint.hostname):\(endpoint.port)")
|
|
// Send a data message to the SimpleTunnel server.
|
|
// self.sendDataMessage(datagram, extraProperties:[
|
|
// TunnelMessageKey.Host.rawValue: endpoint.hostname as AnyObject,
|
|
// TunnelMessageKey.Port.rawValue: Int(endpoint.port)! as AnyObject
|
|
// ])
|
|
self.conn?.writeDatagram(datagram, completionHandler: { conError in
|
|
simpleTunnelLog("write con err: \(String(describing: conError))")
|
|
})
|
|
}
|
|
|
|
// self.UDPFlow.writeDatagrams(readDatagrams, sentBy: readEndpoints) { writeError in
|
|
// simpleTunnelLog("write error \(String(describing: writeError))")
|
|
// }
|
|
}
|
|
}
|
|
|
|
/// Send a datagram received from the SimpleTunnel server to the destination application.
|
|
func sendDataWithEndPoint(_ data: Data, host: String, port: Int) {
|
|
let datagrams = [ data ]
|
|
let endpoints = [ NWHostEndpoint(hostname: host, port: String(port)) ]
|
|
|
|
// Send the datagram to the destination application.
|
|
UDPFlow.writeDatagrams(datagrams, sentBy: endpoints) { error in
|
|
if let error = error {
|
|
simpleTunnelLog("Failed to write datagrams to the UDP Flow: \(error)")
|
|
// self.tunnel?.sendCloseType(.read, forConnection: self.identifier)
|
|
self.UDPFlow.closeWriteWithError(nil)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public func simpleTunnelLog(_ message: String) {
|
|
// NSLog("PSI: \(message)")
|
|
}
|
|
|