summaryrefslogtreecommitdiff
path: root/soundbox/network.go
diff options
context:
space:
mode:
Diffstat (limited to 'soundbox/network.go')
-rw-r--r--soundbox/network.go58
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)
+ }
+}