diff options
author | xegineering <me@xegineering.eu> | 2024-11-17 12:45:35 +0100 |
---|---|---|
committer | xegineering <me@xegineering.eu> | 2024-11-17 12:48:00 +0100 |
commit | d4cf5ed8e38d1e5d0b8110e8959e002c216f073f (patch) | |
tree | 4565ee3c50e6de74b6ce65c615ac71dcf8378a7f | |
parent | a95013aa0813d1644aa15780f360fa4dad2223a8 (diff) | |
download | soundbox-go-d4cf5ed8e38d1e5d0b8110e8959e002c216f073f.tar soundbox-go-d4cf5ed8e38d1e5d0b8110e8959e002c216f073f.tar.zst soundbox-go-d4cf5ed8e38d1e5d0b8110e8959e002c216f073f.zip |
Fix streaming only via first interface candidate
-rw-r--r-- | CHANGELOG.md | 9 | ||||
-rw-r--r-- | soundbox/interfaces.go | 39 | ||||
-rw-r--r-- | soundbox/ipv6.go | 19 | ||||
-rw-r--r-- | soundbox/network.go | 58 | ||||
-rw-r--r-- | soundbox/network_test.go (renamed from soundbox/ipv6_test.go) | 0 | ||||
-rw-r--r-- | soundbox/url.go (renamed from soundbox/stream.go) | 24 | ||||
-rw-r--r-- | soundbox/url_test.go (renamed from soundbox/stream_test.go) | 0 |
7 files changed, 68 insertions, 81 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 5661bf7..b90ce16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,16 @@ The format is based on [Keep a Changelog][keep-a-changelog], and this project adheres to [Semantic Versioning][semantic-versioning]. +## [Unreleased][unreleased] + +### Fixed + +- streaming only via first network interface candidate + + ## [Version 0.1.4][0.1.4] - 2024-11-10 -## Fixed +### Fixed - possible time offsets between soundboxes after longer time diff --git a/soundbox/interfaces.go b/soundbox/interfaces.go deleted file mode 100644 index 04839c7..0000000 --- a/soundbox/interfaces.go +++ /dev/null @@ -1,39 +0,0 @@ -package soundbox - -import ( - "fmt" - "net" -) - -// getInterface is a function guessing which interface should be used for -// soundbox streaming. This is required since soundbox relies on communication -// via IPv6 link-local addresses which require a specified interface to allow -// communication. This function returns the first interface it finds which is -// up and has a link-local address assigned. -func getInterface() (net.Interface, error) { - all, err := net.Interfaces() - if err != nil { - return net.Interface{}, err - } - - for _, iface := range all { - if iface.Flags&net.FlagUp == 0 { - continue - } - addresses, err := iface.Addrs() - if err != nil { - return net.Interface{}, err - } - for _, addr := range addresses { - ip, _, err := net.ParseCIDR(addr.String()) - if err != nil { - return net.Interface{}, err - } - if ip.IsLinkLocalUnicast() { - return iface, nil - } - } - } - - return net.Interface{}, fmt.Errorf("No interface found for soundbox streaming") -} diff --git a/soundbox/ipv6.go b/soundbox/ipv6.go deleted file mode 100644 index 355e233..0000000 --- a/soundbox/ipv6.go +++ /dev/null @@ -1,19 +0,0 @@ -package soundbox - -import ( - "fmt" - "net" -) - -// 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") - } -} 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) + } +} diff --git a/soundbox/ipv6_test.go b/soundbox/network_test.go index 93ac489..93ac489 100644 --- a/soundbox/ipv6_test.go +++ b/soundbox/network_test.go diff --git a/soundbox/stream.go b/soundbox/url.go index 910523d..4ad9908 100644 --- a/soundbox/stream.go +++ b/soundbox/url.go @@ -3,7 +3,6 @@ package soundbox import ( "context" "errors" - "fmt" "io" "net" "os/exec" @@ -22,28 +21,9 @@ const writeTimeout = 1 * time.Second // devices. The devices are referenced via their MAC addresses given by the // targets argument. The ctx argument is passed to cancel the streaming. func StreamURLContext(ctx context.Context, url string, targets []net.HardwareAddr) error { - iface, err := getInterface() - if err != nil { - return err - } - - ips := make([]net.IP, 0) - for _, target := range targets { - ip, err := toLinkLocal(target) - if err != nil { - return err - } - ips = append(ips, ip) - } - conns := make([]net.Conn, 0) - for _, ip := range ips { - var d net.Dialer - conn, err := d.DialContext( - ctx, - "tcp6", - fmt.Sprintf("[%s%%%s]:%d", ip, iface.Name, streamingPort), - ) + for _, target := range targets { + conn, err := dialContext(ctx, target) if err != nil { return err } diff --git a/soundbox/stream_test.go b/soundbox/url_test.go index 41907ad..41907ad 100644 --- a/soundbox/stream_test.go +++ b/soundbox/url_test.go |