A simple mDNS client for device discovery on your local network.
1. git clone https://github.com/NationalAssociationOfRealtors/mdns.git
2. mix do deps.get, deps.compile
3. iex -S mix
The udp server doesn't start automatically, this gives you a chance to bring up your network interface, before starting the server, especially useful when using this with Nerves. To start the server, call Mdns.Server.start
. To handle :a
record requests you will want to set the ip address. This can change periodically, or you may have multiple interfaces. Set or update the ip address used for query responses by calling Mdns.Server.set_ip({192, 168, 1, 4})
.
To add a service to the server call Mdns.Server.add_service(%Mdns.Server.Service{})
an example service might looks like this.
Mdns.Server.add_service(%Mdns.Server.Service{
domain: "nerves.local",
data: "_rosetta._tcp.local",
ttl: 120,
type: :ptr
})
You can also add :a
records so that your service is available from a web browser or pingable. In order to use the ip address set by calling Mdns.Server.set_ip
in responses, use the :ip
atom for the value of your data attribute in the Mdns.Server.Service
struct.
Mdns.Server.add_service(%Mdns.Server.Service{
domain: "rosetta.local",
data: :ip, #this is a special atom that uses the ip address set when calling Mdns.Server.set_ip
ttl: 120,
type: :a
})
And :txt
records as well.
Mdns.Server.add_service(%Mdns.Server.Service{
domain: "_nerves._tcp.local",
data: ["id=123123", "port=8800"],
ttl: 120,
type: :txt
})
To see the server and client in action run mix test
and view the code in test/mdns_test.exs
Once an :a
record has been added(with the correct ip) you should be able to run ping rosetta.local
:~$ ping rosetta.local
PING rosetta.local (192.168.1.4) 56(84) bytes of data.
64 bytes from 192.168.1.4: icmp_seq=1 ttl=64 time=0.279 ms
64 bytes from 192.168.1.4: icmp_seq=2 ttl=64 time=0.261 ms
64 bytes from 192.168.1.4: icmp_seq=3 ttl=64 time=0.329 ms
64 bytes from 192.168.1.4: icmp_seq=4 ttl=64 time=0.270 ms
64 bytes from 192.168.1.4: icmp_seq=5 ttl=64 time=0.227 ms
64 bytes from 192.168.1.4: icmp_seq=6 ttl=64 time=0.215 ms
^C
--- rosetta.local ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 4998ms
rtt min/avg/max/mdev = 0.215/0.263/0.329/0.040 ms
Start the client by calling Mdns.Client.start
, again, this gives the system an opportunity to bring up the network interface. To discover a device in a namespace call Mdns.Client.query(namespace \\ "_services._dns-sd._udp.local")
. Compliant devices will respond with a DNS response. Mdns.Client
will send notify the Mdns Registry
You can add an event handler by calling Mdns.EventManager.add_handler
or Mdns.EventManager.register
. This will send a message to the process that called Mdns.EventManager.add_handler
or Mdns.EventManager.register
. You should handle this message in a handle_info
callback.
Calling Mdns.Client.query("_googlecast._tcp.local")
assuming you have a Chromecast on your network, an event is broadcast that looks like this
{:"_googlecast._tcp.local",
%Mdns.Client.Device{
domain: "CRT-Labs.local",
ip: {192, 168, 1, 138},
payload: %{
"bs" => "FA8FCA79C426",
"ca" => "4101",
"fn" => "CRT-Labs",
"ic" => "/setup/icon.png",
"id" => "e0617de7e2df63476fab257c8327ef3b",
"md" => "Chromecast",
"rm" => "E81C9A486980AA48",
"rs" => "",
"st" => "0",
"ve" => "05"
},
services: [
"CRT-Labs._googlecast._tcp.local"
]
}
}
After calling Mdns.Client.query("_ssh._tcp.local")
in addition to the Chromecast call above, Mdns.Client.devices
will return a devices map similar to this. Assuming you have a device on the network that supports ssh
.
%{"_googlecast._tcp.local": [
%Mdns.Client.Device{
domain: "CRT-Labs.local",
ip: {192, 168, 1, 138},
payload: %{
"bs" => "FA8FCA79C426",
"ca" => "4101",
"fn" => "CRT-Labs",
"ic" => "/setup/icon.png",
"id" => "e0617de7e2df63476fab257c8327ef3b",
"md" => "Chromecast",
"rm" => "E81C9A486980AA48",
"rs" => "",
"st" => "0",
"ve" => "05"
},
services: [
"CRT-Labs._googlecast._tcp.local"
]
}
],
"_ssh._tcp.local": [
%Mdns.Client.Device{
domain: "pc-6105006P.local",
ip: {192, 168, 1, 26},
payload: nil,
services: [
"pc-6105006P._ssh._tcp.local"
]
}
],
other: []
}