30

I have a development webserver hosting as "myhost.local" which is found using Bonjour/mDNS. The server is running avahi-daemon.

The webserver also wants to handle any subdomains of itself. Eg "cat.myhost.local" and "dog.myhost.local" and "guppy.myhost.local".

Given that myhost.local is on a dynamic ip address from dhcp, is there still a way to route all requests for the subdomains to myhost.local?

I'm starting to think it not currently possible...

http://marc.info/?l=freedesktop-avahi&m=119561596630960&w=2

You can do this with the /etc/avahi/hosts file. Alternatively you can
use avahi-publish-host-name.

No, he cannot. Since he wants to define an alias, not a new hostname. I.e. he only wants to register an A RR, no reverse PTR RR. But if you stick something into /etc/avahi/hosts then it registers both, and detects a collision if the PTR RR is non-unique, which would be the case for an alias.

John Mee
  • 50,179
  • 34
  • 152
  • 186

3 Answers3

13

I've solved this as best as I can with the small amount of time I assigned to this task.

But unfortunately I do not think the windows implementation of avahi/msdns/bonjour supports aliases (correct me if I am wrong with examples of how to support this).

What i did was start with the example python script provided on the avahi website :

Create : /usr/bin/avahi-announce-alias

Make it executable & Fill it with

#! /usr/bin/env python
# avahi-alias.py

import avahi, dbus
from encodings.idna import ToASCII

# Got these from /usr/include/avahi-common/defs.h
CLASS_IN = 0x01
TYPE_CNAME = 0x05

TTL = 60

def publish_cname(cname):
    bus = dbus.SystemBus()
    server = dbus.Interface(bus.get_object(avahi.DBUS_NAME, avahi.DBUS_PATH_SERVER),
            avahi.DBUS_INTERFACE_SERVER)
    group = dbus.Interface(bus.get_object(avahi.DBUS_NAME, server.EntryGroupNew()),
            avahi.DBUS_INTERFACE_ENTRY_GROUP)

    rdata = createRR(server.GetHostNameFqdn())
    cname = encode_dns(cname)

    group.AddRecord(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0),
        cname, CLASS_IN, TYPE_CNAME, TTL, rdata)
    group.Commit()


def encode_dns(name):
    out = []
    for part in name.split('.'):
        if len(part) == 0: continue
        out.append(ToASCII(part))
    return '.'.join(out)

def createRR(name):
    out = []
    for part in name.split('.'):
        if len(part) == 0: continue
        out.append(chr(len(part)))
        out.append(ToASCII(part))
    out.append('\0')
    return ''.join(out)

if __name__ == '__main__':
    import time, sys, locale
    for each in sys.argv[1:]:
        name = unicode(each, locale.getpreferredencoding())
        publish_cname(name)
    try:
        # Just loop forever
        while 1: time.sleep(60)
    except KeyboardInterrupt:
        print "Exiting"

