Bonjour, CUPS: The dnssd Backend Explained
Probably the most pervasive system for printer discovery – at least in the SOHO market – is Apple’s Bonjour. It’s based on mDNS, which provides decentralized name announcement and resolution. DNS-SD is layered on top of mDNS to provide a mechanism for service discovery. By their powers combined, clients can find services provided by other nodes on their local network (i.e. networked printers) without needing a central directory or any manual configuration. Apple has published a standard for how network printers should advertise themselves.
CUPS has a special backend for working with Bonjour printers called
dnssd. It provides two distinct functions: browsing for new printers
and addressing of existing printers. Unfortunately it doesn’t seem to
have any real documentation. Usually it Just Works™, but I recently
hit a case where it didn’t and had to go read the source code. I’ve
documented what I found for future reference.
When invoked in discovery mode (as
dnssd with no arguments), the
dnssd backend requests that the local mDNS responder browse for
service records with any of these types:
_riousbprint._tcp(only with mDNSresponder, not Avahi)
The backend then resolves each of the discovered services. If an IPP or
IPPS service record includes the non-standard property
it’s assumed to be shared by a CUPS server. If an LPD service includes
that key it’s assumed to be an LPR gateway for a CUPS server and
ignored. The backend then chooses the best service for each device
following the priority scheme laid out in the standard and reports it
using the standard
backend(7) discovery format, with
the fields set as described below.
- Always set to
- A URL is built using
dnssdas the scheme and the fully-qualified service name as the host. If the service is assumed to be hosted by CUPS the path is set to
/is used. The URI will end up looking like this:
- The model is taken from
product(with the parentheses removed), or
ty, whichever comes last in the service record. The make is taken from the value of the
usb_MFGkey or, if none of the
usb_keys are present and a model key is, from the first word of the model. The make and model are then concatenated separated by a single space. This causes the first workd of the model be repeated if
- Set to the unqualified service name.
- Any properties starting with
usb_CMD) will be used to set the matching parts of the IEEE 1284 device ID string. If no
usb_properties were found, but a model name was seen from a different property, the
MDL1284 properties are set from the make and model strings determined under
device-make-and-model, above. If the
CMD1284 property is not set it’s generated from the
If both the
CMDvalues are generated as described above, a bug causes the semicolon separating them to be omitted. This will cause a device ID that looks like this:
MFG:HP;MDL:LaserJet 4050 Series CMD:PS,PCL;
Such a device ID string prevents automatic driver detection as the CMD property is read as part of the model. This bug was fixed in CUPS v2.2b1 and the fix will presumably be included in v2.2.
- Never set, even though the standard provides the
notefield for this purpose.
dnssd backend is invoked as a normal backend it attempts to
resolve the device URI, then turns around and calls the backend
indicated by the service type. This behavior is somewhat redundant as
all the built-in backends will resolve mDNS service names on their own
dnssd backend doesn’t support non-built-in backends. Perhaps
it’s done that way in case the service properties change without
changing the fully qualified service name.
The hostname will be resolved as a service name if it contains the
._tcp. Resolution will first be attempted in the
domain, then in the domain specified in the URL. If the device fails to
respond it’s marked offline. Otherwise, a new URI is generated based on
the DNS-SD service record. The scheme is set according to the following
|Service Type||Scheme /Backend|
The hostname is set to the one advertised in the service record, usually
an mDNS hostname. If the service advertises a hostname in the
domain but the original URI used a different domain, the resolver will
attempt to find a non-local name for the host by looking up its IP
address and then looking up the reverse DNS entry of that IP. If the
reverse hostname is not in the
local domain it will be used instead of
the one advertised by the service.
The port is taken from the service record. The path is set to the value
rp service property. For
rpo is used
instead. If the service is one of the IPP variants the query is set to
dnssd backend then invokes the backend of the same name as the
resolved URI scheme with the resolved URI. It passes all other arguments
and the standard file descriptors on intact.