How to and Where to add our own custom DNS Url.

Im working on project where I am want to use DNSProxy network extension to our app in iOS and Mac OS. We are able to add DNSProxy extension successfully and getting the call in startProxy and handleNewFlow Method of DNSProxyProvider in iOS. But I have big confusion on the steps and followed used to process the flow and pass it to datagrams and entire implementation.

Here is my confusion:

I have my own DNS resolver API which takes name of remoteHostName as query parameter and return the response in JSON.

  1. First confusion is as per the DNSProxy Documentation every flow needs to processed through these steps, openLocaleEndpoints, ReadDataGrams, outboundCopier (Read in some developer threads), InboundCopiers and finally to WriteDataGrams. Am I correct here?
  2. If above steps are correct, to process each flow then how can I use my own DNS API resolver in between these predefined steps. and How and where to use that response data sent my custom DNS API?
  3. Currently when Im making DNSProxy active then immediately device internet connection getting interrupted, this might be because we are not hanging the flow properly in handleNewFlow Method. Am I correct here?

If you have your own resolver code that is not based on the DNS message format then, for each flow, you need to:

  1. Read DNS query messages off the flow.

  2. Parse them into the format needed by your resolver.

  3. Send it to your resolve.

  4. Get the response.

  5. Format it into a DNS reply message.

  6. Write that to the flow on which you received the query.

With regards steps 1 and 6:

  • UDP flows work in terms of UDP datagrams and each datagram holds exactly one DNS message, so there’s nothing special you need to do there.

  • TCP flows work in terms of a byte stream. Within that stream the DNS messages are framed in the standard DNS-over-TCP mechanism (each DNS message has a 2 byte length header). In step 1 you must unframe the stream to get DNS messages and in step 6 you must frame your DNS messages into a stream.

Share and Enjoy

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

Hi @eskimo,

Thanks for reply,

This is what I have done so far:

override func handleNewFlow(_ flow: NEAppProxyFlow) -> Bool {
        NSLog("DNSProxyProvider: handleFlow")
        
        if #available(iOSApplicationExtension 14.2, *) {
            hostName = flow.remoteHostname!
        } else {}
        if let udpFlow = flow as? NEAppProxyUDPFlow {
            let localHost = (udpFlow.localEndpoint as! NWHostEndpoint).hostname
            let localPort = (udpFlow.localEndpoint as! NWHostEndpoint).port

            proxyUDPFlow = udpFlow
            open()
        }
        
        return false
    }
    
    func open() {
        
        guard let flow = proxyUDPFlow else { return }
        guard let endPoint = flow.localEndpoint as? NWHostEndpoint else { return }
        
        flow.open(withLocalEndpoint: endPoint) { (error) in
            if (error != nil) {
                NSLog("DNSProxyProvider UDP Open flow Error : \(error.debugDescription)")
            } else {
                NSLog("DNSProxyProvider UDP Open flow Success")
                self.handleData(for: flow)
            }
        }
    }
   

    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,endPointValue: (flow.localEndpoint as? NWHostEndpoint)!)
              }
            }
          })
        }

     func outBoundCopier(flow: NEAppProxyUDPFlow, datagrams: [Data], endPointValue:NWHostEndpoint) {
          
         Read DNS query messages off the flow.
         Parse them into the format needed by your resolver.
         Send it to your resolve.
         Get the response.
         Format it into a DNS reply message.

         Send it to inBoundCopier to Write that to the flow on which you received the query.
    }
        
    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)")
            }
              else{
                  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")
        }
    }


Read DNS query messages off the flow.

This Im assuming extract/read each data object from datagrams array.

Parse them into the format needed by your resolver.

Basically we have an API that takes hostname as a query parameter, that sends us Json response. There is not any specific format we use for our resolver.

Basically we have an API that takes hostname as a query parameter, that sends us Json response. There is not any specific format we use for our resolver.

Well, yes there is:

  • Unless your API access DNS query messages, you have to parse the query to extract the info needed by your API.

  • You have to render the JSON response into a DNS response message.

Share and Enjoy

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

How to and Where to add our own custom DNS Url.
 
 
Q