From d316acaa3dfb0a6d24d25994e476d5a715eb61a0 Mon Sep 17 00:00:00 2001 From: Gero Gerke Date: Sun, 9 Apr 2023 19:05:52 +0200 Subject: [PATCH] Add DNS/routing prefs like on desktop (#86) ui: Add DNS/routing prefs like on desktop Fixes https://github.com/tailscale/tailscale/issues/2155 Signed-off-by: Denton Gentry --- cmd/tailscale/main.go | 36 ++++++++++++---- cmd/tailscale/ui.go | 99 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 9 deletions(-) diff --git a/cmd/tailscale/main.go b/cmd/tailscale/main.go index 05bf2be..121f93a 100644 --- a/cmd/tailscale/main.go +++ b/cmd/tailscale/main.go @@ -188,15 +188,18 @@ type SetLoginServerEvent struct { // UIEvent types. type ( - ToggleEvent struct{} - ReauthEvent struct{} - BugEvent struct{} - WebAuthEvent struct{} - GoogleAuthEvent struct{} - LogoutEvent struct{} - OSSLicensesEvent struct{} - BeExitNodeEvent bool - ExitAllowLANEvent bool + ToggleEvent struct{} + ReauthEvent struct{} + BugEvent struct{} + WebAuthEvent struct{} + GoogleAuthEvent struct{} + LogoutEvent struct{} + OSSLicensesEvent struct{} + BeExitNodeEvent bool + ExitAllowLANEvent bool + AllowIncomingTransactionsEvent bool + UseTailscaleDNSEvent bool + UseTailscaleSubnetsEvent bool ) // serverOAuthID is the OAuth ID of the tailscale-android server, used @@ -434,6 +437,15 @@ func (a *App) runBackend() error { case ExitAllowLANEvent: state.Prefs.ExitNodeAllowLANAccess = bool(e) go b.backend.SetPrefs(state.Prefs) + case UseTailscaleDNSEvent: + state.Prefs.CorpDNS = bool(e) + go b.backend.SetPrefs(state.Prefs) + case UseTailscaleSubnetsEvent: + state.Prefs.RouteAll = !bool(e) + go b.backend.SetPrefs(state.Prefs) + case AllowIncomingTransactionsEvent: + state.Prefs.ShieldsUp = !bool(e) + go b.backend.SetPrefs(state.Prefs) case WebAuthEvent: if !signingIn { go b.backend.StartLoginInteractive() @@ -1121,6 +1133,12 @@ func (a *App) processUIEvents(w *app.Window, events []UIEvent, act jni.Object, s requestBackend(e) case ExitAllowLANEvent: requestBackend(e) + case AllowIncomingTransactionsEvent: + requestBackend(e) + case UseTailscaleDNSEvent: + requestBackend(e) + case UseTailscaleSubnetsEvent: + requestBackend(e) case WebAuthEvent: a.store.WriteString(loginMethodPrefKey, loginMethodWeb) requestBackend(e) diff --git a/cmd/tailscale/ui.go b/cmd/tailscale/ui.go index b1f0844..e6d3560 100644 --- a/cmd/tailscale/ui.go +++ b/cmd/tailscale/ui.go @@ -53,6 +53,10 @@ type UI struct { exitLAN widget.Bool + allowIncomingTransactions widget.Bool + useTailscaleDNS widget.Bool + useTailscaleSubnets widget.Bool + // webSigin is the button for the web-based sign-in flow. webSignin widget.Clickable @@ -62,6 +66,9 @@ type UI struct { // openExitDialog opens the exit node picker. openExitDialog widget.Clickable + // openPreferencesDialog opens the preferences dialog + openPreferencesDialog widget.Clickable + signinType signinType setLoginServer bool @@ -80,6 +87,14 @@ type UI struct { list layout.List } + // exitDialog is state for the exit node dialog. + preferencesDialog struct { + show bool + dismiss Dismiss + exits widget.Enum + list layout.List + } + runningExit bool // are we an exit node now? qr struct { @@ -102,6 +117,7 @@ type UI struct { useLoginServer widget.Clickable copy widget.Clickable + preferences widget.Clickable reauth widget.Clickable bug widget.Clickable beExit widget.Clickable @@ -260,6 +276,7 @@ func newUI(store *stateStore) (*UI, error) { ui.search.SingleLine = true ui.loginServer.SingleLine = true ui.exitDialog.list.Axis = layout.Vertical + ui.preferencesDialog.list.Axis = layout.Vertical ui.shareDialog.list.Axis = layout.Vertical // If they've ever set the control plane, give them the debug menu right away. @@ -294,6 +311,8 @@ func (ui *UI) activeDialog() *bool { return &ui.shareDialog.show case ui.exitDialog.show: return &ui.exitDialog.show + case ui.preferencesDialog.show: + return &ui.preferencesDialog.show case ui.aboutDialog.show: return &ui.aboutDialog.show } @@ -384,6 +403,15 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat if ui.exitLAN.Changed() { events = append(events, ExitAllowLANEvent(ui.exitLAN.Value)) } + if ui.allowIncomingTransactions.Changed() { + events = append(events, AllowIncomingTransactionsEvent(ui.allowIncomingTransactions.Value)) + } + if ui.useTailscaleDNS.Changed() { + events = append(events, UseTailscaleDNSEvent(ui.useTailscaleDNS.Value)) + } + if ui.useTailscaleSubnets.Changed() { + events = append(events, UseTailscaleSubnetsEvent(ui.useTailscaleSubnets.Value)) + } if ui.googleSignin.Clicked() { ui.signinType = googleSignin @@ -439,6 +467,10 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat ui.exitDialog.show = true } + if ui.menuClicked(&ui.menu.preferences) || ui.openPreferencesDialog.Clicked() { + ui.preferencesDialog.show = true + } + if ui.menuClicked(&ui.menu.about) { ui.aboutDialog.show = true } @@ -568,6 +600,8 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat ui.layoutExitNodeDialog(gtx, sysIns, state.backend.Exits) + ui.layoutPreferencesDialog(gtx, sysIns, state.backend.Exits) + ui.layoutShareDialog(gtx, sysIns) ui.layoutAboutDialog(gtx, sysIns) @@ -1080,6 +1114,70 @@ func (ui *UI) layoutExitNodeDialog(gtx layout.Context, sysIns system.Insets, exi }) } +// layoutPreferencesDialog lays out the preferences dialog. +func (ui *UI) layoutPreferencesDialog(gtx layout.Context, sysIns system.Insets, exits []Peer) { + d := &ui.preferencesDialog + if d.dismiss.Dismissed(gtx) { + d.show = false + } + if !d.show { + return + } + d.dismiss.Add(gtx, argb(0x66000000)) + layout.Inset{ + Top: sysIns.Top + unit.Dp(16), + Right: sysIns.Right + unit.Dp(16), + Bottom: sysIns.Bottom + unit.Dp(16), + Left: sysIns.Left + unit.Dp(16), + }.Layout(gtx, func(gtx C) D { + return layout.Center.Layout(gtx, func(gtx C) D { + gtx.Constraints.Min.X = gtx.Dp(unit.Dp(300)) + gtx.Constraints.Max.X = gtx.Constraints.Min.X + return layoutDialog(gtx, func(gtx C) D { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, + layout.Rigid(func(gtx C) D { + // Header. + return layout.Inset{ + Top: unit.Dp(16), + Right: unit.Dp(20), + Left: unit.Dp(20), + Bottom: unit.Dp(16), + }.Layout(gtx, func(gtx C) D { + l := material.Body1(ui.theme, "Preferences") + l.Font.Weight = text.Bold + return l.Layout(gtx) + }) + }), + layout.Rigid(func(gtx C) D { + btn := material.CheckBox(ui.theme, &ui.allowIncomingTransactions, "Allow Incoming Connections") + return layout.Inset{ + Right: unit.Dp(16), + Left: unit.Dp(16), + Bottom: unit.Dp(16), + }.Layout(gtx, btn.Layout) + }), + layout.Rigid(func(gtx C) D { + btn := material.CheckBox(ui.theme, &ui.useTailscaleDNS, "Use Tailscale DNS") + return layout.Inset{ + Right: unit.Dp(16), + Left: unit.Dp(16), + Bottom: unit.Dp(16), + }.Layout(gtx, btn.Layout) + }), + layout.Rigid(func(gtx C) D { + btn := material.CheckBox(ui.theme, &ui.useTailscaleSubnets, "Use Tailscale Subnets") + return layout.Inset{ + Right: unit.Dp(16), + Left: unit.Dp(16), + Bottom: unit.Dp(16), + }.Layout(gtx, btn.Layout) + }), + ) + }) + }) + }) +} + // layoutAboutDialog lays out the about dialog. func (ui *UI) layoutAboutDialog(gtx layout.Context, sysIns system.Insets) { d := &ui.aboutDialog @@ -1221,6 +1319,7 @@ func (ui *UI) layoutMenu(gtx layout.Context, sysIns system.Insets, expiry time.T if showExits { items = append(items, menuItem{title: "Use exit node...", btn: &menu.exits}) } + items = append(items, menuItem{title: "Preferences", btn: &menu.preferences}) items = append(items, menuItem{title: "Bug report", btn: &menu.bug}, menuItem{title: "Reauthenticate", btn: &menu.reauth},