From f8c51d801ab1b7c67918316794c6ec7f4ac672b5 Mon Sep 17 00:00:00 2001 From: Jakob Borg Date: Wed, 4 May 2022 18:43:00 +0200 Subject: [PATCH] lib/discover: Filter locally announced addresses (fixes #7289) (#8302) --- lib/discover/local.go | 34 ++++++++++++++++++++++++++++++++++ lib/discover/local_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/lib/discover/local.go b/lib/discover/local.go index a86d45fb4..01e2feb1c 100644 --- a/lib/discover/local.go +++ b/lib/discover/local.go @@ -109,6 +109,13 @@ func (c *localClient) Error() error { // send. func (c *localClient) announcementPkt(instanceID int64, msg []byte) ([]byte, bool) { addrs := c.addrList.AllAddresses() + + // The list of all addresses can include unspecified addresses intended + // for a discovery server to complete, based on the packet source. We + // don't do that for local discovery, so filter out addresses that are + // usable as-is. + addrs = filterUnspecifiedLocal(addrs) + if len(addrs) == 0 { // Nothing to announce return msg, false @@ -281,3 +288,30 @@ func (c *localClient) registerDevice(src net.Addr, device Announce) bool { return isNewDevice } + +// filterUnspecifiedLocal returns the list of addresses after removing any +// unspecified, localhost, multicast, broadcast or port-zero addresses. +func filterUnspecifiedLocal(addrs []string) []string { + filtered := addrs[:0] + for _, addr := range addrs { + u, err := url.Parse(addr) + if err != nil { + continue + } + + tcpAddr, err := net.ResolveTCPAddr("tcp", u.Host) + if err != nil { + continue + } + + switch { + case len(tcpAddr.IP) == 0: + case tcpAddr.Port == 0: + case tcpAddr.IP.IsUnspecified(): + case !tcpAddr.IP.IsGlobalUnicast() && !tcpAddr.IP.IsLinkLocalUnicast(): + default: + filtered = append(filtered, addr) + } + } + return filtered +} diff --git a/lib/discover/local_test.go b/lib/discover/local_test.go index ce5872c2c..6f78e0957 100644 --- a/lib/discover/local_test.go +++ b/lib/discover/local_test.go @@ -9,6 +9,7 @@ package discover import ( "bytes" "context" + "fmt" "net" "testing" @@ -89,3 +90,33 @@ func TestLocalInstanceIDShouldTriggerNew(t *testing.T) { t.Fatal("new instance ID should be new") } } + +func TestFilterUnspecified(t *testing.T) { + addrs := []string{ + "quic://[2001:db8::1]:22000", // OK + "tcp://192.0.2.42:22000", // OK + "quic://[2001:db8::1]:0", // remove, port zero + "tcp://192.0.2.42:0", // remove, port zero + "quic://[::]:22000", // remove, unspecified + "tcp://0.0.0.0:22000", // remove, unspecified + "tcp://[2001:db8::1]", // remove, no port + "tcp://192.0.2.42", // remove, no port + "tcp://foo:bar", // remove, host/port does not resolve + "tcp://127.0.0.1:22000", // remove, not usable from outside + "tcp://[::1]:22000", // remove, not usable from outside + "tcp://224.1.2.3:22000", // remove, not usable from outside (multicast) + "tcp://[fe80::9ef:dff1:b332:5e56]:55681", // OK + "pure garbage", // remove, garbage + "", // remove, garbage + } + exp := []string{ + "quic://[2001:db8::1]:22000", + "tcp://192.0.2.42:22000", + "tcp://[fe80::9ef:dff1:b332:5e56]:55681", + } + res := filterUnspecifiedLocal(addrs) + if fmt.Sprint(res) != fmt.Sprint(exp) { + t.Log(res) + t.Error("filterUnspecified returned invalid addresses") + } +}