### Handle mDNS Server Startup Errors Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/errors.md Shows how to handle errors when creating and starting an mDNS server. This example specifically checks for errors related to multicast availability. ```go package main import ( "fmt" "log" mdns "github.com/hashicorp/mdns" ) func main() { service, err := mdns.NewMDNSService( "MyService", "_http._tcp", "", "", 8080, nil, nil, ) if err != nil { log.Fatalf("Failed to create service: %v\n", err) } server, err := mdns.NewServer(&mdns.Config{Zone: service}) if err != nil { // Handle server startup error if err.Error() == "no multicast listeners could be started" { log.Fatal("Multicast not available on this system/network") } log.Fatalf("Failed to start server: %v\n", err) } defer server.Shutdown() fmt.Println("Server running") } ``` -------------------------------- ### MDNSService Instance Name Examples Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Provides examples for setting the service instance name, including using a descriptive name, the system's hostname, or a custom name with a timestamp. ```go // Descriptive name instance := "My Web Server" // Hostname-based instance, _ := os.Hostname() // Custom with timestamp instance := fmt.Sprintf("app-%d", time.Now().Unix()) ``` -------------------------------- ### MDNSService Configuration Example Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Demonstrates creating an MDNSService instance with standard parameters and setting up a Config with it. Also shows a custom Zone implementation. ```go service, _ := mdns.NewMDNSService( "MyService", "_http._tcp", "local", "", 8080, nil, []string{"version=1.0"}, ) config := &Config{ Zone: service, } // Custom implementation type CustomZone struct{} func (cz *CustomZone) Records(q dns.Question) []dns.RR { // Return records based on question return []dns.RR{} } config := &Config{ Zone: &CustomZone{}, } ``` -------------------------------- ### MDNSService Type Examples Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Shows various examples of defining the service type string, which must follow the DNS-SD format and include the protocol. ```go service := "_http._tcp" // HTTP service := "_https._tcp" // HTTPS service := "_database._tcp" // Custom database service service := "_app._udp" // UDP-based service ``` -------------------------------- ### MDNSService IP Address Examples Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Shows how to configure the IPv4 and/or IPv6 addresses for the service. Examples include auto-discovery, manual IPv4, manual IPv6, and dual-stack configurations. ```go // Auto-discover via hostname lookup ips := nil // Manual IPv4 only ips := []net.IP{ net.ParseIP("192.168.1.100"), } // Manual IPv6 only ips := []net.IP{ net.ParseIP("2001:db8::1"), } // Dual stack ips := []net.IP{ net.ParseIP("192.168.1.100"), net.ParseIP("2001:db8::1"), } ``` -------------------------------- ### MDNSService Hostname Examples Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Demonstrates setting the fully qualified hostname for the service. Examples include using the system's hostname, an explicit FQDN, or a hostname within a specific domain. The hostname must end with a period. ```go hostName := "" // Uses os.Hostname() + "." hostName := "server.local." // Explicit FQDN hostName := "db.consul." // Consul domain hostName, _ := os.Hostname() // Get system hostname hostName += "." // Append period for FQDN ``` -------------------------------- ### Configure MDNS Server with Custom Logging Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-server.md Example of creating an MDNS server with a custom logger. Ensure you import the 'log' and 'os' packages. ```go import "log" import "os" logger := log.New(os.Stdout, "[mdns] ", log.LstdFlags) config := &mdns.Config{ Zone: myService, Logger: logger, LogEmptyResponses: true, } server, _ := mdns.NewServer(config) ``` -------------------------------- ### Custom Zone Implementation Example Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/types.md Provides an example of a custom Zone implementation, MultiServiceZone, which aggregates records from multiple MDNSService instances. ```go type MultiServiceZone struct { services []*mdns.MDNSService } func (mz *MultiServiceZone) Records(q dns.Question) []dns.RR { var records []dns.RR for _, svc := range mz.services { records = append(records, svc.Records(q)...) } return records } ``` -------------------------------- ### Create and Start mDNS Server Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-server.md Use NewServer to create and start a new mDNS server. It requires a Config struct with at least a Zone. The server listens for queries and responds with records from its configured zone. Ensure to call Shutdown when done. ```go package main import ( "fmt" mdns "github.com/hashicorp/mdns" "os" ) func main() { // Create a service to advertise host, _ := os.Hostname() service, _ := mdns.NewMDNSService( host, "_myapp._tcp", "local", "", 8080, nil, []string{"My application service"}, ) // Create and start the server server, err := mdns.NewServer(&mdns.Config{ Zone: service, }) if err != nil { fmt.Printf("Failed to start server: %v\n", err) return } defer server.Shutdown() // Server is now listening and responding to queries fmt.Println("mDNS server started") // Keep running... select {} } ``` -------------------------------- ### Create MDNSService with Explicit Configuration Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-mdns-service.md This example shows how to create an MDNSService with all parameters explicitly defined, including specific IP addresses and TXT records. Use this when you need precise control over the service's network identity and metadata. ```go package main import ( "fmt" "net" mdns "github.com/hashicorp/mdns" ) func main() { // Explicit configuration for all parameters ips := []net.IP{ net.ParseIP("192.168.1.100"), net.ParseIP("2001:db8::1"), } service, err := mdns.NewMDNSService( "Database Server", // instance "_database._tcp", // service "local.", // domain (explicit) "db-server.local.", // hostName (explicit, must be FQDN) 5432, // port ips, // ips (explicit list) []string{ // txt "version=13.0", "replication=true", }, ) if err != nil { fmt.Printf("Error: %v\n", err) return } fmt.Printf("Service ready: %s (port %d)\n", service.Instance, service.Port) } ``` -------------------------------- ### Processing Service Entries Example Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-service-entry.md Demonstrates how to process discovered ServiceEntry instances from the mDNS Query function. It iterates over a channel of entries, printing their details and TXT fields. ```go package main import ( "fmt" mdns "github.com/hashicorp/mdns" "time" ) func main() { entriesCh := make(chan *mdns.ServiceEntry, 4) go func() { for entry := range entriesCh { fmt.Println("=== Service Discovered ===") fmt.Printf("Name: %s\n", entry.Name) fmt.Printf("Host: %s\n", entry.Host) if entry.AddrV4 != nil { fmt.Printf("IPv4: %s\n", entry.AddrV4.String()) } if entry.AddrV6IPAddr != nil { fmt.Printf("IPv6: %s (zone: %s)\n", entry.AddrV6IPAddr.IP.String(), entry.AddrV6IPAddr.Zone) } fmt.Printf("Port: %d\n", entry.Port) fmt.Printf("Info: %s\n", entry.Info) // Access individual TXT fields for i, field := range entry.InfoFields { fmt.Printf(" TXT[%d]: %s\n", i, field) } } }() params := &mdns.QueryParam{ Service: "_http._tcp", Domain: "local", Timeout: 2 * time.Second, Entries: entriesCh, } mdns.Query(params) close(entriesCh) } ``` -------------------------------- ### Parse TXT Record Metadata Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/usage-patterns.md This example shows how to parse the TXT records associated with a discovered service, extracting key-value pairs and handling bare values. This is useful for retrieving service-specific configuration or information. ```go package main import ( "fmt" "log" "strings" mdns "github.com/hashicorp/mdns" "time" ) // ServiceMetadata extracts key-value pairs from TXT records func parseMetadata(entry *mdns.ServiceEntry) map[string]string { metadata := make(map[string]string) for _, field := range entry.InfoFields { // Handle both "key=value" and bare values if idx := strings.Index(field, "="); idx != -1 { key := field[:idx] value := field[idx+1:] metadata[key] = value } else { // Store bare values with generic key metadata[fmt.Sprintf("_text_%d", len(metadata))] = field } } return metadata } func main() { entriesCh := make(chan *mdns.ServiceEntry, 4) params := &mdns.QueryParam{ Service: "_http._tcp", Timeout: 2 * time.Second, Entries: entriesCh, } go mdns.Query(params) for entry := range entriesCh { fmt.Printf("Service: %s\n", entry.Name) metadata := parseMetadata(entry) if len(metadata) > 0 { fmt.Println("Metadata:") for key, value := range metadata { fmt.Printf(" %s: %s\n", key, value) } } } } ``` -------------------------------- ### MDNSService Port Number Examples Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Provides common examples for specifying the service port number, ensuring it is greater than 0. ```go port := 80 // HTTP port := 443 // HTTPS port := 8080 // Common alternate HTTP port := 5432 // PostgreSQL port := 3306 // MySQL ``` -------------------------------- ### Minimal mDNS Server Configuration Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-server.md Configure a new mDNS server with the minimum required 'Zone' field. This example uses the default network interface. ```go // Minimal configuration with default interface config := &mdns.Config{ Zone: myService, } server, _ := mdns.NewServer(config) ``` -------------------------------- ### Service Advertisement Server Workflow Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/module-overview.md Describes the process for advertising a service using the mDNS server. This includes defining the service, configuring the server, and starting the responder. ```go 1. Create MDNSService with connection details 2. Create Config with the service 3. Call NewServer() to start responder 4. Server runs in background, answering queries 5. Call Shutdown() to stop ``` -------------------------------- ### NewServer Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-server.md Creates and starts a new mDNS server that responds to queries for services in its configured zone. It immediately begins listening for mDNS queries on multicast addresses. ```APIDOC ## NewServer ### Description Creates and starts a new mDNS server that responds to queries for services in its configured zone. It immediately begins listening for mDNS queries on multicast addresses. ### Signature ```go func NewServer(config *Config) (*Server, error) ``` ### Parameters #### Path Parameters - **config** (*Config) - Required - Server configuration including Zone and optional interface binding ### Return Value - **Type**: (*Server, error) - **Description**: Returns a new Server instance and nil error on success, or nil and an error if multicast listeners could not be established ### Throws/Errors - **fmt.Errorf("no multicast listeners could be started")** - Unable to bind to both IPv4 and IPv6 multicast addresses ``` -------------------------------- ### MDNSService Domain Suffix Examples Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Illustrates different ways to specify the domain suffix for service discovery, including using the default, an explicit default, or a custom domain. The domain must end with a period if provided. ```go domain := "" // Uses default "local." domain := "local." // Explicit default domain := "consul." // Consul domain domain := "custom.local." // Custom domain ``` -------------------------------- ### Handle Multicast Not Supported Error in mDNS Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/errors.md The 'no multicast listeners could be started' error indicates that the network does not support multicast, which is essential for mDNS. This situation is usually fatal, requiring a switch to unicast DNS or a service registry. ```go // This error is usually fatal; mDNS requires multicast // Solution: switch to unicast DNS or service registry (Consul, etc.) log.Fatal("Multicast not available on this network") ``` -------------------------------- ### Query Available Network Interfaces Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Demonstrates how to list all available network interfaces on the system and filter them by flags like 'Up' and 'Loopback'. ```go interfaces, _ := net.Interfaces() for _, iface := range interfaces { // Filter by flags if iface.Flags&net.FlagUp == 0 { continue // Skip down interfaces } if iface.Flags&net.FlagLoopback != 0 { continue // Skip loopback } addrs, _ := iface.Addrs() fmt.Printf("%s: %v\n", iface.Name, addrs) } ``` -------------------------------- ### Basic Service Discovery Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Demonstrates how to perform a basic mDNS service discovery query to find services of a specific type. ```go package main import ( "context" "fmt" "log" "time" "github.com/hashicorp/mdns" ) func main() { // Set up a context with a timeout for the query. ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Define the service to query for (e.g., "_http._tcp"). squeryParam := mdns.DefaultParams("_http._tcp") // Perform the service discovery query. entries, err := mdns.Query(ctx, queryParam) if err != nil { log.Fatalf("Query failed: %v", err) } // Print the discovered services. if len(entries) == 0 { fmt.Println("No services found.") } else { fmt.Printf("Found %d services:\n", len(entries)) for _, entry := range entries { fmt.Printf(" - Name: %s, Addr: %s, Port: %d\n", entry.Name, entry.Addr.String(), entry.Port) } } } ``` -------------------------------- ### Create mDNS Server with Detailed Logging Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/usage-patterns.md Set up an mDNS server with a custom logger for detailed output, including logging empty responses. This is useful for debugging server behavior. ```go package main import ( "fmt" "log" "os" "os/signal" mdns "github.com/hashicorp/mdns" ) func main() { // Create logger with detailed output logger := log.New( os.Stderr, "[mdns-server] ", log.LstdFlags|log.Lshortfile, ) // Create service service, _ := mdns.NewMDNSService( "example", "_example._tcp", "", "", 9000, nil, []string{"description=Example Service"}, ) // Start server with detailed logging server, err := mdns.NewServer(&mdns.Config{ Zone: service, Logger: logger, LogEmptyResponses: true, // Log queries with no matching records }) if err != nil { logger.Fatalf("Server start failed: %v\n", err) } logger.Println("Server started successfully") // Handle shutdown sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, os.Interrupt) <-sigCh server.Shutdown() logger.Println("Server shutdown complete") } ``` -------------------------------- ### Create MDNSService with Defaults Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-mdns-service.md This snippet demonstrates creating an MDNSService using minimal parameters, relying on default values for domain, hostname, and IP addresses. It's suitable for basic service advertisements where system defaults are acceptable. ```go package main import ( "fmt" mdns "github.com/hashicorp/mdns" "os" ) func main() { // Uses system hostname and auto-discovered IPs service, err := mdns.NewMDNSService( "My Service", // instance "_http._tcp", // service "", // domain (defaults to "local.") "", // hostName (uses os.Hostname()) 8080, // port nil, // ips (auto-discovered) []string{"path=/api"}, // txt ) if err != nil { fmt.Printf("Error creating service: %v\n", err) return } fmt.Printf("Advertising: %s\n", service.Instance) } ``` -------------------------------- ### Handling Multiple Services Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Demonstrates querying for multiple different mDNS service types simultaneously. ```go package main import ( "context" "fmt" "log" "time" "github.com/hashicorp/mdns" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() // Define multiple service types to query. sservicesToQuery := []string{ "_http._tcp", "_ssh._tcp", "_printer._tcp", } results := make(map[string][]mdns.ServiceEntry) // Use a WaitGroup to wait for all queries to complete. var wg sync.WaitGroup for _, serviceType := range servicesToQuery { wg.Add(1) go func(sType string) { defer wg.Done() queryParam := mdns.DefaultParams(sType) entries, err := mdns.Query(ctx, queryParam) if err != nil { log.Printf("Error querying for %s: %v\n", sType, err) return } results[sType] = entries }(serviceType) } wg.Wait() // Print the results. for serviceType, entries := range results { if len(entries) == 0 { fmt.Printf("No %s services found.\n", serviceType) } else { fmt.Printf("Found %d %s services:\n", len(entries), serviceType) for _, entry := range entries { fmt.Printf(" - Name: %s, Addr: %s, Port: %d\n", entry.Name, entry.Addr.String(), entry.Port) } } } } ``` -------------------------------- ### Handle mDNS Query Initialization Errors Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/errors.md Demonstrates how to catch and categorize errors returned by mdns.Query, which typically occur during initialization. This includes network binding issues and configuration problems. ```go package main import ( "fmt" "log" mdns "github.com/hashicorp/mdns" "time" ) func main() { entriesCh := make(chan *mdns.ServiceEntry, 4) params := &mdns.QueryParam{ Service: "_http._tcp", Domain: "local", Timeout: time.Second, Entries: entriesCh, Logger: log.Default(), } // Query returns error only on initialization failures if err := mdns.Query(params); err != nil { log.Printf("Query failed to initialize: %v\n", err) // Categorize error switch { case err.Error() == "Must enable at least one of IPv4 and IPv6 querying": log.Fatal("Configuration error: both IPv4 and IPv6 disabled") case err.Error() == "failed to bind to any unicast udp port": log.Fatal("Network error: unable to bind to UDP unicast socket") case err.Error() == "failed to bind to any multicast udp port": log.Fatal("Network error: unable to bind to UDP multicast socket") default: log.Printf("Unknown error: %v\n", err) } } // Process results (no error means query is running) for entry := range entriesCh { fmt.Printf("Found: %s\n", entry.Name) } } ``` -------------------------------- ### Basic Service Discovery with Defaults Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/usage-patterns.md Performs a simple mDNS lookup for a service using default parameters. Results are collected in a channel. ```go package main import ( "fmt" "log" mdns "github.com/hashicorp/mdns" ) func main() { // Create buffered channel for results entriesCh := make(chan *mdns.ServiceEntry, 4) go func() { for entry := range entriesCh { fmt.Printf("Found: %s at %s:%d\n", entry.Name, entry.Host, entry.Port) } }() // Lookup with simple defaults if err := mdns.Lookup("_http._tcp", entriesCh); err != nil { log.Fatal(err) } close(entriesCh) } ``` -------------------------------- ### Service Discovery Client Workflow Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/module-overview.md Outlines the steps for using the mDNS client to discover services on the network. This involves creating query parameters, setting up a results channel, and calling the appropriate lookup function. ```go 1. Create QueryParam with service name 2. Create buffered channel for results 3. Call Query() or Lookup() 4. Receive ServiceEntry structs from channel 5. Use discovered addresses to connect ``` -------------------------------- ### Service Advertisement Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Shows how to create and run an mDNS responder to advertise a service on the local network. ```go package main import ( "context" "fmt" "log" "time" "github.com/hashicorp/mdns" ) func main() { // Define the service details. sservice := mdns.MDNSService{ Instance: "My-Go-Service", Service: "_http._tcp", Domain: "local.", Port: 8080, Info: "A simple Go HTTP service", Text: []string{"version=1.0"}, } // Create default server parameters. params := mdns.DefaultParams(service.Service) // Create a new mDNS server. server, err := mdns.NewServer(&mdns.Config{Service: &service}) if err != nil { log.Fatalf("Failed to create server: %v", err) } defer server.Shutdown() fmt.Printf("Advertising service %s on port %d...\n", service.Service, service.Port) // Keep the server running indefinitely (or until context cancellation). <-server.ShutdownChan() } ``` -------------------------------- ### Configure mDNS Query with Custom Logger Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Demonstrates setting up a custom logger for diagnostic output in mDNS queries. The logger can be configured with specific output streams and formatting. ```go import ( "log" "os" mdns "github.com/hashicorp/mdns" ) // Create custom logger logger := log.New(os.Stdout, "[mdns] ", log.LstdFlags|log.Lshortfile) params := &QueryParam{ Service: "_http._tcp", Logger: logger, Entries: make(chan *ServiceEntry), } mdns.Query(params) ``` -------------------------------- ### Custom Zone Implementation Workflow Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/module-overview.md Explains how to implement and use custom zones for advanced mDNS service advertisement. This allows for multiple services or dynamic record generation. ```go 1. Implement Zone interface (Records method) 2. Return DNS records based on questions 3. Use custom zone in Server Config 4. Supports multiple services or dynamic behavior ``` -------------------------------- ### Custom Logger for Server Diagnostics Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Illustrates how to provide a custom logger instance to the mDNS server for detailed diagnostics and error reporting. Requires 'log' and 'os' packages. ```go import ( "log" "os" mdns "github.com/hashicorp/mdns" ) logger := log.New(os.Stderr, "[mDNS-Server] ", log.LstdFlags) config := &Config{ Zone: service, Logger: logger, } server, _ := NewServer(config) ``` -------------------------------- ### NewServer Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-server.md Creates a new mDNS server instance with the provided configuration. It sets up logging and network listeners for mDNS communication. ```APIDOC ## NewServer ### Description Creates a new mDNS server instance with the provided configuration. It sets up logging and network listeners for mDNS communication. ### Signature `mdns.NewServer(config *Config) (*Server, error)` ### Parameters #### Request Body - **config** (`*Config`) - Required - The configuration for the mDNS server, including Zone and Logger. ### Response #### Success Response - **Server** (`*Server`) - A pointer to the newly created mDNS server instance. - **error** (`error`) - An error if the server could not be created. ``` -------------------------------- ### TXT Record Parsing Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Demonstrates how to parse TXT records associated with discovered mDNS services. ```go package main import ( "context" "fmt" "log" "strings" "time" "github.com/hashicorp/mdns" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() queryParam := mdns.DefaultParams("_http._tcp") entries, err := mdns.Query(ctx, queryParam) if err != nil { log.Fatalf("Query failed: %v", err) } if len(entries) == 0 { fmt.Println("No services found.") return } fmt.Println("Discovered services with TXT records:") for _, entry := range entries { if len(entry.Text) > 0 { fmt.Printf("Service: %s\n", entry.Name) for _, txt := range entry.Text { // TXT records are typically key=value pairs parts := strings.SplitN(txt, "=", 2) if len(parts) == 2 { fmt.Printf(" - %s: %s\n", parts[0], parts[1]) } else { fmt.Printf(" - %s\n", txt) // Handle cases without '=' } } } } } ``` -------------------------------- ### Query mDNS Service with Specific Interface Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Demonstrates how to retrieve a network interface by name and use it to bind mDNS queries to a specific interface. If Interface is nil, the system default is used. ```go import "net" // Get interface by name iface, _ := net.InterfaceByName("eth0") // Enumerate all interfaces interfaces, _ := net.Interfaces() for _, iface := range interfaces { fmt.Printf("Interface: %s\n", iface.Name) } // Use in query params := &QueryParam{ Service: "_http._tcp", Interface: iface, Entries: make(chan *ServiceEntry), } ``` -------------------------------- ### Implement mDNS Query with Retries Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/errors.md Demonstrates a robust approach to querying for mDNS services by implementing a retry mechanism with exponential backoff. This helps in scenarios where initial queries might fail due to transient network conditions. ```go package main import ( "fmt" "log" mdns "github.com/hashicorp/mdns" "time" ) func queryWithRetry(service string, maxRetries int) { for attempt := 1; attempt <= maxRetries; attempt++ { entriesCh := make(chan *mdns.ServiceEntry, 4) params := &mdns.QueryParam{ Service: service, Timeout: time.Second, Entries: entriesCh, } if err := mdns.Query(params); err != nil { log.Printf("Attempt %d: Query failed: %v\n", attempt, err) if attempt < maxRetries { time.Sleep(time.Duration(attempt) * time.Second) // Exponential backoff continue } else { log.Fatalf("Query failed after %d attempts\n", maxRetries) } } // Successfully started query count := 0 for entry := range entriesCh { count++ fmt.Printf("Found service: %s\n", entry.Name) } fmt.Printf("Found %d services\n", count) break } } func main() { queryWithRetry("_http._tcp", 3) } ``` -------------------------------- ### Server with Detailed Logging Configuration Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Configures an mDNS server with a custom logger and options for logging empty responses. Useful for debugging mDNS communication. ```go import ( "log" "os" mdns "github.com/hashicorp/mdns" ) logger := log.New(os.Stdout, "[mdns] ", log.LstdFlags|log.Lshortfile) service, _ := mdns.NewMDNSService(...) server, _ := mdns.NewServer(&mdns.Config{ Zone: service, Logger: logger, LogEmptyResponses: true, }) defferver.Shutdown() ``` -------------------------------- ### IPv6 Link-Local Handling Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Shows how to configure mDNS queries to specifically handle IPv6 link-local addresses. ```go package main import ( "context" "fmt" "log" "time" "github.com/hashicorp/mdns" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() // Configure query parameters to include IPv6 link-local addresses. queryParam := mdns.DefaultParams("_services._dns-sd._udp") queryParam.EnableIPv6 = true queryParam.IncludeLinkLocal = true entries, err := mdns.Query(ctx, queryParam) if err != nil { log.Fatalf("Query failed: %v", err) } fmt.Printf("Found %d services (including IPv6 link-local):\n", len(entries)) for _, entry := range entries { fmt.Printf(" - Name: %s, Addr: %s, Port: %d\n", entry.Name, entry.Addr.String(), entry.Port) } } ``` -------------------------------- ### Configure Custom Logger for mDNS Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/errors.md Illustrates how to create and use a custom logger with the mdns library for both query and server operations. This allows for detailed logging of network events, errors, and DNS unpacking issues. ```go import ( "log" "os" mdns "github.com/hashicorp/mdns" ) // Create logger with timestamps and file info logger := log.New( os.Stderr, "[mdns] ", log.LstdFlags|log.Lshortfile, ) // Use in Query params := &mdns.QueryParam{ Service: "_http._tcp", Logger: logger, Entries: make(chan *mdns.ServiceEntry), } // Use in Server // Assuming 'service' is a valid *mdns.MDNSService instance // server, _ := mdns.NewServer(&mdns.Config{ // Zone: service, // Logger: logger, // }) ``` -------------------------------- ### Bind Server to Specific Network Interface Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Shows how to bind the mDNS server to a specific network interface by name. Requires importing the 'net' package. ```go import "net" // Bind to specific named interface iface, err := net.InterfaceByName("eth0") if err != nil { panic(err) } config := &Config{ Zone: service, Iface: iface, } server, _ := NewServer(config) ``` -------------------------------- ### Link-local IPv6 Address Handling Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/api-reference-service-entry.md Shows how to format and print a link-local IPv6 address with its zone information. This is crucial for correct routing of such addresses. ```go if entry.AddrV6IPAddr != nil && entry.AddrV6IPAddr.Zone != "" { fmt.Printf("Link-local address: %s%%%s\n", entry.AddrV6IPAddr.IP, entry.AddrV6IPAddr.Zone) } ``` -------------------------------- ### Interface-Specific Queries Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Shows how to direct mDNS queries to a specific network interface. ```go package main import ( "context" "fmt" "log" "time" "github.com/hashicorp/mdns" ) func main() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() queryParam := mdns.DefaultParams("_http._tcp") // Specify the network interface to use for the query (e.g., "eth0"). // Replace "eth0" with the actual interface name on your system. queryParam.Interface = "eth0" entries, err := mdns.Query(ctx, queryParam) if err != nil { log.Fatalf("Query failed on interface %s: %v", queryParam.Interface, err) } fmt.Printf("Found %d services on interface %s:\n", len(entries), queryParam.Interface) for _, entry := range entries { fmt.Printf(" - Name: %s, Addr: %s, Port: %d\n", entry.Name, entry.Addr.String(), entry.Port) } } ``` -------------------------------- ### Error Handling and Retry Logic Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Demonstrates a pattern for handling mDNS query errors and implementing retry logic. ```go package main import ( "context" "fmt" "log" "time" "github.com/hashicorp/mdns" ) func main() { maxRetries := 3 initialDelay := 1 * time.Second var entries []mdns.ServiceEntry var err error for i := 0; i < maxRetries; i++ { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) queryParam := mdns.DefaultParams("_ipp._tcp") entries, err = mdns.Query(ctx, queryParam) cancel() // Ensure cancel is called even if Query returns early if err == nil { fmt.Printf("Successfully found %d services after %d retries.\n", len(entries), i) break // Success, exit loop } log.Printf("Attempt %d failed: %v\n", i+1, err) if i < maxRetries-1 { // Implement exponential backoff or fixed delay select { case <-time.After(initialDelay): initialDelay *= 2 // Example: exponential backoff case <-ctx.Done(): log.Println("Context cancelled during retry delay.") return } } else { log.Fatalf("All %d retries failed. Last error: %v\n", maxRetries, err) } } // Process entries if successful if err == nil { fmt.Printf("Final results: %d services found.\n", len(entries)) } } ``` -------------------------------- ### Basic mDNS Service Advertisement Server Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/usage-patterns.md Sets up and advertises a local mDNS service. It listens for interrupt signals for graceful shutdown. ```go package main import ( "fmt" "log" "os" "os/signal" "syscall" mdns "github.com/hashicorp/mdns" ) func main() { // Create service descriptor hostName, _ := os.Hostname() service, err := mdns.NewMDNSService( hostName, // instance "_myapp._tcp", // service type "", // domain (defaults to "local.") "", // hostName (auto-detected) 8080, // port nil, // ips (auto-discovered) []string{"version=1.0"}, // txt records ) if err != nil { log.Fatalf("Failed to create service: %v\n", err) } // Start server server, err := mdns.NewServer(&mdns.Config{Zone: service}) if err != nil { log.Fatalf("Failed to start server: %v\n", err) } fmt.Printf("Advertising %s on port %d\n", service.Instance, service.Port) // Graceful shutdown sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) <-sigCh server.Shutdown() fmt.Println("Server stopped") } ``` -------------------------------- ### Service Discovery with Custom Query Parameters Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/usage-patterns.md Performs an mDNS query with custom parameters including service type, domain, and a specific timeout. Results are processed after collection. ```go package main import ( "fmt" "log" mdns "github.com/hashicorp/mdns" "time" ) func main() { entriesCh := make(chan *mdns.ServiceEntry, 10) // Collect all results before processing go func() { defer close(entriesCh) params := &mdns.QueryParam{ Service: "_ssh._tcp", Domain: "local", Timeout: 3 * time.Second, // Longer timeout for thorough search Entries: entriesCh, } if err := mdns.Query(params); err != nil { log.Printf("Query error: %v\n", err) } }() // Process results count := 0 for entry := range entriesCh { count++ fmt.Printf("%d. %s\n", count, entry.Name) if entry.AddrV4 != nil { fmt.Printf(" IPv4: %s:%d\n", entry.AddrV4, entry.Port) } if entry.AddrV6IPAddr != nil { fmt.Printf(" IPv6: [%s%%%s]:%d\n", entry.AddrV6IPAddr.IP, entry.AddrV6IPAddr.Zone, entry.Port) } if entry.Info != "" { fmt.Printf(" Info: %s\n", entry.Info) } } fmt.Printf("\nFound %d services\n", count) } ``` -------------------------------- ### Define TXT Records for Service Metadata Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Define service metadata using key=value strings for TXT records. Each string represents a separate TXT record following DNS-SD conventions. ```go txt := []string{ "version=1.0", "path=/api", } ``` ```go txt := []string{ "product=MyApp", "config=prod", "support_email=admin@example.com", } ``` ```go txt := nil // No metadata ``` -------------------------------- ### Basic Service Advertisement Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Advertises a new mDNS service with basic configuration. Ensure the service name, type, and port are correctly set. ```go package main import ( mdns "github.com/hashicorp/mdns" ) func main() { service, _ := mdns.NewMDNSService( "My App", "_myapp._tcp", "", "", 9000, nil, []string{"version=1.0"}, ) server, _ := mdns.NewServer(&mdns.Config{Zone: service}) defer server.Shutdown() select {} } ``` -------------------------------- ### Service Discovery with Custom Interface Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/configuration.md Performs mDNS service discovery on a specific network interface. Set the desired service type, interface, and timeout for the query. ```go import ( "net" mdns "github.com/hashicorp/mdns" ) iface, _ := net.InterfaceByName("eth0") params := &mdns.QueryParam{ Service: "_http._tcp", Interface: iface, Timeout: 2 * time.Second, Entries: make(chan *mdns.ServiceEntry, 4), } mdns.Query(params) ``` -------------------------------- ### Query Service with Retry Logic Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/usage-patterns.md This snippet demonstrates how to query for services with a configurable number of retry attempts and exponential backoff. It's useful for unreliable network conditions. ```go package main import ( "fmt" "log" "math" "time" mdns "github.com/hashicorp/mdns" ) func queryWithRetry(service string, maxAttempts int) (int, error) { for attempt := 1; attempt <= maxAttempts; attempt++ { entriesCh := make(chan *mdns.ServiceEntry, 4) params := &mdns.QueryParam{ Service: service, Timeout: 1 * time.Second, Entries: entriesCh, } if err := mdns.Query(params); err != nil { backoff := time.Duration(math.Pow(2, float64(attempt-1))) * time.Second log.Printf("Attempt %d failed: %v, retrying in %v\n", attempt, err, backoff) if attempt < maxAttempts { time.Sleep(backoff) continue } return 0, err } // Count services found count := 0 for range entriesCh { count++ } return count, nil } return 0, fmt.Errorf("max attempts exceeded") } func main() { count, err := queryWithRetry("_http._tcp", 3) if err != nil { log.Fatalf("Query failed: %v\n", err) } fmt.Printf("Found %d services\n", count) } ``` -------------------------------- ### Context Cancellation for Queries Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/README.md Illustrates how to use context cancellation to stop an ongoing mDNS query gracefully. ```go package main import ( "context" "fmt" "log" "time" "github.com/hashicorp/mdns" ) func main() { // Create a context that will be cancelled after a short duration. ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() // Set up query parameters. queryParam := mdns.DefaultParams("_ssh._tcp") // Start the query in a goroutine. go func() { entries, err := mdns.Query(ctx, queryParam) if err != nil { // Check if the error is due to context cancellation. if err == context.Canceled { fmt.Println("Query was cancelled.") } else { log.Printf("Query failed: %v\n", err) } return } fmt.Printf("Found %d SSH services.\n", len(entries)) }() // Wait for the context to be done (either timeout or explicit cancel). <-ctx.Done() fmt.Println("Main function exiting.") } ``` -------------------------------- ### Handle IPv6 Link-Local Addresses Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/usage-patterns.md This snippet shows how to properly handle IPv6 link-local addresses by including the zone identifier when formatting them for display or connection. It also demonstrates handling IPv4 addresses and displaying service metadata. ```go package main import ( "fmt" "log" mdns "github.com/hashicorp/mdns" "net" "time" ) func main() { entriesCh := make(chan *mdns.ServiceEntry, 4) params := &mdns.QueryParam{ Service: "_http._tcp", Timeout: 2 * time.Second, Entries: entriesCh, } go mdns.Query(params) for entry := range entriesCh { fmt.Printf("Service: %s\n", entry.Name) // Handle IPv4 if entry.AddrV4 != nil { addr := net.TCPAddr{ IP: entry.AddrV4, Port: entry.Port, } fmt.Printf(" IPv4 address: %s\n", addr.String()) } // Handle IPv6 with proper zone qualification if entry.AddrV6IPAddr != nil { // For link-local addresses, zone is required if entry.AddrV6IPAddr.IP.IsLinkLocalUnicast() { fmt.Printf(" IPv6 link-local: [%s%%%s]:%d\n", entry.AddrV6IPAddr.IP, entry.AddrV6IPAddr.Zone, entry.Port) // Build proper address for connection connAddr := net.JoinHostPort( entry.AddrV6IPAddr.IP.String()+"%"+entry.AddrV6IPAddr.Zone, fmt.Sprintf("%d", entry.Port), ) fmt.Printf(" Connection address: %s\n", connAddr) } else { // Global or unique local address fmt.Printf(" IPv6 address: [%s]:%d\n", entry.AddrV6IPAddr.IP, entry.Port) } } // Display metadata if len(entry.InfoFields) > 0 { fmt.Println(" Metadata:") for _, field := range entry.InfoFields { fmt.Printf(" - %s\n", field) } } } } ``` -------------------------------- ### Import mdns Package Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/INDEX.md Import the mdns package to use its functionalities. Ensure you are using a compatible Go version. ```go import mdns "github.com/hashicorp/mdns" ``` -------------------------------- ### Config Structure Definition Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/types.md Defines the configuration for an mDNS server. It includes the zone implementation, network interface for binding, and options for logging. Use this to set up a new mDNS server instance. ```go type Config struct { Zone Zone Iface *net.Interface LogEmptyResponses bool Logger *log.Logger } ``` -------------------------------- ### Server and Service Creation Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/INDEX.md Functions for creating and managing mDNS responders and services. `NewServer` initializes a new mDNS responder, and `NewMDNSService` creates a descriptor for a service. ```APIDOC ## NewServer() ### Description Initializes and creates a new mDNS responder (server). ### Purpose Create mDNS responder. ### File server.go ### Reference [api-reference-server.md](api-reference-server.md) ``` ```APIDOC ## NewMDNSService() ### Description Creates a new service descriptor, defining the details of a service to be advertised. ### Purpose Create service descriptor. ### File zone.go ### Reference [api-reference-mdns-service.md](api-reference-mdns-service.md) ``` -------------------------------- ### Concurrent mDNS Queries Source: https://github.com/hashicorp/mdns/blob/main/_autodocs/module-overview.md Demonstrates safe concurrent execution of mDNS queries. Each call to mdns.Query creates an independent client, allowing multiple queries to run in parallel without thread-safety issues. ```go // Safe: each call creates independent client go mdns.Query(params1) go mdns.Query(params2) ```