summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxegineering <me@xegineering.eu>2024-11-17 12:45:35 +0100
committerxegineering <me@xegineering.eu>2024-11-17 12:48:00 +0100
commitd4cf5ed8e38d1e5d0b8110e8959e002c216f073f (patch)
tree4565ee3c50e6de74b6ce65c615ac71dcf8378a7f
parenta95013aa0813d1644aa15780f360fa4dad2223a8 (diff)
downloadsoundbox-go-d4cf5ed8e38d1e5d0b8110e8959e002c216f073f.tar
soundbox-go-d4cf5ed8e38d1e5d0b8110e8959e002c216f073f.tar.zst
soundbox-go-d4cf5ed8e38d1e5d0b8110e8959e002c216f073f.zip
Fix streaming only via first interface candidate
-rw-r--r--CHANGELOG.md9
-rw-r--r--soundbox/interfaces.go39
-rw-r--r--soundbox/ipv6.go19
-rw-r--r--soundbox/network.go58
-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