Hey @scalo and @eskimo, this might seem a bit off topic from the question but am trying to do something similar. I am trying to figure out a way to send the data received with udpFlow.readDatagrams() to a custom DNS server. Could you please tell me how you have implemented the addEDNS0(datagrams) and the send(modifiedData) functions. That'll be really helpful. Thanks!
Post
Replies
Boosts
Views
Activity
@Matt Thanks for that.
I would recommend even a separate outbound datagram class for each of these NWConnection's that is being opened from your flow.
Yep, I've implemented this, creating a new NWConnection for each datagram received when reading the flow, but it still doesn't seem to be working. I seem to be getting a lot of (3303290176): Closing reads, not closed by plugin and (3303290176): Closing writes, not sending close error messages.
Currently this is what I have implemented. The NEDNSProxyProvider class after handleNewFlow and flow.open is called:
swift
private func handleData(for flow: NEAppProxyUDPFlow) {
flow.readDatagrams(completionHandler: { (data, endpoint, error) in
if let error = error {
NSLog("DNSProxyProvider UDP read data Error : \(error.localizedDescription)")
return
} else {
if let datagrams = data, let _ = endpoint, !datagrams.isEmpty {
self.outBoundCopier(flow: flow, datagrams: datagrams)
}
}
})
}
private func outBoundCopier(flow: NEAppProxyUDPFlow, datagrams: [Data]) {
for data in datagrams {
NSLog("DNSProxyProvider starting connection")
let dnsServer = DNSServerConnection(flow: flow, data: data)
dnsServer.start()
dnsServer.onDataReceived = { (flow, data, isComplete, error, endpoint) in
self.inBoundCopier(flow: flow, data: data, isComplete: isComplete, error: error, endPoint: endpoint)
}
}
}
private func inBoundCopier(flow: NEAppProxyUDPFlow, data: Data?, isComplete: Bool?, error: NWError?, endPoint: NWHostEndpoint) {
switch(data, isComplete, error) {
case (let data?, _ , _):
flow.writeDatagrams([data], sentBy: [endPoint], completionHandler: { (error) in
if let error = error {
NSLog("DNSProxyProvider UDP write Error : \(error.localizedDescription)")
}
NSLog("DNSProxyProvider UDP write completed")
})
case(_, true, _):
flow.closeReadWithError(error)
flow.closeWriteWithError(error)
NSLog("DNSProxyProvider inbound copier completed")
case (_, _, let error?):
NSLog("DNSProxyProvider inbound copier Error : \(error.localizedDescription)")
default: NSLog("DNSProxyProvider inbound copier error")
}
}
And the DNSServerConnection class which handles all the NWConnection's
swift
class DNSServerConnection {
private let connection: NWConnection
private let data: Data
private let flow: NEAppProxyUDPFlow
private let endPoint = NWHostEndpoint(hostname: CUSTOM_DNS_IP, port: CUSTOM_DNS_PORT)
var onDataReceived: ((NEAppProxyUDPFlow, Data?, Bool?, NWError?, NWHostEndpoint) - ())?
init(flow: NEAppProxyUDPFlow, data: Data) {
self.flow = flow
self.data = data
connection = NWConnection(host: CUSTOM_DNS_IP, port: CUSTOM_DNS_PORT, using: .udp)
}
func start() {
connection.stateUpdateHandler = connectionStateChanged(to:)
connection.start(queue: .main)
}
private func connectionStateChanged(to state: NWConnection.State) {
switch state {
case .waiting(let error):
NSLog("DNSProxyProvider Error opening connection: \(error.localizedDescription)")
case .ready:
NSLog("DNSProxyProvider connection ready")
sendDataToServer()
case .failed(let error):
connection.cancel()
NSLog("DNSProxyProvider Error opening connection: \(error.localizedDescription)")
default: break
}
}
private func sendDataToServer() {
connection.send(content: data, completion: .contentProcessed({ (error) in
if let error = error {
NSLog("DNSProxyProvider UDP outbound Error : \(error.localizedDescription)")
}
NSLog("DNSProxyProvider UDP outbound Success")
self.receiveData()
}))
}
private func receiveData() {
connection.receive(minimumIncompleteLength: 1, maximumLength: 2048, completion: { [self] (data, _, isComplete, error) in
if (isComplete) {
NSLog("DNSProxyProvider UDP outbound completed")
self.connection.stateUpdateHandler = nil
self.connection.cancel()
}
onDataReceived?(flow, data, isComplete, error, endPoint)
})
}
}
@Matt thanks, that was really helpful. Better management of threads and writing datagrams to the correct end point did the trick.
I came across NWConnection.batch and was wondering would it be better to do a batch send of datagrams as opposed to creating NWConnection's for each outgoing datagram. Would a solution like this be better ?
Also, could you please advise on the best way to modify the DNS packets received in flow.readDatagrams before sending them to a custom DNS server ?
Hey @dmytrofromukrainka that lib seems to be for Mac OS but I am working with iOS, anyways thanks for the heads up!
Hey @dmytrofromukrainka could you please tell me a bit on how did you add EDNS0 to the packets using the DNS library there ? I can see the DNSOPTResourceRecord class in the lib but not sure if thats useful for this. Have you used this class as well ?