Closed thom-nic closed 6 years ago
Yup, having a config file would be preferable to command line options, definitely! I don't think running multiple daemons, one for each service, would be a good idea. Maybe if it's a multi-homed server (or switch/router like we do at work), but then you'd likely want source routing or network namespaces.
I've considered adding support for libConfuse style files, which I use in other projects, or even some really simple homegrown UNIX-style format. Most traditional UNIX applications use SIGHUP
to reload their config file, so that's what I'd go for here as well.
I could help out with the .conf
file support if you want? I recently added basic support to another project of mine, just to get us off the ground, https://github.com/troglobit/mini-snmpd/commit/0549c55
Cool! I am wondering if key/value config file would work in this case though. Each line would want to look like
[name] IN [RECORD_TYPE] [VALUE] [FLAGS]
It's actually a shame busybox dnsd
doesn't support mdns, might almost be easier to add multicast support to dnsd
😂
Aha, that was one of my follow-up questions, thanks! Well maybe we could have an /etc/mdnsd.conf
for generic options and have a separate directory for records, one per file maybe, in a /etc/mdnsd.d/*.service
or similar?
/etc/mdnsd.conf
to the TODO list, for now, and.service
file support using fopen()
and fgets()
With a separate directory and a service record per file, it would be quite straight forward to drop a ssh.service
record in that directory when, e.g., SSH is enabled/started. We could later on even add inotify support to mdnsd to read all new files dropped there, and stop publishing records of files removed.
What do you think?
(Btw, watch out I'm pushing a slew of build fixes to master)
that sounds awesome!
BTW I noticed the extra printf
I left to log responses is (1) noisier than I anticipated and (2) I forgot the '\n'
!
Might it also be appropriate to look under, say, /var/run/mdnsd.d/
for runtime-configured services? (maybe the service directory is a config option.)
OK, I can remove the extra printf()
you added, if you want?
Yup, /run/mdnsd.d/
support could be useful as well, configurable support (or both by default) sounds great! :+1:
OK, I can remove the extra printf() you added, if you want?
yeah it's useless. thanks.
OK fixing
So I did some more reading because I needed a refresher on all the records needed for DNS-SD. These two references gave a good overview (though they assume "normal" unicast DNS, not mDNS.)
Although I was suggesting BIND-style zone files, it would be overkill to require specifying the PTR
records in a config file, we can infer that - maybe you already had that in mind. The SRV
record could be built if one specifies a port, target, (maybe TTL). The enumeration of multiple values comes in at the point where you want to specify possibly more than one A
and AAAA
records for the SRV
target, and also arbitrary TXT
records.
Also the browse domain I think is unnecessary to specify via configuration.
So just to throw a strawman out there... I guess if you're fond of libconfuse it might look something like:
# /etc/mdnsd.d/my-service._http._tcp.conf
name="My Service" # creates "My Service._http._tcp" entry (or optionally infer from file name?)
type=_http._tcp # or infer from file name?
port=80 # for "My Service._http._tcp" SRV record
target="my-hostname.local" # for "My Service._http._tcp" SRV record
path="/login" # creates standard "My Service._http._tcp" TXT record
txt = { # additional TXT records for "My Service._http._tcp"
"Model=\"ABC 1234\"",
"Version=1.2.3"
}
records = { # entries for SRV target
"my-hostname.local A 169.254.22.22",
"my-hostname.local AAAA fe80::564a:16ff:febb:0"
}
Awesome, thank you for the pointers, I really need to read up myself on this! :smiley:
Just to be clear, I'm not married to libConfuse, it's just my goto hammer factory. Any homegrown parser is usually just as sufficient.
As for your proposal, I think it looks great. Inferring from the filename is awesome, since this means a simple ls
in the directory can be used to get an overview of what services are advertised. We could also infer the port
from _http._tcp
using getservbyname()
(which uses /etc/services
to find the correct port + proto). Same goes for target
, if omitted from the .conf file we could use gethostname()
.
However, hard coding IP addresses is something I particularly dislike, and I think we can handle better. The getifaddrs()
function and inspecting the routing table should help us decide what IP address(es) to advertise (I can help out here :). I also believe we should advertise AAAA record by default, if the egress interface has an IPv6 address set. We could have an option/options for this, as shown below, but do we really need to disable any of them?
Here's a counter proposal:
# /etc/mdnsd.d/my-service._http._tcp.conf
ipv6 = no # or yes, default yes
ipv4 = yes # or no, default yes
target = other-name.local # optional
txt = {
"path=/login",
"Model=\"ABC 1234\"",
"Version=1.2.3"
}
I.e., a really simple service could be created by just touch /etc/mdnsd.d/my-service._http._tcp.conf
(I believe HTTP services default to a path of /
)
Ok I definitely like the idea of not having to infer valid IP addresses and manually specify those A and AAAA records. In the current implementation I am doing all that bookkeeping by killing and restarting mdnsd via a udhcpc
script hook. If the mdnsd daemon can lookup IP addresses assigned to interfaces that's fantastic.
So if target
was omitted, I'd guess the default SRV target and corresponding A
/AAAA
records would be for $(hostname).local
?
Suggest also adding a name
(or instance
?) param so Instance Names can be pretty, e.g. name="Brother MFC Printer". See [RFC Section 4.1.1](https://tools.ietf.org/html/rfc6763#section-4.1.1) and [Appendix D](https://tools.ietf.org/html/rfc6763#appendix-D). I think
my-servicefrom the filename would be the default value if
name` was omitted.
So if target was omitted, I'd guess the default SRV target and corresponding A/AAAA records would be for $(hostname).local?
Yep.
Ooh nice, a name/instance (or maybe 'description'?) and defaulting to the service name would be great.
I think a "Description" could be an additional non-standard TXT record, if one wanted such a thing.
In case this isn't clear: the reason for suggesting a name/instance config value (whichever you call it) is because contrary to hostname convention, DNS-SD encourages a "human readable" instance name for the SRV record like My\ Company\ Foobar\ Thing._http._tcp
which might or might not be preferable in the /etc/mdnsd.d/
config file name
True that. Yeah, I understand :)
The more I think about it though, I'm realizing libConfuse is probably not a great fit for these record files. So I talked to a friend and he told me they'd made something even simpler for mDNSResponder. Here are a few completely made up examples that I'm curious to hear your thoughts on.
# /run/mdnsd/srv/my-service._http._tcp
service _http._tcp # Optional, if not using filename
name My Fine Service
port 80 # Optional here
txt path=/
txt Model="ABC 1234"
txt Version=1.2.3
target some-hostname.local # Optional, like before we can set up A/AAAA records using system hostname
Imagine a network with multiple devices of the same type. Google creates a UUID for each Chromecast, but you could create a unique target name using the device type and the last three octets in the MAC address (provided the three first are always the same). Here are examples for Chromecast
# /run/mdnsd/srv/Chromecast-231321321321df89aedd3213asd123421d3.local
service _googlecast._tcp # Not optional in this case
port 8009
name Living room TV
txt features=0x20f8
txt model=Xbmc,1
txt deviceid=00:13:93:c0:ff:ee
target 231321321321-df89-aedd-3213-asd123421d3.local
Completely useless URL. But with a cname ...
# /run/mdnsd/cname/chromecast
cname chromecast.local
target 231321321321-df89-aedd-3213-asd123421d3.local
shared yes
The shared
flag indicates if more than one device is expected to announce the record, if so all devices retract the cname.
For our service above we could create something like this, never retracting the cname if we're first:
# /run/mdnsd/cname/troglobit.local
cname troglobit.local # Optional, can use filename
target some-hostname.local
shared no
That config format looks 100% acceptable to me.
I'm not sure how critical the cname bit is.. I mean it's nice to have a pretty fqdn for a device but the user never sees it (just us technical people.) For instance I have a bunch of Cisco switches on my network that look like:
$ dns-sd -B _http._tcp
... snip ...
16:34:33.188 Add 3 5 local. _http._tcp. switcha0bc35
16:34:33.188 Add 3 5 local. _http._tcp. switch9d438c
16:34:33.188 Add 3 5 local. _http._tcp. switch741322
16:34:33.188 Add 3 5 local. _http._tcp. switch12112ac
For my devices, I'm doing like you suggest and use the last 3 bytes of the MAC so their A records look like $productName-c0ff33.local
.
Actually I guess you see that target hostname when you either browse to http://foo-c0ff33.local/login
or SSH, or whatever but I don't mind the reasonably short and unique A record name. I'm just wondering how much work you're creating for yourself to track if other products are vying for the same CNAME. Is that shared/CNAME stuff part of the DNS-SD spec?
Cool, let's go with that format then :+1:
The Apple mDNSResponder supports tracking unique cnames, so my friend set up so users could simply connect to http://device.local rather than having to look up the MAC and type http://device-01-02-03.local -- very useful for first time setup after unpacking a new device. (I'd like to replace mDNSResponder with mdnsd, so I'll probably look at cnames if nobody else does :)
@thom-nic Ah, thanks for updating the issue title! :+1:
Small update, I've merged most of #1 during the weekend and started laying the groundwork for config files today. If the heat in Sweden subsides there will be working code in a couple of days.
I saw that, just pulled your changes from master. Looks great so far!
Hey these improvements look really great. I'm going to update soon (on vacation next week though!) and give them a try. Haven't updated sooner because I had the old version working and left it as-is.
Thanks! Yeah it's coming along nicely, but I'm not happy with it yet. Right now I'm trying to figure out why nothing shows up with mdns-scan from (even another) Linux ...
(I'm also on vacation, only way to get some real time off to work on open source ;)
There, major fixes pushed ... seems to work for me :)
I'm considering moving the remainder of the stuff we talked about to separate issues and close this. But I'll let you take a look at what's on master
first, @thom-nic ...
Fantastic! I will probably not have a chance to test this until a week from today, at the earliest. Definitely looking forward to it though!
@thom-nic Awesome, hope you have a great vacation!
Config file support looks great! I have another question but I won't tack it on here...
🎉
I'd call this fixed as of 3ef9cd3bb0e4cfa41b6d1c6d133d543d910ed7ea. Thanks!
You're welcome, it was fun working on mdnsd again! =)
mdnsd currently assumes you want to advertise a single HTTP service however mDNS/DNS-SD allows one to advertise multiple services (e.g. http, tftp, ssh.) Since
SO_REUSEADDR
is being used I think one could run multiplemdnsd
processes, each specifying a different service.So at minimum, as you mentioned in #4 this could get facelifted to use getopt, and take the service type as an optional flag.
However it should, I think, also be possible to respond with multiple DNS records as well e.g. when looking up
foo.local
you could return multiple A or AAAA records. I'm not sure how practical that becomes w/r/t specifying everything as options. Furthermore I'm trying to think through "what happens if/when the network changes?" e.g. ifmdnsd
is started before ethernet is plugged in, wifi goes up or down, DHCP lease acquired or lost. I would have to kill and restart mdnsd when any of those changes occur.I am wondering (and I understand this would be a bit of work!) if it would make sense for mdnsd to take a config file (bind9 format?) and respond to queries for any names in that file. In that case, if network configuration changes, the file could be re-written/ read by mdnsd (use
SIGUSR1
to re-read?) and potentially avoid restarting the daemon.