Documentation
¶
Overview ¶
Package mdns implements Multicast DNS (mDNS) as specified in RFC 6762, providing zero-configuration service discovery on local networks.
This is a from-scratch implementation with no third-party dependencies — only the Go standard library is used.
Quick Start ¶
Register a service and discover peers:
srv, _ := mdns.NewServer(mdns.DefaultConfig())
srv.Start()
defer srv.Close()
// Advertise a service
srv.RegisterService(&mdns.ServiceInstance{
Name: "My Printer",
Type: "_ipp._tcp",
Port: 631,
})
// Browse for services
browser, _ := srv.Browse("_ipp._tcp")
events, _ := browser.Start()
defer browser.Stop()
for ev := range events {
if ev.Action == mdns.EventAdd {
fmt.Printf("Found: %s on %s:%d\n",
ev.Instance.Name, ev.Instance.Host, ev.Instance.Port)
}
}
Key Types ¶
- Server — the core mDNS engine; create with NewServer
- Config — server configuration; use DefaultConfig for defaults
- ServiceInstance — a service to register via Server.RegisterService
- Browser — service discovery via Server.Browse
- ServiceInstanceInfo — resolved service details delivered via events
RFC 6762 Compliance RFC 6762 Compliance" aria-label="Go to RFC 6762 Compliance">¶
This implementation covers all core sections of RFC 6762:
- Probing (§8.1) with simultaneous probe tiebreaking (§8.2)
- Conflict resolution with auto-rename (§9)
- Announcing — double-announce after probe (§8.3)
- Goodbye — TTL=0 on shutdown (§10.1)
- Cache-flush bit semantics (§10.2)
- Known-Answer Suppression with TTL check (§7.1)
- TC bit multipacket queries (§7.2)
- Exponential backoff browsing (§5.2)
- Cache maintenance queries at 80% TTL (§5.2)
- One-shot queries with QU bit (§5.1, §5.4)
- NSEC negative responses (§6.1)
- Reverse address mapping IPv4+IPv6 (§4)
- Passive observation of failures (§10.5)
- Source address verification (§11)
- Legacy unicast query support (§6.7)
- Multicast rate limiting (§6)
- Packet validation opcode/rcode (§18.14)
- DNS-SD service enumeration (RFC 6763)
IPv4 addresses are prioritized for service advertisement. Multiple instances on the same machine are fully supported.
Cross-Platform Support ¶
Builds for macOS (amd64/arm64), Linux (amd64/arm64), and Windows (amd64/arm64).
Index ¶
- Constants
- Variables
- func CheckMulticastRoute() error
- func LookupHost(ctx context.Context, host string, port int) ([]net.IP, error)
- func RRType(t uint16) string
- type Browser
- type Cache
- func (c *Cache) AllRecords() []*ResourceRecord
- func (c *Cache) Expire() int
- func (c *Cache) HasValidRecord(key CacheKey) bool
- func (c *Cache) KnownAnswers(name string, rrType uint16) []*ResourceRecord
- func (c *Cache) Lookup(key CacheKey) []*ResourceRecord
- func (c *Cache) LookupName(name string) []*ResourceRecord
- func (c *Cache) RecordOriginalTTL(key CacheKey) uint32
- func (c *Cache) RecordRemainingTTL(key CacheKey) uint32
- func (c *Cache) Remove(key CacheKey)
- func (c *Cache) RemoveName(name string)
- func (c *Cache) Upsert(rr *ResourceRecord, from net.IP) bool
- type CacheKey
- type Config
- type Header
- type Message
- type MulticastConn
- func (mc *MulticastConn) Close() error
- func (mc *MulticastConn) Packets() <-chan ReceivedPacket
- func (mc *MulticastConn) Port() int
- func (mc *MulticastConn) SetWarningFunc(fn WarningFunc)
- func (mc *MulticastConn) WriteMulticast(data []byte) error
- func (mc *MulticastConn) WriteMulticastV4(data []byte) (int, error)
- func (mc *MulticastConn) WriteMulticastV6(data []byte) (int, error)
- func (mc *MulticastConn) WriteTo(data []byte, addr *net.UDPAddr) (int, error)
- type Question
- type ReceivedPacket
- type ResourceRecord
- type Server
- func (s *Server) Browse(serviceType string) (*Browser, error)
- func (s *Server) Cache() *Cache
- func (s *Server) Close() error
- func (s *Server) Config() Config
- func (s *Server) HostIPs() []net.IP
- func (s *Server) HostName() string
- func (s *Server) RegisterService(svc *ServiceInstance) error
- func (s *Server) ResolveHost(ctx context.Context, host string) ([]net.IP, error)
- func (s *Server) Services() []*ServiceInstance
- func (s *Server) Start() error
- func (s *Server) String() string
- func (s *Server) UnregisterService(svc *ServiceInstance) error
- type ServiceEvent
- type ServiceInstance
- type ServiceInstanceInfo
- type Warning
- type WarningFunc
Constants ¶
const ( EventAdd = 1 EventRemove = 2 )
EventAdd and EventRemove indicate whether a service appeared or disappeared.
const ( DefaultHostTTL = 120 * time.Second // A/AAAA/SRV records DefaultOtherTTL = 75 * time.Second // PTR/TXT records DefaultProbeWait = 250 * time.Millisecond DefaultProbeCount = 3 DefaultAnnounceTTL = time.Second )
DefaultTTL values from RFC 6762 §10.
const ( TypeA uint16 = 1 // IPv4 address TypeNS uint16 = 2 // Name Server TypeCNAME uint16 = 5 // Canonical Name TypeSOA uint16 = 6 // Start of Authority TypePTR uint16 = 12 // Domain Name Pointer TypeTXT uint16 = 16 // Text TypeAAAA uint16 = 28 // IPv6 address TypeSRV uint16 = 33 // Service TypeOPT uint16 = 41 // EDNS0 option TypeNSEC uint16 = 47 // Next Secure (Negative cache) TypeAny uint16 = 255 // Any / all records )
RR type constants (DNS record types).
const ( ClassIN uint16 = 1 // Internet ClassNone uint16 = 254 // None (RFC 4034) ClassAny uint16 = 255 // Any )
RR class constants.
const DefaultDomain = "local."
DefaultDomain is the default mDNS domain.
const DefaultPort = 53533
DefaultPort is the default mDNS port (RFC 6762 §6.1 uses 5353; configurable — the user requested 53533 as the default).
const PacketBufferSize = 9000
PacketBufferSize is the max mDNS packet size (RFC 6762 §17 allows up to 9000, but the classic MTU-safe limit is 9000 for jumbo frames; we use 9000).
Variables ¶
var ( ErrNameTooLong = errors.New("mdns: domain name too long (>255 bytes)") ErrLabelTooLong = errors.New("mdns: label too long (>63 bytes)") ErrInvalidPointer = errors.New("mdns: invalid compression pointer") ErrPointerLoop = errors.New("mdns: compression pointer loop detected") ErrTruncatedName = errors.New("mdns: domain name is truncated") )
Errors.
var ( IPv4MulticastAddr = net.IPv4(224, 0, 0, 251) IPv6MulticastAddr = net.ParseIP("ff02::fb") )
Default IPv4 and IPv6 mDNS multicast addresses (RFC 6762 §3).
Functions ¶
func CheckMulticastRoute ¶ added in v1.3.0
func CheckMulticastRoute() error
CheckMulticastRoute tests whether the system can send multicast packets to the standard mDNS IPv4 multicast address (224.0.0.251).
It creates a temporary UDP socket, sets IP_MULTICAST_IF to the best detected LAN interface, and attempts to write a single byte. If the send fails — most commonly with "no route to host" — the multicast route is broken.
Common causes:
- VPN network extensions (sing-box, Tailscale, Clash TUN mode) installing an RTF_REJECT route on 224.0.0.0/4 (macOS)
- Missing multicast route on headless / container hosts (Linux)
- Firewall blocking outbound multicast
Returns nil if the send succeeds, or an error describing the failure.
This function is safe to call before starting a Server. It is also called automatically during Server.Start().
func LookupHost ¶
LookupHost resolves a hostname (e.g. "myhost.local.") to IP addresses using a one-shot mDNS query. It blocks until at least one address is found or the context is cancelled.
This is a convenience method that creates a temporary Server if needed. For repeated queries, use a persistent Server instance.
Types ¶
type Browser ¶
type Browser struct {
// contains filtered or unexported fields
}
Browser discovers service instances of a given type on the network. It sends periodic multicast queries and collects responses.
func (*Browser) Instances ¶
func (b *Browser) Instances() []*ServiceInstanceInfo
Instances returns all currently known service instances.
func (*Browser) Start ¶
func (b *Browser) Start() (<-chan ServiceEvent, error)
Start begins the browsing loop.
type Cache ¶
type Cache struct {
// contains filtered or unexported fields
}
Cache is a thread-safe DNS record cache with TTL-based expiry. It implements the caching behaviour described in RFC 6762 §5.2 and §10.
func (*Cache) AllRecords ¶
func (c *Cache) AllRecords() []*ResourceRecord
AllRecords returns all non-expired records in the cache (for debugging).
func (*Cache) HasValidRecord ¶
HasValidRecord returns true if the cache has a non-expired record for the key.
func (*Cache) KnownAnswers ¶
func (c *Cache) KnownAnswers(name string, rrType uint16) []*ResourceRecord
KnownAnswers returns all cached records matching name+type whose remaining TTL is more than half of their original TTL. Used for Known-Answer Suppression (RFC 6762 §7.1).
func (*Cache) Lookup ¶
func (c *Cache) Lookup(key CacheKey) []*ResourceRecord
Lookup returns all non-expired records matching the key.
func (*Cache) LookupName ¶
func (c *Cache) LookupName(name string) []*ResourceRecord
LookupName returns all non-expired records for a given name (any type).
func (*Cache) RecordOriginalTTL ¶
RecordOriginalTTL returns the original TTL of the first non-expired record matching the key.
func (*Cache) RecordRemainingTTL ¶
RecordRemainingTTL returns the remaining TTL (in seconds) for the first non-expired record matching the key, or 0 if no record exists. Used by Browser to determine if a cached record needs refresh (RFC 6762 §5.2).
func (*Cache) RemoveName ¶
RemoveName removes all records for a given name.
func (*Cache) Upsert ¶
func (c *Cache) Upsert(rr *ResourceRecord, from net.IP) bool
Upsert adds or updates a record in the cache. If cacheFlush is set (mDNS cache-flush bit), all existing records for the same name/type from a different source are removed (RFC 6762 §10.2). Returns true if the cache was modified.
type Config ¶
type Config struct {
// Port is the UDP port to listen on. Default: 53533.
Port int
// Domain is the mDNS domain suffix. Default: "local.".
Domain string
// EnableIPv6 enables IPv6 multicast. Default: false.
EnableIPv6 bool
// HostName is the host name to advertise (e.g. "myhost").
// If empty, the system hostname is used.
HostName string
// LogFunc is called for debug logging. If nil, no logging is done.
LogFunc func(format string, args ...any)
// WarningFunc is called when the server detects a non-fatal issue
// that may affect functionality (e.g. broken multicast route caused
// by a VPN). If nil, warnings are logged via LogFunc (if set).
WarningFunc WarningFunc
// Interfaces restricts which network interfaces to use.
// If empty, all active multicast interfaces are used.
Interfaces []string
}
Config holds mDNS server configuration.
func DefaultConfig ¶
func DefaultConfig() Config
DefaultConfig returns a Config with sensible defaults.
type Header ¶
type Header struct {
ID uint16
Flags uint16
QDCount uint16 // number of questions
ANCount uint16 // number of answer records
NSCount uint16 // number of authority records
ARCount uint16 // number of additional records
}
Header is the 12-byte DNS message header (RFC 1035 §4.1.1).
type Message ¶
type Message struct {
Header
Questions []*Question
Answers []*ResourceRecord
Authorities []*ResourceRecord
Additionals []*ResourceRecord
}
Message is a complete DNS message (header + sections).
func UnpackMessage ¶
UnpackMessage parses a DNS message from wire format.
func (*Message) IsProbe ¶
IsProbe returns true if this query is a probe (has authority section records). Per RFC 6762 §8.2, a probe query contains proposed records in the Authority Section that answer the question in the Question Section.
func (*Message) IsResponse ¶
IsResponse returns true if the message is a response (QR=1).
func (*Message) IsTruncated ¶
IsTruncated returns true if the TC bit is set (RFC 6762 §7.2).
func (*Message) IsValidmDNS ¶
IsValidmDNS checks basic mDNS compliance (RFC 6762 §18):
- opcode MUST be 0 (QUERY)
- RCode MUST be 0 (NOERROR)
- responses MUST NOT have a non-zero RCode
type MulticastConn ¶
type MulticastConn struct {
// contains filtered or unexported fields
}
MulticastConn wraps a pair of UDP sockets (IPv4 and optionally IPv6) for sending and receiving mDNS multicast traffic.
func NewMulticastConn ¶
func NewMulticastConn(port int, enableIPv6 bool, interfaces []string) (*MulticastConn, error)
NewMulticastConn creates a multicast connection listening on the given port. It creates an IPv4 socket and optionally an IPv6 socket.
If interfaces is non-empty, only the named interfaces are used for multicast group membership and outgoing traffic; otherwise all active multicast interfaces are used.
func (*MulticastConn) Close ¶
func (mc *MulticastConn) Close() error
Close shuts down all sockets and goroutines.
func (*MulticastConn) Packets ¶
func (mc *MulticastConn) Packets() <-chan ReceivedPacket
Packets returns the channel for received packets.
func (*MulticastConn) Port ¶
func (mc *MulticastConn) Port() int
Port returns the configured mDNS port.
func (*MulticastConn) SetWarningFunc ¶ added in v1.3.0
func (mc *MulticastConn) SetWarningFunc(fn WarningFunc)
SetWarningFunc registers a callback for runtime warnings (e.g. send failures). Called by Server.Start() after creating the MulticastConn.
func (*MulticastConn) WriteMulticast ¶
func (mc *MulticastConn) WriteMulticast(data []byte) error
func (*MulticastConn) WriteMulticastV4 ¶
func (mc *MulticastConn) WriteMulticastV4(data []byte) (int, error)
WriteMulticastV4 sends data to the IPv4 multicast group.
func (*MulticastConn) WriteMulticastV6 ¶
func (mc *MulticastConn) WriteMulticastV6(data []byte) (int, error)
WriteMulticastV6 sends data to the IPv6 multicast group.
type Question ¶
type Question struct {
Name string // e.g. "_http._tcp.local."
Type uint16
Class uint16 // without cache-flush bit (always 0 in questions)
}
Question is a DNS question entry.
type ReceivedPacket ¶
ReceivedPacket holds a received UDP packet with its source address.
type ResourceRecord ¶
type ResourceRecord struct {
Name string
Type uint16
Class uint16 // stored without cache-flush bit
TTL uint32
CacheFlush bool // whether the cache-flush bit is set (for mDNS)
// Type-specific data — only the relevant fields are populated.
IP net.IP // A / AAAA
Target string // PTR / CNAME / NS (target domain name)
Priority uint16 // SRV
Weight uint16 // SRV
Port uint16 // SRV
Text []string // TXT
NextDomain string // NSEC
TypeBitMaps []uint16 // NSEC
RawData []byte // for unrecognised types
}
ResourceRecord is a DNS resource record with type-specific RDATA.
func (*ResourceRecord) String ¶
func (rr *ResourceRecord) String() string
String returns a human-readable representation of the RR.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is the core mDNS engine. It handles receiving and sending mDNS packets, responding to queries, probing for unique names, announcing services, and maintaining a record cache.
func (*Server) Browse ¶
Browse creates and starts a browser for the given service type. serviceType should be like "_http._tcp" or "_http._tcp.local.". The returned channel receives service events (add/remove). Call browser.Stop() to stop browsing.
func (*Server) Close ¶
Close shuts down the server, sending goodbye packets for all registered services.
func (*Server) RegisterService ¶
func (s *Server) RegisterService(svc *ServiceInstance) error
RegisterService registers a service instance with the mDNS server. The service will be probed for uniqueness, then announced on the network. Returns an error if a service with the same instance name is already registered.
func (*Server) ResolveHost ¶
ResolveHost resolves a hostname using the running server's cache and network. IPv4 addresses are returned first, then IPv6 (if enabled).
func (*Server) Services ¶
func (s *Server) Services() []*ServiceInstance
Services returns information about all registered services.
func (*Server) UnregisterService ¶
func (s *Server) UnregisterService(svc *ServiceInstance) error
UnregisterService removes a previously registered service and sends goodbye.
type ServiceEvent ¶
type ServiceEvent struct {
Action int // EventAdd or EventRemove
Instance *ServiceInstanceInfo // the discovered service
}
ServiceEvent is sent to Browser subscribers when a service is found or lost.
type ServiceInstance ¶
type ServiceInstance struct {
// Type is the service type, e.g. "_http._tcp".
Type string
// Name is the instance name, e.g. "My Web Server".
Name string
// Domain is the domain, e.g. "local.".
Domain string
// Host is the hostname, e.g. "myhost.local.".
// If empty, the server's hostname is used.
Host string
// Port is the service port.
Port uint16
// IPs is the list of IP addresses for the service.
// If empty, the server's local addresses are used.
IPs []net.IP
// Text is the TXT record data.
Text []string
// Priority is the SRV priority.
Priority uint16
// Weight is the SRV weight.
Weight uint16
// TTL override (0 = use defaults).
TTL uint32
}
ServiceInstance describes a service to be registered or discovered.
func (*ServiceInstance) InstanceName ¶
func (s *ServiceInstance) InstanceName() string
InstanceName returns the full instance name, e.g. "My Web Server._http._tcp.local.".
func (*ServiceInstance) ServiceType ¶
func (s *ServiceInstance) ServiceType() string
ServiceType returns the full service type domain, e.g. "_http._tcp.local.".
type ServiceInstanceInfo ¶
type ServiceInstanceInfo struct {
Name string // instance name (e.g. "My Web Server")
Type string // service type (e.g. "_http._tcp.local.")
Domain string // domain (e.g. "local.")
Host string // hostname (e.g. "myhost.local.")
Port uint16 // service port
IPs []net.IP // IP addresses
Text []string // TXT record data
Priority uint16 // SRV priority
Weight uint16 // SRV weight
}
ServiceInstanceInfo contains discovered service instance details.
func LookupService ¶
func LookupService(ctx context.Context, serviceType string, port int) ([]*ServiceInstanceInfo, error)
LookupService discovers service instances of the given type. It blocks until the context is cancelled or the timeout expires.
func (*ServiceInstanceInfo) String ¶
func (s *ServiceInstanceInfo) String() string
String returns a human-readable description of the service instance.
type Warning ¶ added in v1.3.0
type Warning struct {
// Code is a machine-readable identifier for the warning.
// Known codes:
// - "multicast_route_broken" — the system cannot send multicast packets
// (commonly caused by VPN network extensions corrupting the 224.0.0.0/4 route).
Code string
// Message is a human-readable description of the issue.
Message string
// Hint is a suggested action to resolve the issue.
Hint string
}
Warning describes a non-fatal issue detected by the mDNS server. Warnings are delivered via Config.WarningFunc.
type WarningFunc ¶ added in v1.3.0
type WarningFunc func(Warning)
WarningFunc is a callback invoked when the server detects a non-fatal issue.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
mdns-demo
command
mdns-demo: Zero-config mDNS service announcer and discovery monitor.
|
mdns-demo: Zero-config mDNS service announcer and discovery monitor. |
|
mdns-example
command
|