cmd/tailscale: add "Bug report" item to menu

Signed-off-by: Brad Fitzpatrick <brad@danga.com>
bradfitz/be_exit_node
Brad Fitzpatrick 4 years ago
parent 3b6b9acf2b
commit ed29f4b3d2

@ -40,6 +40,8 @@ type backend struct {
lastCfg *router.Config lastCfg *router.Config
lastDNSCfg *dns.OSConfig lastDNSCfg *dns.OSConfig
logIDPublic string
// avoidEmptyDNS controls whether to use fallback nameservers // avoidEmptyDNS controls whether to use fallback nameservers
// when no nameservers are provided by Tailscale. // when no nameservers are provided by Tailscale.
avoidEmptyDNS bool avoidEmptyDNS bool
@ -117,7 +119,8 @@ func newBackend(dataDir string, jvm *jni.JVM, appCtx jni.Object, store *stateSto
if err != nil { if err != nil {
return nil, fmt.Errorf("runBackend: NewUserspaceEngine: %v", err) return nil, fmt.Errorf("runBackend: NewUserspaceEngine: %v", err)
} }
local, err := ipnlocal.NewLocalBackend(logf, logID.Public().String(), store, dialer, engine) b.logIDPublic = logID.Public().String()
local, err := ipnlocal.NewLocalBackend(logf, b.logIDPublic, store, dialer, engine)
if err != nil { if err != nil {
engine.Close() engine.Close()
return nil, fmt.Errorf("runBackend: NewLocalBackend: %v", err) return nil, fmt.Errorf("runBackend: NewLocalBackend: %v", err)

@ -6,7 +6,9 @@ package main
import ( import (
"context" "context"
"crypto/rand"
"crypto/sha1" "crypto/sha1"
"encoding/hex"
"errors" "errors"
"fmt" "fmt"
"io" "io"
@ -19,6 +21,7 @@ import (
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
"sync/atomic"
"time" "time"
"unsafe" "unsafe"
@ -47,7 +50,8 @@ type App struct {
// appCtx is a global reference to the com.tailscale.ipn.App instance. // appCtx is a global reference to the com.tailscale.ipn.App instance.
appCtx jni.Object appCtx jni.Object
store *stateStore store *stateStore
logIDPublicAtomic atomic.Value // of string
// netStates receives the most recent network state. // netStates receives the most recent network state.
netStates chan BackendState netStates chan BackendState
@ -180,6 +184,7 @@ type FileSendEvent struct {
type ( type (
ToggleEvent struct{} ToggleEvent struct{}
ReauthEvent struct{} ReauthEvent struct{}
BugEvent struct{}
WebAuthEvent struct{} WebAuthEvent struct{}
GoogleAuthEvent struct{} GoogleAuthEvent struct{}
LogoutEvent struct{} LogoutEvent struct{}
@ -266,6 +271,7 @@ func (a *App) runBackend() error {
if err != nil { if err != nil {
return err return err
} }
a.logIDPublicAtomic.Store(b.logIDPublic)
defer b.CloseTUNs() defer b.CloseTUNs()
// Contrary to the documentation for VpnService.Builder.addDnsServer, // Contrary to the documentation for VpnService.Builder.addDnsServer,
@ -1059,6 +1065,11 @@ func (a *App) processUIEvents(w *app.Window, events []UIEvent, act jni.Object, s
default: default:
requestBackend(WebAuthEvent{}) requestBackend(WebAuthEvent{})
} }
case BugEvent:
backendLogID, _ := a.logIDPublicAtomic.Load().(string)
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 WebAuthEvent: case WebAuthEvent:
a.store.WriteString(loginMethodPrefKey, loginMethodWeb) a.store.WriteString(loginMethodPrefKey, loginMethodWeb)
requestBackend(e) requestBackend(e)
@ -1341,3 +1352,9 @@ func fatalErr(err error) {
// TODO: expose in UI. // TODO: expose in UI.
log.Printf("fatal error: %v", err) log.Printf("fatal error: %v", err)
} }
func randHex(n int) string {
b := make([]byte, n)
rand.Read(b)
return hex.EncodeToString(b)
}

@ -83,6 +83,7 @@ type UI struct {
copy widget.Clickable copy widget.Clickable
reauth widget.Clickable reauth widget.Clickable
bug widget.Clickable
exits widget.Clickable exits widget.Clickable
logout widget.Clickable logout widget.Clickable
} }
@ -313,6 +314,11 @@ func (ui *UI) layout(gtx layout.Context, sysIns system.Insets, state *clientStat
events = append(events, ReauthEvent{}) events = append(events, ReauthEvent{})
} }
if ui.menuClicked(&ui.menu.bug) {
events = append(events, BugEvent{})
ui.showCopied(gtx, "bug report marker to clipboard")
}
if ui.menuClicked(&ui.menu.exits) || ui.openExitDialog.Clicked() { if ui.menuClicked(&ui.menu.exits) || ui.openExitDialog.Clicked() {
ui.exitDialog.show = true ui.exitDialog.show = true
} }
@ -965,12 +971,13 @@ func (ui *UI) layoutMenu(gtx layout.Context, sysIns system.Insets, expiry time.T
return layout.NE.Layout(gtx, func(gtx C) D { return layout.NE.Layout(gtx, func(gtx C) D {
menu := &ui.menu menu := &ui.menu
items := []menuItem{ items := []menuItem{
{title: "Copy My IP Address", btn: &menu.copy}, {title: "Copy my IP address", btn: &menu.copy},
} }
if showExits { if showExits {
items = append(items, menuItem{title: "Use exit node...", btn: &menu.exits}) items = append(items, menuItem{title: "Use exit node...", btn: &menu.exits})
} }
items = append(items, items = append(items,
menuItem{title: "Bug report", btn: &menu.bug},
menuItem{title: "Reauthenticate", btn: &menu.reauth}, menuItem{title: "Reauthenticate", btn: &menu.reauth},
menuItem{title: "Log out", btn: &menu.logout}, menuItem{title: "Log out", btn: &menu.logout},
) )

Loading…
Cancel
Save