This script handles the announcement of each individual alias, and will remain running until you kill it. (because of this, we need to create another script which I've shown below)

Create text file /etc/avahi/aliases

This we use to store aliases to this machine one per line

Create directory /etc/avahi/aliases.d/

I haven't actually made use of this in any of the scripts i show here yet, but for those enterprising individuals out there, you can see what needs to be done.

The idea is that you can group aliases into separate text files (which will make more sense when you deal with virtual hosts in apache), this is something many daemon applications on *nix already provide (apache and apt are just two examples).

Create /usr/bin/avahi-announce-aliases

Make it executable and fill it with

#!/usr/bin/env python

import os, sys
from subprocess import Popen


def ensure_file (path):
    """
        Looks for  file at provided path, creates it if it does not exist.
        Returns the file.
    """
    rfile = None    
    if not os.path.exists(path) and os.path.isfile(path) :
        rfile = open(path,"w+");
        print("ensuring file : %s " % path)

    print("file ensured : %s " % path)
    return rfile


command = '/usr/bin/avahi-announce-alias'
alias_pid_path = "/tmp/avahi-aliases.pid"
alias_file_path = "/etc/avahi/aliases"

alias_file = open(alias_file_path)
if not os.path.exists(alias_pid_path) :
    open(alias_pid_path,"w").close()

alias_pid = open(alias_pid_path,"r")


for line in alias_pid :
    txt = line.strip('\n')
    if len(txt) > 0 :
        print("kill %s" % txt )
        os.system("kill %s" % txt)          
alias_pid.close()
alias_pid = open(alias_pid_path,"w+")

for line in alias_file :
    txt = line.strip('\n')
    if len(txt) > 0 :
        print("publishing : << %s >>" % txt)
        process = Popen([command, txt])
        alias_pid.write("%s\n" % str(process.pid))    
alias_pid.close()

print("done")

It is by no means meant to be the pinnacle of python programming, so feel free to make improvements where you see fit.

Usage

If our hostname was "server" and the avahi-hostname was "server.local", then we could fill up the /etc/avahi/aliases text file with your extra hostnames like so :

deluge.server.local
username.server.local
accounts.server.local
something-else.server.local
another.hostname.home

(but really, I'm pretty sure you could have any hostname in there providing you made sure it did not already exist on the network, which is why i just create 'subdomains' of the normal avahi hostname)

Then we run :

sudo avahi-publish-aliases

My main reason for setting this up was to facilitate easier simulation of django & drupal website development on my laptop.

Caveats

My only disappointment is that the windows implementation of Bonjour/Avahi does not support the aliases that this implementation announces, it will only see the main avahi hostname normally announced (ie server.local in our example above).

Shawn Craver
  • 1,986
  • 1
  • 15
  • 16
airtonix
  • 4,772
  • 2
  • 38
  • 36
  • just a word on the PID tracking method here. It will fail eventually. It may even end up killing processes it didn't start. A better way to ensure the right anouncing processes die is to do all this in a bash script that becomes each announcing scripts parent. – airtonix Dec 03 '11 at 07:07
  • 1
    This is Avahi specific. How does this work for OS X's Bonjour at all? – Jonathan Dumaine Dec 04 '13 at 23:29
  • @JonathanDumaine Just like Windows. Which is to say: It Doesn't. An update to the note about PID, I've since implemented this with an upstart job and python-daemon. – airtonix Dec 08 '13 at 08:56
  • I see. I'm sure this is very useful on Avahi machines, but OP asked about Bonjour/mDNS so I had first thought this answer was applicable to OS X which it is not. – Jonathan Dumaine Dec 09 '13 at 01:16
  • @JonathanDumaine The OP was actually asking how to make a linux host provide multiple aliases. it doesn't matter what the other hosts on the network are as long as they can read mDNS packets, which both windows and macos/x with bonjour are capable of doing. Making windows and macosx announce multiple cnames via mDNS however isn't possible (to my knowledge), but in those situations I'd just run ubuntu in a virtual machine with host mode networking. – airtonix Dec 11 '13 at 01:17
  • Looks like this no longer works for OS X 10.10 clients. Other clients still see the names. – Gerd K Dec 13 '14 at 23:53
  • Use `group.AddRecord(avahi.IF_UNSPEC, avahi.PROTO_UNSPEC, dbus.UInt32(0), cname, CLASS_IN, TYPE_A, TTL, ipaddr.IPv4Address('127.0.0.1').packed)` To add an address (A record). Works just fine with Windows Bonjour. Unfortunately, Bonjour throws AAAA replies on the floor, so only IPv4 (A records) may be added. You can verify it by installing your favorite linux on VM, stopping "resolved" daemon (LLDP responder) and keep avahi running. If you assign only IPv6 address to your VM, it won't be reachable from Windows. – Vitaly Greck May 26 '17 at 05:08
  • [link]https://opensource.apple.com/source/mDNSResponder/mDNSResponder-765.50.9/mDNSWindows/mdnsNSP/mdnsNSP.c.auto.html[/link] ### NSPLookupServiceBegin ### // We only support the DNS namespace, TCP and UDP protocols, and IPv4. Only blob results are supported.` – Vitaly Greck May 26 '17 at 05:08
  • [avahi-aliases](https://github.com/scott-ainsworth/avahi-aliases) seems to be the newest tool based on the same approach as the script here. – Stefan Krüger s-light May 08 '22 at 08:52
  • @StefanKrüger because its a fork of my code – airtonix May 10 '22 at 02:21
5

I gave it up as not possible at the time, and I haven't revisited the problem since.

My current solution is to laboriously add each subdomain of interest to the hosts file on the local machine.

John Mee
  • 50,179
  • 34
  • 152
  • 186
  • 3
    If you just want it to work on localhost, you can run a normal dns server on localhost. eg dnsmasq, with this setting: address=/loc/127.0.0.1 that'll reply with 127.0.0.1 to x.loc and x.y.loc, etc.. If you set dnsmasq to use some globally-available nameservers (such as google's) you can set your /etc/resolv.conf to localhost (dnsmasq) and use chattr +i /etc/resolv.conf to stop dhcp/whatever from modifiing /etc/resolv.conf. – JasonWoof Nov 17 '11 at 06:03
0

A better solution would be dnsmasq.

This can be accomplished SO simply... via the arguments/config...

Return ipaddr for all hosts in specified domains.

-A, --address=/<domain>/<ipaddr>

address=/yourdomain.com/10.0.1.100

desertnaut
  • 57,590
  • 26
  • 140
  • 166
Alex Gray
  • 16,007
  • 9
  • 96
  • 118