Access your Bonjour printer over VPN from your iOS device
  • Written by Colin Fitzpatrick
  • March 26th, 2016
  • About Apple

Access your Bonjour printer over VPN from your iOS device

Bonjour automatically locates devices such as printers, other computers, and the services that those devices offer on a local network - but only on a local network. If you need to access a device that requires Bonjour for discovery, and you are not on the same network, Bonjour needs a little help.

For example, a Bonjour enabled printer will appear when you are on the same network, but not when you connected over VPN.

This (accessing the printer over VPN) was the problem I needed to solve.

What I tried that didn’t work:

  1. Apple’s Mac OS X Server
    I had expected this to work, but none of the configurations I tried made the printer appear.

  2. Avahi (with reflector).
    Avahi, the open source implementation of Apple’s Bonjour has a feature to send Bonjour requests (mDNS) between 2 networks. However, you would beed to enable the reflector on the iPhone as well, which is not possible at the moment.

  3. OpenVPN in bridge mode.
    OpenVPN in bridge mode would work but the iPhone/iOS doesn’t support the Tap interface required for bridging.

Luckily, Bonjour is really a DNS/hostname technology. On a UNIX like system- if you look at a /etc/nsswtich.conf you’ll see a line like so:

hosts:          files dns mdns

you’ll see that (in simple terms) hosts are found by first looking at files (/etc/hosts), and then dns, and finally mdns (which is Bonjour).

So Bonjour helps to resolve a hostname in the same way as your hosts file and dns. Which means we can fake Bonjour responces using out of the box DNS. The clever people at Apple have already figured this out and made it easy for us, they call it Wide-Area Bonjour, and it was intended to make Bonjour services work across large coperate and education networks.

So, to make all this work:

  1. Install your VPN Server of choice.
    You can use any VPN Server that allows you to push DNS server settings - which should be all of them. I used OpenVPN and installed it following this guide https://openvpn.net/index.php/open-source/documentation/howto.html.
  2. Next, I setup my own DNS Server. If you have an existing DNS server, you can skip this step.
    I choose to create my own .lan top level domain. This would be used as the default search domain. I used this guide https://www.samculley.co.uk/how-to-install-configure-bind9-on-raspbian-wheezy/ - replacing example.com with lan.
  3. I changed OpenVPN ppp config to use the new dns server.
    Edit /etc/openvpn/server.conf and change the following lines to point to your newly configured DNS server.

    push "dhcp-option DNS 10.0.1.200"  
    push "dhcp-option DOMAIN lan"  
    

    where 10.0.1.200 is the IP address of my DNS server, and lan (the default seach domain) is my top level domain. You’ll need to restart your VPN server to use the new settings.

    I choose to only use the DNS server for VPN clients, but you could configure your DHCP server to use this DNS server for all client.

  4. Add mDNS settings to you DNS hosts file.

    Add a host (A Record) for you printer, for example (for me this was in /etc/bind/zones/db.lan):

    printer.lan.    IN      A       10.0.1.202
    

    Next, the dns-sd command line utility (installed on all Macs or can be downloaded for windows here) will do most of the grunt work for you. When connected to your local lan run the following command:

    dns-sd -Z _ipp._tcp,_universal  
    

    This will produce most of the zone file (bind) configuration you need. For me the output looks like:

    ; To direct clients to browse a different domain, substitute that domain in place of '@'
    lb._dns-sd._udp                                 PTR     @
    
    ; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.
    ; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local
    ; names with the correct fully-qualified (unicast) domain name of the target host offering the service.
    
    _ipp._tcp                                       PTR     Samsung\032SCX-3400\032Series._ipp._tcp
    Samsung\032SCX-3400\032Series._ipp._tcp         SRV     0 0 631 printer.local. ; Replace with unicast FQDN of target host
    Samsung\032SCX-3400\032Series._ipp._tcp         TXT     "txtvers=1" "rp=ipp/printer" "note=" "qtotal=1" "priority=51" "ty=Samsung SCX-3400 Series" "product=(Samsung SCX-3400 Series)" "pdl=application/octet-stream,application/x-QPDL,image/urf" "adminurl=http://printer.local." "usb_MFG=Samsung" "usb_MDL=SCX-3400 Series" "usb_CMD=MFG:Samsung;CMD:SPL,URF,FWV,PIC,BDN,DCU,EXT;MDL:SCX-3400 Series;CLS:PRINTER;MODE:SCN,SPL3,R000105;" "MFG=Samsung" "MDL=SCX-3400 Series" "UUID=16a65700-007c-1000-bb49-001599d33dde" "URF=W8,RS600,IS1,CP1,IFU0,PQ4,OB1" "Bind=F" "Collate=F" "Color=F" "Copies=T" "Duplex=F" "PaperCustom=T" "Punch=0" "Scan=T" "Sort=F" "Staple=F"
    

    Change the SRV line Samsung\032SCX-3400\032Series._ipp._tcp SRV 0 0 631 printer.local. ; Replace with unicast FQDN of target host so that it uses the A record added above Samsung\032SCX-3400\032Series._ipp._tcp SRV 0 0 631 printer.lan.

    Finally, dns-sd -Z output doesn’t include the PTR command needed for printer discovery _universal._sub._ipp._tcp PTR Samsung\032SCX-3400\032Series._ipp._tcp.

    The final output should looks like:

    ; To direct clients to browse a different domain, substitute that domain in place of '@'
    lb._dns-sd._udp         IN      PTR     @
    
    _ipp._tcp                       PTR     Samsung\032SCX-3400\032Series._ipp._tcp
    _universal._sub._ipp._tcp       PTR     Samsung\032SCX-3400\032Series._ipp._tcp
    
    Samsung\032SCX-3400\032Series._ipp._tcp SRV     0 0 631 printer.lan. 
    Samsung\032SCX-3400\032Series._ipp._tcp         TXT     "txtvers=1" "rp=ipp/printer" "note=" "qtotal=1" "priority=51" "ty=Samsung SCX-3400 Series" "product=(Samsung SCX-3400 Series)" "pdl=application/octet-stream,application/x-QPDL,image/urf" "adminurl=http://printer.local." "usb_MFG=Samsung" "usb_MDL=SCX-3400 Series" "usb_CMD=MFG:Samsung;CMD:SPL,URF,FWV,PIC,BDN,DCU,EXT;MDL:SCX-3400 Series;CLS:PRINTER;MODE:SCN,SPL3,R000105;" "MFG=Samsung" "MDL=SCX-3400 Series" "UUID=16a65700-007c-1000-bb49-001599d33dde" "URF=W8,RS600,IS1,CP1,IFU0,PQ4,OB1" "Bind=F" "Collate=F" "Color=F" "Copies=T" "Duplex=F" "PaperCustom=T" "Punch=0" "Scan=T" "Sort=F" "Staple=F"
    

    Reload/Restart your DNS server.

Now, connect to your VPN from your iOS device and the printer should appear when you try to print using the share sheet.

Other uses

This technique can be applied to other Bonjour Services as well. I’ve used it for file sharing and screen sharing — however, it cannot be used to for home sharing, Apple appear to have locked this down.


This blog is mostly for me, a way of remembering things I've done, but maybe other people will find it useful. If you do use anything from here, remember there's ABSOLUTELY NO WARRANTY, expressed or implied.
©2016 Colin Fitzpatrick. All rights reserved.