mdns

package module
v0.0.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2025 License: MIT Imports: 20 Imported by: 0

README ¶

📡 mdns — Multicast DNS (mDNS) for Go

A pure Go implementation of Multicast DNS (mDNS) and DNS-SD (Service Discovery). This library allows you to register services, publish proxy services, and discover services in local networks.


🚀 Installation

go get github.com/elum-utils/mdns

🔧 Usage

1. Register a Local Service
package main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/elum-utils/mdns"
)

var (
	name     = flag.String("name", "GMELUM MDNS", "The name for the service.")
	service  = flag.String("service", "_workstation._tcp", "Set the service type of the new service.")
	domain   = flag.String("domain", "local.", "Set the network domain. Default should be fine.")
	port     = flag.Int("port", 42424, "Set the port the service is listening to.")
)

func main() {
	flag.Parse()

	server, err := mdns.Register(*name, *service, *domain, *port, []string{"txtv=0", "lo=1", "la=2"}, nil)
	if err != nil {
		panic(err)
	}
	defer server.Shutdown()
	log.Println("Published service:")
	log.Println("- Name:", *name)
	log.Println("- Type:", *service)
	log.Println("- Domain:", *domain)
	log.Println("- Port:", *port)

	// Clean exit.
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	<-sig

	log.Println("Shutting down.")
}

2. Register a Proxy Service
package main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/elum-utils/mdns"
)

var (
	name     = flag.String("name", "ProxyService", "Service instance name")
	service  = flag.String("service", "_workstation._tcp", "Service type")
	domain   = flag.String("domain", "local.", "Network domain")
	host     = flag.String("host", "my-host", "Proxy hostname")
	ip       = flag.String("ip", "192.168.1.50", "Proxy IP address")
	port     = flag.Int("port", 42424, "Service port")
	waitTime = flag.Int("wait", 10, "Time in seconds to keep service alive")
)

func main() {
	flag.Parse()

	server, err := mdns.RegisterProxy(*name, *service, *domain, *port, *host,
		[]string{*ip}, []string{"txtv=1", "proxy=true"}, nil)
	if err != nil {
		log.Fatal(err)
	}
	defer server.Shutdown()

	log.Printf("Published proxy service %s on %s:%d\n", *name, *ip, *port)

	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	var tc <-chan time.Time
	if *waitTime > 0 {
		tc = time.After(time.Duration(*waitTime) * time.Second)
	}

	select {
	case <-sig:
	case <-tc:
	}
	log.Println("Shutting down.")
}

3. Discover Services (Browse)
package main

import (
	"context"
	"flag"
	"log"

	"github.com/elum-utils/mdns"
)

var (
	service = flag.String("service", "_workstation._tcp", "Service type")
	domain  = flag.String("domain", "local.", "Search domain")
)

func main() {
	flag.Parse()

	resolver, err := mdns.NewResolver()
	if err != nil {
		log.Fatal("Failed to initialize resolver:", err)
	}

	err = resolver.Browse(context.Background(), *service, *domain, func(se *mdns.ServiceEntry) {
		log.Printf("Found service %s at %s:%d", se.Instance, se.AddrIPv4, se.Port)
	})
	if err != nil {
		log.Fatal("Failed to lookup:", err)
	}

	select {}
}

4. Lookup a Specific Service
package main

import (
	"context"
	"flag"
	"log"

	"github.com/elum-utils/mdns"
)

var (
	name    = flag.String("name", "GoService", "Service instance name to lookup")
	service = flag.String("service", "_workstation._tcp", "Service type")
	domain  = flag.String("domain", "local.", "Search domain")
)

func main() {
	flag.Parse() 

	resolver, err := mdns.NewResolver()
	if err != nil {
		log.Fatal("Failed to initialize resolver:", err)
	}

	err = resolver.Lookup(context.Background(), *name, *service, *domain, func(se *mdns.ServiceEntry) {
		log.Printf("Found service %s at %s:%d", se.Instance, se.AddrIPv4, se.Port)
	})

	if err != nil {
		log.Fatal("Failed to lookup:", err)
	}

	select {}
}

Documentation ¶

Overview ¶

Package mdns implements a multicast DNS (mDNS) resolver and browser for service discovery in local networks. It supports both IPv4 and IPv6 transport and provides interfaces for browsing services and looking up specific service instances.

The package follows the DNS-Based Service Discovery (DNS-SD) specification and is compatible with standard mDNS implementations like Avahi and Bonjour.

Package mdns implements multicast DNS service discovery functionality for local network environments, supporting both IPv4 and IPv6 protocols.

