How to handle NEAppProxyUDPFlow for Https Requests?

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.

https://github.com/Bouke/DNS


    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) }
    }
}
  • Hi, The reason of using API call inside datagrams is, our backend server needs track of all https requests and the data that server send in response of API call, it will decide whether this particular url should be processed/accessible or not.

Add a Comment

Replies

Hi @eskimo, @meaton Can you help me out in this?

Keep in mind that DevForums is not an official support channel. If you want formal support, open a DTS tech support incident.

Looking at your code, the only ‘obvious’ error is in your flowIn(…) method where, on successfully sending your response, you close the flow. That’s not correct. You are expected to leave the flow open so that you can receive more DNS requests for the same flow.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

  • +1 for what Quinn mentioned but also I would be concerned about the lifetime of each of your NEAppProxyUDPFlow objects. You might find it easier if you create an entirely new class for handling each one of these flows. That way you can easily track the lifetime of these objects through your code.

  • Sure @meaton, I will try it. But before that I had one query I asked in above question that is, "is this ok to use API call inside datagrams for loop and then send data to writedatagrams?"

Add a Comment

Ok Thanks @eskimo, I will create new TSI for this.