package discovery import ( "context" "fmt" "net" "strings" "time" ) const requestMagic = "SPEKTR_DISCOVER" const responsePrefix = "SPEKTR|" func Discover(ctx context.Context, port int) (string, error) { conn, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4zero, Port: 0}) if err != nil { return "", err } defer conn.Close() target := &net.UDPAddr{IP: net.IPv4bcast, Port: port} if _, err := conn.WriteToUDP([]byte(requestMagic), target); err != nil { return "", err } if err := conn.SetReadDeadline(time.Now().Add(2 * time.Second)); err != nil { return "", err } buf := make([]byte, 256) for { select { case <-ctx.Done(): return "", ctx.Err() default: } n, _, err := conn.ReadFromUDP(buf) if err != nil { if ne, ok := err.(net.Error); ok && ne.Timeout() { return "", fmt.Errorf("discovery timeout") } return "", err } resp := strings.TrimSpace(string(buf[:n])) if strings.HasPrefix(resp, responsePrefix) { addr := strings.TrimSpace(strings.TrimPrefix(resp, responsePrefix)) if addr != "" { return addr, nil } } } }