Compare commits
52 Commits
bradfitz/g
...
crawshaw/j
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad10cd71a6 | ||
|
|
dea3ef0597 | ||
|
|
3aeb2e204c | ||
|
|
acafe9811f | ||
|
|
48fbe93e72 | ||
|
|
96fd20e3c0 | ||
|
|
7f97cf654d | ||
|
|
3fa863e6d9 | ||
|
|
e862f90e34 | ||
|
|
761fe19e5f | ||
|
|
88107b1287 | ||
|
|
931bcd44cb | ||
|
|
7e9d1f7808 | ||
|
|
8f5b52e571 | ||
|
|
3f4d93feb2 | ||
|
|
a5d701095b | ||
|
|
0c0239242c | ||
|
|
6e38d29485 | ||
|
|
41f6c78c53 | ||
|
|
662c19551a | ||
|
|
4f7751e025 | ||
|
|
4f71319f7c | ||
|
|
3af64765fd | ||
|
|
a084c44afc | ||
|
|
31c13013ae | ||
|
|
9ab2b32569 | ||
|
|
5a94317628 | ||
|
|
37b40b035b | ||
|
|
bc1751a376 | ||
|
|
b14288f96c | ||
|
|
23f01174ea | ||
|
|
40e12c17ec | ||
|
|
f65eb4e5c1 | ||
|
|
8b60936913 | ||
|
|
edb47b98a8 | ||
|
|
a877dd575c | ||
|
|
bf24d54143 | ||
|
|
158202dbb1 | ||
|
|
7795fcf464 | ||
|
|
22ed3c503e | ||
|
|
2e40c4b564 | ||
|
|
913c1bd04f | ||
|
|
688f923db1 | ||
|
|
96160973ce | ||
|
|
7bd89359c9 | ||
|
|
99d223130c | ||
|
|
2352690bde | ||
|
|
8ecee476f6 | ||
|
|
7fddc33481 | ||
|
|
68c42530e9 | ||
|
|
95cddfcc75 | ||
|
|
3baa084548 |
28
.github/workflows/depaware.yml
vendored
Normal file
28
.github/workflows/depaware.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: depaware
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.15
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v1
|
||||
|
||||
- name: depaware tailscaled
|
||||
run: go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscaled
|
||||
|
||||
- name: depaware tailscale
|
||||
run: go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscale
|
||||
10
Makefile
10
Makefile
@@ -4,7 +4,15 @@ usage:
|
||||
vet:
|
||||
go vet ./...
|
||||
|
||||
check: staticcheck vet
|
||||
updatedeps:
|
||||
go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscaled
|
||||
go run github.com/tailscale/depaware --update tailscale.com/cmd/tailscale
|
||||
|
||||
depaware:
|
||||
go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscaled
|
||||
go run github.com/tailscale/depaware --check tailscale.com/cmd/tailscale
|
||||
|
||||
check: staticcheck vet depaware
|
||||
|
||||
staticcheck:
|
||||
go run honnef.co/go/tools/cmd/staticcheck -- $$(go list ./... | grep -v tempfork)
|
||||
|
||||
@@ -86,8 +86,8 @@ func main() {
|
||||
}
|
||||
pkg := typeNameObj.Pkg()
|
||||
gen(buf, imports, typeName, typ, pkg)
|
||||
found = true
|
||||
}
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
@@ -95,6 +95,29 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
w := func(format string, args ...interface{}) {
|
||||
fmt.Fprintf(buf, format+"\n", args...)
|
||||
}
|
||||
w("// Clone duplicates src into dst and reports whether it succeeded.")
|
||||
w("// To succeed, <src, dst> must be of types <*T, *T> or <*T, **T>,")
|
||||
w("// where T is one of %s.", *flagTypes)
|
||||
w("func Clone(dst, src interface{}) bool {")
|
||||
w(" switch src := src.(type) {")
|
||||
for _, typeName := range typeNames {
|
||||
w(" case *%s:", typeName)
|
||||
w(" switch dst := dst.(type) {")
|
||||
w(" case *%s:", typeName)
|
||||
w(" *dst = *src.Clone()")
|
||||
w(" return true")
|
||||
w(" case **%s:", typeName)
|
||||
w(" *dst = src.Clone()")
|
||||
w(" return true")
|
||||
w(" }")
|
||||
}
|
||||
w(" }")
|
||||
w(" return false")
|
||||
w("}")
|
||||
|
||||
contents := new(bytes.Buffer)
|
||||
fmt.Fprintf(contents, header, *flagTypes, pkg.Name)
|
||||
fmt.Fprintf(contents, "import (\n")
|
||||
@@ -143,7 +166,19 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, name string, typ *types
|
||||
|
||||
switch t := typ.Underlying().(type) {
|
||||
case *types.Struct:
|
||||
_ = t
|
||||
// We generate two bits of code simultaneously while we walk the struct.
|
||||
// One is the Clone method itself, which we write directly to buf.
|
||||
// The other is a variable assignment that will fail if the struct
|
||||
// changes without the Clone method getting regenerated.
|
||||
// We write that to regenBuf, and then append it to buf at the end.
|
||||
regenBuf := new(bytes.Buffer)
|
||||
writeRegen := func(format string, args ...interface{}) {
|
||||
fmt.Fprintf(regenBuf, format+"\n", args...)
|
||||
}
|
||||
writeRegen("// A compilation failure here means this code must be regenerated, with command:")
|
||||
writeRegen("// tailscale.com/cmd/cloner -type %s", *flagTypes)
|
||||
writeRegen("var _%sNeedsRegeneration = %s(struct {", name, name)
|
||||
|
||||
name := typ.Obj().Name()
|
||||
fmt.Fprintf(buf, "// Clone makes a deep copy of %s.\n", name)
|
||||
fmt.Fprintf(buf, "// The result aliases no memory with the original.\n")
|
||||
@@ -159,6 +194,9 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, name string, typ *types
|
||||
for i := 0; i < t.NumFields(); i++ {
|
||||
fname := t.Field(i).Name()
|
||||
ft := t.Field(i).Type()
|
||||
|
||||
writeRegen("\t%s %s", fname, importedName(ft))
|
||||
|
||||
if !containsPointers(ft) {
|
||||
continue
|
||||
}
|
||||
@@ -220,6 +258,10 @@ func gen(buf *bytes.Buffer, imports map[string]struct{}, name string, typ *types
|
||||
}
|
||||
writef("return dst")
|
||||
fmt.Fprintf(buf, "}\n\n")
|
||||
|
||||
writeRegen("}{})\n")
|
||||
|
||||
buf.Write(regenBuf.Bytes())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"expvar"
|
||||
"flag"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
@@ -229,10 +230,10 @@ func debugHandler(s *derp.Server) http.Handler {
|
||||
<h1>DERP debug</h1>
|
||||
<ul>
|
||||
`)
|
||||
f("<li><b>Hostname:</b> %v</li>\n", *hostname)
|
||||
f("<li><b>Hostname:</b> %v</li>\n", html.EscapeString(*hostname))
|
||||
f("<li><b>Uptime:</b> %v</li>\n", tsweb.Uptime())
|
||||
f("<li><b>Mesh Key:</b> %v</li>\n", s.HasMeshKey())
|
||||
f("<li><b>Version:</b> %v</li>\n", version.LONG)
|
||||
f("<li><b>Version:</b> %v</li>\n", html.EscapeString(version.LONG))
|
||||
|
||||
f(`<li><a href="/debug/vars">/debug/vars</a> (Go)</li>
|
||||
<li><a href="/debug/varz">/debug/varz</a> (Prometheus)</li>
|
||||
|
||||
@@ -34,6 +34,7 @@ var (
|
||||
logCollection = flag.String("logcollection", "", "If non-empty, logtail collection to log to")
|
||||
nodeExporter = flag.String("node-exporter", "http://localhost:9100", "URL of the local prometheus node exporter")
|
||||
goVarsURL = flag.String("go-vars-url", "http://localhost:8383/debug/vars", "URL of a local Go server's /debug/vars endpoint")
|
||||
insecure = flag.Bool("insecure", false, "serve over http, for development")
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -66,12 +67,15 @@ func main() {
|
||||
httpsrv := &http.Server{
|
||||
Addr: *addr,
|
||||
Handler: mux,
|
||||
TLSConfig: &tls.Config{
|
||||
GetCertificate: ch.GetCertificate,
|
||||
},
|
||||
}
|
||||
|
||||
if err := httpsrv.ListenAndServeTLS("", ""); err != nil && err != http.ErrServerClosed {
|
||||
if !*insecure {
|
||||
httpsrv.TLSConfig = &tls.Config{GetCertificate: ch.GetCertificate}
|
||||
err = httpsrv.ListenAndServeTLS("", "")
|
||||
} else {
|
||||
err = httpsrv.ListenAndServe()
|
||||
}
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package cli
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/tailcfg"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/version/distro"
|
||||
"tailscale.com/wgengine/router"
|
||||
)
|
||||
|
||||
@@ -63,14 +65,21 @@ specify any flags, options are reset to their default.
|
||||
upf.StringVar(&upArgs.advertiseRoutes, "advertise-routes", "", "routes to advertise to other nodes (comma-separated, e.g. 10.0.0.0/8,192.168.0.0/24)")
|
||||
}
|
||||
if runtime.GOOS == "linux" {
|
||||
upf.BoolVar(&upArgs.snat, "snat-subnet-routes", true, "source NAT traffic to local routes advertised with -advertise-routes")
|
||||
upf.StringVar(&upArgs.netfilterMode, "netfilter-mode", "on", "netfilter mode (one of on, nodivert, off)")
|
||||
upf.BoolVar(&upArgs.snat, "snat-subnet-routes", true, "source NAT traffic to local routes advertised with --advertise-routes")
|
||||
upf.StringVar(&upArgs.netfilterMode, "netfilter-mode", defaultNetfilterMode(), "netfilter mode (one of on, nodivert, off)")
|
||||
}
|
||||
return upf
|
||||
})(),
|
||||
Exec: runUp,
|
||||
}
|
||||
|
||||
func defaultNetfilterMode() string {
|
||||
if distro.Get() == distro.Synology {
|
||||
return "off"
|
||||
}
|
||||
return "on"
|
||||
}
|
||||
|
||||
var upArgs struct {
|
||||
server string
|
||||
acceptRoutes bool
|
||||
@@ -151,6 +160,19 @@ func runUp(ctx context.Context, args []string) error {
|
||||
log.Fatalf("too many non-flag arguments: %q", args)
|
||||
}
|
||||
|
||||
if distro.Get() == distro.Synology {
|
||||
notSupported := "not yet supported on Synology; see https://github.com/tailscale/tailscale/issues/451"
|
||||
if upArgs.advertiseRoutes != "" {
|
||||
return errors.New("--advertise-routes is " + notSupported)
|
||||
}
|
||||
if upArgs.acceptRoutes {
|
||||
return errors.New("--accept-routes is " + notSupported)
|
||||
}
|
||||
if upArgs.netfilterMode != "off" {
|
||||
return errors.New("--netfilter-mode values besides \"off\" " + notSupported)
|
||||
}
|
||||
}
|
||||
|
||||
var routes []wgcfg.CIDR
|
||||
if upArgs.advertiseRoutes != "" {
|
||||
advroutes := strings.Split(upArgs.advertiseRoutes, ",")
|
||||
|
||||
219
cmd/tailscale/depaware.txt
Normal file
219
cmd/tailscale/depaware.txt
Normal file
@@ -0,0 +1,219 @@
|
||||
tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/depaware)
|
||||
|
||||
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/negotiate
|
||||
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
||||
github.com/apenwarr/fixconsole from tailscale.com/cmd/tailscale
|
||||
W 💣 github.com/apenwarr/w32 from github.com/apenwarr/fixconsole
|
||||
L github.com/coreos/go-iptables/iptables from tailscale.com/wgengine/router
|
||||
W 💣 github.com/go-ole/go-ole from github.com/go-ole/go-ole/oleutil+
|
||||
W 💣 github.com/go-ole/go-ole/oleutil from tailscale.com/wgengine/winnet
|
||||
L 💣 github.com/godbus/dbus/v5 from tailscale.com/wgengine/router/dns
|
||||
github.com/golang/groupcache/lru from tailscale.com/wgengine/filter+
|
||||
L github.com/jsimonetti/rtnetlink from tailscale.com/wgengine/monitor
|
||||
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
||||
L 💣 github.com/mdlayher/netlink from github.com/jsimonetti/rtnetlink+
|
||||
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
|
||||
github.com/peterbourgon/ff/v2 from github.com/peterbourgon/ff/v2/ffcli
|
||||
github.com/peterbourgon/ff/v2/ffcli from tailscale.com/cmd/tailscale/cli
|
||||
W 💣 github.com/tailscale/winipcfg-go from tailscale.com/net/interfaces+
|
||||
💣 github.com/tailscale/wireguard-go/conn from github.com/tailscale/wireguard-go/device+
|
||||
💣 github.com/tailscale/wireguard-go/device from tailscale.com/wgengine+
|
||||
github.com/tailscale/wireguard-go/device/tokenbucket from github.com/tailscale/wireguard-go/device
|
||||
💣 github.com/tailscale/wireguard-go/ipc from github.com/tailscale/wireguard-go/device
|
||||
W 💣 github.com/tailscale/wireguard-go/ipc/winpipe from github.com/tailscale/wireguard-go/ipc
|
||||
github.com/tailscale/wireguard-go/ratelimiter from github.com/tailscale/wireguard-go/device
|
||||
github.com/tailscale/wireguard-go/replay from github.com/tailscale/wireguard-go/device
|
||||
github.com/tailscale/wireguard-go/rwcancel from github.com/tailscale/wireguard-go/device+
|
||||
github.com/tailscale/wireguard-go/tai64n from github.com/tailscale/wireguard-go/device
|
||||
💣 github.com/tailscale/wireguard-go/tun from github.com/tailscale/wireguard-go/device+
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun from github.com/tailscale/wireguard-go/tun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/iphlpapi from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/namespaceapi from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/nci from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/registry from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/setupapi from github.com/tailscale/wireguard-go/tun/wintun
|
||||
github.com/tailscale/wireguard-go/wgcfg from github.com/tailscale/wireguard-go/conn+
|
||||
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
|
||||
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
|
||||
💣 go4.org/mem from tailscale.com/control/controlclient+
|
||||
inet.af/netaddr from tailscale.com/cmd/tailscale/cli+
|
||||
rsc.io/goversion/version from tailscale.com/version
|
||||
tailscale.com/atomicfile from tailscale.com/ipn+
|
||||
tailscale.com/cmd/tailscale/cli from tailscale.com/cmd/tailscale
|
||||
tailscale.com/control/controlclient from tailscale.com/ipn+
|
||||
tailscale.com/derp from tailscale.com/derp/derphttp+
|
||||
tailscale.com/derp/derphttp from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/derp/derpmap from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/disco from tailscale.com/derp+
|
||||
tailscale.com/internal/deepprint from tailscale.com/ipn+
|
||||
tailscale.com/ipn from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/ipn/ipnstate from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/ipn/policy from tailscale.com/ipn
|
||||
tailscale.com/log/logheap from tailscale.com/control/controlclient
|
||||
tailscale.com/logtail/backoff from tailscale.com/control/controlclient
|
||||
tailscale.com/metrics from tailscale.com/derp
|
||||
tailscale.com/net/dnscache from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/net/interfaces from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli+
|
||||
💣 tailscale.com/net/netns from tailscale.com/control/controlclient+
|
||||
tailscale.com/net/stun from tailscale.com/net/netcheck+
|
||||
tailscale.com/net/tlsdial from tailscale.com/control/controlclient+
|
||||
tailscale.com/net/tsaddr from tailscale.com/ipn+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/paths from tailscale.com/cmd/tailscale/cli
|
||||
tailscale.com/portlist from tailscale.com/ipn
|
||||
tailscale.com/safesocket from tailscale.com/cmd/tailscale/cli
|
||||
💣 tailscale.com/syncs from tailscale.com/net/interfaces+
|
||||
tailscale.com/tailcfg from tailscale.com/cmd/tailscale/cli+
|
||||
DW tailscale.com/tempfork/osexec from tailscale.com/portlist
|
||||
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
||||
tailscale.com/types/empty from tailscale.com/control/controlclient+
|
||||
tailscale.com/types/key from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/types/logger from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/types/nettype from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/types/opt from tailscale.com/control/controlclient+
|
||||
tailscale.com/types/strbuilder from tailscale.com/wgengine/packet
|
||||
tailscale.com/types/structs from tailscale.com/control/controlclient+
|
||||
tailscale.com/util/lineread from tailscale.com/control/controlclient+
|
||||
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/wgengine from tailscale.com/ipn
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/magicsock from tailscale.com/wgengine
|
||||
💣 tailscale.com/wgengine/monitor from tailscale.com/cmd/tailscale/cli+
|
||||
tailscale.com/wgengine/packet from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscale/cli+
|
||||
💣 tailscale.com/wgengine/router/dns from tailscale.com/ipn+
|
||||
tailscale.com/wgengine/tsdns from tailscale.com/ipn+
|
||||
tailscale.com/wgengine/tstun from tailscale.com/wgengine
|
||||
W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router
|
||||
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
|
||||
golang.org/x/crypto/blake2s from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
|
||||
golang.org/x/crypto/chacha20poly1305 from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/crypto/curve25519 from github.com/tailscale/wireguard-go/wgcfg+
|
||||
golang.org/x/crypto/nacl/box from tailscale.com/control/controlclient+
|
||||
golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box
|
||||
golang.org/x/crypto/poly1305 from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
|
||||
golang.org/x/net/bpf from github.com/mdlayher/netlink+
|
||||
golang.org/x/net/context/ctxhttp from golang.org/x/oauth2/internal
|
||||
golang.org/x/net/dns/dnsmessage from tailscale.com/wgengine/tsdns
|
||||
golang.org/x/net/ipv4 from github.com/tailscale/wireguard-go/device
|
||||
golang.org/x/net/ipv6 from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/net/proxy from tailscale.com/net/netns
|
||||
golang.org/x/oauth2 from tailscale.com/control/controlclient+
|
||||
golang.org/x/oauth2/internal from golang.org/x/oauth2
|
||||
golang.org/x/sync/errgroup from tailscale.com/derp
|
||||
golang.org/x/sync/singleflight from tailscale.com/net/dnscache
|
||||
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+
|
||||
LD golang.org/x/sys/unix from github.com/jsimonetti/rtnetlink/internal/unix+
|
||||
W golang.org/x/sys/windows from github.com/apenwarr/fixconsole+
|
||||
W golang.org/x/sys/windows/registry from github.com/tailscale/wireguard-go/tun/wintun+
|
||||
W golang.org/x/text/transform from golang.org/x/text/unicode/norm
|
||||
W golang.org/x/text/unicode/norm from github.com/tailscale/wireguard-go/tun/wintun
|
||||
golang.org/x/time/rate from tailscale.com/types/logger+
|
||||
vendor/golang.org/x/crypto/chacha20 from vendor/golang.org/x/crypto/chacha20poly1305
|
||||
vendor/golang.org/x/crypto/chacha20poly1305 from crypto/tls
|
||||
vendor/golang.org/x/crypto/cryptobyte from crypto/ecdsa+
|
||||
vendor/golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
|
||||
vendor/golang.org/x/crypto/curve25519 from crypto/tls
|
||||
vendor/golang.org/x/crypto/hkdf from crypto/tls
|
||||
vendor/golang.org/x/crypto/poly1305 from vendor/golang.org/x/crypto/chacha20poly1305
|
||||
vendor/golang.org/x/net/dns/dnsmessage from net
|
||||
vendor/golang.org/x/net/http/httpguts from net/http
|
||||
vendor/golang.org/x/net/http/httpproxy from net/http
|
||||
vendor/golang.org/x/net/http2/hpack from net/http
|
||||
vendor/golang.org/x/net/idna from net/http+
|
||||
D vendor/golang.org/x/net/route from net
|
||||
vendor/golang.org/x/sys/cpu from vendor/golang.org/x/crypto/chacha20poly1305
|
||||
vendor/golang.org/x/text/secure/bidirule from vendor/golang.org/x/net/idna
|
||||
vendor/golang.org/x/text/transform from vendor/golang.org/x/text/secure/bidirule+
|
||||
vendor/golang.org/x/text/unicode/bidi from vendor/golang.org/x/net/idna+
|
||||
vendor/golang.org/x/text/unicode/norm from vendor/golang.org/x/net/idna
|
||||
bufio from compress/flate+
|
||||
bytes from bufio+
|
||||
compress/flate from compress/gzip+
|
||||
compress/gzip from net/http+
|
||||
compress/zlib from debug/elf+
|
||||
container/list from crypto/tls+
|
||||
context from crypto/tls+
|
||||
crypto from crypto/ecdsa+
|
||||
crypto/aes from crypto/ecdsa+
|
||||
crypto/cipher from crypto/aes+
|
||||
crypto/des from crypto/tls+
|
||||
crypto/dsa from crypto/x509
|
||||
crypto/ecdsa from crypto/tls+
|
||||
crypto/ed25519 from crypto/tls+
|
||||
crypto/elliptic from crypto/ecdsa+
|
||||
crypto/hmac from crypto/tls+
|
||||
crypto/md5 from crypto/tls+
|
||||
crypto/rand from crypto/ed25519+
|
||||
crypto/rc4 from crypto/tls
|
||||
crypto/rsa from crypto/tls+
|
||||
crypto/sha1 from crypto/tls+
|
||||
crypto/sha256 from crypto/tls+
|
||||
crypto/sha512 from crypto/ecdsa+
|
||||
crypto/subtle from crypto/aes+
|
||||
crypto/tls from github.com/tcnksm/go-httpstat+
|
||||
crypto/x509 from crypto/tls+
|
||||
crypto/x509/pkix from crypto/x509+
|
||||
debug/dwarf from debug/elf+
|
||||
debug/elf from rsc.io/goversion/version
|
||||
debug/macho from rsc.io/goversion/version
|
||||
debug/pe from rsc.io/goversion/version
|
||||
encoding from encoding/json+
|
||||
encoding/asn1 from crypto/x509+
|
||||
encoding/base64 from encoding/json+
|
||||
encoding/binary from compress/gzip+
|
||||
encoding/hex from crypto/x509+
|
||||
encoding/json from expvar+
|
||||
encoding/pem from crypto/tls+
|
||||
errors from bufio+
|
||||
expvar from tailscale.com/derp+
|
||||
flag from github.com/peterbourgon/ff/v2+
|
||||
fmt from compress/flate+
|
||||
hash from compress/zlib+
|
||||
hash/adler32 from compress/zlib
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from tailscale.com/wgengine/magicsock
|
||||
html from tailscale.com/ipn/ipnstate
|
||||
io from bufio+
|
||||
io/ioutil from crypto/tls+
|
||||
log from expvar+
|
||||
math from compress/flate+
|
||||
math/big from crypto/dsa+
|
||||
math/bits from compress/flate+
|
||||
math/rand from github.com/mdlayher/netlink+
|
||||
mime from golang.org/x/oauth2/internal+
|
||||
mime/multipart from net/http
|
||||
mime/quotedprintable from mime/multipart
|
||||
net from crypto/tls+
|
||||
net/http from expvar+
|
||||
net/http/httptrace from github.com/tcnksm/go-httpstat+
|
||||
net/http/internal from net/http
|
||||
net/textproto from mime/multipart+
|
||||
net/url from crypto/x509+
|
||||
os from crypto/rand+
|
||||
os/exec from github.com/coreos/go-iptables/iptables+
|
||||
os/signal from tailscale.com/cmd/tailscale/cli
|
||||
L os/user from github.com/godbus/dbus/v5
|
||||
path from debug/dwarf+
|
||||
path/filepath from crypto/x509+
|
||||
reflect from crypto/x509+
|
||||
regexp from github.com/coreos/go-iptables/iptables+
|
||||
regexp/syntax from regexp
|
||||
LD runtime/cgo
|
||||
runtime/pprof from tailscale.com/log/logheap+
|
||||
sort from compress/flate+
|
||||
strconv from compress/flate+
|
||||
strings from bufio+
|
||||
sync from compress/flate+
|
||||
sync/atomic from context+
|
||||
syscall from crypto/rand+
|
||||
text/tabwriter from github.com/peterbourgon/ff/v2/ffcli+
|
||||
time from compress/gzip+
|
||||
unicode from bytes+
|
||||
unicode/utf16 from encoding/asn1+
|
||||
unicode/utf8 from bufio+
|
||||
unsafe from crypto/internal/subtle+
|
||||
234
cmd/tailscaled/depaware.txt
Normal file
234
cmd/tailscaled/depaware.txt
Normal file
@@ -0,0 +1,234 @@
|
||||
tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/depaware)
|
||||
|
||||
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/negotiate
|
||||
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
|
||||
github.com/apenwarr/fixconsole from tailscale.com/cmd/tailscaled
|
||||
W 💣 github.com/apenwarr/w32 from github.com/apenwarr/fixconsole
|
||||
L github.com/coreos/go-iptables/iptables from tailscale.com/wgengine/router
|
||||
W 💣 github.com/go-ole/go-ole from github.com/go-ole/go-ole/oleutil+
|
||||
W 💣 github.com/go-ole/go-ole/oleutil from tailscale.com/wgengine/winnet
|
||||
L 💣 github.com/godbus/dbus/v5 from tailscale.com/wgengine/router/dns
|
||||
github.com/golang/groupcache/lru from tailscale.com/wgengine/filter+
|
||||
L github.com/jsimonetti/rtnetlink from tailscale.com/wgengine/monitor
|
||||
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
|
||||
github.com/klauspost/compress/fse from github.com/klauspost/compress/huff0
|
||||
github.com/klauspost/compress/huff0 from github.com/klauspost/compress/zstd
|
||||
github.com/klauspost/compress/snappy from github.com/klauspost/compress/zstd
|
||||
github.com/klauspost/compress/zstd from tailscale.com/smallzstd
|
||||
github.com/klauspost/compress/zstd/internal/xxhash from github.com/klauspost/compress/zstd
|
||||
L 💣 github.com/mdlayher/netlink from github.com/jsimonetti/rtnetlink+
|
||||
L 💣 github.com/mdlayher/netlink/nlenc from github.com/jsimonetti/rtnetlink+
|
||||
github.com/pborman/getopt/v2 from tailscale.com/cmd/tailscaled
|
||||
W 💣 github.com/tailscale/winipcfg-go from tailscale.com/net/interfaces+
|
||||
💣 github.com/tailscale/wireguard-go/conn from github.com/tailscale/wireguard-go/device+
|
||||
💣 github.com/tailscale/wireguard-go/device from tailscale.com/wgengine+
|
||||
github.com/tailscale/wireguard-go/device/tokenbucket from github.com/tailscale/wireguard-go/device
|
||||
💣 github.com/tailscale/wireguard-go/ipc from github.com/tailscale/wireguard-go/device
|
||||
W 💣 github.com/tailscale/wireguard-go/ipc/winpipe from github.com/tailscale/wireguard-go/ipc
|
||||
github.com/tailscale/wireguard-go/ratelimiter from github.com/tailscale/wireguard-go/device
|
||||
github.com/tailscale/wireguard-go/replay from github.com/tailscale/wireguard-go/device
|
||||
github.com/tailscale/wireguard-go/rwcancel from github.com/tailscale/wireguard-go/device+
|
||||
github.com/tailscale/wireguard-go/tai64n from github.com/tailscale/wireguard-go/device
|
||||
💣 github.com/tailscale/wireguard-go/tun from github.com/tailscale/wireguard-go/device+
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun from github.com/tailscale/wireguard-go/tun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/iphlpapi from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/namespaceapi from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/nci from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/registry from github.com/tailscale/wireguard-go/tun/wintun
|
||||
W 💣 github.com/tailscale/wireguard-go/tun/wintun/setupapi from github.com/tailscale/wireguard-go/tun/wintun
|
||||
github.com/tailscale/wireguard-go/wgcfg from github.com/tailscale/wireguard-go/conn+
|
||||
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
|
||||
💣 go4.org/mem from tailscale.com/control/controlclient+
|
||||
inet.af/netaddr from tailscale.com/control/controlclient+
|
||||
rsc.io/goversion/version from tailscale.com/version
|
||||
tailscale.com/atomicfile from tailscale.com/ipn+
|
||||
tailscale.com/control/controlclient from tailscale.com/ipn+
|
||||
tailscale.com/derp from tailscale.com/derp/derphttp+
|
||||
tailscale.com/derp/derphttp from tailscale.com/net/netcheck+
|
||||
tailscale.com/disco from tailscale.com/derp+
|
||||
tailscale.com/internal/deepprint from tailscale.com/ipn+
|
||||
tailscale.com/ipn from tailscale.com/ipn/ipnserver
|
||||
tailscale.com/ipn/ipnserver from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/ipn/ipnstate from tailscale.com/ipn+
|
||||
tailscale.com/ipn/policy from tailscale.com/ipn
|
||||
tailscale.com/log/logheap from tailscale.com/control/controlclient
|
||||
tailscale.com/logpolicy from tailscale.com/cmd/tailscaled
|
||||
tailscale.com/logtail from tailscale.com/logpolicy
|
||||
tailscale.com/logtail/backoff from tailscale.com/control/controlclient+
|
||||
tailscale.com/logtail/filch from tailscale.com/logpolicy
|
||||
tailscale.com/metrics from tailscale.com/derp
|
||||
tailscale.com/net/dnscache from tailscale.com/derp/derphttp+
|
||||
tailscale.com/net/interfaces from tailscale.com/ipn+
|
||||
tailscale.com/net/netcheck from tailscale.com/wgengine/magicsock
|
||||
💣 tailscale.com/net/netns from tailscale.com/control/controlclient+
|
||||
💣 tailscale.com/net/netstat from tailscale.com/ipn/ipnserver
|
||||
tailscale.com/net/stun from tailscale.com/net/netcheck+
|
||||
tailscale.com/net/tlsdial from tailscale.com/control/controlclient+
|
||||
tailscale.com/net/tsaddr from tailscale.com/ipn+
|
||||
💣 tailscale.com/net/tshttpproxy from tailscale.com/control/controlclient+
|
||||
tailscale.com/paths from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/portlist from tailscale.com/ipn
|
||||
tailscale.com/safesocket from tailscale.com/ipn/ipnserver
|
||||
tailscale.com/smallzstd from tailscale.com/ipn/ipnserver+
|
||||
💣 tailscale.com/syncs from tailscale.com/net/interfaces+
|
||||
tailscale.com/tailcfg from tailscale.com/control/controlclient+
|
||||
DW tailscale.com/tempfork/osexec from tailscale.com/portlist
|
||||
W tailscale.com/tsconst from tailscale.com/net/interfaces
|
||||
tailscale.com/types/empty from tailscale.com/control/controlclient+
|
||||
tailscale.com/types/key from tailscale.com/derp+
|
||||
tailscale.com/types/logger from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/types/nettype from tailscale.com/wgengine/magicsock
|
||||
tailscale.com/types/opt from tailscale.com/control/controlclient+
|
||||
tailscale.com/types/strbuilder from tailscale.com/wgengine/packet
|
||||
tailscale.com/types/structs from tailscale.com/control/controlclient+
|
||||
tailscale.com/util/lineread from tailscale.com/control/controlclient+
|
||||
tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver
|
||||
tailscale.com/version from tailscale.com/control/controlclient+
|
||||
tailscale.com/version/distro from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine from tailscale.com/cmd/tailscaled+
|
||||
tailscale.com/wgengine/filter from tailscale.com/control/controlclient+
|
||||
tailscale.com/wgengine/magicsock from tailscale.com/cmd/tailscaled+
|
||||
💣 tailscale.com/wgengine/monitor from tailscale.com/wgengine
|
||||
tailscale.com/wgengine/packet from tailscale.com/wgengine+
|
||||
tailscale.com/wgengine/router from tailscale.com/cmd/tailscaled+
|
||||
💣 tailscale.com/wgengine/router/dns from tailscale.com/ipn+
|
||||
tailscale.com/wgengine/tsdns from tailscale.com/ipn+
|
||||
tailscale.com/wgengine/tstun from tailscale.com/wgengine
|
||||
W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router
|
||||
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
|
||||
golang.org/x/crypto/blake2s from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
|
||||
golang.org/x/crypto/chacha20poly1305 from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/crypto/curve25519 from github.com/tailscale/wireguard-go/wgcfg+
|
||||
golang.org/x/crypto/nacl/box from tailscale.com/control/controlclient+
|
||||
golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box
|
||||
golang.org/x/crypto/poly1305 from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/crypto/salsa20/salsa from golang.org/x/crypto/nacl/box+
|
||||
golang.org/x/crypto/ssh/terminal from tailscale.com/logpolicy
|
||||
golang.org/x/net/bpf from github.com/mdlayher/netlink+
|
||||
golang.org/x/net/context/ctxhttp from golang.org/x/oauth2/internal
|
||||
golang.org/x/net/dns/dnsmessage from tailscale.com/wgengine/tsdns
|
||||
golang.org/x/net/ipv4 from github.com/tailscale/wireguard-go/device
|
||||
golang.org/x/net/ipv6 from github.com/tailscale/wireguard-go/device+
|
||||
golang.org/x/net/proxy from tailscale.com/net/netns
|
||||
golang.org/x/oauth2 from tailscale.com/control/controlclient+
|
||||
golang.org/x/oauth2/internal from golang.org/x/oauth2
|
||||
golang.org/x/sync/errgroup from tailscale.com/derp
|
||||
golang.org/x/sync/singleflight from tailscale.com/net/dnscache
|
||||
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+
|
||||
LD golang.org/x/sys/unix from github.com/jsimonetti/rtnetlink/internal/unix+
|
||||
W golang.org/x/sys/windows from github.com/apenwarr/fixconsole+
|
||||
W golang.org/x/sys/windows/registry from github.com/tailscale/wireguard-go/tun/wintun+
|
||||
W golang.org/x/text/transform from golang.org/x/text/unicode/norm
|
||||
W golang.org/x/text/unicode/norm from github.com/tailscale/wireguard-go/tun/wintun
|
||||
golang.org/x/time/rate from tailscale.com/types/logger+
|
||||
vendor/golang.org/x/crypto/chacha20 from vendor/golang.org/x/crypto/chacha20poly1305
|
||||
vendor/golang.org/x/crypto/chacha20poly1305 from crypto/tls
|
||||
vendor/golang.org/x/crypto/cryptobyte from crypto/ecdsa+
|
||||
vendor/golang.org/x/crypto/cryptobyte/asn1 from crypto/ecdsa+
|
||||
vendor/golang.org/x/crypto/curve25519 from crypto/tls
|
||||
vendor/golang.org/x/crypto/hkdf from crypto/tls
|
||||
vendor/golang.org/x/crypto/poly1305 from vendor/golang.org/x/crypto/chacha20poly1305
|
||||
vendor/golang.org/x/net/dns/dnsmessage from net
|
||||
vendor/golang.org/x/net/http/httpguts from net/http
|
||||
vendor/golang.org/x/net/http/httpproxy from net/http
|
||||
vendor/golang.org/x/net/http2/hpack from net/http
|
||||
vendor/golang.org/x/net/idna from net/http+
|
||||
D vendor/golang.org/x/net/route from net
|
||||
vendor/golang.org/x/sys/cpu from vendor/golang.org/x/crypto/chacha20poly1305
|
||||
vendor/golang.org/x/text/secure/bidirule from vendor/golang.org/x/net/idna
|
||||
vendor/golang.org/x/text/transform from vendor/golang.org/x/text/secure/bidirule+
|
||||
vendor/golang.org/x/text/unicode/bidi from vendor/golang.org/x/net/idna+
|
||||
vendor/golang.org/x/text/unicode/norm from vendor/golang.org/x/net/idna
|
||||
bufio from compress/flate+
|
||||
bytes from bufio+
|
||||
compress/flate from compress/gzip+
|
||||
compress/gzip from internal/profile+
|
||||
compress/zlib from debug/elf+
|
||||
container/list from crypto/tls+
|
||||
context from crypto/tls+
|
||||
crypto from crypto/ecdsa+
|
||||
crypto/aes from crypto/ecdsa+
|
||||
crypto/cipher from crypto/aes+
|
||||
crypto/des from crypto/tls+
|
||||
crypto/dsa from crypto/x509
|
||||
crypto/ecdsa from crypto/tls+
|
||||
crypto/ed25519 from crypto/tls+
|
||||
crypto/elliptic from crypto/ecdsa+
|
||||
crypto/hmac from crypto/tls+
|
||||
crypto/md5 from crypto/tls+
|
||||
crypto/rand from crypto/ed25519+
|
||||
crypto/rc4 from crypto/tls
|
||||
crypto/rsa from crypto/tls+
|
||||
crypto/sha1 from crypto/tls+
|
||||
crypto/sha256 from crypto/tls+
|
||||
crypto/sha512 from crypto/ecdsa+
|
||||
crypto/subtle from crypto/aes+
|
||||
crypto/tls from github.com/tcnksm/go-httpstat+
|
||||
crypto/x509 from crypto/tls+
|
||||
crypto/x509/pkix from crypto/x509+
|
||||
debug/dwarf from debug/elf+
|
||||
debug/elf from rsc.io/goversion/version
|
||||
debug/macho from rsc.io/goversion/version
|
||||
debug/pe from rsc.io/goversion/version
|
||||
encoding from encoding/json+
|
||||
encoding/asn1 from crypto/x509+
|
||||
encoding/base64 from encoding/json+
|
||||
encoding/binary from compress/gzip+
|
||||
encoding/hex from crypto/x509+
|
||||
encoding/json from expvar+
|
||||
encoding/pem from crypto/tls+
|
||||
errors from bufio+
|
||||
expvar from tailscale.com/derp+
|
||||
L flag from tailscale.com/net/netns
|
||||
fmt from compress/flate+
|
||||
hash from compress/zlib+
|
||||
hash/adler32 from compress/zlib
|
||||
hash/crc32 from compress/gzip+
|
||||
hash/fnv from tailscale.com/wgengine/magicsock
|
||||
html from html/template+
|
||||
html/template from net/http/pprof
|
||||
io from bufio+
|
||||
io/ioutil from crypto/tls+
|
||||
log from expvar+
|
||||
math from compress/flate+
|
||||
math/big from crypto/dsa+
|
||||
math/bits from compress/flate+
|
||||
math/rand from github.com/mdlayher/netlink+
|
||||
mime from golang.org/x/oauth2/internal+
|
||||
mime/multipart from net/http
|
||||
mime/quotedprintable from mime/multipart
|
||||
net from crypto/tls+
|
||||
net/http from expvar+
|
||||
net/http/httptrace from github.com/tcnksm/go-httpstat+
|
||||
net/http/internal from net/http
|
||||
net/http/pprof from tailscale.com/cmd/tailscaled
|
||||
net/textproto from mime/multipart+
|
||||
net/url from crypto/x509+
|
||||
os from crypto/rand+
|
||||
os/exec from github.com/coreos/go-iptables/iptables+
|
||||
os/signal from tailscale.com/cmd/tailscaled+
|
||||
os/user from github.com/godbus/dbus/v5+
|
||||
path from debug/dwarf+
|
||||
path/filepath from crypto/x509+
|
||||
reflect from crypto/x509+
|
||||
regexp from github.com/coreos/go-iptables/iptables+
|
||||
regexp/syntax from regexp
|
||||
LD runtime/cgo
|
||||
runtime/debug from github.com/klauspost/compress/zstd+
|
||||
runtime/pprof from net/http/pprof+
|
||||
runtime/trace from net/http/pprof
|
||||
sort from compress/flate+
|
||||
strconv from compress/flate+
|
||||
strings from bufio+
|
||||
sync from compress/flate+
|
||||
sync/atomic from context+
|
||||
syscall from crypto/rand+
|
||||
text/tabwriter from runtime/pprof
|
||||
text/template from html/template
|
||||
text/template/parse from html/template+
|
||||
time from compress/gzip+
|
||||
unicode from bytes+
|
||||
unicode/utf16 from encoding/asn1+
|
||||
unicode/utf8 from bufio+
|
||||
unsafe from crypto/internal/subtle+
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/wgengine"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
"tailscale.com/wgengine/netstack"
|
||||
"tailscale.com/wgengine/router"
|
||||
)
|
||||
|
||||
@@ -137,11 +136,7 @@ func run() error {
|
||||
|
||||
var e wgengine.Engine
|
||||
if args.fake {
|
||||
impl := netstack.Impl
|
||||
if args.tunname != "netstack" {
|
||||
impl = nil
|
||||
}
|
||||
e, err = wgengine.NewFakeUserspaceEngine(logf, 0, impl)
|
||||
e, err = wgengine.NewFakeUserspaceEngine(logf, 0)
|
||||
} else {
|
||||
e, err = wgengine.NewUserspaceEngine(logf, args.tunname, args.port)
|
||||
}
|
||||
|
||||
@@ -717,6 +717,8 @@ func decode(res *http.Response, v interface{}, serverKey *wgcfg.Key, mkey *wgcfg
|
||||
return decodeMsg(msg, v, serverKey, mkey)
|
||||
}
|
||||
|
||||
var dumpMapResponse, _ = strconv.ParseBool(os.Getenv("TS_DEBUG_MAPRESPONSE"))
|
||||
|
||||
func (c *Direct) decodeMsg(msg []byte, v interface{}) error {
|
||||
c.mu.Lock()
|
||||
mkey := c.persist.PrivateMachineKey
|
||||
@@ -741,6 +743,11 @@ func (c *Direct) decodeMsg(msg []byte, v interface{}) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dumpMapResponse {
|
||||
var buf bytes.Buffer
|
||||
json.Indent(&buf, b, "", " ")
|
||||
log.Printf("MapResponse: %s", buf.Bytes())
|
||||
}
|
||||
if err := json.Unmarshal(b, v); err != nil {
|
||||
return fmt.Errorf("response: %v", err)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
"go4.org/mem"
|
||||
"tailscale.com/util/lineread"
|
||||
"tailscale.com/version/distro"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -23,8 +24,14 @@ func init() {
|
||||
}
|
||||
|
||||
func osVersionLinux() string {
|
||||
dist := distro.Get()
|
||||
propFile := "/etc/os-release"
|
||||
if dist == distro.Synology {
|
||||
propFile = "/etc.defaults/VERSION"
|
||||
}
|
||||
|
||||
m := map[string]string{}
|
||||
lineread.File("/etc/os-release", func(line []byte) error {
|
||||
lineread.File(propFile, func(line []byte) error {
|
||||
eq := bytes.IndexByte(line, '=')
|
||||
if eq == -1 {
|
||||
return nil
|
||||
@@ -71,6 +78,9 @@ func osVersionLinux() string {
|
||||
return fmt.Sprintf("%s%s", v, attr)
|
||||
}
|
||||
}
|
||||
if dist == distro.Synology {
|
||||
return fmt.Sprintf("Synology %s%s", m["productversion"], attr)
|
||||
}
|
||||
return fmt.Sprintf("Other%s", attr)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,10 @@ func osVersionWindows() string {
|
||||
s := strings.TrimSpace(string(out))
|
||||
s = strings.TrimPrefix(s, "Microsoft Windows [")
|
||||
s = strings.TrimSuffix(s, "]")
|
||||
s = strings.TrimPrefix(s, "Version ") // is this localized? do it last in case.
|
||||
return s // "10.0.19041.388", ideally
|
||||
|
||||
// "Version 10.x.y.z", with "Version" localized. Keep only stuff after the space.
|
||||
if sp := strings.Index(s, " "); sp != -1 {
|
||||
s = s[sp+1:]
|
||||
}
|
||||
return s // "10.0.19041.388", ideally
|
||||
}
|
||||
|
||||
@@ -219,7 +219,6 @@ const (
|
||||
AllowSingleHosts WGConfigFlags = 1 << iota
|
||||
AllowSubnetRoutes
|
||||
AllowDefaultRoute
|
||||
HackDefaultRoute
|
||||
)
|
||||
|
||||
// EndpointDiscoSuffix is appended to the hex representation of a peer's discovery key
|
||||
@@ -274,11 +273,7 @@ func (nm *NetworkMap) WGCfg(logf logger.Logf, flags WGConfigFlags) (*wgcfg.Confi
|
||||
logf("wgcfg: %v skipping default route", peer.Key.ShortString())
|
||||
continue
|
||||
}
|
||||
if (flags & HackDefaultRoute) != 0 {
|
||||
allowedIP = wgcfg.CIDR{IP: wgcfg.IPv4(10, 0, 0, 0), Mask: 8}
|
||||
logf("wgcfg: %v converting default route => %v", peer.Key.ShortString(), allowedIP.String())
|
||||
}
|
||||
} else if allowedIP.Mask < 32 {
|
||||
} else if cidrIsSubnet(peer, allowedIP) {
|
||||
if (flags & AllowSubnetRoutes) == 0 {
|
||||
logf("wgcfg: %v skipping subnet route", peer.Key.ShortString())
|
||||
continue
|
||||
@@ -291,6 +286,29 @@ func (nm *NetworkMap) WGCfg(logf logger.Logf, flags WGConfigFlags) (*wgcfg.Confi
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// cidrIsSubnet reports whether cidr is a non-default-route subnet
|
||||
// exported by node that is not one of its own self addresses.
|
||||
func cidrIsSubnet(node *tailcfg.Node, cidr wgcfg.CIDR) bool {
|
||||
if cidr.Mask == 0 {
|
||||
return false
|
||||
}
|
||||
if cidr.Mask < 32 {
|
||||
// Fast path for IPv4, to avoid loop below.
|
||||
//
|
||||
// TODO: if cidr.IP is an IPv6 address, we could do "< 128"
|
||||
// to avoid the range over node.Addresses. Or we could
|
||||
// just remove this fast path and unconditionally do the range
|
||||
// loop.
|
||||
return true
|
||||
}
|
||||
for _, selfCIDR := range node.Addresses {
|
||||
if cidr == selfCIDR {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func appendEndpoint(peer *wgcfg.Peer, epStr string) error {
|
||||
if epStr == "" {
|
||||
return nil
|
||||
|
||||
16
go.mod
16
go.mod
@@ -12,28 +12,28 @@ require (
|
||||
github.com/go-ole/go-ole v1.2.4
|
||||
github.com/godbus/dbus/v5 v5.0.3
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e
|
||||
github.com/google/go-cmp v0.5.0
|
||||
github.com/google/go-cmp v0.4.0
|
||||
github.com/goreleaser/nfpm v1.1.10
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4
|
||||
github.com/klauspost/compress v1.10.10
|
||||
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1
|
||||
github.com/kr/pty v1.1.1
|
||||
github.com/mdlayher/netlink v1.1.0
|
||||
github.com/miekg/dns v1.1.30
|
||||
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3
|
||||
github.com/peterbourgon/ff/v2 v2.0.0
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f
|
||||
github.com/tailscale/depaware v0.0.0-20200914232109-e09ee10c1824
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200916205758-decb9ee8e170
|
||||
github.com/tailscale/wireguard-go v0.0.0-20200902185615-1997cf6f9fe4
|
||||
github.com/tcnksm/go-httpstat v0.2.0
|
||||
github.com/toqueteos/webbrowser v1.2.0
|
||||
go4.org/mem v0.0.0-20200706164138-185c595c3ecc
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0
|
||||
golang.org/x/tools v0.0.0-20200707200213-416e8f4faf8a
|
||||
gvisor.dev/gvisor v0.0.0-20200903175658-a842a338ecd9
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425
|
||||
honnef.co/go/tools v0.0.1-2020.1.4
|
||||
inet.af/netaddr v0.0.0-20200810144936-56928fe48a98
|
||||
rsc.io/goversion v1.2.0
|
||||
|
||||
298
go.sum
298
go.sum
@@ -1,24 +1,8 @@
|
||||
bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.52.1-0.20200122224058-0482b626c726/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Masterminds/semver/v3 v3.0.3 h1:znjIyLfpXEDQjOIEWh+ehwpTU14UzUPub3c3sm36u14=
|
||||
github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
||||
github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
|
||||
github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible h1:5svnBTFgJjZvGKyYBtMB0+m5wvrbUHiqye8wRJMlnYI=
|
||||
github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
@@ -37,139 +21,44 @@ github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e h1:hHg27A0RSSp2Om9lubZpiMgVbvn39bsUmW9U5h0twqc=
|
||||
github.com/cavaliercoder/go-cpio v0.0.0-20180626203310-925f9528c45e/go.mod h1:oDpT4efm8tSYHXV5tHSdRvBet/b/QzxZ+XyyPehvm3A=
|
||||
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422 h1:8eZxmY1yvxGHzdzTEhI09npjMVGzNAdrqzruTX6jcK4=
|
||||
github.com/cenkalti/backoff v1.1.1-0.20190506075156-2146c9339422/go.mod h1:b6Nc7NRH5C4aCISLry0tLnTjcuTEvoiqcWDdsU0sOGM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/containerd/cgroups v0.0.0-20181219155423-39b18af02c41 h1:5yg0k8gqOssNLsjjCtXIADoPbAtUtQZJfC8hQ4r2oFY=
|
||||
github.com/containerd/cgroups v0.0.0-20181219155423-39b18af02c41/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
|
||||
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e h1:GdiIYd8ZDOrT++e1NjhSD4rGt9zaJukHm4rt5F4mRQc=
|
||||
github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
|
||||
github.com/containerd/containerd v1.3.4 h1:3o0smo5SKY7H6AJCmJhsnCjR2/V2T8VmiHt7seN2/kI=
|
||||
github.com/containerd/containerd v1.3.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
|
||||
github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
|
||||
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00 h1:lsjC5ENBl+Zgf38+B0ymougXFp0BaubeIVETltYZTQw=
|
||||
github.com/containerd/fifo v0.0.0-20191213151349-ff969a566b00/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
|
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328 h1:PRTagVMbJcCezLcHXe8UJvR1oBzp2lG3CEumeFOLOds=
|
||||
github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
|
||||
github.com/containerd/ttrpc v0.0.0-20200121165050-0be804eadb15 h1:+jgiLE5QylzgADj0Yldb4id1NQNRrDOROj7KDvY9PEc=
|
||||
github.com/containerd/ttrpc v0.0.0-20200121165050-0be804eadb15/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
|
||||
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737 h1:HovfQDS/K3Mr7eyS0QJLxE1CbVUhjZCl6g3OhFJgP1o=
|
||||
github.com/containerd/typeurl v0.0.0-20200205145503-b45ef1f1f737/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
|
||||
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
|
||||
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
|
||||
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.4.2-0.20191028175130-9e7d5ac5ea55/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.3.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dpjacques/clockwork v0.1.1-0.20190114191937-d864eecc357b/go.mod h1:D8mP2A8vVT2GkXqPorSBmhnshhkFBYgzhA90KmJt25Y=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
github.com/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
|
||||
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079 h1:JFTFz3HZTGmgMz4E1TabNBNJljROSYgja1b4l50FNVs=
|
||||
github.com/gofrs/flock v0.6.1-0.20180915234121-886344bea079/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
|
||||
github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-github/v28 v28.1.2-0.20191108005307-e555eab49ce8/go.mod h1:g82e6OHbJ0WYrYeOrid1MMfHAtqjxBz+N74tfAt9KrQ=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0 h1:BW6OvS3kpT5UEPbCZ+KyX/OB4Ks9/MNMhWjqPPkZxsE=
|
||||
github.com/google/rpmpack v0.0.0-20191226140753-aa36bfddb3a0/go.mod h1:RaTPr0KUf2K7fnZYLNDrr8rxAamWs3iNywJLtQ2AzBg=
|
||||
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8 h1:8nlgEAjIalk6uj/CGKCdOO8CQqTeysvcW4RFZ6HbkGM=
|
||||
github.com/google/subcommands v1.0.2-0.20190508160503-636abe8753b8/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/goreleaser/nfpm v1.1.10 h1:0nwzKUJTcygNxTzVKq2Dh9wpVP1W2biUH6SNKmoxR3w=
|
||||
github.com/goreleaser/nfpm v1.1.10/go.mod h1:oOcoGRVwvKIODz57NUfiRwFWGfn00NXdgnn6MrYtO5k=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4 h1:nwOc1YaOrYJ37sEBrtWZrdqzK22hiJs3GpDmP3sR2Yw=
|
||||
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.10.10 h1:a/y8CglcM7gLGYmlbP/stPE5sR3hbhFRUjCBfd/0B3I=
|
||||
github.com/klauspost/compress v1.10.10/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1 h1:zc0R6cOw98cMengLA0fvU55mqbnN7sd/tBMLzSejp+M=
|
||||
github.com/kr/pty v1.1.4-0.20190131011033-7dc38fb350b1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-zglob v0.0.1 h1:xsEx/XUoVlI6yXjqBK062zYhRTZltCNmYPx6v+8DNaY=
|
||||
@@ -180,56 +69,38 @@ github.com/mdlayher/netlink v1.1.0 h1:mpdLgm+brq10nI9zM1BpX1kpDbh3NLl3RSnVq6ZSkf
|
||||
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
|
||||
github.com/miekg/dns v1.1.30 h1:Qww6FseFn8PRfw07jueqIXqodm0JKiiKuK0DeXSqfyo=
|
||||
github.com/miekg/dns v1.1.30/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9 h1:Sha2bQdoWE5YQPTlJOL31rmce94/tYi113SlFo1xQ2c=
|
||||
github.com/mohae/deepcopy v0.0.0-20170308212314-bb9b5e7adda9/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7 h1:lDH9UUVJtmYCjyT0CI4q8xvlXPxeZ0gYCVvWbmPlp88=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y=
|
||||
github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
|
||||
github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.0.2-0.20181111125026-1722abf79c2f h1:Pyp2f/uuhJIcUgnIeZaAbwOcyNz8TBlEe6mPpC8kXq8=
|
||||
github.com/opencontainers/runtime-spec v1.0.2-0.20181111125026-1722abf79c2f/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 h1:YtFkrqsMEj7YqpIhRteVxJxCeC3jJBieuLr0d4C4rSA=
|
||||
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
|
||||
github.com/peterbourgon/ff/v2 v2.0.0 h1:lx0oYI5qr/FU1xnpNhQ+EZM04gKgn46jyYvGEEqBBbY=
|
||||
github.com/peterbourgon/ff/v2 v2.0.0/go.mod h1:xjwr+t+SjWm4L46fcj/D+Ap+6ME7+HqFzaP22pP5Ggk=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7 h1:+/+DxvQaYifJ+grD4klzrS5y+KJXldn/2YTl5JG+vZ8=
|
||||
github.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b h1:+gCnWOZV8Z/8jehJ2CdqB47Z3S+SREmQcuXkRFLNsiI=
|
||||
github.com/sassoftware/go-rpmutils v0.0.0-20190420191620-a8f1baeba37b/go.mod h1:am+Fp8Bt506lA3Rk3QCmSqmYmLMnPDhdDUcosQCAx+I=
|
||||
github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
|
||||
github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2 h1:b6uOv7YOFK0TYG7HtkIgExQo+2RdLuwRft63jn2HWj8=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/tailscale/depaware v0.0.0-20200909185729-8ca448326e3a h1:PjVmKyzFfgQrdrmX7kpRkKXkvwMZP/MF3nJT/WJyjW8=
|
||||
github.com/tailscale/depaware v0.0.0-20200909185729-8ca448326e3a/go.mod h1:H0k9mKUzaDpb22Zn2FiSzY3zeRbAiZ7wUFxKJ7kp8GE=
|
||||
github.com/tailscale/depaware v0.0.0-20200910145248-cb751026f10d h1:tjLqVTL0IZdV2kBvM2WqCjug6IBJTWOYiM8wqPk2Xp0=
|
||||
github.com/tailscale/depaware v0.0.0-20200910145248-cb751026f10d/go.mod h1:H0k9mKUzaDpb22Zn2FiSzY3zeRbAiZ7wUFxKJ7kp8GE=
|
||||
github.com/tailscale/depaware v0.0.0-20200914201916-3f1070fd0d55 h1:hLAgSpbb0rfOq9jziQbvOsOarpfTDxnFbG8kG6INFeY=
|
||||
github.com/tailscale/depaware v0.0.0-20200914201916-3f1070fd0d55/go.mod h1:nyzwKFaLuckPu3dAJHH7B6lMi4xDBWzD0r3pEpGZm2Y=
|
||||
github.com/tailscale/depaware v0.0.0-20200914232109-e09ee10c1824 h1:MD/YQ8xI070ZqFC3SnLAlhPPUNfeRKErQaAaXc/r4dQ=
|
||||
github.com/tailscale/depaware v0.0.0-20200914232109-e09ee10c1824/go.mod h1:nyzwKFaLuckPu3dAJHH7B6lMi4xDBWzD0r3pEpGZm2Y=
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f h1:uFj5bslHsMzxIM8UTjAhq4VXeo6GfNW91rpoh/WMJaY=
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200413171540-609dcf2df55f/go.mod h1:x880GWw5fvrl2DVTQ04ttXQD4DuppTt1Yz6wLibbjNE=
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200916205758-decb9ee8e170 h1:vJ0twi0120W/LKiDxzXROSVx1F4pIKZBQqvtPahnH60=
|
||||
github.com/tailscale/winipcfg-go v0.0.0-20200916205758-decb9ee8e170/go.mod h1:x880GWw5fvrl2DVTQ04ttXQD4DuppTt1Yz6wLibbjNE=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20200902185615-1997cf6f9fe4 h1:UiTXdZChEWxxci7bx+jS9OyHQx2IA8zmMWQqp5wfP7c=
|
||||
github.com/tailscale/wireguard-go v0.0.0-20200902185615-1997cf6f9fe4/go.mod h1:WXq+IkSOJGIgfF1XW+4z4oW+LX/TXzU9DcKlT5EZLi4=
|
||||
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
|
||||
@@ -238,224 +109,81 @@ github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9r
|
||||
github.com/toqueteos/webbrowser v1.2.0/go.mod h1:XWoZq4cyp9WeUeak7w7LXRUQf1F1ATJMir8RTqb4ayM=
|
||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86 h1:7SWt9pGCMaw+N1ZhRsaLKaYNviFhxambdoaoYlDqz1w=
|
||||
github.com/vishvananda/netlink v1.0.1-0.20190930145447-2ec5bdc52b86/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
|
||||
github.com/vishvananda/netns v0.0.0-20200520041808-52d707b772fe h1:mjAZxE1nh8yvuwhGHpdDqdhtNu2dgbpk93TwoXuk5so=
|
||||
github.com/vishvananda/netns v0.0.0-20200520041808-52d707b772fe/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.2.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go4.org/mem v0.0.0-20200706164138-185c595c3ecc h1:paujszgN6SpsO/UsXC7xax3gQAKz/XQKCYZLQdU34Tw=
|
||||
go4.org/mem v0.0.0-20200706164138-185c595c3ecc/go.mod h1:NEYvpHWemiG/E5UWfaN5QAIGZeT1sa0Z2UNk6oeMb/k=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191002192127-34f69633bfdc/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
|
||||
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5 h1:WQ8q63x+f/zpC8Ac1s9wLElVoHhm32p6tudrU72n1QA=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344 h1:vGXIOMxbNfDTk/aXCmfdLgkrSV+Z2tcbze+pEc3v5W4=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190310054646-10058d7d4faa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5 h1:LfCXLvNmTYH9kEmVgqbnsWfruoXZIrh4YBgqVHtDvw0=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3 h1:5B6i6EAiSYyejWfvc5Rc9BbI3rzIsrrXfAQBWnYfn+w=
|
||||
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d h1:QQrM/CCYEzTs91GZylDCQjGHudbPTxF/1fvXdVh5lMo=
|
||||
golang.org/x/sys v0.0.0-20200812155832-6a926be9bd1d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d h1:/iIZNFGxc/a7C3yWjGcnboV+Tkc7mxr+p6fDztwoxuM=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425 h1:VvQyQJN0tSuecqgcIxMWnnfG5kSmgy9KZR9sW3W5QeA=
|
||||
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200707200213-416e8f4faf8a h1:YAl/dx/kLsMMIWGqfhFHW9ckqGhmq7Ki0dfoKAgvFTE=
|
||||
golang.org/x/tools v0.0.0-20200707200213-416e8f4faf8a/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24 h1:wDju+RU97qa0FZT0QnZDg9Uc2dH0Ql513kFvHocz+WM=
|
||||
google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.29.0 h1:2pJjwYOdkZ9HlN4sWRYBg9ttH5bCOlsueaM+b/oYjwo=
|
||||
google.golang.org/grpc v1.29.0/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
gvisor.dev/gvisor v0.0.0-20200903175658-a842a338ecd9 h1:w2M4YLwjhea3cX8qp7pOvMg97svEgPzpAcGZHb7BVwc=
|
||||
gvisor.dev/gvisor v0.0.0-20200903175658-a842a338ecd9/go.mod h1:n17AP1iZxpRCzqyHLJdpa2e1SzY1rNZ9tt3/MePxlGs=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
inet.af/netaddr v0.0.0-20200810144936-56928fe48a98 h1:bWyWDZP0l6VnQ1TDKf6yNwuiEDV6Q3q1Mv34m+lzT1I=
|
||||
inet.af/netaddr v0.0.0-20200810144936-56928fe48a98/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=
|
||||
rsc.io/goversion v1.2.0/go.mod h1:Eih9y/uIBS3ulggl7KNJ09xGSLcuNaLgmvvqa07sgfo=
|
||||
|
||||
9
internal/tooldeps/tooldeps.go
Normal file
9
internal/tooldeps/tooldeps.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tooldeps
|
||||
|
||||
import (
|
||||
_ "github.com/tailscale/depaware/depaware"
|
||||
)
|
||||
@@ -8,22 +8,29 @@ import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/control/controlclient"
|
||||
"tailscale.com/ipn"
|
||||
"tailscale.com/logtail/backoff"
|
||||
"tailscale.com/net/netstat"
|
||||
"tailscale.com/safesocket"
|
||||
"tailscale.com/smallzstd"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/util/pidowner"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/wgengine"
|
||||
)
|
||||
@@ -84,11 +91,22 @@ type server struct {
|
||||
}
|
||||
|
||||
func (s *server) serveConn(ctx context.Context, c net.Conn, logf logger.Logf) {
|
||||
br := bufio.NewReader(c)
|
||||
|
||||
// First see if it's an HTTP request.
|
||||
c.SetReadDeadline(time.Now().Add(time.Second))
|
||||
peek, _ := br.Peek(4)
|
||||
c.SetReadDeadline(time.Time{})
|
||||
if string(peek) == "GET " {
|
||||
http.Serve(&oneConnListener{altReaderNetConn{br, c}}, localhostHandler(c))
|
||||
return
|
||||
}
|
||||
|
||||
s.addConn(c)
|
||||
logf("incoming control connection")
|
||||
defer s.removeAndCloseConn(c)
|
||||
for ctx.Err() == nil {
|
||||
msg, err := ipn.ReadMsg(c)
|
||||
msg, err := ipn.ReadMsg(br)
|
||||
if err != nil {
|
||||
if ctx.Err() == nil {
|
||||
logf("ReadMsg: %v", err)
|
||||
@@ -394,3 +412,83 @@ func BabysitProc(ctx context.Context, args []string, logf logger.Logf) {
|
||||
func FixedEngine(eng wgengine.Engine) func() (wgengine.Engine, error) {
|
||||
return func() (wgengine.Engine, error) { return eng, nil }
|
||||
}
|
||||
|
||||
type dummyAddr string
|
||||
type oneConnListener struct {
|
||||
conn net.Conn
|
||||
}
|
||||
|
||||
func (l *oneConnListener) Accept() (c net.Conn, err error) {
|
||||
c = l.conn
|
||||
if c == nil {
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
err = nil
|
||||
l.conn = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (l *oneConnListener) Close() error { return nil }
|
||||
|
||||
func (l *oneConnListener) Addr() net.Addr { return dummyAddr("unused-address") }
|
||||
|
||||
func (a dummyAddr) Network() string { return string(a) }
|
||||
func (a dummyAddr) String() string { return string(a) }
|
||||
|
||||
type altReaderNetConn struct {
|
||||
r io.Reader
|
||||
net.Conn
|
||||
}
|
||||
|
||||
func (a altReaderNetConn) Read(p []byte) (int, error) { return a.r.Read(p) }
|
||||
|
||||
func localhostHandler(c net.Conn) http.Handler {
|
||||
la, lerr := netaddr.ParseIPPort(c.LocalAddr().String())
|
||||
ra, rerr := netaddr.ParseIPPort(c.RemoteAddr().String())
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "<html><body><h1>tailscale</h1>\n")
|
||||
if lerr != nil || rerr != nil {
|
||||
io.WriteString(w, "failed to parse remote address")
|
||||
return
|
||||
}
|
||||
if !la.IP.IsLoopback() || !ra.IP.IsLoopback() {
|
||||
io.WriteString(w, "not loopback")
|
||||
return
|
||||
}
|
||||
tab, err := netstat.Get()
|
||||
if err == netstat.ErrNotImplemented {
|
||||
io.WriteString(w, "status page not available on "+runtime.GOOS)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
io.WriteString(w, "failed to get netstat table")
|
||||
return
|
||||
}
|
||||
pid := peerPid(tab.Entries, la, ra)
|
||||
if pid == 0 {
|
||||
io.WriteString(w, "peer pid not found")
|
||||
return
|
||||
}
|
||||
uid, err := pidowner.OwnerOfPID(pid)
|
||||
if err != nil {
|
||||
io.WriteString(w, "owner of peer pid not found")
|
||||
return
|
||||
}
|
||||
u, err := user.LookupId(uid)
|
||||
if err != nil {
|
||||
io.WriteString(w, "User lookup failed")
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "Hello, %s", html.EscapeString(u.Username))
|
||||
})
|
||||
}
|
||||
|
||||
func peerPid(entries []netstat.Entry, la, ra netaddr.IPPort) int {
|
||||
for _, e := range entries {
|
||||
if e.Local == ra && e.Remote == la {
|
||||
return e.Pid
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
20
ipn/local.go
20
ipn/local.go
@@ -555,7 +555,7 @@ func (b *LocalBackend) updateDNSMap(netMap *controlclient.NetworkMap) {
|
||||
|
||||
nameToIP := make(map[string]netaddr.IP)
|
||||
set := func(name string, addrs []wgcfg.CIDR) {
|
||||
if len(addrs) == 0 {
|
||||
if len(addrs) == 0 || name == "" {
|
||||
return
|
||||
}
|
||||
nameToIP[name] = netaddr.IPFrom16(addrs[0].IP.Addr)
|
||||
@@ -953,12 +953,6 @@ func (b *LocalBackend) authReconfig() {
|
||||
flags |= controlclient.AllowDefaultRoute
|
||||
// TODO(apenwarr): Make subnet routes a different pref?
|
||||
flags |= controlclient.AllowSubnetRoutes
|
||||
// TODO(apenwarr): Remove this once we sort out subnet routes.
|
||||
// Right now default routes are broken in Windows, but
|
||||
// controlclient doesn't properly send subnet routes. So
|
||||
// let's convert a default route into a subnet route in order
|
||||
// to allow experimentation.
|
||||
flags |= controlclient.HackDefaultRoute
|
||||
}
|
||||
if uc.AllowSingleHosts {
|
||||
flags |= controlclient.AllowSingleHosts
|
||||
@@ -1251,11 +1245,21 @@ func (b *LocalBackend) requestEngineStatusAndWait() {
|
||||
// rebooting will fix it.
|
||||
func (b *LocalBackend) Logout() {
|
||||
b.mu.Lock()
|
||||
b.assertClientLocked()
|
||||
c := b.c
|
||||
b.netMap = nil
|
||||
b.mu.Unlock()
|
||||
|
||||
if c == nil {
|
||||
// Double Logout can happen via repeated IPN
|
||||
// connections to ipnserver making it repeatedly
|
||||
// transition from 1->0 total connections, which on
|
||||
// Windows by default ("client mode") causes a Logout
|
||||
// on the transition to zero.
|
||||
// Previously this crashed when we asserted that c was non-nil
|
||||
// here.
|
||||
return
|
||||
}
|
||||
|
||||
c.Logout()
|
||||
|
||||
b.mu.Lock()
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
package ipn
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -20,7 +21,7 @@ import (
|
||||
// being logged by the expected functions. Update these tests if moving log lines between
|
||||
// functions.
|
||||
func TestLocalLogLines(t *testing.T) {
|
||||
logListen := tstest.ListenFor(t.Logf, []string{
|
||||
logListen := tstest.NewLogLineTracker(t.Logf, []string{
|
||||
"SetPrefs: %v",
|
||||
"peer keys: %s",
|
||||
"v%v peers: %v",
|
||||
@@ -48,6 +49,7 @@ func TestLocalLogLines(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer lb.Shutdown()
|
||||
|
||||
// custom adjustments for required non-nil fields
|
||||
lb.prefs = NewPrefs()
|
||||
@@ -55,30 +57,10 @@ func TestLocalLogLines(t *testing.T) {
|
||||
// hacky manual override of the usual log-on-change behaviour of keylogf
|
||||
lb.keyLogf = logListen.Logf
|
||||
|
||||
// testing infrastructure
|
||||
type linesTest struct {
|
||||
name string
|
||||
want []string
|
||||
}
|
||||
|
||||
tests := []linesTest{
|
||||
{
|
||||
name: "after prefs",
|
||||
want: []string{
|
||||
"peer keys: %s",
|
||||
"v%v peers: %v",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "after peers",
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
testLogs := func(want linesTest) func(t *testing.T) {
|
||||
testWantRemain := func(wantRemain ...string) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
if linesLeft := logListen.Check(); len(linesLeft) != len(want.want) {
|
||||
t.Errorf("got %v, expected %v", linesLeft, want)
|
||||
if remain := logListen.Check(); !reflect.DeepEqual(remain, wantRemain) {
|
||||
t.Errorf("remain %q, want %q", remain, wantRemain)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,7 +71,7 @@ func TestLocalLogLines(t *testing.T) {
|
||||
prefs.Persist = persist
|
||||
lb.SetPrefs(prefs)
|
||||
|
||||
t.Run(tests[0].name, testLogs(tests[0]))
|
||||
t.Run("after_prefs", testWantRemain("peer keys: %s", "v%v peers: %v"))
|
||||
|
||||
// log peers, peer keys
|
||||
status := &wgengine.Status{
|
||||
@@ -103,5 +85,5 @@ func TestLocalLogLines(t *testing.T) {
|
||||
}
|
||||
lb.parseWgStatus(status)
|
||||
|
||||
t.Run(tests[1].name, testLogs(tests[1]))
|
||||
t.Run("after_peers", testWantRemain())
|
||||
}
|
||||
|
||||
@@ -15,15 +15,18 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogHeap writes a JSON logtail record with the base64 heap pprof to
|
||||
// os.Stderr.
|
||||
// LogHeap uploads a JSON logtail record with the base64 heap pprof by means
|
||||
// of an HTTP POST request to the endpoint referred to in postURL.
|
||||
func LogHeap(postURL string) {
|
||||
if postURL == "" {
|
||||
return
|
||||
}
|
||||
runtime.GC()
|
||||
buf := new(bytes.Buffer)
|
||||
pprof.WriteHeapProfile(buf)
|
||||
if err := pprof.WriteHeapProfile(buf); err != nil {
|
||||
log.Printf("LogHeap: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
@@ -8,8 +8,10 @@ import (
|
||||
"os/exec"
|
||||
"syscall"
|
||||
|
||||
"github.com/tailscale/winipcfg-go"
|
||||
"go4.org/mem"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/tsconst"
|
||||
"tailscale.com/util/lineread"
|
||||
)
|
||||
|
||||
@@ -71,3 +73,22 @@ func likelyHomeRouterIPWindows() (ret netaddr.IP, ok bool) {
|
||||
})
|
||||
return ret, !ret.IsZero()
|
||||
}
|
||||
|
||||
// NonTailscaleMTUs returns a map of interface LUID to interface MTU,
|
||||
// for all interfaces except Tailscale tunnels.
|
||||
func NonTailscaleMTUs() (map[uint64]uint32, error) {
|
||||
ifs, err := winipcfg.GetInterfaces()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := map[uint64]uint32{}
|
||||
for _, iface := range ifs {
|
||||
if iface.Description == tsconst.WintunInterfaceDesc {
|
||||
continue
|
||||
}
|
||||
ret[iface.Luid] = iface.Mtu
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
@@ -236,7 +236,6 @@ func (c *Client) ReceiveSTUNPacket(pkt []byte, src netaddr.IPPort) {
|
||||
|
||||
tx, addr, port, err := stun.ParseResponse(pkt)
|
||||
if err != nil {
|
||||
c.mu.Unlock()
|
||||
if _, err := stun.ParseBindingRequest(pkt); err == nil {
|
||||
// This was probably our own netcheck hairpin
|
||||
// check probe coming in late. Ignore.
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !linux
|
||||
// +build !linux,!windows
|
||||
|
||||
package netns
|
||||
|
||||
|
||||
123
net/netns/netns_windows.go
Normal file
123
net/netns/netns_windows.go
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package netns
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tailscale/winipcfg-go"
|
||||
"golang.org/x/sys/windows"
|
||||
"tailscale.com/net/interfaces"
|
||||
)
|
||||
|
||||
// control binds c to the Windows interface that holds a default
|
||||
// route, and is not the Tailscale WinTun interface.
|
||||
func control(network, address string, c syscall.RawConn) error {
|
||||
canV4, canV6 := false, false
|
||||
switch network {
|
||||
case "tcp", "udp":
|
||||
canV4, canV6 = true, true
|
||||
case "tcp4", "udp4":
|
||||
canV4 = true
|
||||
case "tcp6", "udp6":
|
||||
canV6 = true
|
||||
}
|
||||
|
||||
if canV4 {
|
||||
if4, err := getDefaultInterface(winipcfg.AF_INET)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bindSocket4(c, if4); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if canV6 {
|
||||
if6, err := getDefaultInterface(winipcfg.AF_INET6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bindSocket6(c, if6); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// getDefaultInterface returns the index of the interface that has the
|
||||
// non-Tailscale default route for the given address family.
|
||||
func getDefaultInterface(family winipcfg.AddressFamily) (ifidx uint32, err error) {
|
||||
ifs, err := interfaces.NonTailscaleMTUs()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
routes, err := winipcfg.GetRoutes(family)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
bestMetric := ^uint32(0)
|
||||
// The zero index means "unspecified". If we end up passing zero
|
||||
// to bindSocket*(), it unsets the binding and lets the socket
|
||||
// behave as normal again, which is what we want if there's no
|
||||
// default route we can use.
|
||||
var index uint32
|
||||
for _, route := range routes {
|
||||
if route.DestinationPrefix.PrefixLength != 0 || ifs[route.InterfaceLuid] == 0 {
|
||||
continue
|
||||
}
|
||||
if route.Metric < bestMetric {
|
||||
bestMetric = route.Metric
|
||||
index = route.InterfaceIndex
|
||||
}
|
||||
}
|
||||
|
||||
return index, nil
|
||||
}
|
||||
|
||||
const sockoptBoundInterface = 31
|
||||
|
||||
// bindSocket4 binds the given RawConn to the network interface with
|
||||
// index ifidx, for IPv4 traffic only.
|
||||
func bindSocket4(c syscall.RawConn, ifidx uint32) error {
|
||||
// For v4 the interface index must be passed as a big-endian
|
||||
// integer, regardless of platform endianness.
|
||||
index := nativeToBigEndian(ifidx)
|
||||
var controlErr error
|
||||
err := c.Control(func(fd uintptr) {
|
||||
controlErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IP, sockoptBoundInterface, int(index))
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return controlErr
|
||||
}
|
||||
|
||||
// bindSocket6 binds the given RawConn to the network interface with
|
||||
// index ifidx, for IPv6 traffic only.
|
||||
func bindSocket6(c syscall.RawConn, ifidx uint32) error {
|
||||
var controlErr error
|
||||
err := c.Control(func(fd uintptr) {
|
||||
controlErr = windows.SetsockoptInt(windows.Handle(fd), windows.IPPROTO_IPV6, sockoptBoundInterface, int(ifidx))
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return controlErr
|
||||
}
|
||||
|
||||
// nativeToBigEndian returns i converted into big-endian
|
||||
// representation, suitable for passing to Windows APIs that require a
|
||||
// mangled uint32.
|
||||
func nativeToBigEndian(i uint32) uint32 {
|
||||
var b [4]byte
|
||||
binary.BigEndian.PutUint32(b[:], i)
|
||||
return *(*uint32)(unsafe.Pointer(&b[0]))
|
||||
}
|
||||
36
net/netstat/netstat.go
Normal file
36
net/netstat/netstat.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package netstat returns the local machine's network connection table.
|
||||
package netstat
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
|
||||
"inet.af/netaddr"
|
||||
)
|
||||
|
||||
var ErrNotImplemented = errors.New("not implemented for GOOS=" + runtime.GOOS)
|
||||
|
||||
type Entry struct {
|
||||
Local, Remote netaddr.IPPort
|
||||
Pid int
|
||||
State string // TODO: type?
|
||||
}
|
||||
|
||||
// Table contains local machine's TCP connection entries.
|
||||
//
|
||||
// Currently only TCP (IPv4 and IPv6) are included.
|
||||
type Table struct {
|
||||
Entries []Entry
|
||||
}
|
||||
|
||||
// Get returns the connection table.
|
||||
//
|
||||
// It returns ErrNotImplemented if the table is not available for the
|
||||
// current operating system.
|
||||
func Get() (*Table, error) {
|
||||
return get()
|
||||
}
|
||||
11
net/netstat/netstat_noimpl.go
Normal file
11
net/netstat/netstat_noimpl.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows
|
||||
|
||||
package netstat
|
||||
|
||||
func get() (*Table, error) {
|
||||
return nil, ErrNotImplemented
|
||||
}
|
||||
22
net/netstat/netstat_test.go
Normal file
22
net/netstat/netstat_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package netstat
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
nt, err := Get()
|
||||
if err == ErrNotImplemented {
|
||||
t.Skip("TODO: not implemented")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, e := range nt.Entries {
|
||||
t.Logf("Entry: %+v", e)
|
||||
}
|
||||
}
|
||||
178
net/netstat/netstat_windows.go
Normal file
178
net/netstat/netstat_windows.go
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package netstat returns the local machine's network connection table.
|
||||
package netstat
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"inet.af/netaddr"
|
||||
)
|
||||
|
||||
// See https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-getextendedtcptable
|
||||
|
||||
// TCP_TABLE_OWNER_PID_ALL means to include the PID info. The table type
|
||||
// we get back from Windows depends on AF_INET vs AF_INET6:
|
||||
// MIB_TCPTABLE_OWNER_PID for v4 or MIB_TCP6TABLE_OWNER_PID for v6.
|
||||
const tcpTableOwnerPidAll = 5
|
||||
|
||||
var (
|
||||
iphlpapi = syscall.NewLazyDLL("iphlpapi.dll")
|
||||
getTCPTable = iphlpapi.NewProc("GetExtendedTcpTable")
|
||||
// TODO: GetExtendedUdpTable also? if/when needed.
|
||||
)
|
||||
|
||||
type _MIB_TCPROW_OWNER_PID struct {
|
||||
state uint32
|
||||
localAddr uint32
|
||||
localPort uint32
|
||||
remoteAddr uint32
|
||||
remotePort uint32
|
||||
pid uint32
|
||||
}
|
||||
|
||||
type _MIB_TCP6ROW_OWNER_PID struct {
|
||||
localAddr [16]byte
|
||||
localScope uint32
|
||||
localPort uint32
|
||||
remoteAddr [16]byte
|
||||
remoteScope uint32
|
||||
remotePort uint32
|
||||
state uint32
|
||||
pid uint32
|
||||
}
|
||||
|
||||
func get() (*Table, error) {
|
||||
t := new(Table)
|
||||
if err := t.addEntries(windows.AF_INET); err != nil {
|
||||
return nil, fmt.Errorf("failed to get IPv4 entries: %w", err)
|
||||
}
|
||||
if err := t.addEntries(windows.AF_INET6); err != nil {
|
||||
return nil, fmt.Errorf("failed to get IPv6 entries: %w", err)
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func (t *Table) addEntries(fam int) error {
|
||||
var size uint32
|
||||
var addr unsafe.Pointer
|
||||
var buf []byte
|
||||
for {
|
||||
err, _, _ := getTCPTable.Call(
|
||||
uintptr(addr),
|
||||
uintptr(unsafe.Pointer(&size)),
|
||||
1, // sorted
|
||||
uintptr(fam),
|
||||
tcpTableOwnerPidAll,
|
||||
0, // reserved; "must be zero"
|
||||
)
|
||||
if err == 0 {
|
||||
break
|
||||
}
|
||||
if err == uintptr(syscall.ERROR_INSUFFICIENT_BUFFER) {
|
||||
const maxSize = 10 << 20
|
||||
if size > maxSize || size < 4 {
|
||||
return fmt.Errorf("unreasonable kernel-reported size %d", size)
|
||||
}
|
||||
buf = make([]byte, size)
|
||||
addr = unsafe.Pointer(&buf[0])
|
||||
continue
|
||||
}
|
||||
return syscall.Errno(err)
|
||||
}
|
||||
if len(buf) < int(size) {
|
||||
return errors.New("unexpected size growth from system call")
|
||||
}
|
||||
buf = buf[:size]
|
||||
|
||||
numEntries := *(*uint32)(unsafe.Pointer(&buf[0]))
|
||||
buf = buf[4:]
|
||||
|
||||
var recSize int
|
||||
switch fam {
|
||||
case windows.AF_INET:
|
||||
recSize = 6 * 4
|
||||
case windows.AF_INET6:
|
||||
recSize = 6*4 + 16*2
|
||||
}
|
||||
dataLen := numEntries * uint32(recSize)
|
||||
if uint32(len(buf)) > dataLen {
|
||||
buf = buf[:dataLen]
|
||||
}
|
||||
for len(buf) >= recSize {
|
||||
switch fam {
|
||||
case windows.AF_INET:
|
||||
row := (*_MIB_TCPROW_OWNER_PID)(unsafe.Pointer(&buf[0]))
|
||||
t.Entries = append(t.Entries, Entry{
|
||||
Local: ipport4(row.localAddr, port(&row.localPort)),
|
||||
Remote: ipport4(row.remoteAddr, port(&row.remotePort)),
|
||||
Pid: int(row.pid),
|
||||
State: state(row.state),
|
||||
})
|
||||
case windows.AF_INET6:
|
||||
row := (*_MIB_TCP6ROW_OWNER_PID)(unsafe.Pointer(&buf[0]))
|
||||
t.Entries = append(t.Entries, Entry{
|
||||
Local: ipport6(row.localAddr, row.localScope, port(&row.localPort)),
|
||||
Remote: ipport6(row.remoteAddr, row.remoteScope, port(&row.remotePort)),
|
||||
Pid: int(row.pid),
|
||||
State: state(row.state),
|
||||
})
|
||||
}
|
||||
buf = buf[recSize:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var states = []string{
|
||||
"",
|
||||
"CLOSED",
|
||||
"LISTEN",
|
||||
"SYN-SENT",
|
||||
"SYN-RECEIVED",
|
||||
"ESTABLISHED",
|
||||
"FIN-WAIT-1",
|
||||
"FIN-WAIT-2",
|
||||
"CLOSE-WAIT",
|
||||
"CLOSING",
|
||||
"LAST-ACK",
|
||||
"DELETE-TCB",
|
||||
}
|
||||
|
||||
func state(v uint32) string {
|
||||
if v < uint32(len(states)) {
|
||||
return states[v]
|
||||
}
|
||||
return fmt.Sprintf("unknown-state-%d", v)
|
||||
}
|
||||
|
||||
func ipport4(addr uint32, port uint16) netaddr.IPPort {
|
||||
a4 := (*[4]byte)(unsafe.Pointer(&addr))
|
||||
return netaddr.IPPort{
|
||||
IP: netaddr.IPv4(a4[0], a4[1], a4[2], a4[3]),
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
func ipport6(addr [16]byte, scope uint32, port uint16) netaddr.IPPort {
|
||||
ip := netaddr.IPFrom16(addr)
|
||||
if scope != 0 {
|
||||
// TODO: something better here?
|
||||
ip = ip.WithZone(fmt.Sprint(scope))
|
||||
}
|
||||
return netaddr.IPPort{
|
||||
IP: ip,
|
||||
Port: port,
|
||||
}
|
||||
}
|
||||
|
||||
func port(v *uint32) uint16 {
|
||||
p := (*[4]byte)(unsafe.Pointer(v))
|
||||
return binary.BigEndian.Uint16(p[:2])
|
||||
}
|
||||
@@ -30,7 +30,7 @@ if [ $# != 1 ]; then
|
||||
fi
|
||||
|
||||
fail=0
|
||||
for file in $(find $1 -name '*.go'); do
|
||||
for file in $(find $1 -name '*.go' -not -path '*/.git/*'); do
|
||||
case $file in
|
||||
$1/tempfork/*)
|
||||
# Skip, tempfork of third-party code
|
||||
|
||||
58
syncs/locked.go
Normal file
58
syncs/locked.go
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13,!go1.16
|
||||
|
||||
// This file makes assumptions about the inner workings of sync.Mutex and sync.RWMutex.
|
||||
// This includes not just their memory layout but their invariants and functionality.
|
||||
// To prevent accidents, it is limited to a known good subset of Go versions.
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
mutexLocked = 1
|
||||
|
||||
// sync.Mutex field offsets
|
||||
stateOffset = 0
|
||||
|
||||
// sync.RWMutext field offsets
|
||||
mutexOffset = 0
|
||||
readerCountOffset = 16
|
||||
)
|
||||
|
||||
// add returns a pointer with value p + off.
|
||||
func add(p unsafe.Pointer, off uintptr) unsafe.Pointer {
|
||||
return unsafe.Pointer(uintptr(p) + off)
|
||||
}
|
||||
|
||||
// AssertLocked panics if m is not locked.
|
||||
func AssertLocked(m *sync.Mutex) {
|
||||
p := add(unsafe.Pointer(m), stateOffset)
|
||||
if atomic.LoadInt32((*int32)(p))&mutexLocked == 0 {
|
||||
panic("mutex is not locked")
|
||||
}
|
||||
}
|
||||
|
||||
// AssertRLocked panics if rw is not locked for reading or writing.
|
||||
func AssertRLocked(rw *sync.RWMutex) {
|
||||
p := add(unsafe.Pointer(rw), readerCountOffset)
|
||||
if atomic.LoadInt32((*int32)(p)) != 0 {
|
||||
// There are readers present or writers pending, so someone has a read lock.
|
||||
return
|
||||
}
|
||||
// No readers.
|
||||
AssertWLocked(rw)
|
||||
}
|
||||
|
||||
// AssertWLocked panics if rw is not locked for writing.
|
||||
func AssertWLocked(rw *sync.RWMutex) {
|
||||
m := (*sync.Mutex)(add(unsafe.Pointer(rw), mutexOffset))
|
||||
AssertLocked(m)
|
||||
}
|
||||
123
syncs/locked_test.go
Normal file
123
syncs/locked_test.go
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build go1.13,!go1.16
|
||||
|
||||
//lint:file-ignore SA2001 the empty critical sections are part of triggering different internal mutex states
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func wantPanic(t *testing.T, fn func()) {
|
||||
t.Helper()
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
fn()
|
||||
t.Fatal("failed to panic")
|
||||
}
|
||||
|
||||
func TestAssertLocked(t *testing.T) {
|
||||
m := new(sync.Mutex)
|
||||
wantPanic(t, func() { AssertLocked(m) })
|
||||
m.Lock()
|
||||
AssertLocked(m)
|
||||
m.Unlock()
|
||||
wantPanic(t, func() { AssertLocked(m) })
|
||||
// Test correct handling of mutex with waiter.
|
||||
m.Lock()
|
||||
AssertLocked(m)
|
||||
go func() {
|
||||
m.Lock()
|
||||
m.Unlock()
|
||||
}()
|
||||
// Give the goroutine above a few moments to get started.
|
||||
// The test will pass whether or not we win the race,
|
||||
// but we want to run sometimes, to get the test coverage.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
AssertLocked(m)
|
||||
}
|
||||
|
||||
func TestAssertWLocked(t *testing.T) {
|
||||
m := new(sync.RWMutex)
|
||||
wantPanic(t, func() { AssertWLocked(m) })
|
||||
m.Lock()
|
||||
AssertWLocked(m)
|
||||
m.Unlock()
|
||||
wantPanic(t, func() { AssertWLocked(m) })
|
||||
// Test correct handling of mutex with waiter.
|
||||
m.Lock()
|
||||
AssertWLocked(m)
|
||||
go func() {
|
||||
m.Lock()
|
||||
m.Unlock()
|
||||
}()
|
||||
// Give the goroutine above a few moments to get started.
|
||||
// The test will pass whether or not we win the race,
|
||||
// but we want to run sometimes, to get the test coverage.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
AssertWLocked(m)
|
||||
}
|
||||
|
||||
func TestAssertRLocked(t *testing.T) {
|
||||
m := new(sync.RWMutex)
|
||||
wantPanic(t, func() { AssertRLocked(m) })
|
||||
|
||||
m.Lock()
|
||||
AssertRLocked(m)
|
||||
m.Unlock()
|
||||
|
||||
m.RLock()
|
||||
AssertRLocked(m)
|
||||
m.RUnlock()
|
||||
|
||||
wantPanic(t, func() { AssertRLocked(m) })
|
||||
|
||||
// Test correct handling of mutex with waiter.
|
||||
m.RLock()
|
||||
AssertRLocked(m)
|
||||
go func() {
|
||||
m.RLock()
|
||||
m.RUnlock()
|
||||
}()
|
||||
// Give the goroutine above a few moments to get started.
|
||||
// The test will pass whether or not we win the race,
|
||||
// but we want to run sometimes, to get the test coverage.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
AssertRLocked(m)
|
||||
m.RUnlock()
|
||||
|
||||
// Test correct handling of rlock with write waiter.
|
||||
m.RLock()
|
||||
AssertRLocked(m)
|
||||
go func() {
|
||||
m.Lock()
|
||||
m.Unlock()
|
||||
}()
|
||||
// Give the goroutine above a few moments to get started.
|
||||
// The test will pass whether or not we win the race,
|
||||
// but we want to run sometimes, to get the test coverage.
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
AssertRLocked(m)
|
||||
m.RUnlock()
|
||||
|
||||
// Test correct handling of rlock with other rlocks.
|
||||
// This is a bit racy, but losing the race hurts nothing,
|
||||
// and winning the race means correct test coverage.
|
||||
m.RLock()
|
||||
AssertRLocked(m)
|
||||
go func() {
|
||||
m.RLock()
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
m.RUnlock()
|
||||
}()
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
AssertRLocked(m)
|
||||
m.RUnlock()
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package syncs contains addition sync types.
|
||||
// Package syncs contains additional sync types and functionality.
|
||||
package syncs
|
||||
|
||||
import "sync/atomic"
|
||||
|
||||
95
syncs/watchdog.go
Normal file
95
syncs/watchdog.go
Normal file
@@ -0,0 +1,95 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Watch monitors mu for contention.
|
||||
// On first call, and at every tick, Watch locks and unlocks mu.
|
||||
// (Tick should be large to avoid adding contention to mu.)
|
||||
// Max is the maximum length of time Watch will wait to acquire the lock.
|
||||
// The time required to lock mu is sent on the returned channel.
|
||||
// Watch exits when ctx is done, and closes the returned channel.
|
||||
func Watch(ctx context.Context, mu sync.Locker, tick, max time.Duration) chan time.Duration {
|
||||
// Set up the return channel.
|
||||
c := make(chan time.Duration)
|
||||
var (
|
||||
closemu sync.Mutex
|
||||
closed bool
|
||||
)
|
||||
sendc := func(d time.Duration) {
|
||||
closemu.Lock()
|
||||
defer closemu.Unlock()
|
||||
if closed {
|
||||
// Drop values written after c is closed.
|
||||
return
|
||||
}
|
||||
c <- d
|
||||
}
|
||||
closec := func() {
|
||||
closemu.Lock()
|
||||
defer closemu.Unlock()
|
||||
close(c)
|
||||
closed = true
|
||||
}
|
||||
|
||||
// check locks the mutex and writes how long it took to c.
|
||||
// check returns ~immediately.
|
||||
check := func() {
|
||||
// Start a race between two goroutines.
|
||||
// One locks the mutex; the other times out.
|
||||
// Ensure that only one of the two gets to write its result.
|
||||
// Since the common case is that locking the mutex is fast,
|
||||
// let the timeout goroutine exit early when that happens.
|
||||
var sendonce sync.Once
|
||||
done := make(chan bool)
|
||||
go func() {
|
||||
start := time.Now()
|
||||
mu.Lock()
|
||||
mu.Unlock() //lint:ignore SA2001 ignore the empty critical section
|
||||
elapsed := time.Since(start)
|
||||
if elapsed > max {
|
||||
elapsed = max
|
||||
}
|
||||
close(done)
|
||||
sendonce.Do(func() { sendc(elapsed) })
|
||||
}()
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(max):
|
||||
// the other goroutine may not have sent a value
|
||||
sendonce.Do(func() { sendc(max) })
|
||||
case <-done:
|
||||
// the other goroutine sent a value
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Check once at startup.
|
||||
// This is mainly to make testing easier.
|
||||
check()
|
||||
|
||||
// Start the watchdog goroutine.
|
||||
// It checks the mutex every tick, until ctx is done.
|
||||
go func() {
|
||||
ticker := time.NewTicker(tick)
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
closec()
|
||||
ticker.Stop()
|
||||
return
|
||||
case <-ticker.C:
|
||||
check()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
return c
|
||||
}
|
||||
71
syncs/watchdog_test.go
Normal file
71
syncs/watchdog_test.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package syncs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Time-based tests are fundamentally flaky.
|
||||
// We use exaggerated durations in the hopes of minimizing such issues.
|
||||
|
||||
func TestWatchUncontended(t *testing.T) {
|
||||
mu := new(sync.Mutex)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Once an hour, and now, check whether we can lock mu in under an hour.
|
||||
tick := time.Hour
|
||||
max := time.Hour
|
||||
c := Watch(ctx, mu, tick, max)
|
||||
d := <-c
|
||||
if d == max {
|
||||
t.Errorf("uncontended mutex did not lock in under %v", max)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchContended(t *testing.T) {
|
||||
mu := new(sync.Mutex)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
// Every hour, and now, check whether we can lock mu in under a millisecond,
|
||||
// which is enough time for an uncontended mutex by several orders of magnitude.
|
||||
tick := time.Hour
|
||||
max := time.Millisecond
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
c := Watch(ctx, mu, tick, max)
|
||||
d := <-c
|
||||
if d != max {
|
||||
t.Errorf("contended mutex locked in under %v", max)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatchMultipleValues(t *testing.T) {
|
||||
mu := new(sync.Mutex)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel() // not necessary, but keep vet happy
|
||||
// Check the mutex every millisecond.
|
||||
// The goal is to see that we get a sufficient number of values out of the channel.
|
||||
tick := time.Millisecond
|
||||
max := time.Millisecond
|
||||
c := Watch(ctx, mu, tick, max)
|
||||
start := time.Now()
|
||||
n := 0
|
||||
for d := range c {
|
||||
n++
|
||||
if d == max {
|
||||
t.Errorf("uncontended mutex did not lock in under %v", max)
|
||||
}
|
||||
if n == 10 {
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
if elapsed := time.Since(start); elapsed > 100*time.Millisecond {
|
||||
t.Errorf("expected 1 event per millisecond, got only %v events in %v", n, elapsed)
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
package tailcfg
|
||||
|
||||
//go:generate go run tailscale.com/cmd/cloner -type=User,Node,Hostinfo,NetInfo,Group,Role,Capability -output=tailcfg_clone.go
|
||||
//go:generate go run tailscale.com/cmd/cloner -type=User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig -output=tailcfg_clone.go
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
@@ -2,11 +2,15 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability; DO NOT EDIT.
|
||||
// Code generated by tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig; DO NOT EDIT.
|
||||
|
||||
package tailcfg
|
||||
|
||||
import (
|
||||
"github.com/tailscale/wireguard-go/wgcfg"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/types/opt"
|
||||
"tailscale.com/types/structs"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -23,6 +27,19 @@ func (src *User) Clone() *User {
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _UserNeedsRegeneration = User(struct {
|
||||
ID UserID
|
||||
LoginName string
|
||||
DisplayName string
|
||||
ProfilePicURL string
|
||||
Domain string
|
||||
Logins []LoginID
|
||||
Roles []RoleID
|
||||
Created time.Time
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Node.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Node) Clone() *Node {
|
||||
@@ -42,6 +59,27 @@ func (src *Node) Clone() *Node {
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _NodeNeedsRegeneration = Node(struct {
|
||||
ID NodeID
|
||||
Name string
|
||||
User UserID
|
||||
Key NodeKey
|
||||
KeyExpiry time.Time
|
||||
Machine MachineKey
|
||||
DiscoKey DiscoKey
|
||||
Addresses []wgcfg.CIDR
|
||||
AllowedIPs []wgcfg.CIDR
|
||||
Endpoints []string
|
||||
DERP string
|
||||
Hostinfo Hostinfo
|
||||
Created time.Time
|
||||
LastSeen *time.Time
|
||||
KeepAlive bool
|
||||
MachineAuthorized bool
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Hostinfo.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Hostinfo) Clone() *Hostinfo {
|
||||
@@ -57,6 +95,23 @@ func (src *Hostinfo) Clone() *Hostinfo {
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _HostinfoNeedsRegeneration = Hostinfo(struct {
|
||||
IPNVersion string
|
||||
FrontendLogID string
|
||||
BackendLogID string
|
||||
OS string
|
||||
OSVersion string
|
||||
DeviceModel string
|
||||
Hostname string
|
||||
GoArch string
|
||||
RoutableIPs []wgcfg.CIDR
|
||||
RequestTags []string
|
||||
Services []Service
|
||||
NetInfo *NetInfo
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of NetInfo.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *NetInfo) Clone() *NetInfo {
|
||||
@@ -74,6 +129,21 @@ func (src *NetInfo) Clone() *NetInfo {
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _NetInfoNeedsRegeneration = NetInfo(struct {
|
||||
MappingVariesByDestIP opt.Bool
|
||||
HairPinning opt.Bool
|
||||
WorkingIPv6 opt.Bool
|
||||
WorkingUDP opt.Bool
|
||||
UPnP opt.Bool
|
||||
PMP opt.Bool
|
||||
PCP opt.Bool
|
||||
PreferredDERP int
|
||||
LinkType string
|
||||
DERPLatency map[string]float64
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Group.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Group) Clone() *Group {
|
||||
@@ -86,6 +156,14 @@ func (src *Group) Clone() *Group {
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _GroupNeedsRegeneration = Group(struct {
|
||||
ID GroupID
|
||||
Name string
|
||||
Members []ID
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Role.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Role) Clone() *Role {
|
||||
@@ -98,6 +176,14 @@ func (src *Role) Clone() *Role {
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _RoleNeedsRegeneration = Role(struct {
|
||||
ID RoleID
|
||||
Name string
|
||||
Capabilities []CapabilityID
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Capability.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Capability) Clone() *Capability {
|
||||
@@ -108,3 +194,146 @@ func (src *Capability) Clone() *Capability {
|
||||
*dst = *src
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _CapabilityNeedsRegeneration = Capability(struct {
|
||||
ID CapabilityID
|
||||
Type CapType
|
||||
Val ID
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of Login.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *Login) Clone() *Login {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
dst := new(Login)
|
||||
*dst = *src
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _LoginNeedsRegeneration = Login(struct {
|
||||
_ structs.Incomparable
|
||||
ID LoginID
|
||||
Provider string
|
||||
LoginName string
|
||||
DisplayName string
|
||||
ProfilePicURL string
|
||||
Domain string
|
||||
}{})
|
||||
|
||||
// Clone makes a deep copy of DNSConfig.
|
||||
// The result aliases no memory with the original.
|
||||
func (src *DNSConfig) Clone() *DNSConfig {
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
dst := new(DNSConfig)
|
||||
*dst = *src
|
||||
dst.Nameservers = append(src.Nameservers[:0:0], src.Nameservers...)
|
||||
dst.Domains = append(src.Domains[:0:0], src.Domains...)
|
||||
return dst
|
||||
}
|
||||
|
||||
// A compilation failure here means this code must be regenerated, with command:
|
||||
// tailscale.com/cmd/cloner -type User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig
|
||||
var _DNSConfigNeedsRegeneration = DNSConfig(struct {
|
||||
Nameservers []netaddr.IP
|
||||
Domains []string
|
||||
PerDomain bool
|
||||
Proxied bool
|
||||
}{})
|
||||
|
||||
// Clone duplicates src into dst and reports whether it succeeded.
|
||||
// To succeed, <src, dst> must be of types <*T, *T> or <*T, **T>,
|
||||
// where T is one of User,Node,Hostinfo,NetInfo,Group,Role,Capability,Login,DNSConfig.
|
||||
func Clone(dst, src interface{}) bool {
|
||||
switch src := src.(type) {
|
||||
case *User:
|
||||
switch dst := dst.(type) {
|
||||
case *User:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **User:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *Node:
|
||||
switch dst := dst.(type) {
|
||||
case *Node:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **Node:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *Hostinfo:
|
||||
switch dst := dst.(type) {
|
||||
case *Hostinfo:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **Hostinfo:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *NetInfo:
|
||||
switch dst := dst.(type) {
|
||||
case *NetInfo:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **NetInfo:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *Group:
|
||||
switch dst := dst.(type) {
|
||||
case *Group:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **Group:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *Role:
|
||||
switch dst := dst.(type) {
|
||||
case *Role:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **Role:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *Capability:
|
||||
switch dst := dst.(type) {
|
||||
case *Capability:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **Capability:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *Login:
|
||||
switch dst := dst.(type) {
|
||||
case *Login:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **Login:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
case *DNSConfig:
|
||||
switch dst := dst.(type) {
|
||||
case *DNSConfig:
|
||||
*dst = *src.Clone()
|
||||
return true
|
||||
case **DNSConfig:
|
||||
*dst = src.Clone()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
11
tsconst/interface.go
Normal file
11
tsconst/interface.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package tsconst exports some constants used elsewhere in the
|
||||
// codebase.
|
||||
package tsconst
|
||||
|
||||
// WintunInterfaceDesc is the description attached to Tailscale
|
||||
// interfaces on Windows. This is set by our modified WinTun driver.
|
||||
const WintunInterfaceDesc = "Tailscale Tunnel"
|
||||
@@ -45,46 +45,47 @@ func PanicOnLog() {
|
||||
log.SetOutput(panicLogWriter{})
|
||||
}
|
||||
|
||||
// ListenFor produces a LogListener wrapping a given logf with the given logStrings
|
||||
func ListenFor(logf logger.Logf, logStrings []string) *LogListener {
|
||||
ret := LogListener{
|
||||
// NewLogLineTracker produces a LogLineTracker wrapping a given logf that tracks whether expectedFormatStrings were seen.
|
||||
func NewLogLineTracker(logf logger.Logf, expectedFormatStrings []string) *LogLineTracker {
|
||||
ret := &LogLineTracker{
|
||||
logf: logf,
|
||||
listenFor: logStrings,
|
||||
listenFor: expectedFormatStrings,
|
||||
seen: make(map[string]bool),
|
||||
}
|
||||
for _, line := range logStrings {
|
||||
for _, line := range expectedFormatStrings {
|
||||
ret.seen[line] = false
|
||||
}
|
||||
return &ret
|
||||
return ret
|
||||
}
|
||||
|
||||
// LogListener takes a list of log lines to listen for
|
||||
type LogListener struct {
|
||||
// LogLineTracker is a logger that tracks which log format patterns it's
|
||||
// seen and can report which expected ones were not seen later.
|
||||
type LogLineTracker struct {
|
||||
logf logger.Logf
|
||||
listenFor []string
|
||||
|
||||
mu sync.Mutex
|
||||
seen map[string]bool
|
||||
seen map[string]bool // format string => false (if not yet seen but wanted) or true (once seen)
|
||||
}
|
||||
|
||||
// Logf records and logs a given line
|
||||
func (ll *LogListener) Logf(format string, args ...interface{}) {
|
||||
ll.mu.Lock()
|
||||
if _, ok := ll.seen[format]; ok {
|
||||
ll.seen[format] = true
|
||||
// Logf logs to its underlying logger and also tracks that the given format pattern has been seen.
|
||||
func (lt *LogLineTracker) Logf(format string, args ...interface{}) {
|
||||
lt.mu.Lock()
|
||||
if v, ok := lt.seen[format]; ok && !v {
|
||||
lt.seen[format] = true
|
||||
}
|
||||
ll.mu.Unlock()
|
||||
ll.logf(format, args)
|
||||
lt.mu.Unlock()
|
||||
lt.logf(format, args...)
|
||||
}
|
||||
|
||||
// Check returns which lines haven't been logged yet
|
||||
func (ll *LogListener) Check() []string {
|
||||
ll.mu.Lock()
|
||||
defer ll.mu.Unlock()
|
||||
// Check returns which format strings haven't been logged yet.
|
||||
func (lt *LogLineTracker) Check() []string {
|
||||
lt.mu.Lock()
|
||||
defer lt.mu.Unlock()
|
||||
var notSeen []string
|
||||
for _, line := range ll.listenFor {
|
||||
if !ll.seen[line] {
|
||||
notSeen = append(notSeen, line)
|
||||
for _, format := range lt.listenFor {
|
||||
if !lt.seen[format] {
|
||||
notSeen = append(notSeen, format)
|
||||
}
|
||||
}
|
||||
return notSeen
|
||||
|
||||
@@ -4,43 +4,45 @@
|
||||
|
||||
package tstest
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLogListener(t *testing.T) {
|
||||
var (
|
||||
func TestLogLineTracker(t *testing.T) {
|
||||
const (
|
||||
l1 = "line 1: %s"
|
||||
l2 = "line 2: %s"
|
||||
l3 = "line 3: %s"
|
||||
|
||||
lineList = []string{l1, l2}
|
||||
)
|
||||
|
||||
ll := ListenFor(t.Logf, lineList)
|
||||
lt := NewLogLineTracker(t.Logf, []string{l1, l2})
|
||||
|
||||
if len(ll.Check()) != len(lineList) {
|
||||
t.Errorf("expected %v, got %v", lineList, ll.Check())
|
||||
if got, want := lt.Check(), []string{l1, l2}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Check = %q; want %q", got, want)
|
||||
}
|
||||
|
||||
ll.Logf(l3, "hi")
|
||||
lt.Logf(l3, "hi")
|
||||
|
||||
if len(ll.Check()) != len(lineList) {
|
||||
t.Errorf("expected %v, got %v", lineList, ll.Check())
|
||||
if got, want := lt.Check(), []string{l1, l2}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Check = %q; want %q", got, want)
|
||||
}
|
||||
|
||||
ll.Logf(l1, "hi")
|
||||
lt.Logf(l1, "hi")
|
||||
|
||||
if len(ll.Check()) != len(lineList)-1 {
|
||||
t.Errorf("expected %v, got %v", lineList, ll.Check())
|
||||
if got, want := lt.Check(), []string{l2}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Check = %q; want %q", got, want)
|
||||
}
|
||||
|
||||
ll.Logf(l1, "bye")
|
||||
lt.Logf(l1, "bye")
|
||||
|
||||
if len(ll.Check()) != len(lineList)-1 {
|
||||
t.Errorf("expected %v, got %v", lineList, ll.Check())
|
||||
if got, want := lt.Check(), []string{l2}; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Check = %q; want %q", got, want)
|
||||
}
|
||||
|
||||
ll.Logf(l2, "hi")
|
||||
if ll.Check() != nil {
|
||||
t.Errorf("expected empty list, got ll.Check()")
|
||||
lt.Logf(l2, "hi")
|
||||
|
||||
if got, want := lt.Check(), []string(nil); !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("Check = %q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ func goroutineDump() (int, string) {
|
||||
return p.Count(), b.String()
|
||||
}
|
||||
|
||||
func (r *ResourceCheck) Assert(t *testing.T) {
|
||||
func (r *ResourceCheck) Assert(t testing.TB) {
|
||||
t.Helper()
|
||||
want := r.startNumRoutines
|
||||
|
||||
@@ -68,5 +68,4 @@ func (r *ResourceCheck) Assert(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
t.Logf("ResourceCheck ok: goroutines before=%d after=%d\n", got, want)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ package tsweb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
@@ -15,23 +16,23 @@ type response struct {
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
// TODO: Header
|
||||
|
||||
// JSONHandlerFunc only take *http.Request as argument to avoid any misuse of http.ResponseWriter.
|
||||
// The function's results must be (status int, data interface{}, err error).
|
||||
// Return a HTTPError to show an error message, otherwise JSONHandler will only show "internal server error".
|
||||
// JSONHandlerFunc is an HTTP ReturnHandler that writes JSON responses to the client.
|
||||
//
|
||||
// Return a HTTPError to show an error message, otherwise JSONHandlerFunc will
|
||||
// only report "internal server error" to the user.
|
||||
type JSONHandlerFunc func(r *http.Request) (status int, data interface{}, err error)
|
||||
|
||||
// ServeHTTP calls the JSONHandlerFunc and automatically marshals http responses.
|
||||
// ServeHTTPReturn implements the ReturnHandler interface.
|
||||
//
|
||||
// Use the following code to unmarshal the request body
|
||||
//
|
||||
// body := new(DataType)
|
||||
// if err := json.NewDecoder(r.Body).Decode(body); err != nil {
|
||||
// return http.StatusBadRequest, nil, err
|
||||
// }
|
||||
//
|
||||
// Check jsonhandler_text.go for examples
|
||||
func (fn JSONHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
// See jsonhandler_text.go for examples.
|
||||
func (fn JSONHandlerFunc) ServeHTTPReturn(w http.ResponseWriter, r *http.Request) error {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
var resp *response
|
||||
status, data, err := fn(r)
|
||||
@@ -53,6 +54,13 @@ func (fn JSONHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
Error: werr.Msg,
|
||||
Data: data,
|
||||
}
|
||||
// Unwrap the HTTPError here because we are communicating with
|
||||
// the client in this handler. We don't want the wrapping
|
||||
// ReturnHandler to do it too.
|
||||
err = werr.Err
|
||||
if werr.Msg != "" {
|
||||
err = fmt.Errorf("%s: %w", werr.Msg, err)
|
||||
}
|
||||
} else {
|
||||
resp = &response{
|
||||
Status: "error",
|
||||
@@ -61,13 +69,17 @@ func (fn JSONHandlerFunc) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
b, jerr := json.Marshal(resp)
|
||||
if jerr != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte(`{"status":"error","error":"json marshal error"}`))
|
||||
return
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w, and then we could not respond: %v", err, jerr)
|
||||
}
|
||||
return jerr
|
||||
}
|
||||
|
||||
w.WriteHeader(status)
|
||||
w.Write(b)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
t.Run("200 simple", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", nil)
|
||||
h21.ServeHTTP(w, r)
|
||||
h21.ServeHTTPReturn(w, r)
|
||||
checkStatus(w, "success", http.StatusOK)
|
||||
})
|
||||
|
||||
@@ -72,7 +72,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", nil)
|
||||
h.ServeHTTP(w, r)
|
||||
h.ServeHTTPReturn(w, r)
|
||||
checkStatus(w, "error", http.StatusForbidden)
|
||||
})
|
||||
|
||||
@@ -83,7 +83,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
t.Run("200 get data", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("GET", "/", nil)
|
||||
h22.ServeHTTP(w, r)
|
||||
h22.ServeHTTPReturn(w, r)
|
||||
checkStatus(w, "success", http.StatusOK)
|
||||
})
|
||||
|
||||
@@ -102,21 +102,21 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
t.Run("200 post data", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", strings.NewReader(`{"Name": "tailscale"}`))
|
||||
h31.ServeHTTP(w, r)
|
||||
h31.ServeHTTPReturn(w, r)
|
||||
checkStatus(w, "success", http.StatusOK)
|
||||
})
|
||||
|
||||
t.Run("400 bad json", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", strings.NewReader(`{`))
|
||||
h31.ServeHTTP(w, r)
|
||||
h31.ServeHTTPReturn(w, r)
|
||||
checkStatus(w, "error", http.StatusBadRequest)
|
||||
})
|
||||
|
||||
t.Run("400 post data error", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", strings.NewReader(`{}`))
|
||||
h31.ServeHTTP(w, r)
|
||||
h31.ServeHTTPReturn(w, r)
|
||||
resp := checkStatus(w, "error", http.StatusBadRequest)
|
||||
if resp.Error != "name is empty" {
|
||||
t.Fatalf("wrong error")
|
||||
@@ -141,7 +141,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
t.Run("200 post data", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", strings.NewReader(`{"Price": 10}`))
|
||||
h32.ServeHTTP(w, r)
|
||||
h32.ServeHTTPReturn(w, r)
|
||||
resp := checkStatus(w, "success", http.StatusOK)
|
||||
t.Log(resp.Data)
|
||||
if resp.Data.Price != 20 {
|
||||
@@ -152,7 +152,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
t.Run("400 post data error", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", strings.NewReader(`{}`))
|
||||
h32.ServeHTTP(w, r)
|
||||
h32.ServeHTTPReturn(w, r)
|
||||
resp := checkStatus(w, "error", http.StatusBadRequest)
|
||||
if resp.Error != "price is empty" {
|
||||
t.Fatalf("wrong error")
|
||||
@@ -162,7 +162,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
t.Run("500 internal server error", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
r := httptest.NewRequest("POST", "/", strings.NewReader(`{"Name": "root"}`))
|
||||
h32.ServeHTTP(w, r)
|
||||
h32.ServeHTTPReturn(w, r)
|
||||
resp := checkStatus(w, "error", http.StatusInternalServerError)
|
||||
if resp.Error != "internal server error" {
|
||||
t.Fatalf("wrong error")
|
||||
@@ -174,7 +174,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
r := httptest.NewRequest("POST", "/", nil)
|
||||
JSONHandlerFunc(func(r *http.Request) (int, interface{}, error) {
|
||||
return http.StatusOK, make(chan int), nil
|
||||
}).ServeHTTP(w, r)
|
||||
}).ServeHTTPReturn(w, r)
|
||||
resp := checkStatus(w, "error", http.StatusInternalServerError)
|
||||
if resp.Error != "json marshal error" {
|
||||
t.Fatalf("wrong error")
|
||||
@@ -186,7 +186,7 @@ func TestNewJSONHandler(t *testing.T) {
|
||||
r := httptest.NewRequest("POST", "/", nil)
|
||||
JSONHandlerFunc(func(r *http.Request) (status int, data interface{}, err error) {
|
||||
return
|
||||
}).ServeHTTP(w, r)
|
||||
}).ServeHTTPReturn(w, r)
|
||||
checkStatus(w, "error", http.StatusInternalServerError)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -236,8 +236,13 @@ func (h retHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
case hErrOK:
|
||||
// Handler asked us to send an error. Do so, if we haven't
|
||||
// already sent a response.
|
||||
msg.Err = hErr.Msg
|
||||
if hErr.Err != nil {
|
||||
msg.Err = hErr.Err.Error()
|
||||
if msg.Err == "" {
|
||||
msg.Err = hErr.Err.Error()
|
||||
} else {
|
||||
msg.Err = msg.Err + ": " + hErr.Err.Error()
|
||||
}
|
||||
}
|
||||
if lw.code != 0 {
|
||||
h.logf("[unexpected] handler returned HTTPError %v, but already sent a response with code %d", hErr, lw.code)
|
||||
|
||||
@@ -122,7 +122,7 @@ func TestStdHandler(t *testing.T) {
|
||||
Host: "example.com",
|
||||
Method: "GET",
|
||||
RequestURI: "/foo",
|
||||
Err: testErr.Error(),
|
||||
Err: "not found: " + testErr.Error(),
|
||||
Code: 404,
|
||||
},
|
||||
},
|
||||
@@ -139,6 +139,7 @@ func TestStdHandler(t *testing.T) {
|
||||
Host: "example.com",
|
||||
Method: "GET",
|
||||
RequestURI: "/foo",
|
||||
Err: "not found",
|
||||
Code: 404,
|
||||
},
|
||||
},
|
||||
@@ -189,7 +190,7 @@ func TestStdHandler(t *testing.T) {
|
||||
Host: "example.com",
|
||||
Method: "GET",
|
||||
RequestURI: "/foo",
|
||||
Err: testErr.Error(),
|
||||
Err: "not found: " + testErr.Error(),
|
||||
Code: 200,
|
||||
},
|
||||
},
|
||||
|
||||
25
util/pidowner/pidowner.go
Normal file
25
util/pidowner/pidowner.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package pidowner handles lookups from process ID to its owning user.
|
||||
package pidowner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var ErrNotImplemented = errors.New("not implemented for GOOS=" + runtime.GOOS)
|
||||
|
||||
var ErrProcessNotFound = errors.New("process not found")
|
||||
|
||||
// OwnerOfPID returns the user ID that owns the given process ID.
|
||||
//
|
||||
// The returned user ID is suitable to passing to os/user.LookupId.
|
||||
//
|
||||
// The returned error will be ErrNotImplemented for operating systems where
|
||||
// this isn't supported.
|
||||
func OwnerOfPID(pid int) (userID string, err error) {
|
||||
return ownerOfPID(pid)
|
||||
}
|
||||
37
util/pidowner/pidowner_linux.go
Normal file
37
util/pidowner/pidowner_linux.go
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pidowner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"tailscale.com/util/lineread"
|
||||
)
|
||||
|
||||
func ownerOfPID(pid int) (userID string, err error) {
|
||||
file := fmt.Sprintf("/proc/%d/status", pid)
|
||||
err = lineread.File(file, func(line []byte) error {
|
||||
if len(line) < 4 || string(line[:4]) != "Uid:" {
|
||||
return nil
|
||||
}
|
||||
f := strings.Fields(string(line))
|
||||
if len(f) >= 2 {
|
||||
userID = f[1] // real userid
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if os.IsNotExist(err) {
|
||||
return "", ErrProcessNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if userID == "" {
|
||||
return "", fmt.Errorf("missing Uid line in %s", file)
|
||||
}
|
||||
return userID, nil
|
||||
}
|
||||
9
util/pidowner/pidowner_noimpl.go
Normal file
9
util/pidowner/pidowner_noimpl.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !windows,!linux
|
||||
|
||||
package pidowner
|
||||
|
||||
func ownerOfPID(pid int) (userID string, err error) { return "", ErrNotImplemented }
|
||||
51
util/pidowner/pidowner_test.go
Normal file
51
util/pidowner/pidowner_test.go
Normal file
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pidowner
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/user"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestOwnerOfPID(t *testing.T) {
|
||||
id, err := OwnerOfPID(os.Getpid())
|
||||
if err == ErrNotImplemented {
|
||||
t.Skip(err)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Logf("id=%q", id)
|
||||
|
||||
u, err := user.LookupId(id)
|
||||
if err != nil {
|
||||
t.Fatalf("LookupId: %v", err)
|
||||
}
|
||||
t.Logf("Got: %+v", u)
|
||||
}
|
||||
|
||||
// validate that OS implementation returns ErrProcessNotFound.
|
||||
func TestNotFoundError(t *testing.T) {
|
||||
// Try a bunch of times to stumble upon a pid that doesn't exist...
|
||||
const tries = 50
|
||||
for i := 0; i < tries; i++ {
|
||||
_, err := OwnerOfPID(rand.Intn(1e9))
|
||||
if err == ErrNotImplemented {
|
||||
t.Skip(err)
|
||||
}
|
||||
if err == nil {
|
||||
// We got unlucky and this pid existed. Try again.
|
||||
continue
|
||||
}
|
||||
if err == ErrProcessNotFound {
|
||||
// Pass.
|
||||
return
|
||||
}
|
||||
t.Fatalf("Error is not ErrProcessNotFound: %T %v", err, err)
|
||||
}
|
||||
t.Errorf("after %d tries, couldn't find a process that didn't exist", tries)
|
||||
}
|
||||
36
util/pidowner/pidowner_windows.go
Normal file
36
util/pidowner/pidowner_windows.go
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package pidowner
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func ownerOfPID(pid int) (userID string, err error) {
|
||||
procHnd, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION, false, uint32(pid))
|
||||
if err == syscall.Errno(0x57) { // invalid parameter, for PIDs that don't exist
|
||||
return "", ErrProcessNotFound
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("OpenProcess: %T %#v", err, err)
|
||||
}
|
||||
defer windows.CloseHandle(procHnd)
|
||||
|
||||
var tok windows.Token
|
||||
if err := windows.OpenProcessToken(procHnd, windows.TOKEN_QUERY, &tok); err != nil {
|
||||
return "", fmt.Errorf("OpenProcessToken: %w", err)
|
||||
}
|
||||
|
||||
tokUser, err := tok.GetTokenUser()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GetTokenUser: %w", err)
|
||||
}
|
||||
|
||||
sid := tokUser.User.Sid
|
||||
return sid.String(), nil
|
||||
}
|
||||
40
version/distro/distro.go
Normal file
40
version/distro/distro.go
Normal file
@@ -0,0 +1,40 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package distro reports which distro we're running on.
|
||||
package distro
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
type Distro string
|
||||
|
||||
const (
|
||||
Debian = Distro("debian")
|
||||
Arch = Distro("arch")
|
||||
Synology = Distro("synology")
|
||||
)
|
||||
|
||||
// Get returns the current distro, or the empty string if unknown.
|
||||
func Get() Distro {
|
||||
if runtime.GOOS == "linux" {
|
||||
return linuxDistro()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func linuxDistro() Distro {
|
||||
if fi, err := os.Stat("/usr/syno"); err == nil && fi.IsDir() {
|
||||
return Synology
|
||||
}
|
||||
if _, err := os.Stat("/etc/debian_version"); err == nil {
|
||||
return Debian
|
||||
}
|
||||
if _, err := os.Stat("/etc/arch-release"); err == nil {
|
||||
return Arch
|
||||
}
|
||||
return ""
|
||||
}
|
||||
29
wgengine/magicsock/discopingpurpose_string.go
Normal file
29
wgengine/magicsock/discopingpurpose_string.go
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Code generated by "stringer -type=discoPingPurpose -trimprefix=ping"; DO NOT EDIT.
|
||||
|
||||
package magicsock
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[pingDiscovery-0]
|
||||
_ = x[pingHeartbeat-1]
|
||||
_ = x[pingCLI-2]
|
||||
}
|
||||
|
||||
const _discoPingPurpose_name = "DiscoveryHeartbeatCLI"
|
||||
|
||||
var _discoPingPurpose_index = [...]uint8{0, 9, 18, 21}
|
||||
|
||||
func (i discoPingPurpose) String() string {
|
||||
if i < 0 || i >= discoPingPurpose(len(_discoPingPurpose_index)-1) {
|
||||
return "discoPingPurpose(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
return _discoPingPurpose_name[_discoPingPurpose_index[i]:_discoPingPurpose_index[i+1]]
|
||||
}
|
||||
@@ -648,8 +648,8 @@ func (c *Conn) Ping(ip netaddr.IP, cb func(*ipnstate.PingResult)) {
|
||||
}
|
||||
|
||||
dk, ok := c.discoOfNode[peer.Key]
|
||||
if !ok {
|
||||
res.Err = "no discovery key for peer (pre 0.100?)"
|
||||
if !ok { // peer is using outdated Tailscale version (pre-0.100)
|
||||
res.Err = "no discovery key for peer (pre Tailscale 0.100 version?). Try: ping 100.x.y.z"
|
||||
cb(res)
|
||||
return
|
||||
}
|
||||
@@ -3465,6 +3465,7 @@ func (de *discoEndpoint) sendDiscoPing(ep netaddr.IPPort, txid stun.TxID, logLev
|
||||
// discoPingPurpose is the reason why a discovery ping message was sent.
|
||||
type discoPingPurpose int
|
||||
|
||||
//go:generate stringer -type=discoPingPurpose -trimprefix=ping
|
||||
const (
|
||||
// pingDiscovery means that purpose of a ping was to see if a
|
||||
// path was valid.
|
||||
@@ -3817,33 +3818,3 @@ type ippCacheKey struct {
|
||||
|
||||
// derpStr replaces DERP IPs in s with "derp-".
|
||||
func derpStr(s string) string { return strings.ReplaceAll(s, "127.3.3.40:", "derp-") }
|
||||
|
||||
// XXXX temporary hack for demo
|
||||
func (c *Conn) WhoIs(ip netaddr.IP) string {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.netMap == nil || !ip.Is4() {
|
||||
return ""
|
||||
}
|
||||
nodeHasIP := func(n *tailcfg.Node) bool {
|
||||
for _, a := range n.Addresses {
|
||||
if a.Mask == 32 && a.IP.Addr == ip.As16() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
for _, p := range c.netMap.Peers {
|
||||
if nodeHasIP(p) {
|
||||
userProfile, ok := c.netMap.UserProfiles[p.User]
|
||||
if ok {
|
||||
if name := userProfile.DisplayName; name != "" {
|
||||
return name + " from " + p.Hostinfo.Hostname
|
||||
}
|
||||
return userProfile.LoginName + " from " + p.Hostinfo.Hostname
|
||||
}
|
||||
return fmt.Sprintf("%s from %s", ip, p.Hostinfo.Hostname)
|
||||
}
|
||||
}
|
||||
return fmt.Sprintf("mysterious person %v", ip)
|
||||
}
|
||||
|
||||
@@ -783,7 +783,8 @@ func testActiveDiscovery(t *testing.T, d *devices) {
|
||||
|
||||
start := time.Now()
|
||||
logf := func(msg string, args ...interface{}) {
|
||||
msg = fmt.Sprintf("%s: %s", time.Since(start), msg)
|
||||
t.Helper()
|
||||
msg = fmt.Sprintf("%s: %s", time.Since(start).Truncate(time.Microsecond), msg)
|
||||
tlogf(msg, args...)
|
||||
}
|
||||
|
||||
@@ -811,7 +812,8 @@ func testActiveDiscovery(t *testing.T, d *devices) {
|
||||
|
||||
mustDirect := func(m1, m2 *magicStack) {
|
||||
lastLog := time.Now().Add(-time.Minute)
|
||||
for deadline := time.Now().Add(5 * time.Second); time.Now().Before(deadline); time.Sleep(10 * time.Millisecond) {
|
||||
// See https://github.com/tailscale/tailscale/issues/654 for a discussion of this deadline.
|
||||
for deadline := time.Now().Add(10 * time.Second); time.Now().Before(deadline); time.Sleep(10 * time.Millisecond) {
|
||||
pst := m1.Status().Peer[m2.Public()]
|
||||
if pst.CurAddr != "" {
|
||||
logf("direct link %s->%s found with addr %s", m1, m2, pst.CurAddr)
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package netstack wires up gVisor's netstack into Tailscale.
|
||||
package netstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"gvisor.dev/gvisor/pkg/tcpip"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/adapters/gonet"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/buffer"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/header"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/link/channel"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/network/ipv4"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/stack"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/icmp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/tcp"
|
||||
"gvisor.dev/gvisor/pkg/tcpip/transport/udp"
|
||||
"gvisor.dev/gvisor/pkg/waiter"
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/wgengine"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
"tailscale.com/wgengine/packet"
|
||||
"tailscale.com/wgengine/tstun"
|
||||
)
|
||||
|
||||
func Impl(logf logger.Logf, tundev *tstun.TUN, e wgengine.Engine, mc *magicsock.Conn) error {
|
||||
ipstack := stack.New(stack.Options{
|
||||
NetworkProtocols: []stack.NetworkProtocol{ipv4.NewProtocol()},
|
||||
TransportProtocols: []stack.TransportProtocol{tcp.NewProtocol(), udp.NewProtocol(), icmp.NewProtocol4()},
|
||||
})
|
||||
|
||||
const mtu = 1500
|
||||
linkEP := channel.New(512, mtu, "")
|
||||
|
||||
const nicID = 1
|
||||
if err := ipstack.CreateNIC(nicID, linkEP); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
ipstack.AddAddress(nicID, ipv4.ProtocolNumber, tcpip.Address(net.ParseIP("100.96.188.101").To4()))
|
||||
|
||||
// Add 0.0.0.0/0 default route.
|
||||
subnet, _ := tcpip.NewSubnet(tcpip.Address(strings.Repeat("\x00", 4)), tcpip.AddressMask(strings.Repeat("\x00", 4)))
|
||||
ipstack.SetRouteTable([]tcpip.Route{
|
||||
{
|
||||
Destination: subnet,
|
||||
NIC: nicID,
|
||||
},
|
||||
})
|
||||
|
||||
// use Forwarder to accept any connection from stack
|
||||
fwd := tcp.NewForwarder(ipstack, 0, 16, func(r *tcp.ForwarderRequest) {
|
||||
logf("XXX ForwarderRequest: %v", r)
|
||||
var wq waiter.Queue
|
||||
ep, err := r.CreateEndpoint(&wq)
|
||||
if err != nil {
|
||||
r.Complete(true)
|
||||
return
|
||||
}
|
||||
r.Complete(false)
|
||||
c := gonet.NewTCPConn(&wq, ep)
|
||||
// TCP echo
|
||||
go echo(c, e, mc)
|
||||
|
||||
})
|
||||
ipstack.SetTransportProtocolHandler(tcp.ProtocolNumber, fwd.HandlePacket)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
packetInfo, ok := linkEP.ReadContext(context.Background())
|
||||
if !ok {
|
||||
logf("XXX ReadContext-for-write = ok=false")
|
||||
continue
|
||||
}
|
||||
pkt := packetInfo.Pkt
|
||||
hdrNetwork := pkt.NetworkHeader()
|
||||
hdrTransport := pkt.TransportHeader()
|
||||
|
||||
full := make([]byte, 0, pkt.Size())
|
||||
full = append(full, hdrNetwork.View()...)
|
||||
full = append(full, hdrTransport.View()...)
|
||||
full = append(full, pkt.Data.ToView()...)
|
||||
|
||||
logf("XXX packet Write out: % x", full)
|
||||
if err := tundev.InjectOutbound(full); err != nil {
|
||||
log.Printf("netstack inject outbound: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
tundev.PostFilterIn = func(p *packet.ParsedPacket, t *tstun.TUN) filter.Response {
|
||||
var pn tcpip.NetworkProtocolNumber
|
||||
switch p.IPVersion {
|
||||
case 4:
|
||||
pn = header.IPv4ProtocolNumber
|
||||
case 6:
|
||||
pn = header.IPv6ProtocolNumber
|
||||
}
|
||||
logf("XXX packet in (from %v): % x", p.SrcIP, p.Buffer())
|
||||
vv := buffer.View(append([]byte(nil), p.Buffer()...)).ToVectorisedView()
|
||||
packetBuf := stack.NewPacketBuffer(stack.PacketBufferOptions{
|
||||
Data: vv,
|
||||
})
|
||||
linkEP.InjectInbound(pn, packetBuf)
|
||||
return filter.Accept
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func echo(c *gonet.TCPConn, e wgengine.Engine, mc *magicsock.Conn) {
|
||||
defer c.Close()
|
||||
src, _ := netaddr.FromStdIP(c.RemoteAddr().(*net.TCPAddr).IP)
|
||||
fmt.Fprintf(c, "Hello, %s! Thanks for connecting to me on port %v (Try other ports too!)\nEchoing...\n",
|
||||
mc.WhoIs(src),
|
||||
c.LocalAddr().(*net.TCPAddr).Port)
|
||||
buf := make([]byte, 1500)
|
||||
for {
|
||||
n, err := c.Read(buf)
|
||||
if err != nil {
|
||||
log.Printf("Err: %v", err)
|
||||
break
|
||||
}
|
||||
c.Write(buf[:n])
|
||||
}
|
||||
log.Print("Connection closed")
|
||||
}
|
||||
@@ -10,12 +10,18 @@ import (
|
||||
"tailscale.com/types/logger"
|
||||
)
|
||||
|
||||
// We use file-ignore below instead of ignore because on some platforms,
|
||||
// the lint exception is necessary and on others it is not,
|
||||
// and plain ignore complains if the exception is unnecessary.
|
||||
|
||||
//lint:file-ignore U1000 reconfigTimeout is used on some platforms but not others
|
||||
|
||||
// reconfigTimeout is the time interval within which Manager.{Up,Down} should complete.
|
||||
//
|
||||
// This is particularly useful because certain conditions can cause indefinite hangs
|
||||
// (such as improper dbus auth followed by contextless dbus.Object.Call).
|
||||
// Such operations should be wrapped in a timeout context.
|
||||
const reconfigTimeout = time.Second //lint:ignore U1000 used on Linux at least, maybe others later
|
||||
const reconfigTimeout = time.Second
|
||||
|
||||
type managerImpl interface {
|
||||
// Up updates system DNS settings to match the given configuration.
|
||||
|
||||
@@ -7,114 +7,49 @@ package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"sort"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
ole "github.com/go-ole/go-ole"
|
||||
winipcfg "github.com/tailscale/winipcfg-go"
|
||||
"github.com/tailscale/wireguard-go/conn"
|
||||
"github.com/tailscale/wireguard-go/device"
|
||||
"github.com/tailscale/wireguard-go/tun"
|
||||
"golang.org/x/sys/windows"
|
||||
"tailscale.com/net/interfaces"
|
||||
"tailscale.com/wgengine/winnet"
|
||||
)
|
||||
|
||||
const (
|
||||
sockoptIP_UNICAST_IF = 31
|
||||
sockoptIPV6_UNICAST_IF = 31
|
||||
)
|
||||
|
||||
func htonl(val uint32) uint32 {
|
||||
bytes := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(bytes, val)
|
||||
return *(*uint32)(unsafe.Pointer(&bytes[0]))
|
||||
}
|
||||
|
||||
func bindSocketRoute(family winipcfg.AddressFamily, device *device.Device, ourLuid uint64, lastLuid *uint64) error {
|
||||
routes, err := winipcfg.GetRoutes(family)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lowestMetric := ^uint32(0)
|
||||
index := uint32(0) // Zero is "unspecified", which for IP_UNICAST_IF resets the value, which is what we want.
|
||||
luid := uint64(0) // Hopefully luid zero is unspecified, but hard to find docs saying so.
|
||||
for _, route := range routes {
|
||||
if route.DestinationPrefix.PrefixLength != 0 || route.InterfaceLuid == ourLuid {
|
||||
continue
|
||||
}
|
||||
if route.Metric < lowestMetric {
|
||||
lowestMetric = route.Metric
|
||||
index = route.InterfaceIndex
|
||||
luid = route.InterfaceLuid
|
||||
}
|
||||
}
|
||||
if luid == *lastLuid {
|
||||
return nil
|
||||
}
|
||||
*lastLuid = luid
|
||||
if false {
|
||||
bind, ok := device.Bind().(conn.BindSocketToInterface)
|
||||
if !ok {
|
||||
return fmt.Errorf("unexpected device.Bind type %T", device.Bind())
|
||||
}
|
||||
// TODO(apenwarr): doesn't work with magic socket yet.
|
||||
if family == winipcfg.AF_INET {
|
||||
return bind.BindSocketToInterface4(index, false)
|
||||
} else if family == winipcfg.AF_INET6 {
|
||||
return bind.BindSocketToInterface6(index, false)
|
||||
}
|
||||
} else {
|
||||
log.Printf("WARNING: skipping windows socket binding.")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func monitorDefaultRoutes(device *device.Device, autoMTU bool, tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, error) {
|
||||
// monitorDefaultRoutes subscribes to route change events and updates
|
||||
// the Tailscale tunnel interface's MTU to match that of the
|
||||
// underlying default route.
|
||||
//
|
||||
// This is an attempt at making the MTU mostly correct, but in
|
||||
// practice this entire piece of code ends up just using the 1280
|
||||
// value passed in at device construction time. This code might make
|
||||
// the MTU go lower due to very low-MTU IPv4 interfaces.
|
||||
//
|
||||
// TODO: this code is insufficient to control the MTU correctly. The
|
||||
// correct way to do it is per-peer PMTU discovery, and synthesizing
|
||||
// ICMP fragmentation-needed messages within tailscaled. This code may
|
||||
// address a few rare corner cases, but is unlikely to significantly
|
||||
// help with MTU issues compared to a static 1280B implementation.
|
||||
func monitorDefaultRoutes(tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, error) {
|
||||
guid := tun.GUID()
|
||||
ourLuid, err := winipcfg.InterfaceGuidToLuid(&guid)
|
||||
lastLuid4 := uint64(0)
|
||||
lastLuid6 := uint64(0)
|
||||
lastMtu := uint32(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doIt := func() error {
|
||||
err = bindSocketRoute(winipcfg.AF_INET, device, ourLuid, &lastLuid4)
|
||||
mtu, err := getDefaultRouteMTU()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = bindSocketRoute(winipcfg.AF_INET6, device, ourLuid, &lastLuid6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !autoMTU {
|
||||
return nil
|
||||
}
|
||||
mtu := uint32(0)
|
||||
if lastLuid4 != 0 {
|
||||
iface, err := winipcfg.InterfaceFromLUID(lastLuid4)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if iface.Mtu > 0 {
|
||||
mtu = iface.Mtu
|
||||
}
|
||||
}
|
||||
if lastLuid6 != 0 {
|
||||
iface, err := winipcfg.InterfaceFromLUID(lastLuid6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if iface.Mtu > 0 && iface.Mtu < mtu {
|
||||
mtu = iface.Mtu
|
||||
}
|
||||
}
|
||||
|
||||
if mtu > 0 && (lastMtu == 0 || lastMtu != mtu) {
|
||||
iface, err := winipcfg.GetIpInterface(ourLuid, winipcfg.AF_INET)
|
||||
if err != nil {
|
||||
@@ -135,18 +70,21 @@ func monitorDefaultRoutes(device *device.Device, autoMTU bool, tun *tun.NativeTu
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tun.ForceMTU(int(iface.NlMtu)) //TODO: it sort of breaks the model with v6 mtu and v4 mtu being different. Just set v4 one for now.
|
||||
tun.ForceMTU(int(iface.NlMtu))
|
||||
iface, err = winipcfg.GetIpInterface(ourLuid, winipcfg.AF_INET6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
iface.NlMtu = mtu - 80
|
||||
if iface.NlMtu < 1280 {
|
||||
iface.NlMtu = 1280
|
||||
}
|
||||
err = iface.Set()
|
||||
if err != nil {
|
||||
return err
|
||||
if !isMissingIPv6Err(err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
iface.NlMtu = mtu - 80
|
||||
if iface.NlMtu < 1280 {
|
||||
iface.NlMtu = 1280
|
||||
}
|
||||
err = iface.Set()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
lastMtu = mtu
|
||||
}
|
||||
@@ -168,10 +106,67 @@ func monitorDefaultRoutes(device *device.Device, autoMTU bool, tun *tun.NativeTu
|
||||
return cb, nil
|
||||
}
|
||||
|
||||
func setFirewall(ifcGUID *windows.GUID) (bool, error) {
|
||||
c := ole.Connection{}
|
||||
err := c.Initialize()
|
||||
func getDefaultRouteMTU() (uint32, error) {
|
||||
mtus, err := interfaces.NonTailscaleMTUs()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
routes, err := winipcfg.GetRoutes(winipcfg.AF_INET)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
best := ^uint32(0)
|
||||
mtu := uint32(0)
|
||||
for _, route := range routes {
|
||||
if route.DestinationPrefix.PrefixLength != 0 {
|
||||
continue
|
||||
}
|
||||
routeMTU := mtus[route.InterfaceLuid]
|
||||
if routeMTU == 0 {
|
||||
continue
|
||||
}
|
||||
if route.Metric < best {
|
||||
best = route.Metric
|
||||
mtu = routeMTU
|
||||
}
|
||||
}
|
||||
|
||||
routes, err = winipcfg.GetRoutes(winipcfg.AF_INET6)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
best = ^uint32(0)
|
||||
for _, route := range routes {
|
||||
if route.DestinationPrefix.PrefixLength != 0 {
|
||||
continue
|
||||
}
|
||||
routeMTU := mtus[route.InterfaceLuid]
|
||||
if routeMTU == 0 {
|
||||
continue
|
||||
}
|
||||
if route.Metric < best {
|
||||
best = route.Metric
|
||||
if routeMTU < mtu {
|
||||
mtu = routeMTU
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mtu, nil
|
||||
}
|
||||
|
||||
// setPrivateNetwork marks the provided network adapter's category to private.
|
||||
// It returns (false, nil) if the adapter was not found.
|
||||
func setPrivateNetwork(ifcGUID *windows.GUID) (bool, error) {
|
||||
// NLM_NETWORK_CATEGORY values.
|
||||
const (
|
||||
categoryPublic = 0
|
||||
categoryPrivate = 1
|
||||
categoryDomain = 2
|
||||
)
|
||||
var c ole.Connection
|
||||
if err := c.Initialize(); err != nil {
|
||||
return false, fmt.Errorf("c.Initialize: %v", err)
|
||||
}
|
||||
defer c.Uninitialize()
|
||||
@@ -194,10 +189,8 @@ func setFirewall(ifcGUID *windows.GUID) (bool, error) {
|
||||
return false, fmt.Errorf("nco.GetAdapterId: %v", err)
|
||||
}
|
||||
if aid != ifcGUID.String() {
|
||||
log.Printf("skipping adapter id: %v", aid)
|
||||
continue
|
||||
}
|
||||
log.Printf("found! adapter id: %v", aid)
|
||||
|
||||
n, err := nco.GetNetwork()
|
||||
if err != nil {
|
||||
@@ -210,15 +203,11 @@ func setFirewall(ifcGUID *windows.GUID) (bool, error) {
|
||||
return false, fmt.Errorf("GetCategory: %v", err)
|
||||
}
|
||||
|
||||
if cat == 0 {
|
||||
err = n.SetCategory(1)
|
||||
if err != nil {
|
||||
if cat != categoryPrivate {
|
||||
if err := n.SetCategory(categoryPrivate); err != nil {
|
||||
return false, fmt.Errorf("SetCategory: %v", err)
|
||||
}
|
||||
} else {
|
||||
log.Printf("setFirewall: already category %v", cat)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -228,7 +217,6 @@ func setFirewall(ifcGUID *windows.GUID) (bool, error) {
|
||||
func configureInterface(cfg *Config, tun *tun.NativeTun) error {
|
||||
const mtu = 0
|
||||
guid := tun.GUID()
|
||||
log.Printf("wintun GUID is %v", guid)
|
||||
iface, err := winipcfg.InterfaceFromGUID(&guid)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -238,17 +226,20 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) error {
|
||||
// It takes a weirdly long time for Windows to notice the
|
||||
// new interface has come up. Poll periodically until it
|
||||
// does.
|
||||
for i := 0; i < 20; i++ {
|
||||
found, err := setFirewall(&guid)
|
||||
const tries = 20
|
||||
for i := 0; i < tries; i++ {
|
||||
found, err := setPrivateNetwork(&guid)
|
||||
if err != nil {
|
||||
log.Printf("setFirewall: %v", err)
|
||||
// fall through anyway, this isn't fatal.
|
||||
}
|
||||
if found {
|
||||
break
|
||||
log.Printf("setPrivateNetwork(try=%d): %v", i, err)
|
||||
} else {
|
||||
if found {
|
||||
return
|
||||
}
|
||||
log.Printf("setPrivateNetwork(try=%d): not found", i)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
log.Printf("setPrivateNetwork: adapter %v not found after %d tries, giving up", guid, tries)
|
||||
}()
|
||||
|
||||
routes := []winipcfg.RouteData{}
|
||||
@@ -328,7 +319,6 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) error {
|
||||
}
|
||||
deduplicatedRoutes = append(deduplicatedRoutes, &routes[i])
|
||||
}
|
||||
log.Printf("routes: %v", routes)
|
||||
|
||||
var errAcc error
|
||||
err = iface.SyncRoutes(deduplicatedRoutes)
|
||||
@@ -342,7 +332,6 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) error {
|
||||
log.Printf("getipif: %v", err)
|
||||
return err
|
||||
}
|
||||
log.Printf("foundDefault4: %v", foundDefault4)
|
||||
if foundDefault4 {
|
||||
ipif.UseAutomaticMetric = false
|
||||
ipif.Metric = 0
|
||||
@@ -358,28 +347,44 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) error {
|
||||
|
||||
ipif, err = iface.GetIpInterface(winipcfg.AF_INET6)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err != nil && errAcc == nil {
|
||||
errAcc = err
|
||||
}
|
||||
if foundDefault6 {
|
||||
ipif.UseAutomaticMetric = false
|
||||
ipif.Metric = 0
|
||||
}
|
||||
if mtu > 0 {
|
||||
ipif.NlMtu = uint32(mtu)
|
||||
}
|
||||
ipif.DadTransmits = 0
|
||||
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
|
||||
err = ipif.Set()
|
||||
if err != nil && errAcc == nil {
|
||||
errAcc = err
|
||||
if !isMissingIPv6Err(err) {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if foundDefault6 {
|
||||
ipif.UseAutomaticMetric = false
|
||||
ipif.Metric = 0
|
||||
}
|
||||
if mtu > 0 {
|
||||
ipif.NlMtu = uint32(mtu)
|
||||
}
|
||||
ipif.DadTransmits = 0
|
||||
ipif.RouterDiscoveryBehavior = winipcfg.RouterDiscoveryDisabled
|
||||
err = ipif.Set()
|
||||
if err != nil && errAcc == nil {
|
||||
errAcc = err
|
||||
}
|
||||
}
|
||||
|
||||
return errAcc
|
||||
}
|
||||
|
||||
// isMissingIPv6Err reports whether err is due to IPv6 not being enabled on the machine.
|
||||
//
|
||||
// It only currently supports the errors returned by winipcfg.Interface.GetIpInterface.
|
||||
func isMissingIPv6Err(err error) bool {
|
||||
if se, ok := err.(*os.SyscallError); ok {
|
||||
switch se.Syscall {
|
||||
case "iphlpapi.GetIpInterfaceEntry":
|
||||
// ERROR_NOT_FOUND from means the address family (IPv6) is not found.
|
||||
// (ERROR_FILE_NOT_FOUND means that the interface doesn't exist.)
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/netioapi/nf-netioapi-getipinterfaceentry
|
||||
return se.Err == windows.ERROR_NOT_FOUND
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// routeLess reports whether ri should sort before rj.
|
||||
// The actual sort order doesn't appear to matter. The caller just
|
||||
// wants them sorted to be able to de-dup.
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"inet.af/netaddr"
|
||||
"tailscale.com/net/tsaddr"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/version/distro"
|
||||
"tailscale.com/wgengine/router/dns"
|
||||
)
|
||||
|
||||
@@ -172,18 +173,18 @@ func (r *linuxRouter) Set(cfg *Config) error {
|
||||
return err
|
||||
}
|
||||
|
||||
newAddrs, err := cidrDiff("addr", r.addrs, cfg.LocalAddrs, r.addAddress, r.delAddress, r.logf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.addrs = newAddrs
|
||||
|
||||
newRoutes, err := cidrDiff("route", r.routes, cfg.Routes, r.addRoute, r.delRoute, r.logf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.routes = newRoutes
|
||||
|
||||
newAddrs, err := cidrDiff("addr", r.addrs, cfg.LocalAddrs, r.addAddress, r.delAddress, r.logf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.addrs = newAddrs
|
||||
|
||||
switch {
|
||||
case cfg.SNATSubnetRoutes == r.snatSubnetRoutes:
|
||||
// state already correct, nothing to do.
|
||||
@@ -210,6 +211,9 @@ func (r *linuxRouter) Set(cfg *Config) error {
|
||||
// reflect the new mode, and r.snatSubnetRoutes is updated to reflect
|
||||
// the current state of subnet SNATing.
|
||||
func (r *linuxRouter) setNetfilterMode(mode NetfilterMode) error {
|
||||
if distro.Get() == distro.Synology {
|
||||
mode = NetfilterOff
|
||||
}
|
||||
if r.netfilterMode == mode {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,6 +7,9 @@ package router
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
winipcfg "github.com/tailscale/winipcfg-go"
|
||||
"github.com/tailscale/wireguard-go/device"
|
||||
@@ -22,6 +25,9 @@ type winRouter struct {
|
||||
wgdev *device.Device
|
||||
routeChangeCallback *winipcfg.RouteChangeCallback
|
||||
dns *dns.Manager
|
||||
|
||||
mu sync.Mutex
|
||||
firewallRuleIP string // the IP rule exists for, or "" if rule doesn't exist
|
||||
}
|
||||
|
||||
func newUserspaceRouter(logf logger.Logf, wgdev *device.Device, tundev tun.Device) (Router, error) {
|
||||
@@ -47,21 +53,70 @@ func newUserspaceRouter(logf logger.Logf, wgdev *device.Device, tundev tun.Devic
|
||||
}
|
||||
|
||||
func (r *winRouter) Up() error {
|
||||
// MonitorDefaultRoutes handles making sure our wireguard UDP
|
||||
// traffic goes through the old route, not recursively through the VPN.
|
||||
r.removeFirewallAcceptRule()
|
||||
|
||||
var err error
|
||||
r.routeChangeCallback, err = monitorDefaultRoutes(r.wgdev, true, r.nativeTun)
|
||||
r.routeChangeCallback, err = monitorDefaultRoutes(r.nativeTun)
|
||||
if err != nil {
|
||||
log.Fatalf("MonitorDefaultRoutes: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeFirewallAcceptRule removes the "Tailscale-In" firewall rule.
|
||||
//
|
||||
// If it doesn't already exist, this currently returns an error but TODO: it should not.
|
||||
//
|
||||
// So callers should ignore its error for now.
|
||||
func (r *winRouter) removeFirewallAcceptRule() error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
|
||||
r.firewallRuleIP = ""
|
||||
|
||||
cmd := exec.Command("netsh", "advfirewall", "firewall", "delete", "rule", "name=Tailscale-In", "dir=in")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// addFirewallAcceptRule adds a firewall rule to allow all incoming
|
||||
// traffic to the given IP (the Tailscale adapter's IP) for network
|
||||
// adapters in category private. (as previously set by
|
||||
// setPrivateNetwork)
|
||||
//
|
||||
// It returns (false, nil) if the firewall rule was already previously existed with this IP.
|
||||
func (r *winRouter) addFirewallAcceptRule(ipStr string) (added bool, err error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
if ipStr == r.firewallRuleIP {
|
||||
return false, nil
|
||||
}
|
||||
cmd := exec.Command("netsh", "advfirewall", "firewall", "add", "rule", "name=Tailscale-In", "dir=in", "action=allow", "localip="+ipStr, "profile=private", "enable=yes")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
r.firewallRuleIP = ipStr
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *winRouter) Set(cfg *Config) error {
|
||||
if cfg == nil {
|
||||
cfg = &shutdownConfig
|
||||
}
|
||||
|
||||
if len(cfg.LocalAddrs) == 1 && cfg.LocalAddrs[0].Bits == 32 {
|
||||
ipStr := cfg.LocalAddrs[0].IP.String()
|
||||
if ok, err := r.addFirewallAcceptRule(ipStr); err != nil {
|
||||
r.logf("addFirewallRule(%q): %v", ipStr, err)
|
||||
} else if ok {
|
||||
r.logf("added firewall rule Tailscale-In for %v", ipStr)
|
||||
}
|
||||
} else {
|
||||
r.removeFirewallAcceptRule()
|
||||
}
|
||||
|
||||
err := configureInterface(cfg, r.nativeTun)
|
||||
if err != nil {
|
||||
r.logf("ConfigureInterface: %v", err)
|
||||
@@ -76,12 +131,15 @@ func (r *winRouter) Set(cfg *Config) error {
|
||||
}
|
||||
|
||||
func (r *winRouter) Close() error {
|
||||
r.removeFirewallAcceptRule()
|
||||
|
||||
if err := r.dns.Down(); err != nil {
|
||||
return fmt.Errorf("dns down: %w", err)
|
||||
}
|
||||
if r.routeChangeCallback != nil {
|
||||
r.routeChangeCallback.Unregister()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import (
|
||||
"tailscale.com/types/key"
|
||||
"tailscale.com/types/logger"
|
||||
"tailscale.com/version"
|
||||
"tailscale.com/version/distro"
|
||||
"tailscale.com/wgengine/filter"
|
||||
"tailscale.com/wgengine/magicsock"
|
||||
"tailscale.com/wgengine/monitor"
|
||||
@@ -137,13 +138,6 @@ type EngineConfig struct {
|
||||
// Fake determines whether this engine is running in fake mode,
|
||||
// which disables such features as DNS configuration and unrestricted ICMP Echo responses.
|
||||
Fake bool
|
||||
|
||||
// FakeImpl, if non-nil, specifies which type of fake implementation to
|
||||
// use. Two values are typical: nil, for a basic ping-only fake
|
||||
// implementation, and netstack.Impl, which brings in gvisor's netstack
|
||||
// to the binary. The desire to keep that out of some binaries is why
|
||||
// this func exists, so wgengine need not depend on gvisor.
|
||||
FakeImpl FakeImplFunc
|
||||
}
|
||||
|
||||
type Loggify struct {
|
||||
@@ -155,9 +149,7 @@ func (l *Loggify) Write(b []byte) (int, error) {
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
type FakeImplFunc func(logger.Logf, *tstun.TUN, Engine, *magicsock.Conn) error
|
||||
|
||||
func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16, impl FakeImplFunc) (Engine, error) {
|
||||
func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16) (Engine, error) {
|
||||
logf("Starting userspace wireguard engine (FAKE tuntap device).")
|
||||
conf := EngineConfig{
|
||||
Logf: logf,
|
||||
@@ -165,7 +157,6 @@ func NewFakeUserspaceEngine(logf logger.Logf, listenPort uint16, impl FakeImplFu
|
||||
RouterGen: router.NewFake,
|
||||
ListenPort: listenPort,
|
||||
Fake: true,
|
||||
FakeImpl: impl,
|
||||
}
|
||||
return NewUserspaceEngineAdvanced(conf)
|
||||
}
|
||||
@@ -227,6 +218,12 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
|
||||
e.linkState, _ = getLinkState()
|
||||
logf("link state: %+v", e.linkState)
|
||||
|
||||
// Respond to all pings only in fake mode.
|
||||
if conf.Fake {
|
||||
e.tundev.PostFilterIn = echoRespondToAll
|
||||
}
|
||||
e.tundev.PreFilterOut = e.handleLocalPackets
|
||||
|
||||
mon, err := monitor.New(logf, func() { e.LinkChange(false) })
|
||||
if err != nil {
|
||||
e.tundev.Close()
|
||||
@@ -255,18 +252,6 @@ func newUserspaceEngineAdvanced(conf EngineConfig) (_ Engine, reterr error) {
|
||||
return nil, fmt.Errorf("wgengine: %v", err)
|
||||
}
|
||||
|
||||
if conf.Fake {
|
||||
if impl := conf.FakeImpl; impl != nil {
|
||||
if err := impl(logf, e.tundev, e, e.magicConn); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
// Respond to all pings only in fake mode.
|
||||
e.tundev.PostFilterIn = echoRespondToAll
|
||||
}
|
||||
}
|
||||
e.tundev.PreFilterOut = e.handleLocalPackets
|
||||
|
||||
// flags==0 because logf is already nested in another logger.
|
||||
// The outer one can display the preferred log prefixes, etc.
|
||||
dlog := log.New(&Loggify{logf}, "", 0)
|
||||
@@ -1260,9 +1245,8 @@ func diagnoseLinuxTUNFailure(logf logger.Logf) {
|
||||
}
|
||||
logf("is CONFIG_TUN enabled in your kernel? `modprobe tun` failed with: %s", modprobeOut)
|
||||
|
||||
distro := linuxDistro()
|
||||
switch distro {
|
||||
case "debian":
|
||||
switch distro.Get() {
|
||||
case distro.Debian:
|
||||
dpkgOut, err := exec.Command("dpkg", "-S", "kernel/drivers/net/tun.ko").CombinedOutput()
|
||||
if len(bytes.TrimSpace(dpkgOut)) == 0 || err != nil {
|
||||
logf("tun module not loaded nor found on disk")
|
||||
@@ -1271,7 +1255,7 @@ func diagnoseLinuxTUNFailure(logf logger.Logf) {
|
||||
if !bytes.Contains(dpkgOut, kernel) {
|
||||
logf("kernel/drivers/net/tun.ko found on disk, but not for current kernel; are you in middle of a system update and haven't rebooted? found: %s", dpkgOut)
|
||||
}
|
||||
case "arch":
|
||||
case distro.Arch:
|
||||
findOut, err := exec.Command("find", "/lib/modules/", "-path", "*/net/tun.ko*").CombinedOutput()
|
||||
if len(bytes.TrimSpace(findOut)) == 0 || err != nil {
|
||||
logf("tun module not loaded nor found on disk")
|
||||
@@ -1282,13 +1266,3 @@ func diagnoseLinuxTUNFailure(logf logger.Logf) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func linuxDistro() string {
|
||||
if _, err := os.Stat("/etc/debian_version"); err == nil {
|
||||
return "debian"
|
||||
}
|
||||
if _, err := os.Stat("/etc/arch-release"); err == nil {
|
||||
return "arch"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user