Perhaps this could be repetitive or basic question but I have implemented following code of NEDNSProxyProvider. My basic requirement is I want to process the flow but instead of using data from datagrams I want to use data received from our custom DNS server. After tons of articles documentation Im able to write following code. But it's failing continuously in writeDataGrams with "Invalid arguments data" and "The operation could not be completed because Flow not connected". I know somethings is wrong in processing the data but what is wrong Im not able to figure out. Also I want to know is this even possible to achieve this by using API call inside datagrams for loop and then send data to writedatagrams?
After getting JSONResponse Im using third party library to convert query form JSONData binary before sending it to writeDataGrams.
override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
NSLog("DNSProxyProvider: handleFlow")
var handled: Bool = false
if #available(iOSApplicationExtension 14.2, *) {
hostName = flow.remoteHostname!
}
if let udpFlow = flow as? NEAppProxyUDPFlow {
udpFlow.open(withLocalEndpoint: udpFlow.localEndpoint as? NWHostEndpoint) { error in
if error == nil {
self.flowOut(flow as! NEAppProxyUDPFlow)
} else {
NSLog("Error in opening Flow")
}
}
handled = true
} else {
handled = false
NSLog("Unsupported Flow")
}
return handled
}
/*
Read From flow, then write to remote endpoint.
*/
private func flowOut(_ flow: NEAppProxyUDPFlow) {
flow.readDatagrams(completionHandler: { (datagrams, endpoints, error) in
self.proxyUDPFlow = flow
if error != nil {
NSLog("ERROR: 'readDatagramsWithCompletionHandler' failed with: \(String(describing: error?.localizedDescription))")
return
}
if datagrams?.count == 0 {
flow.closeReadWithError(error)
flow.closeWriteWithError(error)
return
}
guard let dataArray = datagrams else { return }
if #available(iOSApplicationExtension 14.2, *) {
for (index, data) in dataArray.enumerated() {
var hostEndPoint: NWHostEndpoint = endpoints?[index] as! NWHostEndpoint
hostEndPoint = NWHostEndpoint(hostname: hostEndPoint.hostname, port: hostEndPoint.port)
guard let hostname = flow.remoteHostname else { return }
let dNSRequest = self.configureDNSRequest(hostname)
let urlsession = URLSession.shared.dataTask(with: dNSRequest) { data, response, error in
if let data = data {
do {
let reply = try JSONDecoder().decode(JSONReply.self, from: data)
let requestQuery = Message(
type: .response,
questions: [
Question(name: reply.questions[0].name, type: .pointer)
])
let requestData = try requestQuery.serialize()
self.flowIn(responsdata: requestData, flow, endpoint: hostEndPoint)
} catch let error {
print("error \(error)")
}
}
}
urlsession.resume()
}
}
})
}
private func flowIn(responsdata: Data, _ flow: NEAppProxyUDPFlow, endpoint: NWHostEndpoint) {
let resultData = Data(responsdata)
flow.writeDatagrams([resultData], sentBy: [endpoint], completionHandler: { error in
// Flow not connected
if error != nil {
os_log("Error in resolving query \(error)")
self.logger.log("error => \(error)")
} else {
self.proxyUDPFlow?.closeReadWithError(error)
self.proxyUDPFlow?.closeWriteWithError(error)
}
})
}
private func configureDNSRequest(_ hostName: String) -> URLRequest {
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = “customserver.com"
urlComponents.path = “/resolverquery"
urlComponents.queryItems = [
URLQueryItem(name: "name", value: hostname),
URLQueryItem(name: "type", value: "A")
]
guard let url = urlComponents.url else { assert(false) }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue(“xyzabcd”, forHTTPHeaderField: "Client-ID")
request.setValue("*/*", forHTTPHeaderField: "Accept")
request.setValue("keep-alive", forHTTPHeaderField: "Connection")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
return request
}
}
extension Data {
func object<T>(at index: Index = 0) -> T {
subdata(in: index..<self.index(index, offsetBy: MemoryLayout<T>.size))
.withUnsafeBytes { $0.load(as: T.self) }
}
}