diff options
Diffstat (limited to 'soundbox/network.go')
-rw-r--r-- | soundbox/network.go | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/soundbox/network.go b/soundbox/network.go new file mode 100644 index 0000000..6e7acea --- /dev/null +++ b/soundbox/network.go @@ -0,0 +1,58 @@ +package soundbox + +import ( + "context" + "fmt" + "net" + "time" +) + +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) + } +} |