Madness.Service (Madness v0.5.0)

View Source

A DNS-SD service instance to publish.

Construct one with new/1 (which validates and normalises) rather than building the struct by hand:

{:ok, service} =
  Madness.Service.new(
    name: "My Printer",
    type: "_ipp._tcp",
    port: 631,
    txt: %{"rp" => "ipp/print", "pdl" => "application/pdf"}
  )

Fields

  • :name - the service instance name, e.g. "My Printer". This is a single DNS label and is limited to 63 bytes. It is a human-readable display name, not an identifier: responders rename it on conflict (see Madness.publish/2), so never treat it as stable.

  • :type - the DNS-SD service type, e.g. "_ipp._tcp". Must begin with an underscore and end in ._tcp or ._udp (RFC 6763 §7). A subtype uses its DNS name, e.g. "_printer._sub._http._tcp"; backend-specific registration syntax is handled internally.

  • :domain - the domain to publish in. Defaults to "local", which is the only value mDNS actually serves; the field exists because the delegated backends take it as a parameter.

  • :host - the target host name for the SRV record, or nil (the default) to let the responder substitute its own host name.

    Leave this as nil unless the target name already has A or AAAA records; the responder does not create address records for an arbitrary target.

  • :port - the TCP or UDP port the service listens on.

  • :txt - service metadata, as a list of already-encoded "key=value" binaries or as a map. See "TXT records" below.

TXT records

A map is normalised to DNS-SD wire entries and sorted by key:

txt: %{"path" => "/index.html"}   # => ["path=/index.html"]

A key whose value is true becomes a bare key with no =, which DNS-SD defines as "attribute present, no value":

txt: %{"paperless" => true}       # => ["paperless"]

A list is taken as-is, for callers that have already encoded their entries:

txt: ["path=/index.html"]

A key ends at the first =, must be non-empty printable US-ASCII, and is compared case-insensitively when detecting duplicates. Values are opaque binary data. Each entry is limited to 255 bytes.

The default, [], publishes a TXT record containing a single empty string. DNS-SD requires a TXT record even when a service has no metadata.

Summary

Types

t()

A DNS-SD service instance to publish

A TXT record given as a map, or as pre-encoded key=value entries

Functions

Builds a validated t/0 from a keyword list or map.

Same as new/1 but raises ArgumentError on invalid input.

Types

t()

@type t() :: %Madness.Service{
  domain: String.t(),
  host: String.t() | nil,
  name: String.t(),
  port: :inet.port_number(),
  txt: [binary()],
  type: String.t()
}

A DNS-SD service instance to publish

txt()

@type txt() :: %{optional(String.t()) => binary() | true} | [binary()]

A TXT record given as a map, or as pre-encoded key=value entries

Functions

new(fields)

@spec new(keyword() | map()) :: {:ok, t()} | {:error, term()}

Builds a validated t/0 from a keyword list or map.

:name, :type and :port are required; :domain defaults to "local", :host to nil and :txt to [].

Examples

iex> {:ok, service} = Madness.Service.new(name: "Web", type: "_http._tcp", port: 80)
iex> {service.name, service.type, service.domain, service.port}
{"Web", "_http._tcp", "local", 80}

iex> {:ok, service} =
...>   Madness.Service.new(
...>     name: "Web",
...>     type: "_http._tcp",
...>     port: 80,
...>     txt: %{"path" => "/", "secure" => true}
...>   )
iex> service.txt
["path=/", "secure"]

iex> Madness.Service.new(name: "Web", type: "http", port: 80)
{:error, {:invalid_type, "http"}}

iex> Madness.Service.new(name: "Web", type: "_http._tcp", port: 99_999)
{:error, {:invalid_port, 99_999}}

new!(fields)

@spec new!(keyword() | map()) :: t()

Same as new/1 but raises ArgumentError on invalid input.