This chapter describes the Bonjour operations of service publication, browsing, and resolution that underlay the three network service API layers, and the API layers themselves. You should read this chapter if you want to write an application or tool that publishes or discovers network services.
The network services architecture in Bonjour includes an easy-to-use mechanism for publishing, discovering, and using IP-based services. Bonjour supports three fundamental operations, each of which is a necessary part of zero-configuration network services:
Publication (advertising a service)
Discovery (browsing for available services)
Resolution (translating service instance names to addresses and port numbers for use)
These operations are discussed in detail in the following sections.
To publish a service, an application or device must register the service with a Multicast DNS responder, either through a high-level API or by communicating directly with the responder (
mDNSResponder). Bonjour also supports storing records on conventional DNS servers as well, using Dynamic DNS Update.
When a service is registered, three related DNS records are created: a service (SRV) record, a pointer (PTR) record, and a text (TXT) record. The TXT record contains additional data needed to resolve or use the service, although it is also often empty.
The SRV record maps the name of the service instance to the information needed by a client to actually use the service. Clients then store the service instance name as a persistent way to access the service, and perform a DNS query for the host name and port number when it’s time to connect. This additional level of indirection provides for two important features. First, the service is identified by a human-readable name instead of a domain name and port number. Second, clients can access the service even if its port number, IP address, or host name changes, as long as the service name remains the same.
The SRV record contains two pieces of information to identify a service:
The host name is the domain name where the service can currently be found. The reason a host name is given instead of a single IP address is that it could be a multi-homed host with more than one IP address, or it could have IPv6 addresses as well as IPv4 addresses, and so on. Identifying the host by name allows all these cases to be handled gracefully.
The port number identifies the UDP or TCP port for the service.
SRV records are named according to the following convention:
<Instance Name>.<Service Type>.<Domain>
<Instance Name>, the name of a service instance, can be any UTF-8-encoded Unicode string, and is intended to be human readable.
<Service Type> is a standard IP protocol name, preceded by an underscore, followed by the host-to-host transport protocol (TCP or UDP), also preceded by an underscore. For example, a Trivial FTP service running over UDP would have a service type of
_tftp._udp, and an IPP printing service running over TCP would register under the
_ipp._tcp service type. The list of official protocol names is maintained by IANA; see Domain Naming Conventions for more information.
<Domain> is a standard DNS domain. This may be a specific domain, such as
apple.com., or the generic suffix
local. for a service accessible only on the local link.
Here is an example of the SRV record (in the standard DNS record format) for a print spooler named
PrintsAlot running on TCP port
PrintsAlot._printer._tcp.local. 120 IN SRV 0 0 515 blackhawk.local.
This record would be created on the Multicast DNS responder of a printer called
blackhawk.local. on the local link. The initial
120 represents the time-to-live (TTL) value which is used for caching. The two zeros are weight and priority values, used in traditional DNS when choosing between multiple records that match a given name; for multicast DNS purposes, these values are ignored.
For more information about domain, service, and instance names, see Domain Naming Conventions.
PTR records enable service discovery by mapping the type of the service to a list of names of specific instances of that type of service. This record adds yet another layer of indirection so services can be found just by looking up PTR records labeled with the service type.
The record contains just one piece of information, the name of the service instance (which is the same as the name of the SRV record). PTR records are accordingly named just like SRV records but without the instance name:
Here is an example of a PTR record for a print spooler named
_printer._tcp.local. 28800 PTR PrintsAlot._printer._tcp.local.
28800 is the time-to-live (TTL) value, measured in seconds.
The TXT record has the same name as the corresponding SRV record, and can contain a small amount of additional information about the service instance, typically no more than 100–200 bytes at most. This record may also be empty. For example, a network game could advertise the name of the map being used in a multiplayer game, and a chat program could advertise the availability of the user (for example, idle, away, or available). If you need to transmit larger amounts of data, the host should establish a connection with the client and send the data directly.
Historically, this record has been used for multiple services running on the same port at the same IP address, for example multiple print queues running on the same print server. In this case additional information in the TXT record can be used to identify the intended print queue, as shown in this example:
Queue name (from TXT record)
This kind of practice was necessary because service types have historically been associated with well known ports. Designers of new Bonjour protocols are encouraged to run each instance of their service on a different dynamically allocated port number, instead of trying to run them all on the same well known port number and using extra information to specify which instance the client is trying to talk to.
The nature and format of the data in the TXT record are specific to each type of service, so each new service type needs to also define the format of data for its associated TXT record (if any), and publish this format as part of the protocol specification.
Publication: An Example
For a concrete example, consider a hypothetical device that shares music over a local network—an IP-enabled jukebox. Suppose that its transport protocol is TCP and its application protocol goes by the name
music. When someone plugs the device into an Ethernet hub, a number of things happen, as shown in Figure 4-1.
In step 1, the device randomly selects the link-local IP address
169.254.150.84, randomly selected from the IPv4 link-local range
169.254.0.0 with a subnet mask of
255.255.0.0, and announces it to the network. Because no devices respond to the announcement, the device takes the address as its own. In step 2, it starts up its own Multicast DNS responder, requests the host name
eds-musicbox.local., verifies its availability, and takes the name as its own. Next, in step 3, the device starts up a music sharing service on TCP port
1010. Finally, in step 4, it publishes the service, of type
_music._tcp, under the name
Ed’s Party Mix, in the
local. domain, first making sure that no service exists under the same name. This creates two records:
An SRV record named
Ed’s Party Mix._music._tcp.local.that points to
eds-musicbox.local.on TCP port
A PTR record named
_music._tcp.local.that points to the
Ed’s Party Mix._music._tcp.local.service.
Service discovery makes use of the DNS records registered during service publication to find all named instances of a particular type of service. To do this, an application performs a query for PTR records matching a service type, such as
_http._tcp, usually through a higher-level API. The Multicast DNS responders running on each device return PTR records with service instance names.
Figure 4-2 illustrates a client application browsing for music sharing services. In step 1, the client application issues a query for services of type
local. domain to the standard multicast address
. Every Multicast DNS responder on the network hears the request, but only the music sharing device responds with a PTR record (step 2). The resulting PTR record holds the service instance name,
Ed's Party Mix._music._tcp.local. in this case. The client app can then extract the service instance name from the PTR record and add it to an onscreen list of music servers.
Service discovery typically takes place only once in a while—for example, when a user first selects a printer. This operation saves the service instance name, the intended stable identifier for any given instance of a service. Port numbers, IP addresses, and even host names can change from day to day, but a user should not need to reselect a printer every time this happens. Accordingly, resolution from a service name to socket information does not happen until the service is actually used.
To resolve a service, an application performs a DNS lookup for a SRV record with the name of the service. The Multicast DNS responder responds with the SRV record containing the current information.
Figure 4-3 illustrates service resolution in the music sharing example. The resolution process begins with a DNS query to the multicast address
18.104.22.168 asking for the
Ed’s Party Mix._music._tcp.local. SRV record (step 1). In step 2, this query returns the service’s host name and port number (
1010). In step 3, the client sends out a multicast request for the IP address. In step 4, this request resolves to the IP address
169.254.150.84. Then the client can use the IP address and port number to connect to the service. This process takes place each time the service is used, thereby always finding the service’s most current address and port number.