iOS 17 mDNS IP resolving issue

Hello there,

We have an iPad application which uses mDNS to find specific devices on the network then it resolves an IP address so then the application can connect to it through websocket. It has been working for years now. Recently our clients started to update their iPads to iOS 17 and suddenly this functionality stopped working.

When I wanted to test out what's going on I found out that when I run the application on an iPad simulator on my macbook it can resolve the IP address immediately but when I run it on an iPad it cannot. That seemed weird so I decided to look into the code and I saw that the NetServiceBrowser api had been deprecated and I thought that maybe that's the problem so I refactored the code to use NWBrowser which was rather easy it found the service, but then when I wanted to meg an NWConnection to it the same error happened. From macOS it works fine but on the iPad the connection's state never gets ready, it hangs on the preparing state.

I created a new test application just with this functionality to test it on an iPhone too. Well it seems that the issue is appearing on the iOS too.

One other thing to mention, I created a simple node.js application which uses mDNS broadcast to simulate this device which we're trying to connect. The weird part that both the iPad and the iPhone can resolve it's address.

I'm curious if something has changed since iOS 16, I couldn't find anything and I don't know where to go next, or how can somebody reproduce this error without the device. Any help is appreciated.

Here is my discovery code:

import UIKit
import Network

class ViewController: UIViewController {
    
    var browser: NWBrowser!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        browser = NWBrowser(for: .bonjour(type: "_http._tcp", domain: ""), using: .tcp)
        
        browser.stateUpdateHandler = { newState in
            switch newState {
            case .failed(let error):
                print("NW Browser: now in Error state: \(error)")
                self.browser.cancel()
            case .ready:
                print("NW Browser: new bonjour discovery - ready")
            case .setup:
                print("NW Browser: ooh, apparently in SETUP state")
            default:
                break
            }
        }
        
        browser.browseResultsChangedHandler = { ( results, changes ) in
            print("NW Browser: Scan results found:")
            for result in results {
                switch result.endpoint {
                case let .service(name: name, type: _, domain: _, interface: _):
                    // All of our device has 'justfit' in their name
                    if name.uppercased().contains("JUSTFIT"){
                        print(name)
                        let proto: NWParameters = .tcp
                        if let opt = proto.defaultProtocolStack.internetProtocol as? NWProtocolIP.Options {
                            opt.version = .v4
                        }
                        let connection = NWConnection(to: result.endpoint, using: proto)
                        connection.stateUpdateHandler = { state in
                            if state == .ready {
                                if let path = connection.currentPath, let endpoint = path.remoteEndpoint {
                                    switch(endpoint) {
                                    case let .hostPort(host: host, port: port):
                                        print("IP: \(host), port: \(port)")
                                        break
                                    default:
                                        break
                                    }
                                    connection.cancel()
                                }
                            } else {
                                print(state)
                            }
                        }
                        connection.start(queue: .main)
                    }
                default:
                    break
                }
            }
        }
        browser.start(queue: .main)
    }
}

Replies

Also one other thing I checked is that in wireshark I tried to see what packets are being sent.

From the computer I can see the following:

3348	170.255747	192.168.0.109	224.0.0.251	MDNS	124	Standard query 0x0000 PTR _http._tcp.local, "QU" question A justfit2f2e50.local, "QU" question PTR justfit2f2e50._http._tcp.local

But from the iPad on this gets sent:

6559	370.688798	192.168.0.102	224.0.0.251	MDNS	104	Standard query 0x0000 PTR _http._tcp.local, "QU" question PTR justfit2f2e50._http._tcp.local

The fact that Network framework has no mechanism for resolving a service to a DNS name and IP address set is a known issue (r. 73266838). If you want to track the state of that, file your own bug about this and ask that it be marked as a dup of that one.

In terms of workarounds, the approach you’re using is less than ideal because it’s unnecessarily connecting to the service. A better option is to drop down to a lower-level API. Historically that was NSNetService, and now it’s DNS-SD. See this post.

Share and Enjoy

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

Have you solved it? I also encountered the same problem. IOS17 devices cannot resolve the device IP, but IOS16 can. At the same time, I found that IOS17 can parse the IP of ESP32 through mdns, but the same code cannot parse the ESP8266.

Add a Comment

Unfortunately I wasn't able to solve it with DNS-SD either. However managed to do a workaround with this sample code: https://developer.apple.com/library/archive/samplecode/SimplePing/Introduction/Intro.html

I pinged the IP addresses on the local network.