From 63d9c7b9b3a838e4e88cc3c3a8ec59169e4badd5 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Tue, 28 Dec 2021 11:26:19 -0800 Subject: [PATCH] derp: add Client.LocalAddr method So magicsock can later ask a DERP connection whether its source IP would've changed if it reconnected. Updates #3619 Change-Id: Ibc8810340c511d6786b60c78c1a61c09f5800e40 Signed-off-by: Brad Fitzpatrick --- derp/derp_client.go | 32 +++++++++++++++++++++++++++----- derp/derp_server.go | 4 ++-- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/derp/derp_client.go b/derp/derp_client.go index 2a97ed299..90b5bb0e8 100644 --- a/derp/derp_client.go +++ b/derp/derp_client.go @@ -12,10 +12,12 @@ import ( "fmt" "io" "sync" + "sync/atomic" "time" "go4.org/mem" "golang.org/x/time/rate" + "inet.af/netaddr" "tailscale.com/types/key" "tailscale.com/types/logger" ) @@ -37,8 +39,8 @@ type Client struct { rate *rate.Limiter // if non-nil, rate limiter to use // Owned by Recv: - peeked int // bytes to discard on next Recv - readErr error // sticky read error + peeked int // bytes to discard on next Recv + readErr atomic.Value // of error; sticky (set by Recv) } // ClientOpt is an option passed to NewClient. @@ -441,13 +443,14 @@ func (c *Client) Recv() (m ReceivedMessage, err error) { } func (c *Client) recvTimeout(timeout time.Duration) (m ReceivedMessage, err error) { - if c.readErr != nil { - return nil, c.readErr + readErr, _ := c.readErr.Load().(error) + if readErr != nil { + return nil, readErr } defer func() { if err != nil { err = fmt.Errorf("derp.Recv: %w", err) - c.readErr = err + c.readErr.Store(err) } }() @@ -587,3 +590,22 @@ func (c *Client) setSendRateLimiter(sm ServerInfoMessage) { sm.TokenBucketBytesBurst) } } + +// LocalAddr returns the TCP connection's local address. +// +// If the client is broken in some previously detectable way, it +// returns an error. +func (c *Client) LocalAddr() (netaddr.IPPort, error) { + readErr, _ := c.readErr.Load().(error) + if readErr != nil { + return netaddr.IPPort{}, readErr + } + if c.nc == nil { + return netaddr.IPPort{}, errors.New("nil conn") + } + a := c.nc.LocalAddr() + if a == nil { + return netaddr.IPPort{}, errors.New("nil addr") + } + return netaddr.ParseIPPort(a.String()) +} diff --git a/derp/derp_server.go b/derp/derp_server.go index 57d0315eb..041e8b374 100644 --- a/derp/derp_server.go +++ b/derp/derp_server.go @@ -23,6 +23,7 @@ import ( "math" "math/big" "math/rand" + "net" "net/http" "os" "os/exec" @@ -283,9 +284,8 @@ type PacketForwarder interface { // It is a defined type so that non-net connections can be used. type Conn interface { io.WriteCloser - + LocalAddr() net.Addr // The *Deadline methods follow the semantics of net.Conn. - SetDeadline(time.Time) error SetReadDeadline(time.Time) error SetWriteDeadline(time.Time) error