Files
appchk-app/GlassDNS/DNSProxyProvider.swift
2020-02-07 16:08:57 +01:00

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)")
}