Package mdns provides a complete implementation of multicast DNS service registration and discovery, allowing services to announce their availability and respond to queries on the local network.

Package mdns defines the core data structures and interfaces for multicast DNS service discovery, including service records, lookup parameters, and discovery result handling.

Package mdns provides utility functions for DNS name processing and manipulation, supporting both service name parsing and DNS presentation format unescaping.

Index ¶

Constants ¶

This section is empty.

Variables ¶

View Source
var DefaultInterval = 5 * time.Minute

DefaultInterval is used when a Backoff is initialised with a zero-value Interval.

View Source
var DefaultMaxDuration = 6 * time.Hour

DefaultMaxDuration is maximum amount of time that the backoff will delay for.

Functions ¶

This section is empty.

Types ¶

type Backoff ¶

type Backoff struct {
	// contains filtered or unexported fields
}

A Backoff contains the information needed to intelligently backoff and retry operations using an exponential backoff algorithm. It should be initialised with a call to `New`.

Only use a Backoff from a single goroutine, it is not safe for concurrent access.

func NewBackoff ¶

func NewBackoff(max time.Duration, interval time.Duration) *Backoff

New creates a new backoff with the specified max duration and interval. Zero values may be used to use the default values.

Panics if either max or interval is negative.

func NewWithoutJitter ¶

func NewWithoutJitter(max time.Duration, interval time.Duration) *Backoff

NewWithoutJitter works similarly to New, except that the created Backoff will not use jitter.

func (*Backoff) Duration ¶

func (b *Backoff) Duration() time.Duration

Duration returns a time.Duration appropriate for the backoff, incrementing the attempt counter.

func (*Backoff) Reset ¶

func (b *Backoff) Reset()

Reset resets the attempt counter of a backoff.

It should be called when the rate-limited action succeeds.

func (*Backoff) SetDecay ¶

func (b *Backoff) SetDecay(decay time.Duration)

SetDecay sets the duration after which the try counter will be reset. Panics if decay is smaller than 0.

The decay only kicks in if at least the last backoff + decay has elapsed since the last try.

type ClientOption ¶

type ClientOption func(*clientOpts)

ClientOption defines a function type for configuring client options.

func SelectIPTraffic ¶

func SelectIPTraffic(t IPType) ClientOption

SelectIPTraffic configures the type of IP packets (IPv4, IPv6, or both) that the client will listen for. This selection applies to the transport layer but does not filter the DNS record types contained in the packets.

func SelectIfaces ¶

func SelectIfaces(ifaces []net.Interface) ClientOption

SelectIfaces specifies which network interfaces should be used for mDNS operations. If not provided, all multicast-capable interfaces will be used automatically.

type IPType ¶

type IPType uint8

IPType specifies the IP traffic type the client listens for. Note that mDNS packets may contain records of multiple types regardless of the transport protocol (e.g., IPv4 packets can contain AAAA records).

const (
	IPv4        IPType = 0x01
	IPv6        IPType = 0x02
	IPv4AndIPv6 IPType = (IPv4 | IPv6) // Default option
)

IPType options for configuring client network traffic preferences.

type Resolver ¶

type Resolver struct {
	// contains filtered or unexported fields
}

Resolver provides the main interface for service discovery operations, including browsing for services and looking up specific service instances.

func NewResolver ¶

func NewResolver(options ...ClientOption) (*Resolver, error)

NewResolver creates a new mDNS resolver and joins the required UDP multicast groups to listen for mDNS messages on the specified interfaces.

Returns an error if multicast group joining fails on all interfaces.

func (*Resolver) Browse ¶

func (r *Resolver) Browse(ctx context.Context, service, domain string, handler ServiceHandler) error

Browse discovers all services of the specified type in the given domain. The handler function is called for each service instance found or removed.

The browsing operation continues until the context is cancelled or an error occurs. Multiple service instances may be reported concurrently.

func (*Resolver) Lookup ¶

func (r *Resolver) Lookup(ctx context.Context, instance, service, domain string, handler ServiceHandler) error

Lookup searches for a specific service instance by name and type in the given domain. The handler is called when the instance is found or updated.

This is more specific than Browse and targets a single service instance.

type Server ¶

type Server struct {
	// contains filtered or unexported fields
}

Server manages the network connections and protocol handling for a registered mDNS service. It responds to queries, sends announcements, and handles the complete service lifecycle.

func Register ¶

func Register(ctx context.Context, instance, service, domain string, port int, text []string, ifaces []net.Interface) (*Server, error)

Register creates and announces a new service instance on the local network. It automatically determines the host's IP addresses and hostname, and starts the service announcement process using mDNS protocol.

