Madness
Madness queries mDNS and publishes DNS-SD services from Elixir. Queries support IPv4 and IPv6 and return records as a lazy stream or process messages. Publishing delegates to Avahi or Apple's mDNSResponder daemon.
Installation
Add Madness to mix.exs:
def deps do
[
{:madness, "~> 0.5.0"}
]
end
Publishing through Avahi uses the required rebus dependency included by
Madness. Publishing on one specific interface also requires the optional
inertial package:
{:inertial, "~> 2.2"}
Querying
request/2 returns a stream by default:
records =
Madness.request({"_http._tcp.local", :ptr})
|> Enum.to_list()
Multiple questions can share one query:
Madness.request([
{"mydevice.local", :a},
{"mydevice.local", :aaaa}
])
|> Enum.to_list()
For an OTP process, use message mode:
{:ok, ref} = Madness.request({"_http._tcp.local", :ptr}, into: :self)
receive do
{^ref, %Madness.Record{} = record} -> IO.inspect(record)
{^ref, :done} -> :ok
end
Responses include Answer and Additional records. Use
record.metadata.section to distinguish them. Non-PTR queries can finish once
all questions are answered, including by an NSEC negative answer. PTR queries
wait for the configured timeout because more responders may reply.
Common options are timeout: 5_000, family: :any | :inet | :inet6,
interface: :any | name | index, and unicast_response: true | false. See
Madness.request/2 for the complete API.
Publishing
Publish a service and keep the returned handle:
{:ok, publication} =
Madness.publish(
name: "Web",
type: "_http._tcp",
port: 4000,
txt: %{"path" => "/"}
)
:ok = Madness.unpublish(publication)
The publication follows the lifetime of its owner process and is withdrawn
automatically when that process exits. Pass owner: when a short-lived setup
process publishes for a long-lived server. Monitor publication.pid if the
service should be republished after a daemon restart or another terminal
publication failure.
Publishing uses config :madness, backend: :auto by default. Automatic
selection probes Avahi and then mDNSResponder, settling on the first reachable
daemon. You can configure :avahi, :mdns_responder, a custom
Madness.Responder module, or false. The native responder is not implemented.
Avahi supports interface: and family: scopes. mDNSResponder supports
interface: but cannot restrict an advertisement to one address family;
Madness returns an error rather than widening a requested scope. An
interface-scoped publication requires inertial and terminates if the bound
interface disappears or can no longer be observed.
See Madness.publish/2 and Madness.Service for service fields, lifecycle
notifications, subtypes, and validation rules.
Development
Run the normal checks with:
mix precommit
The unit suite uses in-process daemon fakes. With Docker available,
mix test.avahi runs the Avahi integration suite against a real daemon.
License
MIT