diff --git a/cmd/tailscale/backend.go b/cmd/tailscale/backend.go index fdd6afd..9260cf2 100644 --- a/cmd/tailscale/backend.go +++ b/cmd/tailscale/backend.go @@ -29,6 +29,7 @@ import ( "tailscale.com/types/logger" "tailscale.com/util/dnsname" "tailscale.com/wgengine" + "tailscale.com/wgengine/netstack" "tailscale.com/wgengine/router" ) @@ -120,6 +121,9 @@ func newBackend(dataDir string, jvm *jni.JVM, appCtx jni.Object, store *stateSto return nil, fmt.Errorf("runBackend: NewUserspaceEngine: %v", err) } b.logIDPublic = logID.Public().String() + if err := startNetstack(log.Printf, dialer, engine); err != nil { + return nil, fmt.Errorf("startNetstack: %w", err) + } local, err := ipnlocal.NewLocalBackend(logf, b.logIDPublic, store, dialer, engine) if err != nil { engine.Close() @@ -428,3 +432,17 @@ func (b *backend) getDNSBaseConfig() (dns.OSConfig, error) { return config, nil } + +func startNetstack(logf logger.Logf, dialer *tsdial.Dialer, e wgengine.Engine) error { + tunDev, magicConn, ok := e.(wgengine.InternalsGetter).GetInternals() + if !ok { + return fmt.Errorf("%T is not a wgengine.InternalsGetter", e) + } + ns, err := netstack.Create(logf, tunDev, e, magicConn, dialer) + if err != nil { + return fmt.Errorf("netstack.Create: %w", err) + } + ns.ProcessLocalIPs = false + ns.ProcessSubnets = true + return ns.Start() +} diff --git a/cmd/tailscale/main.go b/cmd/tailscale/main.go index 4ab8520..6c45dea 100644 --- a/cmd/tailscale/main.go +++ b/cmd/tailscale/main.go @@ -188,6 +188,7 @@ type ( WebAuthEvent struct{} GoogleAuthEvent struct{} LogoutEvent struct{} + BeExitNodeEvent bool ) // serverOAuthID is the OAuth ID of the tailscale-android server, used @@ -416,6 +417,9 @@ func (a *App) runBackend() error { case ToggleEvent: state.Prefs.WantRunning = !state.Prefs.WantRunning go b.backend.SetPrefs(state.Prefs) + case BeExitNodeEvent: + state.Prefs.SetAdvertiseExitNode(bool(e)) + go b.backend.SetPrefs(state.Prefs) case WebAuthEvent: if !signingIn { go b.backend.StartLoginInteractive() @@ -860,6 +864,7 @@ func (a *App) runUI() error { } case p := <-a.prefs: ui.enabled.Value = p.WantRunning + ui.runningExit = p.AdvertisesExitNode() w.Invalidate() case state.browseURL = <-a.browseURLs: ui.signinType = noSignin @@ -1070,6 +1075,8 @@ func (a *App) processUIEvents(w *app.Window, events []UIEvent, act jni.Object, s logMarker := fmt.Sprintf("BUG-%v-%v-%v", backendLogID, time.Now().UTC().Format("20060102150405Z"), randHex(8)) log.Printf("user bugreport: %s", logMarker) w.WriteClipboard(logMarker) + case BeExitNodeEvent: + requestBackend(e) case WebAuthEvent: a.store.WriteString(loginMethodPrefKey, loginMethodWeb) requestBackend(e) diff --git a/cmd/tailscale/ui.go b/cmd/tailscale/ui.go index d32a1f8..8d59ecb 100644 --- a/cmd/tailscale/ui.go +++ b/cmd/tailscale/ui.go @@ -10,6 +10,7 @@ import ( "fmt" "image" "image/color" + "strings" "time" "gioui.org/f32" @@ -70,6 +71,9 @@ type UI struct { list layout.List } + showDebugMenu bool + runningExit bool // are we an exit node now? + intro struct { list layout.List start widget.Clickable @@ -84,6 +88,7 @@ type UI struct { copy widget.Clickable reauth widget.Clickable bug widget.Clickable + beExit widget.Clickable exits widget.Clickable logout widget.Clickable } @@ -260,7 +265,11 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat for _, e := range ui.search.Events() { if _, ok := e.(widget.ChangeEvent); ok { - events = append(events, SearchEvent{Query: ui.search.Text()}) + text := ui.search.Text() + if strings.EqualFold(text, "debug") { + ui.showDebugMenu = true + } + events = append(events, SearchEvent{Query: text}) break } } @@ -319,6 +328,16 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat ui.showCopied(gtx, "bug report marker to clipboard") } + if ui.menuClicked(&ui.menu.beExit) { + ui.runningExit = !ui.runningExit + events = append(events, BeExitNodeEvent(ui.runningExit)) + if ui.runningExit { + ui.showMessage(gtx, "Running exit node") + } else { + ui.showMessage(gtx, "Stopped running exit node") + } + } + if ui.menuClicked(&ui.menu.exits) || ui.openExitDialog.Clicked() { ui.exitDialog.show = true } @@ -981,6 +1000,15 @@ func (ui *UI) layoutMenu(gtx layout.Context, sysIns system.Insets, expiry time.T menuItem{title: "Reauthenticate", btn: &menu.reauth}, menuItem{title: "Log out", btn: &menu.logout}, ) + if ui.runningExit || ui.showDebugMenu { + var title string + if ui.runningExit { + title = "Stop running exit node [BETA]" + } else { + title = "Run exit node [BETA]" + } + items = append(items, menuItem{title: title, btn: &menu.beExit}) + } return layoutMenu(ui.theme, gtx, items, func(gtx C) D { var expiryStr string const fmtStr = time.Stamp diff --git a/go.mod b/go.mod index ad73391..a037450 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( golang.org/x/sys v0.0.0-20211205182925-97ca703d548d golang.zx2c4.com/wireguard v0.0.0-20211116201604-de7c702ace45 inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6 - tailscale.com v1.1.1-0.20220106164417-64c2657448c9 + tailscale.com v1.1.1-0.20220107180532-1a4e8da0849f ) require ( diff --git a/go.sum b/go.sum index 8c9210c..04cb7c7 100644 --- a/go.sum +++ b/go.sum @@ -705,3 +705,7 @@ tailscale.com v1.1.1-0.20220103195654-dd45bba76b3a h1:f1Ixr2b9pnSeVLRt1caMxd4rJV tailscale.com v1.1.1-0.20220103195654-dd45bba76b3a/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= tailscale.com v1.1.1-0.20220106164417-64c2657448c9 h1:LFkTqkAfdB+FzWS0KmOwv2Y5ba0Kbbc3IQfHi6G09fA= tailscale.com v1.1.1-0.20220106164417-64c2657448c9/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= +tailscale.com v1.1.1-0.20220107005935-1b426cc232c0 h1:Z2+Z5HEPrInsP7BlE/2kMohRRWRvjxzJiLryhsrT+/c= +tailscale.com v1.1.1-0.20220107005935-1b426cc232c0/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4= +tailscale.com v1.1.1-0.20220107180532-1a4e8da0849f h1:PIv9L0qXK6HZuAer6hg6mmSByzJ5jv1MR5J7Wlbnfek= +tailscale.com v1.1.1-0.20220107180532-1a4e8da0849f/go.mod h1:kjVy3ji2OH5lZhPLIIRacoY3CN4Bo3Yyb2mtoM8nfJ4=