From f3b13604b3dd2ed5875528999873ada3aff06111 Mon Sep 17 00:00:00 2001 From: Brad Fitzpatrick Date: Mon, 21 Mar 2022 14:10:25 -0700 Subject: [PATCH] control/controlclient, ipn/ipnlocal, tailcfg: add MapResponse.PopBrowserURL Updates #3802 Change-Id: I89481fc5782a0cc8084354706f8f28d94f197325 Signed-off-by: Brad Fitzpatrick --- control/controlclient/direct.go | 20 ++++++++++++++++---- control/controlclient/map.go | 1 + ipn/ipnlocal/local.go | 9 ++++++++- tailcfg/tailcfg.go | 10 ++++++++-- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/control/controlclient/direct.go b/control/controlclient/direct.go index 8aafee2d1..d14910e9f 100644 --- a/control/controlclient/direct.go +++ b/control/controlclient/direct.go @@ -69,6 +69,7 @@ type Direct struct { keepSharerAndUserSplit bool skipIPForwardingCheck bool pinger Pinger + popBrowser func(url string) // or nil mu sync.Mutex // mutex guards the following fields serverKey key.MachinePublic // original ("legacy") nacl crypto_box-based public key @@ -100,9 +101,10 @@ type Options struct { NewDecompressor func() (Decompressor, error) KeepAlive bool Logf logger.Logf - HTTPTestClient *http.Client // optional HTTP client to use (for tests only) - DebugFlags []string // debug settings to send to control - LinkMonitor *monitor.Mon // optional link monitor + HTTPTestClient *http.Client // optional HTTP client to use (for tests only) + DebugFlags []string // debug settings to send to control + LinkMonitor *monitor.Mon // optional link monitor + PopBrowserURL func(url string) // optional func to open browser // KeepSharerAndUserSplit controls whether the client // understands Node.Sharer. If false, the Sharer is mapped to the User. @@ -198,6 +200,7 @@ func NewDirect(opts Options) (*Direct, error) { linkMon: opts.LinkMonitor, skipIPForwardingCheck: opts.SkipIPForwardingCheck, pinger: opts.Pinger, + popBrowser: opts.PopBrowserURL, } if opts.Hostinfo == nil { c.SetHostinfo(hostinfo.New()) @@ -849,7 +852,15 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm metricMapResponsePings.Add(1) go answerPing(c.logf, c.httpc, pr) } - + if u := resp.PopBrowserURL; u != "" && u != sess.lastPopBrowserURL { + sess.lastPopBrowserURL = u + if c.popBrowser != nil { + c.logf("netmap: control says to open URL %v; opening...", u) + c.popBrowser(u) + } else { + c.logf("netmap: control says to open URL %v; no popBrowser func", u) + } + } if resp.ControlTime != nil && !resp.ControlTime.IsZero() { c.logf.JSON(1, "controltime", resp.ControlTime.UTC()) } @@ -858,6 +869,7 @@ func (c *Direct) sendMapRequest(ctx context.Context, maxPolls int, cb func(*netm } else { vlogf("netmap: got new map") } + select { case timeoutReset <- struct{}{}: vlogf("netmap: sent timer reset") diff --git a/control/controlclient/map.go b/control/controlclient/map.go index 9109f1602..c7bb8db76 100644 --- a/control/controlclient/map.go +++ b/control/controlclient/map.go @@ -44,6 +44,7 @@ type mapSession struct { previousPeers []*tailcfg.Node // for delta-purposes lastDomain string lastHealth []string + lastPopBrowserURL string // netMapBuilding is non-nil during a netmapForResponse call, // containing the value to be returned, once fully populated. diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index bcad6c64a..ac8352f6c 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -973,6 +973,7 @@ func (b *LocalBackend) Start(opts ipn.Options) error { DebugFlags: debugFlags, LinkMonitor: b.e.GetLinkMonitor(), Pinger: b.e, + PopBrowserURL: b.tellClientToBrowseToURL, // Don't warn about broken Linux IP forwarding when // netstack is being used. @@ -1370,12 +1371,18 @@ func (b *LocalBackend) popBrowserAuthNow() { b.blockEngineUpdates(true) b.stopEngineAndWait() - b.send(ipn.Notify{BrowseToURL: &url}) + b.tellClientToBrowseToURL(url) if b.State() == ipn.Running { b.enterState(ipn.Starting) } } +func (b *LocalBackend) tellClientToBrowseToURL(url string) { + if url != "" { + b.send(ipn.Notify{BrowseToURL: &url}) + } +} + // For testing lazy machine key generation. var panicOnMachineKeyGeneration = envknob.Bool("TS_DEBUG_PANIC_MACHINE_KEY") diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index adb17a103..c4ec0d44b 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -65,7 +65,8 @@ type CapabilityVersion int // 26: 2022-01-12: (nothing, just bumping for 1.20.0) // 27: 2022-02-18: start of SSHPolicy being respected // 28: 2022-03-09: client can communicate over Noise. -const CurrentCapabilityVersion CapabilityVersion = 28 +// 29: 2022-03-09: MapResponse.PopBrowserURL +const CurrentCapabilityVersion CapabilityVersion = 29 type StableID string @@ -1221,7 +1222,7 @@ type PingRequest struct { type MapResponse struct { // KeepAlive, if set, represents an empty message just to keep // the connection alive. When true, all other fields except - // PingRequest are ignored. + // PingRequest, ControlTime, and PopBrowserURL are ignored. KeepAlive bool `json:",omitempty"` // PingRequest, if non-empty, is a request to the client to @@ -1231,6 +1232,11 @@ type MapResponse struct { // KeepAlive true or false). PingRequest *PingRequest `json:",omitempty"` + // PopBrowserURL, if non-empty, is a URL for the client to + // open to complete an action. The client should dup suppress + // identical URLs and only open it once for the same URL. + PopBrowserURL string + // Networking // Node describes the node making the map request.