Thanks to Apple, a lot of modern printers and scanners today come with AirPrint and AirScan, which allows seamless operation of capable devices over the network, e.g. from an iPhone or a Mac. Linux even went ahead and added support for that and now users of this OS can finally print too :)
However, due to how AirPrint works, it does not work across Layer 2 domains such as VLANs, or over IP-routed networks. Luckily, this is not a problem that can’t be solved. In this post I am publishing the process I followed to allow printing over any type of network, even across countries.
In order to understand the problem, we first need to look at how AirPrint works. It uses mDNS (which is a DNS server scoped for a single local area network only) to publish some information to all devices that are listening using multicast. Some terms that you may have heard that are related are Bonjour, Zeroconf, Avahi, etc.
That means that all devices that use this protocol listen for question from others in the same LAN and if they’re looking for a service that they’re offering (e.g. AirPrint in this example) then they respond with information on how they can access that service. This may include the IP address and the Port, as well as additional settings, e.g. whether duplex printing is supported.
Due to how multicast is used, all these messages about available services do not leave the local network. They are by design confined within the VLAN limits, because if they’re propagated past this border then others will learn this information, which may be sensitive.
But what happens if you want to access a specific printer that you put in a different network, for security reasons, from your device?
The easy solution to this problem is to use an mDNS repeater or an mDNS reflector. This is a service that typically runs on a router or some other device that has Layer 2 access to all the networks you’re interested in and listens for these messages on all of them. When it receives a question, it then forwards this to all the other networks, across the Layer 2 boundary. If your iPad on the Home VLAN asks, your router will relay this question to your IoT VLAN with the copier, listen for a response, and then send it back to you, as a proxy.
The problem with this solution is that typically everything is being shared. All devices in all participating / configured LANs will learn about every service in every other network. This may not be intended, and could lead into problems, when for example your Guest LAN can suddenly see your NAS, your TV, your speakers, etc.
Moreover, this assumes that all the LANs we are interested in are connected to a single device, which may not be true. What if we have two remote sites that still want (for some reason) to be able to see each other’s printers? We can’t enable mDNS repeating across all the Internet..
We need a way to selectively allow devices in any network to see other devices anywhere on our home, or even the entire Internet.
The concept of search domains
(or DNSSL in IPv6) exists for decades and allows a network administrator to
relay to all devices in a particular network what their FQDN is. It can be
configured for example in a DHCP server as search.example.com
, and then if
devices a
and b
(these are their hostnames) join, then they know that
they’re actually a.search.example.com
and b.search.example.com
. By design,
when a device is looking for local services, it will send a DNS query to this
domain name and ask it, too.
If this is not set, and .local
is used as a default, it has to resort to
traditional Layer 2 mechanisms using multicast. But this can be any “real”
domain name and the devices will then perform a “proper” DNS query, using their
configured DNS servers, over the Internet, and try to retrieve the results.
What all this means is that given a domain name that is resolvable over the Internet, we can add all the required DNS records for AirPrint to work there, and then any network that has this as its search domain will be able to retrieve the information.
Now that we know how we can publish any information to any device, simply by using a real domain name and configuring our router (DHCP or DNSSL), we need to know what our existing printer is publishing. This is more or less unique per printer and it’s better to figure out what is actually being set by the manufacturer. To do this, we need to connect our laptop in the same VLAN as the printer we want to use. Then, by using some tools we can find the record that is being published.
If you run:
avahi-browse -avr
you can get a list of all multicast services that are visible to your computer. This guide works with any device, including Apple TVs, HomePods, or anything else. AirPrint is only used as an example.
If you can’t get avahi-browse
on your computer, you can also run:
sudo tcpdump -vv -e -n "ip multicast"
which will also print (eventually) all the multicast DNS records. An example output that you may see is the following:
193.5.16.238.5353 > 224.0.0.251.5353: [udp sum ok] 0*- [0q] 1/0/6
_ipp._tcp.local. PTR PrinterName._ipp._tcp.local.
PrinterName.local. A 193.5.16.238,
PrinterName.local. AAAA 2a0d:3dc0:200:40:1337:ff:fe99:7331,
PrinterName._ipp._tcp.local. TXT "txtvers=1" "qtotal=1"
"pdl=application/octet-stream,image/urf,image/jpeg,image/pwg-raster"
"rp=ipp/print" "note=Printer Location" "ty=Printer Name"
"product=(PrinterModel)"
"adminurl=http://193.5.16.238/airprint.php" "priority=25"
"usb_MFG=Make" "usb_MDL=Model" "usb_CMD=PJL,PCL,PCLXL,URF" "Color=T" "Copies=T"
"Duplex=T" "Fax=T" "Scan=T" "PaperCustom=T" "Binary=T" "Transparent=T" "TBCP=F"
"rfo=ipp/faxout"
"URF=BLABLABLABLABLA"
"kind=document,envelope,label,postcard" "PaperMax=legal-A4"
"UUID=13377331-1337-7331-1337-b42273317331" "print_wfds=F",
PrinterName._ipp._tcp.local. SRV PrinterName.local.:631 0 0,
PrinterName.local. NSEC, PrinterName._ipp._tcp.local. NSEC (799)
Of course, some values e.g. the IP addresses will be different, as well as the
features supported by the printer (e.g. Scan
may be F
if it’s just a
printer, or -god forbid- Fax
may be F
and you’ll be left out of the
previous millennium).
This is a pretty good list of all the DNS records that we need to add. You can
see some TXT
, A
, AAAA
, SRV
, etc. Now we just need to add them to our
DNS server, and things should work just fine.
For this part you can use any domain that can be resolved by all your devices, even non-public ones. In order to eliminate an entire class of problems in the future for my setup, I used a real domain that can be resolved from anywhere on the Internet. I strongly recommend you do the same.
In addition to that, for simplicity and cleanliness, I decided to use an entire
domain just for this use. I picked kalimera.cy
, since “kalimera” is Greek
for.. Bonjour.
The DNS records below assume you have a single search domain, but what I actually found more convenient is to use a different domain per LAN, so I can control which devices are discoverable by which LAN. For example, I may want to give my family access to the Apple TV, and my family and guests access to the printer. If you have multiple subdomains you can play around and save on a few DNS records, but even repeating the entire thing once per LAN is fine.
lb._dns-sd._udp.zurich-family IN PTR zurich-family
b._dns-sd._udp.zurich-family IN PTR zurich-family
_cups._sub._ipp._tcp.zurich-family IN PTR officecopier._ipp._tcp.zurich-family
_universal._sub._ipp._tcp.zurich-family IN PTR officecopier._ipp._tcp.zurich-family
officecopier._ipp._tcp.zurich-family IN SRV 0 0 631 officecopier.zurich-family
officecopier._ipp._tcp.zurich-family IN TXT "txtvers=1" "qtotal=1" "pdl=application/octet-stream,image/urf,image/jpeg,image/pwg-raster" "rp=ipp/print" "note=Zurich Office" "ty=Printer Model" "product=(Printer Model)" "adminurl=http://193.5.16.238/airprint.php" "priority=25" "usb_MFG=Manufacturer" "usb_MDL=Model" "usb_CMD=PJL,PCL,PCLXL,URF" "Color=T" "Copies=T" "Duplex=T" "Fax=T" "Scan=T" "PaperCustom=T" "Binary=T" "Transparent=T" "TBCP=F" "rfo=ipp/faxout" "URF=BLABLA" "kind=document,envelope,label,postcard" "PaperMax=legal-A4" "UUID=13371337-1337-1337-1337-133713371337" "print_wfds=F"
officecopier.zurich-family IN A 193.5.16.238
officecopier.zurich-family IN AAAA 2a0d:3dc0:200:40::40
In the example above, zurich-family.kalimera.cy
is the search domain,
officecopier
is the name of the printer, Zurich Office
is its location, and
the two addresses in A
and AAAA
are its IP addresses.
If you now publish these DNS records, you add the domain in your DHCP or DNSSL, and just for good measure turn off and on again your WiFi on your phone, you will see that the printer is discoverable and all its capabilities (such as color, duplex printing, etc.) are being recognized by your device. It’s similar on macOS, Linux, etc.
You can add this search domain to any network, anywhere in the world, and it will still detect your printer. This doesn’t seem right..
The only thing we did so far was to add discoverability. We’re simply telling any device that asks where to find our printer. And sure, there’s a printer there (if the IP addresses are correct). But it doesn’t mean that the device can also print.
What the DNS records above say is that there’s an IPP server at
2a0d:3dc0:200:40::40
on port 631
with the capabilities described. Whether
you can reach this IP address, whether you’re allowed to talk to this port,
whether you’re allowed to print or not, is up to you to ensure and discover.
You have to enable AirPrint on your printer, you have to add a firewall to only allow connections that you trust to this printer, etc. What we did above is just an advertisement. Even if you hide this, your device is still discoverable in port scans, so even if you don’t publish this on your guest network, if they can reach the IP and Port of your printer then they can print just fine.
Based on the above, for AirPrint to work, you simply need to have some DNS records to tell the devices about your printer, plus have the IPP protocol enabled and reachable by the clients. Since we’re outsourcing the DNS part to a server, your printer only has to do the IPP part.
Moreover, if you have a non-networked printer, such as one that you can connect
to via USB, if you leave it permanently attached to a Linux desktop, or even a
Raspberry Pi, you can
easily
turn it into a networked printer. Then, just look at what your linux computer
publishes in mDNS and change the TXT
record above accordingly. Just a
warning: I’ve only did this with AirPrint, not AirScan, so I am not sure if you
can also expose your scanner too.
Based on the content above, you may be left wondering whether you need something that I used. Hopefully this section can clarify things:
No, you can follow all of the above on an entirely IPv6-only network.
No, but maybe you should consider deploying it some day ;)
No, the above works perfectly fine with private IPv4 and/or private IPv6 addresses. There’s no need for public ones. Just make sure that all the devices you want to be able to print can reach the private IP of the printer.
No, you can use subdomains of an existing one. I just wanted to keep things simpler and cleaner on my side, and this is why I went with a completely new domain name.
No, you can configure your internal resolver (e.g. dnsmasq
) to serve these
records and it will still work. Just make sure that all devices that need to
print use your DNS servers (and therefore receive these records).
Finally, I’ll try to predict some frequently answered questions and provide some content here, in hopes that you satisfy your curiosity.
We’re merely setting DNS records that point to a service here. There’s nothing to secure. What you need to do is configure your printer and your firewall accordingly to make sure only allowlisted IP addresses can reach it. Of course, you can also configure authentication in order to print, on the application layer. These are all out of scope.
Just as someone can find your SSH server on the Internet without having a DNS record pointing to it they can also find and connect to your printer if you don’t take appropriate measures.
Yes, you can add multiple printers and they will all show up on your device. Just add more DNS records on the same search domain and they will all be discoverable. Of course, you need to use a different printer name, so you can differentiate between them.
Yes, I am also able to use the above and have access to my scanners via AirScan too.
Most likely. The method above can publish any DNS record you want and make it accessible to any network you want. In theory, all multicast-discovered services can be extended. I can think of two issues that I came across that may prevent your device from working:
Some devices don’t accept communication from outside their Layer 2 network. Even if traffic reaches them, it is ignored or dropped. Your device will discover their existence, but won’t be able to talk to them. What I did in this case was NAT all traffic from legitimate sources going to the device’s IP and that made it all appear as if it was coming from the router’s IP, which is of course within the LAN.
Some devices don’t have a static DNS record like the AirPrint ones. They may
publish different content on every advertisement or depending on their state.
Obviously if you retrieve e.g. the TXT
record once, it will not be valid
forever. The only solution I found for these ones is to write some software
that will listen for these message and update the global DNS zone that you
have. This is unfortunately a bit tricky. Luckily, at least some of them will
follow the outdated TXT
record and fetch the new state after they establish
communication.