hashicorp/mdns

repository·main·Indexed 23 days ago

https://github.com/hashicorp/mdns

A simple Golang library for implementing mDNS (Multicast DNS) clients and servers, enabling peer-to-peer service discovery on local networks without an authoritative DNS server. It provides tools to publish service entries using MDNSService and NewServer, and to discover services via Lookup and QueryContext.

Tokens
2.7K
Snippets
6
Records
14
Agent score
78%

What's inside mdns

  1. Implement the Zone interface for dynamic records

    main

    The Zone interface is used to integrate with the mDNS server to serve records dynamically. To implement it, you must provide a Records method.

    type Zone interface {
        Records(q dns.Question) []dns.RR
    }

    MDNSService implements this interface. When Records(q dns.Question) is called, it evaluates the DNS question (name and type) and returns the appropriate dns.RR (Resource Records) such as PTR, SRV, A, AAAA, or TXT records based on the service configuration.

  2. Publish a service entry using mdns

    main

    To make a service discoverable on the local network, create an mDNS service entry and start an mDNS server.

    1. Use mdns.NewMDNSService to define the service details, including the hostname, service type (e.g., _foobar._tcp), port, and optional text information.
    2. Use mdns.NewServer with a mdns.Config containing your service to start the server.
    3. Ensure you call server.Shutdown() to clean up resources when the application exits.

    Note: mDNS relies on multicasting, which is often restricted in cloud or shared infrastructure environments. It is best suited for home, office, or private network environments.

    // Setup our service export
    host, _ := os.Hostname()
    info := []string{"My awesome service"}
    service, _ := mdns.NewMDNSService(host, "_foobar._tcp", "", "", 8000, nil, info)
    
    // Create the mDNS server, defer shutdown
    server, _ := mdns.NewServer(&mdns.Config{Zone: service})
    defer server.Shutdown()
  3. Lookup service providers using mdns

    main

    To discover services on the local network, use mdns.Lookup.

    1. Create a channel of type chan *mdns.ServiceEntry to receive the discovered services. The channel buffer size should be sufficient for the expected number of results.
    2. Start a goroutine to listen to the channel and process the *mdns.ServiceEntry objects as they arrive.
    3. Call mdns.Lookup(serviceType, entriesCh) where serviceType is the identifier of the service you are searching for (e.g., _foobar._tcp).
    4. Close the channel once the lookup process is complete to signal the listener to stop.
    // Make a channel for results and start listening
    entriesCh := make(chan *mdns.ServiceEntry, 4)
    go func() {
        for entry := range entriesCh {
            fmt.Printf("Got new entry: %v\n", entry)
        }
    }()
    
    // Start the lookup
    mdns.Lookup("_foobar._tcp", entriesCh)
    close(entriesCh)
  4. Configure the mDNS server with Config

    main

    The Config struct defines the operational parameters for an mDNS server. To respond to queries, you must provide a Zone implementation.

    Fields

    • Zone (Required): Must be provided to support responding to queries.
    • Iface (Optional): A *net.Interface to bind the multicast listener to. If omitted, the system default multicast interface is used.
    • LogEmptyResponses (Optional): If true, the server logs an informative message when a query is received for which the server has no matching records.
    • Logger (Optional): An alternative *log.Logger. If nil, the server defaults to log.Default().
    type Config struct {
    	Zone Zone
    	Iface *net.Interface
    	LogEmptyResponses bool
    	Logger *log.Logger
    }
  5. Create a new MDNSService instance

    main

    Use NewMDNSService to initialize a service. This function performs validation on the provided arguments and handles default value inference.

    Validation Rules:

    • instance and service must not be empty.
    • port must not be 0.
    • domain and hostName must be valid FQDNs (ending in a period).

    Automatic Discovery:

    • If domain is empty, it defaults to local..
    • If hostName is empty, it uses os.Hostname() and appends a period.
    • If ips is empty, it performs a network lookup for the hostName (and attempts a lookup with the domain suffix if the first attempt fails).
  6. Shutdown the mDNS server

    main

    The Shutdown method stops the mDNS server by closing the underlying IPv4 and IPv6 multicast UDP listeners and signaling background routines to stop.

    Calling Shutdown multiple times is safe; subsequent calls will return nil without error.

    func (s *Server) Shutdown() error
  7. Initialize a new mDNS server with NewServer

    main

    Use NewServer to create and start an mDNS server. The function automatically starts background goroutines to listen for both IPv4 and IPv6 multicast queries on the standard mDNS port (5353).

    If the provided Config.Iface is nil, the server attempts to use the system default multicast interfaces.

    Returns:

    • *Server: A pointer to the initialized server.
    • error: Returns an error if no multicast listeners could be started.

    Example

    config := &mdns.Config{
        Zone: myZone, // myZone must implement the Zone interface
    }
    server, err := mdns.NewServer(config)
    if err != nil {
        log.Fatal(err)
    }
    func NewServer(config *Config) (*Server, error)
  8. Define an mDNS service with MDNSService

    main

    To export a named service via mDNS, use the MDNSService struct. This struct implements the Zone interface, allowing it to be integrated with an mDNS server to serve records dynamically.

    When creating a service, you can provide specific details or allow the library to infer defaults from the operating system:

    • Instance: The unique name for the service instance (e.g., "My Printer").
    • Service: The service type (e.g., "_http._tcp.").
    • Domain: The domain (defaults to "local.").
    • HostName: The DNS name of the host (defaults to the OS hostname).
    • Port: The service port.
    • IPs: A list of IP addresses. If empty, the library attempts to look up the host's IPs via the OS.
    • TXT: A list of strings for TXT records.

    Note: All domain and hostname strings must be Fully Qualified Domain Names (FQDNs) and must end with a period ('.').

  9. Configure mDNS query parameters with DefaultParams()

    main
    Instead of manually constructing a QueryParam struct, use DefaultParams(service string) to get a pre-configured object with sensible defaults. You can then modify specific fields before passing it to Query() or QueryContext().
  10. Perform mDNS service lookups with Lookup()

    main

    Use Lookup for a simple, high-level way to discover services. It uses default parameters (1-second timeout, local domain) and streams discovered ServiceEntry objects to the provided channel.

    Note: The Entries channel should be buffered or read continuously to prevent the query from blocking, as sends to the channel are non-blocking and will drop entries if the channel is full.

  11. Perform advanced mDNS queries with QueryContext()

    main

    Use QueryContext when you need fine-grained control over the lookup process, such as specifying a custom timeout, a specific network interface, or preferred unicast responses. It accepts a context.Context to allow for cancellation of the query.

    Key QueryParam options:

    • Service: The service string to lookup (e.g., _http._tcp).
    • Domain: The lookup domain (defaults to local).
    • Timeout: How long to wait for results.
    • Interface: A specific *net.Interface to use for multicast.
    • Entries: A send-only channel chan<- *ServiceEntry where results are streamed.
    • WantUnicastResponse: If true, prefers unicast responses as per RFC 5.4.
    • DisableIPv4 / DisableIPv6: Controls whether the client uses these protocols for mDNS operations.
  12. MDNSService struct fields

    main

    The MDNSService struct contains the configuration for the service being published:

    FieldTypeDescription
    InstancestringInstance name (e.g., "hostService name")
    ServicestringService name (e.g., "_http._tcp.")
    DomainstringDomain (if blank, assumes "local.")
    HostNamestringHost machine DNS name (e.g., "mymachine.net.")
    PortintService Port
    IPs[]net.IPIP addresses for the service's host
    TXT[]stringService TXT records