package soundbox import ( "context" "fmt" "net" "time" ) // streamingPort is the default network port a soundbox is listening to for // incoming audio stream data. const streamingPort = 5316 const dialTimeoutSeconds = 3 // toLinkLocal converts a MAC address to the corresponding IPv6 link-local // address. func toLinkLocal(ha net.HardwareAddr) (net.IP, error) { switch len(ha) { case 6: ip := net.IP{0xfe, 0x80, 0, 0, 0, 0, 0, 0, ha[0] ^ 0b10, ha[1], ha[2], 0xff, 0xfe, ha[3], ha[4], ha[5]} return ip, nil default: return nil, fmt.Errorf("Only IEEE 802 MAC-48 addresses supported") } } func dialContext(ctx context.Context, ha net.HardwareAddr) (net.Conn, error) { ip, err := toLinkLocal(ha) if err != nil { return nil, err } ifaces, err := net.Interfaces() if err != nil { return nil, err } c := make(chan net.Conn) dialContext, cancel := context.WithTimeout(ctx, dialTimeoutSeconds*time.Second) defer cancel() for _, iface := range ifaces { go func() { var d net.Dialer conn, err := d.DialContext( ctx, "tcp6", fmt.Sprintf("[%s%%%s]:%d", ip, iface.Name, streamingPort), ) if err == nil { c <- conn } }() } select { case conn := <-c: return conn, nil case <-dialContext.Done(): return nil, fmt.Errorf("Could not dial TCP connection to %v on port %d on any interface.", ha, streamingPort) } }