The function validates required parameters and returns a Server instance that manages the service lifecycle. The service will be automatically probed for conflicts and announced on the network.

Returns an error if any required parameter is missing or if network initialization fails.

func RegisterProxy ¶

func RegisterProxy(ctx context.Context, instance, service, domain string, port int, host string, ips []string, text []string, ifaces []net.Interface) (*Server, error)

RegisterProxy registers a service proxy with explicitly provided network configuration, skipping automatic hostname and IP address detection.

This function is useful for services running behind proxies or in containers where the external visibility differs from the internal host configuration.

Returns an error if any required parameter is invalid or if network initialization fails.

func (*Server) SetText ¶

func (s *Server) SetText(text []string)

SetText updates the service's TXT records and announces the changes to the network. This can be used to update service metadata at runtime.

func (*Server) Start ¶ added in v0.0.3

func (s *Server) Start() error

func (*Server) TTL ¶

func (s *Server) TTL(ttl uint32)

TTL sets the Time-To-Live value for DNS responses sent by this server. This controls how long other hosts should cache the service records.

type ServiceEntry ¶

type ServiceEntry struct {
	ServiceRecord

	HostName string   `json:"hostname"` // Host machine's DNS name (FQDN)
	Port     int      `json:"port"`     // Service port number
	Text     []string `json:"text"`     // Service metadata from TXT records
	TTL      uint32   `json:"ttl"`      // Time-to-live from DNS record
	AddrIPv4 []net.IP `json:"-"`        // IPv4 addresses for the service
	AddrIPv6 []net.IP `json:"-"`        // IPv6 addresses for the service

	// Event indicates whether this is an "Add" or "Rmv" event for
	// service availability changes during browsing
	Event string `json:"event"`

	// Flags bitmask indicating which DNS record types have been received
	// for this service entry (PTR, SRV, TXT, A, AAAA)
	Flags uint32 `json:"flags"`

	// IfIndex specifies the network interface index where the service
	// was discovered, useful for multi-homed systems
	IfIndex int `json:"ifindex"`
}

ServiceEntry represents a complete service discovery result containing all available information about a service instance found on the network.

This structure is passed to the ServiceHandler callback and contains both the service description and network connectivity information.

func NewServiceEntry ¶

func NewServiceEntry(instance, service string, domain string) *ServiceEntry

NewServiceEntry constructs a new ServiceEntry with the basic service identification parameters. Additional fields like addresses and port are populated as DNS responses are received and processed.

type ServiceHandler ¶

type ServiceHandler func(*ServiceEntry)

ServiceHandler is a callback function type that processes discovered service entries. It is invoked for each service instance found during browsing or lookup operations.

The function receives a ServiceEntry pointer containing all available service information including network addresses, port, and metadata.

type ServiceRecord ¶

type ServiceRecord struct {
	Instance string   `json:"name"`     // Instance name (e.g. "My Web Server")
	Service  string   `json:"type"`     // Service type (e.g. _http._tcp.)
	Subtypes []string `json:"subtypes"` // Optional service subtypes
	Domain   string   `json:"domain"`   // Domain (defaults to "local" if empty)
	// contains filtered or unexported fields
}

ServiceRecord contains the fundamental description of a service for both registration and discovery purposes. It follows the DNS-SD naming conventions and structure.

func NewServiceRecord ¶

func NewServiceRecord(instance, service string, domain string) *ServiceRecord

NewServiceRecord constructs a new ServiceRecord with the given parameters and precomputes the DNS names for efficient query processing.

The service parameter may include subtypes using the format "service,subtype1,subtype2" as per DNS-SD conventions.

func (*ServiceRecord) ServiceInstanceName ¶

func (s *ServiceRecord) ServiceInstanceName() string

ServiceInstanceName returns the complete service instance name in the format required for DNS queries (e.g. "My Web Server._http._tcp.local.").

This is the name used for SRV and TXT record queries when looking up specific service instances.

func (*ServiceRecord) ServiceName ¶

func (s *ServiceRecord) ServiceName() string

ServiceName returns the complete service name in the format required for DNS queries (e.g. "_http._tcp.local.").

This is the name used for PTR record queries when browsing services.

func (*ServiceRecord) ServiceTypeName ¶

func (s *ServiceRecord) ServiceTypeName() string

ServiceTypeName returns the complete identifier for DNS-SD service type enumeration queries as specified in RFC 6763.

The format is "_services._dns-sd._udp.<domain>" and is used to discover all available service types in a domain.

Directories ¶

Path Synopsis
examples
browse command
lookup command
proxy command
register command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL