nw_endpoint_type_bonjour_service offers no port or TXT record?

It seems endpoints of type nw_endpoint_type_bonjour_service offer no port number or TXT record. Or at least I am not smart enough to find the functions which provides these things. 😀

The functions with names most suggestive of providing these things don't work, which isn't surprising since they are documented (in the header) to work only for endpoints of other types.

This seems like a massive oversight, so I am doubting my sanity. Surely nearly everybody who wants to use nw_endpoint_type_bonjour_service wants a port number and many will want a TXT record.

I have resorted to calling DNSServiceResolve to fill the gap. It works but seems… drastic. It makes me wonder why I don't bypass nw_browser_create in favor of DNSServiceBrowse in the first place.

What am I missing?

Answered by DTS Engineer in 746791022

It seems endpoints of type nw_endpoint_type_bonjour_service offer no port number or TXT record.

Right. A Bonjour endpoint describes how to connect to a service and for those you don’t need the host name, port number, or TXT record.

What am I missing?

For the host name and port number, our recommendation is that you connect to the service using a connect-to-service API. The most common case here is that you’re Network framework to connect, where you pass a Bonjour endpoint directly to nw_connection_create and it will internally resolve the service and connect.

There are two common situations where you end up needing to resolve a service:

  • When using a API that doesn’t support connect-to-service semantics, for example, BSD Sockets

  • When forming a URL to pass to, say, Safari

In that case, yeah, you have to resolve the service yourself using other APIs. I tend to do this using NSNetService [1] but it’s fine to use DNSServiceResolve.

For the TXT record, usually folks want that as part of browsing and so get that from the nw_browse_result_t, after setting nw_browse_descriptor_set_include_txt_record.

Share and Enjoy

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

[1] See this post.

Accepted Answer

It seems endpoints of type nw_endpoint_type_bonjour_service offer no port number or TXT record.

Right. A Bonjour endpoint describes how to connect to a service and for those you don’t need the host name, port number, or TXT record.

What am I missing?

For the host name and port number, our recommendation is that you connect to the service using a connect-to-service API. The most common case here is that you’re Network framework to connect, where you pass a Bonjour endpoint directly to nw_connection_create and it will internally resolve the service and connect.

There are two common situations where you end up needing to resolve a service:

  • When using a API that doesn’t support connect-to-service semantics, for example, BSD Sockets

  • When forming a URL to pass to, say, Safari

In that case, yeah, you have to resolve the service yourself using other APIs. I tend to do this using NSNetService [1] but it’s fine to use DNSServiceResolve.

For the TXT record, usually folks want that as part of browsing and so get that from the nw_browse_result_t, after setting nw_browse_descriptor_set_include_txt_record.

Share and Enjoy

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

[1] See this post.

Unsurprisingly, I accept that response.

I am a bit surprised to hear that I don't need the port number, but in the interests of sticking with Network rather than dropping down to DNSServiceResolve, I will take your word for it and see how far I can get.

I'll also try nw_browse_descriptor_set_include_txt_record, which I somehow missed.

Oh, hey, turns out I did not miss nw_browse_descriptor_set_include_txt_record. 😀 My code is still calling it!

Assuming my code sticks with DNSServiceResolve, it should stop also calling nw_browse_descriptor_set_include_txt_record.

But in the interest of ditching DNSServiceResolve rather than nw_browse_descriptor_set_include_txt_record

What I am likely to have done wrong such that the latter has so far yielded me no joy?

In case it helps, my code for this is pretty straightforward-looking (to me anyway):

   nw_browse_descriptor_t descriptor (nw_browse_descriptor_create_bonjour_service (bonjourType.UTF8String, nil));
  NSAssert (descriptor, @"%@ unable to create descriptor", self);
  nw_browse_descriptor_set_include_txt_record (descriptor, true);
  _browser = nw_browser_create (descriptor, nil);
  NSAssert (_browser, @"%@ unable to create browser", self);

And, just to close the circle, here's a snippet from my results handler:

  nw_endpoint_t endpoint = nw_browse_result_copy_endpoint (addition);
  NSAssert (endpoint, @"%@ unable to copy endpoint", self);
  char const * serviceName = nw_endpoint_get_bonjour_service_name (endpoint);
  NSAssert (serviceName, @"%@ unable to obtain service name", self);
  nw_txt_record_t txtRecord (nw_endpoint_copy_txt_record (endpoint));

We survive the assertions, but txtRecord gets nil.

This is consistent with what I remember from the day I shifted to DNSServiceResolve.

I am a bit surprised to hear that I don't need the port number

To understand that you need to look at what happens with DNS-SD on the ‘wire’. When you browse the client sends out multicast DNS queries for PTR records of the form _ttt._ppp.ddd, where ttt is the service type, ppp is the protocol, and ddd is the domain. These PTR records point to SRV records of the form nnn._ttt._ppp.ddd, where nnn is the user-visible service name. And that’s all the browse does. It returns a list of the found PTR records.

When you resolve, or use a connect-to-service API, the queries the SRV record. The reply contains a DNS name and a port number. It’s also expected to include, as additional records, the A and AAAA records required to resolve the DNS name. The key things to note here are:

  • You don’t get the DNS name or port number until you start the resolve operation.

  • Resolving generates traffic on the wire, so we recommend against resolving every service you find.

You can see this in action with the dns-sd tool:

% dns-sd -Q _ipp._tcp.local. PTR IN          
DATE: ---Tue 07 Mar 2023---
 9:11:18.704  ...STARTING...
Timestamp     A/R  …  Name              …  Rdata
 9:11:18.705  Add  …  _ipp._tcp.local.  …  Darth Inker._ipp._tcp.local.
…
^C
% dns-sd -Q "Darth Inker._ipp._tcp.local." SRV IN
DATE: ---Tue 07 Mar 2023---
 9:11:25.866  ...STARTING...
Timestamp     A/R  …  Name                            …  Rdata
 9:11:25.867  Add  …  Darth\032Inker._ipp._tcp.local. …  0 0 631 darth-inker.local.
…
^C

The reason why TXT record browsing is optional is that it generates more traffic. If you enable it, the browse has to query the TXT record for each PTR it finds. For example:

% dns-sd -Q "Darth Inker._ipp._tcp.local." TXT IN
DATE: ---Tue 07 Mar 2023---
 9:11:30.635  ...STARTING...
Timestamp     A/R  …  Name                            …  Rdata
 9:11:30.635  Add  …  Darth\032Inker._ipp._tcp.local. …  617 bytes: 09 74 78 …
…
^C

Share and Enjoy

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

It's not that I didn't believe you. 😀 It's merely that I'm so used to having a port number in hand that I was surprised to learn for this API it isn't required. In a perfect world, I'd prefer not to have to think about port numbers.

However, if nw_endpoint_copy_txt_record doesn't work for nw_endpoint_type_bonjour_service even after nw_browse_descriptor_set_include_txt_record (see above), then it's academic. If I need to resolve to obtain the TXT record then I might as well not throw away the port number which comes along with it.

nw_endpoint_type_bonjour_service offers no port or TXT record?
 
 
Q