Merge branch 'main' into report-status-systemd

pull/967/head
Christine Dodrill 4 years ago committed by GitHub
commit 2485faf69a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

2
.gitignore vendored

@ -6,8 +6,6 @@
*.so *.so
*.dylib *.dylib
cmd/relaynode/relaynode
cmd/taillogin/taillogin
cmd/tailscale/tailscale cmd/tailscale/tailscale
cmd/tailscaled/tailscaled cmd/tailscaled/tailscaled

@ -169,6 +169,9 @@ func runStatus(ctx context.Context, args []string) error {
} }
for _, peer := range st.Peers() { for _, peer := range st.Peers() {
ps := st.Peer[peer] ps := st.Peer[peer]
if ps.ShareeNode {
continue
}
active := peerActive(ps) active := peerActive(ps)
if statusArgs.active && !active { if statusArgs.active && !active {
continue continue

@ -28,11 +28,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
github.com/tailscale/wireguard-go/tai64n 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+ 💣 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 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/tailscale/wireguard-go/wgcfg from github.com/tailscale/wireguard-go/conn+
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
@ -91,7 +86,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/wgengine/tstun from tailscale.com/wgengine tailscale.com/wgengine/tstun from tailscale.com/wgengine
W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router 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/blake2b from golang.org/x/crypto/nacl/box
golang.org/x/crypto/blake2s from github.com/tailscale/wireguard-go/device+ 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/chacha20 from golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/chacha20poly1305 from crypto/tls+ golang.org/x/crypto/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+ golang.org/x/crypto/cryptobyte from crypto/ecdsa+
@ -120,11 +115,11 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+ golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+
LD golang.org/x/sys/unix from github.com/jsimonetti/rtnetlink/internal/unix+ 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 from github.com/apenwarr/fixconsole+
W golang.org/x/sys/windows/registry from github.com/tailscale/wireguard-go/tun/wintun+ W golang.org/x/sys/windows/registry from golang.zx2c4.com/wireguard/windows/tunnel/winipcfg+
golang.org/x/text/secure/bidirule from golang.org/x/net/idna golang.org/x/text/secure/bidirule from golang.org/x/net/idna
golang.org/x/text/transform from golang.org/x/text/secure/bidirule+ golang.org/x/text/transform from golang.org/x/text/secure/bidirule+
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+ golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
golang.org/x/text/unicode/norm from github.com/tailscale/wireguard-go/tun/wintun+ golang.org/x/text/unicode/norm from golang.org/x/net/idna
golang.org/x/time/rate from tailscale.com/types/logger+ golang.org/x/time/rate from tailscale.com/types/logger+
bufio from compress/flate+ bufio from compress/flate+
bytes from bufio+ bytes from bufio+
@ -199,6 +194,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
reflect from crypto/x509+ reflect from crypto/x509+
regexp from github.com/coreos/go-iptables/iptables+ regexp from github.com/coreos/go-iptables/iptables+
regexp/syntax from regexp regexp/syntax from regexp
runtime/debug from golang.org/x/sync/singleflight
runtime/pprof from tailscale.com/log/logheap+ runtime/pprof from tailscale.com/log/logheap+
sort from compress/flate+ sort from compress/flate+
strconv from compress/flate+ strconv from compress/flate+

@ -31,11 +31,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
github.com/tailscale/wireguard-go/tai64n 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+ 💣 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 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/tailscale/wireguard-go/wgcfg from github.com/tailscale/wireguard-go/conn+
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
💣 go4.org/mem from tailscale.com/control/controlclient+ 💣 go4.org/mem from tailscale.com/control/controlclient+
@ -101,7 +96,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/wgengine/tstun from tailscale.com/wgengine tailscale.com/wgengine/tstun from tailscale.com/wgengine
W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router 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/blake2b from golang.org/x/crypto/nacl/box
golang.org/x/crypto/blake2s from github.com/tailscale/wireguard-go/device+ 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/chacha20 from golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/chacha20poly1305 from crypto/tls+ golang.org/x/crypto/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+ golang.org/x/crypto/cryptobyte from crypto/ecdsa+
@ -112,7 +107,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
golang.org/x/crypto/nacl/secretbox from golang.org/x/crypto/nacl/box 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/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/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/bpf from github.com/mdlayher/netlink+
golang.org/x/net/context/ctxhttp from golang.org/x/oauth2/internal golang.org/x/net/context/ctxhttp from golang.org/x/oauth2/internal
golang.org/x/net/dns/dnsmessage from net+ golang.org/x/net/dns/dnsmessage from net+
@ -131,11 +125,12 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+ golang.org/x/sys/cpu from golang.org/x/crypto/blake2b+
LD golang.org/x/sys/unix from github.com/jsimonetti/rtnetlink/internal/unix+ 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 from github.com/apenwarr/fixconsole+
W golang.org/x/sys/windows/registry from github.com/tailscale/wireguard-go/tun/wintun+ W golang.org/x/sys/windows/registry from golang.zx2c4.com/wireguard/windows/tunnel/winipcfg+
golang.org/x/term from tailscale.com/logpolicy
golang.org/x/text/secure/bidirule from golang.org/x/net/idna golang.org/x/text/secure/bidirule from golang.org/x/net/idna
golang.org/x/text/transform from golang.org/x/text/secure/bidirule+ golang.org/x/text/transform from golang.org/x/text/secure/bidirule+
golang.org/x/text/unicode/bidi from golang.org/x/net/idna+ golang.org/x/text/unicode/bidi from golang.org/x/net/idna+
golang.org/x/text/unicode/norm from github.com/tailscale/wireguard-go/tun/wintun+ golang.org/x/text/unicode/norm from golang.org/x/net/idna
golang.org/x/time/rate from tailscale.com/types/logger+ golang.org/x/time/rate from tailscale.com/types/logger+
bufio from compress/flate+ bufio from compress/flate+
bytes from bufio+ bytes from bufio+

@ -268,11 +268,13 @@ func (c *Client) authRoutine() {
for { for {
c.mu.Lock() c.mu.Lock()
c.logf("authRoutine: %s", c.state)
expiry := c.expiry
goal := c.loginGoal goal := c.loginGoal
ctx := c.authCtx ctx := c.authCtx
synced := c.synced if goal != nil {
c.logf("authRoutine: %s; wantLoggedIn=%v", c.state, goal.wantLoggedIn)
} else {
c.logf("authRoutine: %s; goal=nil", c.state)
}
c.mu.Unlock() c.mu.Unlock()
select { select {
@ -293,51 +295,13 @@ func (c *Client) authRoutine() {
} }
if goal == nil { if goal == nil {
// Wait for something interesting to happen // Wait for user to Login or Logout.
var exp <-chan time.Time <-ctx.Done()
var expTimer *time.Timer
if expiry != nil && !expiry.IsZero() {
// if expiry is in the future, don't delay
// past that time.
// If it's in the past, then it's already
// being handled by someone, so no need to
// wake ourselves up again.
now := c.timeNow()
if expiry.Before(now) {
delay := expiry.Sub(now)
if delay > 5*time.Second {
delay = time.Second
}
expTimer = time.NewTimer(delay)
exp = expTimer.C
}
}
select {
case <-ctx.Done():
if expTimer != nil {
expTimer.Stop()
}
c.logf("authRoutine: context done.") c.logf("authRoutine: context done.")
case <-exp: continue
// Unfortunately the key expiry isn't provided
// by the control server until mapRequest.
// So we have to do some hackery with c.expiry
// in here.
// TODO(apenwarr): add a key expiry field in RegisterResponse.
c.logf("authRoutine: key expiration check.")
if synced && expiry != nil && !expiry.IsZero() && expiry.Before(c.timeNow()) {
c.logf("Key expired; setting loggedIn=false.")
c.mu.Lock()
c.loginGoal = &LoginGoal{
wantLoggedIn: c.loggedIn,
}
c.loggedIn = false
c.expiry = nil
c.mu.Unlock()
}
} }
} else if !goal.wantLoggedIn {
if !goal.wantLoggedIn {
err := c.direct.TryLogout(ctx) err := c.direct.TryLogout(ctx)
if err != nil { if err != nil {
report(err, "TryLogout") report(err, "TryLogout")

@ -46,6 +46,7 @@ import (
"tailscale.com/types/structs" "tailscale.com/types/structs"
"tailscale.com/util/systemd" "tailscale.com/util/systemd"
"tailscale.com/version" "tailscale.com/version"
"tailscale.com/wgengine/filter"
) )
type Persist struct { type Persist struct {
@ -544,7 +545,7 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM
} }
request := tailcfg.MapRequest{ request := tailcfg.MapRequest{
Version: 5, Version: 6,
KeepAlive: c.keepAlive, KeepAlive: c.keepAlive,
NodeKey: tailcfg.NodeKey(persist.PrivateNodeKey.Public()), NodeKey: tailcfg.NodeKey(persist.PrivateNodeKey.Public()),
DiscoKey: c.discoPubKey, DiscoKey: c.discoPubKey,
@ -639,6 +640,7 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM
var lastDERPMap *tailcfg.DERPMap var lastDERPMap *tailcfg.DERPMap
var lastUserProfile = map[tailcfg.UserID]tailcfg.UserProfile{} var lastUserProfile = map[tailcfg.UserID]tailcfg.UserProfile{}
var lastParsedPacketFilter []filter.Match
// If allowStream, then the server will use an HTTP long poll to // If allowStream, then the server will use an HTTP long poll to
// return incremental results. There is always one response right // return incremental results. There is always one response right
@ -716,6 +718,10 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM
resp.Peers = filtered resp.Peers = filtered
} }
if pf := resp.PacketFilter; pf != nil {
lastParsedPacketFilter = c.parsePacketFilter(pf)
}
nm := &NetworkMap{ nm := &NetworkMap{
NodeKey: tailcfg.NodeKey(persist.PrivateNodeKey.Public()), NodeKey: tailcfg.NodeKey(persist.PrivateNodeKey.Public()),
PrivateKey: persist.PrivateNodeKey, PrivateKey: persist.PrivateNodeKey,
@ -730,7 +736,7 @@ func (c *Direct) PollNetMap(ctx context.Context, maxPolls int, cb func(*NetworkM
Domain: resp.Domain, Domain: resp.Domain,
DNS: resp.DNSConfig, DNS: resp.DNSConfig,
Hostinfo: resp.Node.Hostinfo, Hostinfo: resp.Node.Hostinfo,
PacketFilter: c.parsePacketFilter(resp.PacketFilter), PacketFilter: lastParsedPacketFilter,
DERPMap: lastDERPMap, DERPMap: lastDERPMap,
Debug: resp.Debug, Debug: resp.Debug,
} }

@ -1,6 +1,6 @@
module tailscale.com module tailscale.com
go 1.14 go 1.15
require ( require (
github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5 github.com/alexbrainman/sspi v0.0.0-20180613141037-e580b900e9f5
@ -23,20 +23,22 @@ require (
github.com/miekg/dns v1.1.30 github.com/miekg/dns v1.1.30
github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3 github.com/pborman/getopt v0.0.0-20190409184431-ee0cd42419d3
github.com/peterbourgon/ff/v2 v2.0.0 github.com/peterbourgon/ff/v2 v2.0.0
github.com/tailscale/depaware v0.0.0-20201003033024-5d95aab075be github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027
github.com/tailscale/wireguard-go v0.0.0-20201021041318-a6168fd06b3f github.com/tailscale/wireguard-go v0.0.0-20201210001956-32a957fb6709
github.com/tcnksm/go-httpstat v0.2.0 github.com/tcnksm/go-httpstat v0.2.0
github.com/toqueteos/webbrowser v1.2.0 github.com/toqueteos/webbrowser v1.2.0
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 go4.org/mem v0.0.0-20201119185036-c04c5a6ff174
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392
golang.org/x/mod v0.4.0 // indirect
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b golang.org/x/net v0.0.0-20201110031124-69a78807bb2b
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d 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-20201020160332-67f06af15bc9
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd golang.org/x/sys v0.0.0-20201211002650-1f0c578a6b29
golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b
golang.org/x/time v0.0.0-20191024005414-555d28b269f0 golang.org/x/time v0.0.0-20191024005414-555d28b269f0
golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58
golang.zx2c4.com/wireguard/windows v0.1.2-0.20201113162609-9b85be97fdf8 golang.zx2c4.com/wireguard/windows v0.1.2-0.20201113162609-9b85be97fdf8
honnef.co/go/tools v0.0.1-2020.1.4 honnef.co/go/tools v0.1.0
inet.af/netaddr v0.0.0-20200810144936-56928fe48a98 inet.af/netaddr v0.0.0-20200810144936-56928fe48a98
rsc.io/goversion v1.2.0 rsc.io/goversion v1.2.0
) )

@ -99,6 +99,10 @@ github.com/tailscale/depaware v0.0.0-20201003033024-5d95aab075be h1:ZKe3kVGbu/go
github.com/tailscale/depaware v0.0.0-20201003033024-5d95aab075be/go.mod h1:jissDaJNHiyV2tFdr3QyNEfsZrax/i2yQiSO+CljThI= github.com/tailscale/depaware v0.0.0-20201003033024-5d95aab075be/go.mod h1:jissDaJNHiyV2tFdr3QyNEfsZrax/i2yQiSO+CljThI=
github.com/tailscale/wireguard-go v0.0.0-20201021041318-a6168fd06b3f h1:KMx58dbn2YCutzOvjNHgmvbwQH7nGE8H+J42Nenjl/M= github.com/tailscale/wireguard-go v0.0.0-20201021041318-a6168fd06b3f h1:KMx58dbn2YCutzOvjNHgmvbwQH7nGE8H+J42Nenjl/M=
github.com/tailscale/wireguard-go v0.0.0-20201021041318-a6168fd06b3f/go.mod h1:WXq+IkSOJGIgfF1XW+4z4oW+LX/TXzU9DcKlT5EZLi4= github.com/tailscale/wireguard-go v0.0.0-20201021041318-a6168fd06b3f/go.mod h1:WXq+IkSOJGIgfF1XW+4z4oW+LX/TXzU9DcKlT5EZLi4=
github.com/tailscale/depaware v0.0.0-20201210233412-71b54857b5d9 h1:IquU2Mhy4Q+xUYoRLHMiEamd80OShhYXhTNywEbQu3g=
github.com/tailscale/depaware v0.0.0-20201210233412-71b54857b5d9/go.mod h1:jissDaJNHiyV2tFdr3QyNEfsZrax/i2yQiSO+CljThI=
github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027 h1:lK99QQdH3yBWY6aGilF+IRlQIdmhzLrsEmF6JgN+Ryw=
github.com/tailscale/depaware v0.0.0-20201214215404-77d1e9757027/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=
github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0= github.com/tcnksm/go-httpstat v0.2.0 h1:rP7T5e5U2HfmOBmZzGgGZjBQ5/GluWUylujl0tJ04I0=
github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8= github.com/tcnksm/go-httpstat v0.2.0/go.mod h1:s3JVJFtQxtBEBC9dwcdTTXS9xFnM3SXAZwPG41aurT8=
github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ= github.com/toqueteos/webbrowser v1.2.0 h1:tVP/gpK69Fx+qMJKsLE7TD8LuGWPnEV71wBN9rrstGQ=
@ -107,23 +111,26 @@ github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8= github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo= 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/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 h1:vSug/WNOi2+4jrKdivxayTN/zd8EA1UrStjpWvvo1jk= go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 h1:vSug/WNOi2+4jrKdivxayTN/zd8EA1UrStjpWvvo1jk=
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g= go4.org/mem v0.0.0-20201119185036-c04c5a6ff174/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 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-20191002192127-34f69633bfdc/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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
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 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392 h1:xYJJ3S178yv++9zXV/hnr29plCAGO9vAFG9dorqaFQc=
golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= 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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0 h1:8pl+sMODzuvGJkmj2W4kZihvVb5mKm8pB/X44PIQHv8=
golang.org/x/mod v0.4.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-20180724234803-3673e40ba225/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-20190108225652-1e06a53dbb7e/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -136,6 +143,7 @@ golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@ -143,8 +151,11 @@ golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BG
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
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 h1:qwRHBd0NqMbJxfbotnDhm2ByMI1Shq4Y6oRJo21SGJA=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190312061237-fead79001313/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-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -153,6 +164,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/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-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -161,8 +173,15 @@ golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201107080550-4d91cf3a1aaf/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201202213521-69691e467435 h1:25AvDqqB9PrNqj1FLf2/70I4W0L19qqoaFq3gjNwbKk=
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201211002650-1f0c578a6b29 h1:hAYi5mzhvBeCfkgaIHGZ8R+Q04WjSW5ZvQO3BZ94dHY=
golang.org/x/sys v0.0.0-20201211002650-1f0c578a6b29/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b h1:a0ErnNnPKmhDyIXQvdZr+Lq8dc8xpMeqkF8y5PgQU4Q=
golang.org/x/term v0.0.0-20201207232118-ee85cb95a76b/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
@ -171,11 +190,11 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqG
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/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-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200609164405-eb789aa7ce50/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20201001230009-b5b87423c93b/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201001230009-b5b87423c93b/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d h1:vWQvJ/Z0Lu+9/8oQ/pAYXNzbc7CMnBl+tULGVHOy3oE= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58 h1:1Bs6RVeBFtLZ8Yi1Hk07DiOqzvwLD/4hln4iahvFlag=
golang.org/x/tools v0.0.0-20201002184944-ecd9fd270d5d/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -196,8 +215,8 @@ 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.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 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.1-2020.1.4 h1:UoveltGrhghAA7ePc+e+QYDHXrBps2PqFZiHkGR/xK8= honnef.co/go/tools v0.1.0 h1:AWNL1W1i7f0wNZ8VwOKNJ0sliKvOF/adn0EHenfUh+c=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.1.0/go.mod h1:XtegFAyX/PfluP4921rXU5IkjkqBCDnUq4W8VCIoKvM=
inet.af/netaddr v0.0.0-20200810144936-56928fe48a98 h1:bWyWDZP0l6VnQ1TDKf6yNwuiEDV6Q3q1Mv34m+lzT1I= 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= inet.af/netaddr v0.0.0-20200810144936-56928fe48a98/go.mod h1:qqYzz/2whtrbWJvt+DNWQyvekNN4ePQZcg2xc2/Yjww=
rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w= rsc.io/goversion v1.2.0 h1:SPn+NLTiAG7w30IRK/DKp1BjvpWabYgxlLp/+kx5J8w=

@ -64,6 +64,12 @@ type PeerStatus struct {
LastHandshake time.Time // with local wireguard LastHandshake time.Time // with local wireguard
KeepAlive bool KeepAlive bool
// ShareeNode indicates this node exists in the netmap because
// it's owned by a shared-to user and that node might connect
// to us. These nodes should be hidden by "tailscale status"
// etc by default.
ShareeNode bool `json:",omitempty"`
// InNetworkMap means that this peer was seen in our latest network map. // InNetworkMap means that this peer was seen in our latest network map.
// In theory, all of InNetworkMap and InMagicSock and InEngine should all be true. // In theory, all of InNetworkMap and InMagicSock and InEngine should all be true.
InNetworkMap bool InNetworkMap bool
@ -218,6 +224,9 @@ func (sb *StatusBuilder) AddPeer(peer key.Public, st *PeerStatus) {
if st.KeepAlive { if st.KeepAlive {
e.KeepAlive = true e.KeepAlive = true
} }
if st.ShareeNode {
e.ShareeNode = true
}
} }
type StatusUpdater interface { type StatusUpdater interface {

@ -222,6 +222,7 @@ func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
KeepAlive: p.KeepAlive, KeepAlive: p.KeepAlive,
Created: p.Created, Created: p.Created,
LastSeen: lastSeen, LastSeen: lastSeen,
ShareeNode: p.Hostinfo.ShareeNode,
}) })
} }
} }
@ -1257,7 +1258,7 @@ func routerConfig(cfg *wgcfg.Config, prefs *Prefs) *router.Config {
for _, addr := range cfg.Addresses { for _, addr := range cfg.Addresses {
addrs = append(addrs, wgcfg.CIDR{ addrs = append(addrs, wgcfg.CIDR{
IP: addr.IP, IP: addr.IP,
Mask: 32, Mask: addr.Mask,
}) })
} }
@ -1561,7 +1562,6 @@ func (b *LocalBackend) TestOnlyPublicKeys() (machineKey tailcfg.MachineKey, node
// 1.0.x. But eventually we want to stop sending the machine key to // 1.0.x. But eventually we want to stop sending the machine key to
// clients. We can't do that until 1.0.x is no longer supported. // clients. We can't do that until 1.0.x is no longer supported.
func temporarilySetMachineKeyInPersist() bool { func temporarilySetMachineKeyInPersist() bool {
//lint:ignore S1008 for comments
switch runtime.GOOS { switch runtime.GOOS {
case "darwin", "ios", "android": case "darwin", "ios", "android":
// iOS, macOS, Android users can't downgrade anyway. // iOS, macOS, Android users can't downgrade anyway.

@ -5,6 +5,7 @@
package ipn package ipn
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
@ -276,6 +277,14 @@ func LoadPrefs(filename string) (*Prefs, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("LoadPrefs open: %w", err) // err includes path return nil, fmt.Errorf("LoadPrefs open: %w", err) // err includes path
} }
if bytes.Contains(data, jsonEscapedZero) {
// Tailscale 1.2.0 - 1.2.8 on Windows had a memory corruption bug
// in the backend process that ended up sending NULL bytes over JSON
// to the frontend which wrote them out to JSON files on disk.
// So if we see one, treat is as corrupt and the user will need
// to log in again. (better than crashing)
return nil, os.ErrNotExist
}
p, err := PrefsFromBytes(data, false) p, err := PrefsFromBytes(data, false)
if err != nil { if err != nil {
return nil, fmt.Errorf("LoadPrefs(%q) decode: %w", filename, err) return nil, fmt.Errorf("LoadPrefs(%q) decode: %w", filename, err)

@ -7,6 +7,7 @@ package ipn
import ( import (
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@ -371,3 +372,25 @@ func TestLoadPrefsNotExist(t *testing.T) {
} }
t.Fatalf("unexpected prefs=%#v, err=%v", p, err) t.Fatalf("unexpected prefs=%#v, err=%v", p, err)
} }
// TestLoadPrefsFileWithZeroInIt verifies that LoadPrefs hanldes corrupted input files.
// See issue #954 for details.
func TestLoadPrefsFileWithZeroInIt(t *testing.T) {
f, err := ioutil.TempFile("", "TestLoadPrefsFileWithZeroInIt")
if err != nil {
t.Fatal(err)
}
path := f.Name()
if _, err := f.Write(jsonEscapedZero); err != nil {
t.Fatal(err)
}
f.Close()
defer os.Remove(path)
p, err := LoadPrefs(path)
if errors.Is(err, os.ErrNotExist) {
// expected.
return
}
t.Fatalf("unexpected prefs=%#v, err=%v", p, err)
}

@ -25,7 +25,7 @@ import (
"strings" "strings"
"time" "time"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/term"
"tailscale.com/atomicfile" "tailscale.com/atomicfile"
"tailscale.com/logtail" "tailscale.com/logtail"
"tailscale.com/logtail/filch" "tailscale.com/logtail/filch"
@ -311,7 +311,7 @@ func tryFixLogStateLocation(dir, cmdname string) {
// given collection name. // given collection name.
func New(collection string) *Policy { func New(collection string) *Policy {
var lflags int var lflags int
if terminal.IsTerminal(2) || runtime.GOOS == "windows" { if term.IsTerminal(2) || runtime.GOOS == "windows" {
lflags = 0 lflags = 0
} else { } else {
lflags = log.LstdFlags lflags = log.LstdFlags

@ -14,6 +14,7 @@ import (
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" "os"
"strconv"
"time" "time"
"tailscale.com/logtail/backoff" "tailscale.com/logtail/backoff"
@ -259,8 +260,17 @@ func (l *logger) uploading(ctx context.Context) {
for { for {
body := l.drainPending() body := l.drainPending()
if l.zstdEncoder != nil { origlen := -1 // sentinel value: uncompressed
body = l.zstdEncoder.EncodeAll(body, nil) // Don't attempt to compress tiny bodies; not worth the CPU cycles.
if l.zstdEncoder != nil && len(body) > 256 {
zbody := l.zstdEncoder.EncodeAll(body, nil)
// Only send it compressed if the bandwidth savings are sufficient.
// Just the extra headers associated with enabling compression
// are 50 bytes by themselves.
if len(body)-len(zbody) > 64 {
origlen = len(body)
body = zbody
}
} }
for len(body) > 0 { for len(body) > 0 {
@ -269,7 +279,7 @@ func (l *logger) uploading(ctx context.Context) {
return return
default: default:
} }
uploaded, err := l.upload(ctx, body) uploaded, err := l.upload(ctx, body, origlen)
if err != nil { if err != nil {
fmt.Fprintf(l.stderr, "logtail: upload: %v\n", err) fmt.Fprintf(l.stderr, "logtail: upload: %v\n", err)
} }
@ -287,7 +297,10 @@ func (l *logger) uploading(ctx context.Context) {
} }
} }
func (l *logger) upload(ctx context.Context, body []byte) (uploaded bool, err error) { // upload uploads body to the log server.
// origlen indicates the pre-compression body length.
// origlen of -1 indicates that the body is not compressed.
func (l *logger) upload(ctx context.Context, body []byte, origlen int) (uploaded bool, err error) {
req, err := http.NewRequest("POST", l.url, bytes.NewReader(body)) req, err := http.NewRequest("POST", l.url, bytes.NewReader(body))
if err != nil { if err != nil {
// I know of no conditions under which this could fail. // I know of no conditions under which this could fail.
@ -295,8 +308,9 @@ func (l *logger) upload(ctx context.Context, body []byte) (uploaded bool, err er
// TODO record logs to disk // TODO record logs to disk
panic("logtail: cannot build http request: " + err.Error()) panic("logtail: cannot build http request: " + err.Error())
} }
if l.zstdEncoder != nil { if origlen != -1 {
req.Header.Add("Content-Encoding", "zstd") req.Header.Add("Content-Encoding", "zstd")
req.Header.Add("Orig-Content-Length", strconv.Itoa(origlen))
} }
req.Header["User-Agent"] = nil // not worth writing one; save some bytes req.Header["User-Agent"] = nil // not worth writing one; save some bytes
@ -306,7 +320,7 @@ func (l *logger) upload(ctx context.Context, body []byte) (uploaded bool, err er
req = req.WithContext(ctx) req = req.WithContext(ctx)
compressedNote := "not-compressed" compressedNote := "not-compressed"
if l.zstdEncoder != nil { if origlen != -1 {
compressedNote = "compressed" compressedNote = "compressed"
} }

@ -64,11 +64,11 @@ func likelyHomeRouterIPDarwinExec() (ret netaddr.IP, ok bool) {
if err == nil && isPrivateIP(ip) { if err == nil && isPrivateIP(ip) {
ret = ip ret = ip
// We've found what we're looking for. // We've found what we're looking for.
return stopReadingNetstatTable return errStopReadingNetstatTable
} }
return nil return nil
}) })
return ret, !ret.IsZero() return ret, !ret.IsZero()
} }
var stopReadingNetstatTable = errors.New("found private gateway") var errStopReadingNetstatTable = errors.New("found private gateway")

@ -710,6 +710,7 @@ func (rs *reportState) probePortMapServices() {
uc.WriteTo(pcpPacket(myIP, tempPort, false), port5351) uc.WriteTo(pcpPacket(myIP, tempPort, false), port5351)
res := make([]byte, 1500) res := make([]byte, 1500)
sentPCPDelete := false
for { for {
n, addr, err := uc.ReadFrom(res) n, addr, err := uc.ReadFrom(res)
if err != nil { if err != nil {
@ -727,6 +728,8 @@ func (rs *reportState) probePortMapServices() {
if n == 60 && res[0] == 0x02 { // right length and version 2 if n == 60 && res[0] == 0x02 { // right length and version 2
rs.setOptBool(&rs.report.PCP, true) rs.setOptBool(&rs.report.PCP, true)
if !sentPCPDelete {
sentPCPDelete = true
// And now delete the mapping. // And now delete the mapping.
// (PCP is the only protocol of the three that requires // (PCP is the only protocol of the three that requires
// we cause a side effect to detect whether it's present, // we cause a side effect to detect whether it's present,
@ -736,6 +739,7 @@ func (rs *reportState) probePortMapServices() {
} }
} }
} }
}
var pmpPacket = []byte{0, 0} // version 0, opcode 0 = "Public address request" var pmpPacket = []byte{0, 0} // version 0, opcode 0 = "Public address request"
@ -747,6 +751,7 @@ var uPnPPacket = []byte("M-SEARCH * HTTP/1.1\r\n" +
var v4unspec, _ = netaddr.ParseIP("0.0.0.0") var v4unspec, _ = netaddr.ParseIP("0.0.0.0")
// pcpPacket generates a PCP packet with a MAP opcode.
func pcpPacket(myIP netaddr.IP, mapToLocalPort int, delete bool) []byte { func pcpPacket(myIP netaddr.IP, mapToLocalPort int, delete bool) []byte {
const udpProtoNumber = 17 const udpProtoNumber = 17
lifetimeSeconds := uint32(1) lifetimeSeconds := uint32(1)
@ -754,17 +759,24 @@ func pcpPacket(myIP netaddr.IP, mapToLocalPort int, delete bool) []byte {
lifetimeSeconds = 0 lifetimeSeconds = 0
} }
const opMap = 1 const opMap = 1
// 24 byte header + 36 byte map opcode
pkt := make([]byte, (32+32+128)/8+(96+8+24+16+16+128)/8) pkt := make([]byte, (32+32+128)/8+(96+8+24+16+16+128)/8)
// The header (https://tools.ietf.org/html/rfc6887#section-7.1)
pkt[0] = 2 // version pkt[0] = 2 // version
pkt[1] = opMap pkt[1] = opMap
binary.BigEndian.PutUint32(pkt[4:8], lifetimeSeconds) binary.BigEndian.PutUint32(pkt[4:8], lifetimeSeconds)
myIP16 := myIP.As16() myIP16 := myIP.As16()
copy(pkt[8:], myIP16[:]) copy(pkt[8:], myIP16[:])
rand.Read(pkt[24 : 24+12])
pkt[36] = udpProtoNumber // The map opcode body (https://tools.ietf.org/html/rfc6887#section-11.1)
binary.BigEndian.PutUint16(pkt[40:], uint16(mapToLocalPort)) mapOp := pkt[24:]
rand.Read(mapOp[:12]) // 96 bit mappping nonce
mapOp[12] = udpProtoNumber
binary.BigEndian.PutUint16(mapOp[16:], uint16(mapToLocalPort))
v4unspec16 := v4unspec.As16() v4unspec16 := v4unspec.As16()
copy(pkt[40:], v4unspec16[:]) copy(mapOp[20:], v4unspec16[:])
return pkt return pkt
} }

@ -33,7 +33,7 @@ func NewWaitGroupChan() *WaitGroupChan {
} }
// DoneChan returns a channel that's closed on completion. // DoneChan returns a channel that's closed on completion.
func (c *WaitGroupChan) DoneChan() <-chan struct{} { return c.done } func (wg *WaitGroupChan) DoneChan() <-chan struct{} { return wg.done }
// Add adds delta, which may be negative, to the WaitGroupChan // Add adds delta, which may be negative, to the WaitGroupChan
// counter. If the counter becomes zero, all goroutines blocked on // counter. If the counter becomes zero, all goroutines blocked on
@ -46,10 +46,10 @@ func (c *WaitGroupChan) DoneChan() <-chan struct{} { return c.done }
// than zero, may happen at any time. Typically this means the calls // than zero, may happen at any time. Typically this means the calls
// to Add should execute before the statement creating the goroutine // to Add should execute before the statement creating the goroutine
// or other event to be waited for. // or other event to be waited for.
func (c *WaitGroupChan) Add(delta int) { func (wg *WaitGroupChan) Add(delta int) {
n := atomic.AddInt64(&c.n, int64(delta)) n := atomic.AddInt64(&wg.n, int64(delta))
if n == 0 { if n == 0 {
close(c.done) close(wg.done)
} }
} }

@ -112,8 +112,6 @@ type User struct {
Logins []LoginID Logins []LoginID
Roles []RoleID Roles []RoleID
Created time.Time Created time.Time
// Note: be sure to update Clone when adding new fields
} }
type Login struct { type Login struct {
@ -153,12 +151,9 @@ type Node struct {
Created time.Time Created time.Time
LastSeen *time.Time `json:",omitempty"` LastSeen *time.Time `json:",omitempty"`
KeepAlive bool // open and keep open a connection to this peer KeepAlive bool `json:",omitempty"` // open and keep open a connection to this peer
MachineAuthorized bool // TODO(crawshaw): replace with MachineStatus
// NOTE: any new fields containing pointers in this type MachineAuthorized bool `json:",omitempty"` // TODO(crawshaw): replace with MachineStatus
// require changes to Node.Clone.
} }
type MachineStatus int type MachineStatus int
@ -275,9 +270,6 @@ type Service struct {
Description string `json:",omitempty"` // text description of service Description string `json:",omitempty"` // text description of service
// TODO(apenwarr): allow advertising services on subnet IPs? // TODO(apenwarr): allow advertising services on subnet IPs?
// TODO(apenwarr): add "tags" here for each service? // TODO(apenwarr): add "tags" here for each service?
// NOTE: any new fields containing pointers in this type
// require changes to Hostinfo.Clone.
} }
// Hostinfo contains a summary of a Tailscale host. // Hostinfo contains a summary of a Tailscale host.
@ -287,7 +279,7 @@ type Service struct {
type Hostinfo struct { type Hostinfo struct {
// TODO(crawshaw): mark all these fields ",omitempty" when all the // TODO(crawshaw): mark all these fields ",omitempty" when all the
// iOS apps are updated with the latest swift version of this struct. // iOS apps are updated with the latest swift version of this struct.
IPNVersion string // version of this code IPNVersion string `json:",omitempty"` // version of this code
FrontendLogID string `json:",omitempty"` // logtail ID of frontend instance FrontendLogID string `json:",omitempty"` // logtail ID of frontend instance
BackendLogID string `json:",omitempty"` // logtail ID of backend instance BackendLogID string `json:",omitempty"` // logtail ID of backend instance
OS string // operating system the client runs on (a version.OS value) OS string // operating system the client runs on (a version.OS value)
@ -295,6 +287,7 @@ type Hostinfo struct {
DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone 11 Pro") DeviceModel string `json:",omitempty"` // mobile phone model ("Pixel 3a", "iPhone 11 Pro")
Hostname string // name of the host the client runs on Hostname string // name of the host the client runs on
ShieldsUp bool `json:",omitempty"` // indicates whether the host is blocking incoming connections ShieldsUp bool `json:",omitempty"` // indicates whether the host is blocking incoming connections
ShareeNode bool `json:",omitempty"` // indicates this node exists in netmap because it's owned by a shared-to user
GoArch string `json:",omitempty"` // the host's GOARCH value (of the running binary) GoArch string `json:",omitempty"` // the host's GOARCH value (of the running binary)
RoutableIPs []wgcfg.CIDR `json:",omitempty"` // set of IP ranges this client can route RoutableIPs []wgcfg.CIDR `json:",omitempty"` // set of IP ranges this client can route
RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim RequestTags []string `json:",omitempty"` // set of ACL tags this node wants to claim
@ -302,7 +295,7 @@ type Hostinfo struct {
NetInfo *NetInfo `json:",omitempty"` NetInfo *NetInfo `json:",omitempty"`
// NOTE: any new fields containing pointers in this type // NOTE: any new fields containing pointers in this type
// require changes to Hostinfo.Clone and Hostinfo.Equal. // require changes to Hostinfo.Equal.
} }
// NetInfo contains information about the host's network state. // NetInfo contains information about the host's network state.
@ -354,7 +347,7 @@ type NetInfo struct {
// the control plane. // the control plane.
DERPLatency map[string]float64 `json:",omitempty"` DERPLatency map[string]float64 `json:",omitempty"`
// Update Clone and BasicallyEqual when adding fields. // Update BasicallyEqual when adding fields.
} }
func (ni *NetInfo) String() string { func (ni *NetInfo) String() string {
@ -482,7 +475,8 @@ type MapRequest struct {
// History of versions: // History of versions:
// 3: implicit compression, keep-alives // 3: implicit compression, keep-alives
// 4: opt-in keep-alives via KeepAlive field, opt-in compression via Compress // 4: opt-in keep-alives via KeepAlive field, opt-in compression via Compress
// 5: 2020-10-19, implies IncludeIPv6, DeltaPeers/DeltaUserProfiles, supports MagicDNS // 5: 2020-10-19, implies IncludeIPv6, delta Peers/UserProfiles, supports MagicDNS
// 6: 2020-12-07: means MapResponse.PacketFilter nil means unchanged
Version int Version int
Compress string // "zstd" or "" (no compression) Compress string // "zstd" or "" (no compression)
KeepAlive bool // whether server should send keep-alives back to us KeepAlive bool // whether server should send keep-alives back to us
@ -519,6 +513,8 @@ type MapRequest struct {
// router but their IP forwarding is broken. // router but their IP forwarding is broken.
// * "v6-overlay": IPv6 development flag to have control send // * "v6-overlay": IPv6 development flag to have control send
// v6 node addrs // v6 node addrs
// * "minimize-netmap": have control minimize the netmap, removing
// peers that are unreachable per ACLS.
DebugFlags []string `json:",omitempty"` DebugFlags []string `json:",omitempty"`
} }
@ -601,14 +597,15 @@ type MapResponse struct {
// Peers, if non-empty, is the complete list of peers. // Peers, if non-empty, is the complete list of peers.
// It will be set in the first MapResponse for a long-polled request/response. // It will be set in the first MapResponse for a long-polled request/response.
// Subsequent responses will be delta-encoded if DeltaPeers was set in the request. // Subsequent responses will be delta-encoded if MapRequest.Version >= 5 and server
// chooses, in which case Peers will be nil or zero length.
// If Peers is non-empty, PeersChanged and PeersRemoved should // If Peers is non-empty, PeersChanged and PeersRemoved should
// be ignored (and should be empty). // be ignored (and should be empty).
// Peers is always returned sorted by Node.ID. // Peers is always returned sorted by Node.ID.
Peers []*Node `json:",omitempty"` Peers []*Node `json:",omitempty"`
// PeersChanged are the Nodes (identified by their ID) that // PeersChanged are the Nodes (identified by their ID) that
// have changed or been added since the past update on the // have changed or been added since the past update on the
// HTTP response. It's only set if MapRequest.DeltaPeers was true. // HTTP response. It's not used by the server if MapRequest.Version < 5.
// PeersChanged is always returned sorted by Node.ID. // PeersChanged is always returned sorted by Node.ID.
PeersChanged []*Node `json:",omitempty"` PeersChanged []*Node `json:",omitempty"`
// PeersRemoved are the NodeIDs that are no longer in the peer list. // PeersRemoved are the NodeIDs that are no longer in the peer list.
@ -624,11 +621,25 @@ type MapResponse struct {
SearchPaths []string `json:",omitempty"` SearchPaths []string `json:",omitempty"`
DNSConfig DNSConfig `json:",omitempty"` DNSConfig DNSConfig `json:",omitempty"`
// ACLs // Domain is the name of the network that this node is
// in. It's either of the form "example.com" (for user
// foo@example.com, for multi-user networks) or
// "foo@gmail.com" (for siloed users on shared email
// providers). Its exact form should not be depended on; new
// forms are coming later.
Domain string Domain string
// PacketFilter are the firewall rules.
//
// For MapRequest.Version >= 6, a nil value means the most
// previously streamed non-nil MapResponse.PacketFilter within
// the same HTTP response. A non-nil but empty list always means
// no PacketFilter (that is, to block everything).
PacketFilter []FilterRule PacketFilter []FilterRule
UserProfiles []UserProfile // as of 1.1.541: may be new or updated user profiles only
UserProfiles []UserProfile // as of 1.1.541 (mapver 5): may be new or updated user profiles only
Roles []Role // deprecated; clients should not rely on Roles Roles []Role // deprecated; clients should not rely on Roles
// TODO: Groups []Group // TODO: Groups []Group
// TODO: Capabilities []Capability // TODO: Capabilities []Capability

@ -106,6 +106,7 @@ var _HostinfoNeedsRegeneration = Hostinfo(struct {
DeviceModel string DeviceModel string
Hostname string Hostname string
ShieldsUp bool ShieldsUp bool
ShareeNode bool
GoArch string GoArch string
RoutableIPs []wgcfg.CIDR RoutableIPs []wgcfg.CIDR
RequestTags []string RequestTags []string

@ -23,9 +23,12 @@ func fieldsOf(t reflect.Type) (fields []string) {
func TestHostinfoEqual(t *testing.T) { func TestHostinfoEqual(t *testing.T) {
hiHandles := []string{ hiHandles := []string{
"IPNVersion", "FrontendLogID", "BackendLogID", "OS", "OSVersion", "IPNVersion", "FrontendLogID", "BackendLogID",
"DeviceModel", "Hostname", "ShieldsUp", "GoArch", "RoutableIPs", "OS", "OSVersion", "DeviceModel", "Hostname",
"RequestTags", "Services", "NetInfo", "ShieldsUp", "ShareeNode",
"GoArch",
"RoutableIPs", "RequestTags",
"Services", "NetInfo",
} }
if have := fieldsOf(reflect.TypeOf(Hostinfo{})); !reflect.DeepEqual(have, hiHandles) { if have := fieldsOf(reflect.TypeOf(Hostinfo{})); !reflect.DeepEqual(have, hiHandles) {
t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n", t.Errorf("Hostinfo.Equal check might be out of sync\nfields: %q\nhandled: %q\n",
@ -169,6 +172,11 @@ func TestHostinfoEqual(t *testing.T) {
&Hostinfo{Services: []Service{Service{Proto: TCP, Port: 1234, Description: "foo"}}}, &Hostinfo{Services: []Service{Service{Proto: TCP, Port: 1234, Description: "foo"}}},
true, true,
}, },
{
&Hostinfo{ShareeNode: true},
&Hostinfo{},
false,
},
} }
for i, tt := range tests { for i, tt := range tests {
got := tt.a.Equal(tt.b) got := tt.a.Equal(tt.b)

@ -44,11 +44,12 @@ func AddHandlers(mux *http.ServeMux) {
func Cmdline(w http.ResponseWriter, r *http.Request) { func Cmdline(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff") w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("Content-Type", "text/plain; charset=utf-8")
fmt.Fprintf(w, strings.Join(os.Args, "\x00")) fmt.Fprint(w, strings.Join(os.Args, "\x00"))
} }
func sleep(w http.ResponseWriter, d time.Duration) { func sleep(w http.ResponseWriter, d time.Duration) {
var clientGone <-chan bool var clientGone <-chan bool
//lint:ignore SA1019 CloseNotifier is deprecated but it functions and this is a temporary fork
if cn, ok := w.(http.CloseNotifier); ok { if cn, ok := w.(http.CloseNotifier); ok {
clientGone = cn.CloseNotify() clientGone = cn.CloseNotify()
} }

@ -44,6 +44,16 @@ func registerCommonDebug(mux *http.ServeMux) {
mux.Handle("/debug/pprof/", Protected(http.DefaultServeMux)) // to net/http/pprof mux.Handle("/debug/pprof/", Protected(http.DefaultServeMux)) // to net/http/pprof
mux.Handle("/debug/vars", Protected(http.DefaultServeMux)) // to expvar mux.Handle("/debug/vars", Protected(http.DefaultServeMux)) // to expvar
mux.Handle("/debug/varz", Protected(http.HandlerFunc(varzHandler))) mux.Handle("/debug/varz", Protected(http.HandlerFunc(varzHandler)))
mux.Handle("/debug/gc", Protected(http.HandlerFunc(gcHandler)))
}
func gcHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("running GC...\n"))
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
runtime.GC()
w.Write([]byte("Done.\n"))
} }
func DefaultCertDir(leafDir string) string { func DefaultCertDir(leafDir string) string {

@ -10,7 +10,7 @@ package version
// Long is a full version number for this build, of the form // Long is a full version number for this build, of the form
// "x.y.z-commithash", or "date.yyyymmdd" if no actual version was // "x.y.z-commithash", or "date.yyyymmdd" if no actual version was
// provided. // provided.
const Long = "date.20201107" const Long = "date.20201207"
// Short is a short version number for this build, of the form // Short is a short version number for this build, of the form
// "x.y.z", or "date.yyyymmdd" if no actual version was provided. // "x.y.z", or "date.yyyymmdd" if no actual version was provided.

@ -31,7 +31,7 @@ case $# in
if [ -z "$extra_hash_or_dir" ]; then if [ -z "$extra_hash_or_dir" ]; then
# Nothing, empty extra hash is fine. # Nothing, empty extra hash is fine.
extra_hash="" extra_hash=""
elif [ -d "$extra_hash_or_dir/.git" ]; then elif [ -e "$extra_hash_or_dir/.git" ]; then
extra_hash=$(git_hash_dirty "$extra_hash_or_dir" HEAD) extra_hash=$(git_hash_dirty "$extra_hash_or_dir" HEAD)
elif ! expr "$extra_hash_or_dir" : "^[0-9a-f]*$"; then elif ! expr "$extra_hash_or_dir" : "^[0-9a-f]*$"; then
echo "Invalid extra hash '$extra_hash_or_dir', must be a git commit or path to a git repo" >&2 echo "Invalid extra hash '$extra_hash_or_dir', must be a git commit or path to a git repo" >&2

@ -110,37 +110,58 @@ func inTest() bool {
// A Conn routes UDP packets and actively manages a list of its endpoints. // A Conn routes UDP packets and actively manages a list of its endpoints.
// It implements wireguard/conn.Bind. // It implements wireguard/conn.Bind.
type Conn struct { type Conn struct {
pconnPort uint16 // the preferred port from opts.Port; 0 means auto // This block mirrors the contents and field order of the Options
pconn4 *RebindingUDPConn // struct. Initialized once at construction, then constant.
pconn6 *RebindingUDPConn // non-nil if IPv6 available
logf logger.Logf
port uint16 // the preferred port from opts.Port; 0 means auto
epFunc func(endpoints []string) epFunc func(endpoints []string)
derpActiveFunc func() derpActiveFunc func()
logf logger.Logf
sendLogLimit *rate.Limiter
netChecker *netcheck.Client
idleFunc func() time.Duration // nil means unknown idleFunc func() time.Duration // nil means unknown
packetListener nettype.PacketListener
noteRecvActivity func(tailcfg.DiscoKey) // or nil, see Options.NoteRecvActivity noteRecvActivity func(tailcfg.DiscoKey) // or nil, see Options.NoteRecvActivity
simulatedNetwork bool simulatedNetwork bool
// ================================================================
// No locking required to access these fields, either because
// they're static after construction, or are wholly owned by a
// single goroutine.
connCtx context.Context // closed on Conn.Close
connCtxCancel func() // closes connCtx
// pconn4 and pconn6 are the underlying UDP sockets used to
// send/receive packets for wireguard and other magicsock
// protocols.
pconn4 *RebindingUDPConn
pconn6 *RebindingUDPConn
// netChecker is the prober that discovers local network
// conditions, including the closest DERP relay and NAT mappings.
netChecker *netcheck.Client
// sendLogLimit is a rate limiter for errors logged in the (hot)
// packet sending codepath. It's so that, if magicsock gets into a
// bad state, we don't spam one error per wireguard packet being
// transmitted.
// TODO(danderson): now that we have global rate-limiting, is this still useful?
sendLogLimit *rate.Limiter
// bufferedIPv4From and bufferedIPv4Packet are owned by // bufferedIPv4From and bufferedIPv4Packet are owned by
// ReceiveIPv4, and used when both a DERP and IPv4 packet arrive // ReceiveIPv4, and used when both a DERP and IPv4 packet arrive
// at the same time. It stores the IPv4 packet for use in the next call. // at the same time. It stores the IPv4 packet for use in the next call.
bufferedIPv4From netaddr.IPPort // if non-zero, then bufferedIPv4Packet is valid bufferedIPv4From netaddr.IPPort // if non-zero, then bufferedIPv4Packet is valid
bufferedIPv4Packet []byte // the received packet (reused, owned by ReceiveIPv4) bufferedIPv4Packet []byte // the received packet (reused, owned by ReceiveIPv4)
connCtx context.Context // closed on Conn.Close
connCtxCancel func() // closes connCtx
// stunReceiveFunc holds the current STUN packet processing func. // stunReceiveFunc holds the current STUN packet processing func.
// Its Loaded value is always non-nil. // Its Loaded value is always non-nil.
stunReceiveFunc atomic.Value // of func(p []byte, fromAddr *net.UDPAddr) stunReceiveFunc atomic.Value // of func(p []byte, fromAddr *net.UDPAddr)
// udpRecvCh and derpRecvCh are used by ReceiveIPv4 to multiplex
// reads from DERP and the pconn4.
udpRecvCh chan udpReadResult udpRecvCh chan udpReadResult
derpRecvCh chan derpReadResult derpRecvCh chan derpReadResult
// packetListener optionally specifies a test hook to open a PacketConn.
packetListener nettype.PacketListener
// ============================================================ // ============================================================
mu sync.Mutex // guards all following fields; see userspaceEngine lock ordering rules mu sync.Mutex // guards all following fields; see userspaceEngine lock ordering rules
muCond *sync.Cond muCond *sync.Cond
@ -148,17 +169,43 @@ type Conn struct {
started bool // Start was called started bool // Start was called
closed bool // Close was called closed bool // Close was called
// endpointsUpdateActive indicates that updateEndpoints is
// currently running. It's used to deduplicate concurrent endpoint
// update requests.
endpointsUpdateActive bool endpointsUpdateActive bool
// wantEndpointsUpdate, if non-empty, means that a new endpoints
// update should begin immediately after the currently-running one
// completes. It can only be non-empty if
// endpointsUpdateActive==true.
wantEndpointsUpdate string // true if non-empty; string is reason wantEndpointsUpdate string // true if non-empty; string is reason
// lastEndpoints records the endpoints found during the previous
// endpoint discovery. It's used to avoid duplicate endpoint
// change notifications.
lastEndpoints []string lastEndpoints []string
// peerSet is the set of peers that are currently configured in
// WireGuard. These are not used to filter inbound or outbound
// traffic at all, but only to track what state can be cleaned up
// in other maps below that are keyed by peer public key.
peerSet map[key.Public]struct{} peerSet map[key.Public]struct{}
// discoPrivate is the private naclbox key used for active
// discovery traffic. It's created once near (but not during)
// construction.
discoPrivate key.Private discoPrivate key.Private
discoPublic tailcfg.DiscoKey // public of discoPrivate discoPublic tailcfg.DiscoKey // public of discoPrivate
discoShort string // ShortString of discoPublic (to save logging work later) discoShort string // ShortString of discoPublic (to save logging work later)
// nodeOfDisco tracks the networkmap Node entity for each peer
// discovery key.
//
// TODO(danderson): the only thing we ever use from this is the
// peer's WireGuard public key. This could be a map of DiscoKey to
// NodeKey.
nodeOfDisco map[tailcfg.DiscoKey]*tailcfg.Node nodeOfDisco map[tailcfg.DiscoKey]*tailcfg.Node
discoOfNode map[tailcfg.NodeKey]tailcfg.DiscoKey discoOfNode map[tailcfg.NodeKey]tailcfg.DiscoKey
discoOfAddr map[netaddr.IPPort]tailcfg.DiscoKey // validated non-DERP paths only discoOfAddr map[netaddr.IPPort]tailcfg.DiscoKey // validated non-DERP paths only
// endpointsOfDisco tracks the wireguard-go endpoints for peers
// with recent activity.
endpointOfDisco map[tailcfg.DiscoKey]*discoEndpoint // those with activity only endpointOfDisco map[tailcfg.DiscoKey]*discoEndpoint // those with activity only
sharedDiscoKey map[tailcfg.DiscoKey]*[32]byte // nacl/box precomputed key sharedDiscoKey map[tailcfg.DiscoKey]*[32]byte // nacl/box precomputed key
@ -173,18 +220,35 @@ type Conn struct {
// 10.0.0.1:1 -> [10.0.0.1:1, 10.0.0.2:2] // 10.0.0.1:1 -> [10.0.0.1:1, 10.0.0.2:2]
// 10.0.0.2:2 -> [10.0.0.1:1, 10.0.0.2:2] // 10.0.0.2:2 -> [10.0.0.1:1, 10.0.0.2:2]
// 10.0.0.3:3 -> [10.0.0.3:3] // 10.0.0.3:3 -> [10.0.0.3:3]
//
// Used only to communicate with legacy, pre-active-discovery
// clients.
addrsByUDP map[netaddr.IPPort]*AddrSet addrsByUDP map[netaddr.IPPort]*AddrSet
// addrsByKey maps from public keys (as seen by incoming DERP // addrsByKey maps from public keys (as seen by incoming DERP
// packets) to its AddrSet (the same values as in addrsByUDP). // packets) to its AddrSet (the same values as in addrsByUDP).
//
// Used only to communicate with legacy, pre-active-discovery
// clients.
addrsByKey map[key.Public]*AddrSet addrsByKey map[key.Public]*AddrSet
// netInfoFunc is a callback that provides a tailcfg.NetInfo when
// discovered network conditions change.
//
// TODO(danderson): why can't it be set at construction time?
// There seem to be a few natural places in ipn/local.go to
// swallow untimely invocations.
netInfoFunc func(*tailcfg.NetInfo) // nil until set netInfoFunc func(*tailcfg.NetInfo) // nil until set
// netInfoLast is the NetInfo provided in the last call to
// netInfoFunc. It's used to deduplicate calls to netInfoFunc.
//
// TODO(danderson): should all the deduping happen in
// ipn/local.go? We seem to be doing dedupe at several layers, and
// magicsock could do with any complexity reduction it can get.
netInfoLast *tailcfg.NetInfo netInfoLast *tailcfg.NetInfo
derpMap *tailcfg.DERPMap // nil (or zero regions/nodes) means DERP is disabled derpMap *tailcfg.DERPMap // nil (or zero regions/nodes) means DERP is disabled
netMap *controlclient.NetworkMap netMap *controlclient.NetworkMap
privateKey key.Private privateKey key.Private // WireGuard private key for this node
everHadKey bool // whether we ever had a non-zero private key everHadKey bool // whether we ever had a non-zero private key
myDerp int // nearest DERP region ID; 0 means none/unknown myDerp int // nearest DERP region ID; 0 means none/unknown
derpStarted chan struct{} // closed on first connection to DERP; for tests & cleaner Close derpStarted chan struct{} // closed on first connection to DERP; for tests & cleaner Close
@ -370,7 +434,7 @@ func newConn() *Conn {
// It doesn't start doing anything until Start is called. // It doesn't start doing anything until Start is called.
func NewConn(opts Options) (*Conn, error) { func NewConn(opts Options) (*Conn, error) {
c := newConn() c := newConn()
c.pconnPort = opts.Port c.port = opts.Port
c.logf = opts.logf() c.logf = opts.logf()
c.epFunc = opts.endpointsFunc() c.epFunc = opts.endpointsFunc()
c.derpActiveFunc = opts.derpActiveFunc() c.derpActiveFunc = opts.derpActiveFunc()
@ -834,9 +898,9 @@ func (c *Conn) determineEndpoints(ctx context.Context) (ipPorts []string, reason
// port mapping on their router to the same explicit // port mapping on their router to the same explicit
// port that tailscaled is running with. Worst case // port that tailscaled is running with. Worst case
// it's an invalid candidate mapping. // it's an invalid candidate mapping.
if nr.MappingVariesByDestIP.EqualBool(true) && c.pconnPort != 0 { if nr.MappingVariesByDestIP.EqualBool(true) && c.port != 0 {
if ip, _, err := net.SplitHostPort(nr.GlobalV4); err == nil { if ip, _, err := net.SplitHostPort(nr.GlobalV4); err == nil {
addAddr(net.JoinHostPort(ip, strconv.Itoa(int(c.pconnPort))), "port_in") addAddr(net.JoinHostPort(ip, strconv.Itoa(int(c.port))), "port_in")
} }
} }
} }
@ -2499,18 +2563,18 @@ func (c *Conn) bind1(ruc **RebindingUDPConn, which string) error {
var pc net.PacketConn var pc net.PacketConn
var err error var err error
listenCtx := context.Background() // unused without DNS name to resolve listenCtx := context.Background() // unused without DNS name to resolve
if c.pconnPort == 0 && DefaultPort != 0 { if c.port == 0 && DefaultPort != 0 {
pc, err = c.listenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, DefaultPort)) pc, err = c.listenPacket(listenCtx, which, net.JoinHostPort(host, fmt.Sprint(DefaultPort)))
if err != nil { if err != nil {
c.logf("magicsock: bind: default port %s/%v unavailable; picking random", which, DefaultPort) c.logf("magicsock: bind: default port %s/%v unavailable; picking random", which, DefaultPort)
} }
} }
if pc == nil { if pc == nil {
pc, err = c.listenPacket(listenCtx, which, fmt.Sprintf("%s:%d", host, c.pconnPort)) pc, err = c.listenPacket(listenCtx, which, net.JoinHostPort(host, fmt.Sprint(c.port)))
} }
if err != nil { if err != nil {
c.logf("magicsock: bind(%s/%v): %v", which, c.pconnPort, err) c.logf("magicsock: bind(%s/%v): %v", which, c.port, err)
return fmt.Errorf("magicsock: bind: %s/%d: %v", which, c.pconnPort, err) return fmt.Errorf("magicsock: bind: %s/%d: %v", which, c.port, err)
} }
if *ruc == nil { if *ruc == nil {
*ruc = new(RebindingUDPConn) *ruc = new(RebindingUDPConn)
@ -2527,19 +2591,19 @@ func (c *Conn) Rebind() {
host = "127.0.0.1" host = "127.0.0.1"
} }
listenCtx := context.Background() // unused without DNS name to resolve listenCtx := context.Background() // unused without DNS name to resolve
if c.pconnPort != 0 { if c.port != 0 {
c.pconn4.mu.Lock() c.pconn4.mu.Lock()
if err := c.pconn4.pconn.Close(); err != nil { if err := c.pconn4.pconn.Close(); err != nil {
c.logf("magicsock: link change close failed: %v", err) c.logf("magicsock: link change close failed: %v", err)
} }
packetConn, err := c.listenPacket(listenCtx, "udp4", fmt.Sprintf("%s:%d", host, c.pconnPort)) packetConn, err := c.listenPacket(listenCtx, "udp4", fmt.Sprintf("%s:%d", host, c.port))
if err == nil { if err == nil {
c.logf("magicsock: link change rebound port: %d", c.pconnPort) c.logf("magicsock: link change rebound port: %d", c.port)
c.pconn4.pconn = packetConn.(*net.UDPConn) c.pconn4.pconn = packetConn.(*net.UDPConn)
c.pconn4.mu.Unlock() c.pconn4.mu.Unlock()
return return
} }
c.logf("magicsock: link change unable to bind fixed port %d: %v, falling back to random port", c.pconnPort, err) c.logf("magicsock: link change unable to bind fixed port %d: %v, falling back to random port", c.port, err)
c.pconn4.mu.Unlock() c.pconn4.mu.Unlock()
} }
c.logf("magicsock: link change, binding new port") c.logf("magicsock: link change, binding new port")

@ -56,7 +56,7 @@ func init() {
// conditions in tests. In particular, you can't expect two test magicsocks // conditions in tests. In particular, you can't expect two test magicsocks
// to be able to connect to each other through a test DERP unless they are // to be able to connect to each other through a test DERP unless they are
// both fully initialized before you try. // both fully initialized before you try.
func (c *Conn) WaitReady(t *testing.T) { func (c *Conn) WaitReady(t testing.TB) {
t.Helper() t.Helper()
timer := time.NewTimer(10 * time.Second) timer := time.NewTimer(10 * time.Second)
defer timer.Stop() defer timer.Stop()
@ -130,7 +130,7 @@ type magicStack struct {
// newMagicStack builds and initializes an idle magicsock and // newMagicStack builds and initializes an idle magicsock and
// friends. You need to call conn.SetNetworkMap and dev.Reconfig // friends. You need to call conn.SetNetworkMap and dev.Reconfig
// before anything interesting happens. // before anything interesting happens.
func newMagicStack(t *testing.T, logf logger.Logf, l nettype.PacketListener, derpMap *tailcfg.DERPMap) *magicStack { func newMagicStack(t testing.TB, logf logger.Logf, l nettype.PacketListener, derpMap *tailcfg.DERPMap) *magicStack {
t.Helper() t.Helper()
privateKey, err := wgcfg.NewPrivateKey() privateKey, err := wgcfg.NewPrivateKey()
@ -377,7 +377,7 @@ collectEndpoints:
} }
} }
func pickPort(t *testing.T) uint16 { func pickPort(t testing.TB) uint16 {
t.Helper() t.Helper()
conn, err := net.ListenPacket("udp4", "127.0.0.1:0") conn, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil { if err != nil {
@ -1344,3 +1344,75 @@ func TestDiscoEndpointAlignment(t *testing.T) {
t.Error("expected false on second call") t.Error("expected false on second call")
} }
} }
func BenchmarkReceiveFrom(b *testing.B) {
port := pickPort(b)
conn, err := NewConn(Options{
Logf: b.Logf,
Port: port,
EndpointsFunc: func(eps []string) {
b.Logf("endpoints: %q", eps)
},
})
if err != nil {
b.Fatal(err)
}
defer conn.Close()
sendConn, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
b.Fatal(err)
}
defer sendConn.Close()
var dstAddr net.Addr = conn.pconn4.LocalAddr()
sendBuf := make([]byte, 1<<10)
for i := range sendBuf {
sendBuf[i] = 'x'
}
buf := make([]byte, 2<<10)
for i := 0; i < b.N; i++ {
if _, err := sendConn.WriteTo(sendBuf, dstAddr); err != nil {
b.Fatalf("WriteTo: %v", err)
}
n, ep, addr, err := conn.ReceiveIPv4(buf)
if err != nil {
b.Fatal(err)
}
_ = n
_ = ep
_ = addr
}
}
func BenchmarkReceiveFrom_Native(b *testing.B) {
recvConn, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
b.Fatal(err)
}
defer recvConn.Close()
recvConnUDP := recvConn.(*net.UDPConn)
sendConn, err := net.ListenPacket("udp4", "127.0.0.1:0")
if err != nil {
b.Fatal(err)
}
defer sendConn.Close()
var dstAddr net.Addr = recvConn.LocalAddr()
sendBuf := make([]byte, 1<<10)
for i := range sendBuf {
sendBuf[i] = 'x'
}
buf := make([]byte, 2<<10)
for i := 0; i < b.N; i++ {
if _, err := sendConn.WriteTo(sendBuf, dstAddr); err != nil {
b.Fatalf("WriteTo: %v", err)
}
if _, _, err := recvConnUDP.ReadFromUDP(buf); err != nil {
b.Fatalf("ReadFromUDP: %v", err)
}
}
}

@ -12,7 +12,6 @@ import (
"time" "time"
"github.com/tailscale/wireguard-go/tun" "github.com/tailscale/wireguard-go/tun"
wgregistry "github.com/tailscale/wireguard-go/tun/wintun/registry"
"golang.org/x/sys/windows/registry" "golang.org/x/sys/windows/registry"
"tailscale.com/types/logger" "tailscale.com/types/logger"
) )
@ -42,7 +41,7 @@ func newManager(mconfig ManagerConfig) managerImpl {
const keyOpenTimeout = time.Minute const keyOpenTimeout = time.Minute
func setRegistryString(path, name, value string) error { func setRegistryString(path, name, value string) error {
key, err := wgregistry.OpenKeyWait(registry.LOCAL_MACHINE, path, registry.SET_VALUE, keyOpenTimeout) key, err := openKeyWait(registry.LOCAL_MACHINE, path, registry.SET_VALUE, keyOpenTimeout)
if err != nil { if err != nil {
return fmt.Errorf("opening %s: %w", path, err) return fmt.Errorf("opening %s: %w", path, err)
} }

@ -0,0 +1,76 @@
// 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.
//
// The code in this file originates from https://git.zx2c4.com/wireguard-go:
// Copyright (C) 2017-2020 WireGuard LLC. All Rights Reserved.
// Copying license: https://git.zx2c4.com/wireguard-go/tree/COPYING
package dns
import (
"fmt"
"runtime"
"strings"
"time"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
func openKeyWait(k registry.Key, path string, access uint32, timeout time.Duration) (registry.Key, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
deadline := time.Now().Add(timeout)
pathSpl := strings.Split(path, "\\")
for i := 0; ; i++ {
keyName := pathSpl[i]
isLast := i+1 == len(pathSpl)
event, err := windows.CreateEvent(nil, 0, 0, nil)
if err != nil {
return 0, fmt.Errorf("windows.CreateEvent: %v", err)
}
defer windows.CloseHandle(event)
var key registry.Key
for {
err = windows.RegNotifyChangeKeyValue(windows.Handle(k), false, windows.REG_NOTIFY_CHANGE_NAME, event, true)
if err != nil {
return 0, fmt.Errorf("windows.RegNotifyChangeKeyValue: %v", err)
}
var accessFlags uint32
if isLast {
accessFlags = access
} else {
accessFlags = registry.NOTIFY
}
key, err = registry.OpenKey(k, keyName, accessFlags)
if err == windows.ERROR_FILE_NOT_FOUND || err == windows.ERROR_PATH_NOT_FOUND {
timeout := time.Until(deadline) / time.Millisecond
if timeout < 0 {
timeout = 0
}
s, err := windows.WaitForSingleObject(event, uint32(timeout))
if err != nil {
return 0, fmt.Errorf("windows.WaitForSingleObject: %v", err)
}
if s == uint32(windows.WAIT_TIMEOUT) { // windows.WAIT_TIMEOUT status const is misclassified as error in golang.org/x/sys/windows
return 0, fmt.Errorf("timeout waiting for registry key")
}
} else if err != nil {
return 0, fmt.Errorf("registry.OpenKey(%v): %v", path, err)
} else {
if isLast {
return key, nil
}
defer key.Close()
break
}
}
k = key
}
}

@ -39,12 +39,8 @@ import (
// address a few rare corner cases, but is unlikely to significantly // address a few rare corner cases, but is unlikely to significantly
// help with MTU issues compared to a static 1280B implementation. // help with MTU issues compared to a static 1280B implementation.
func monitorDefaultRoutes(tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, error) { func monitorDefaultRoutes(tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, error) {
guid := tun.GUID() ourLuid := winipcfg.LUID(tun.LUID())
ourLuid, err := winipcfg.LUIDFromGUID(&guid)
lastMtu := uint32(0) lastMtu := uint32(0)
if err != nil {
return nil, fmt.Errorf("error mapping GUID %v to LUID: %w", guid, err)
}
doIt := func() error { doIt := func() error {
mtu, err := getDefaultRouteMTU() mtu, err := getDefaultRouteMTU()
if err != nil { if err != nil {
@ -91,7 +87,7 @@ func monitorDefaultRoutes(tun *tun.NativeTun) (*winipcfg.RouteChangeCallback, er
} }
return nil return nil
} }
err = doIt() err := doIt()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -159,7 +155,7 @@ func getDefaultRouteMTU() (uint32, error) {
// setPrivateNetwork marks the provided network adapter's category to private. // setPrivateNetwork marks the provided network adapter's category to private.
// It returns (false, nil) if the adapter was not found. // It returns (false, nil) if the adapter was not found.
func setPrivateNetwork(ifcGUID *windows.GUID) (bool, error) { func setPrivateNetwork(ifcLUID winipcfg.LUID) (bool, error) {
// NLM_NETWORK_CATEGORY values. // NLM_NETWORK_CATEGORY values.
const ( const (
categoryPublic = 0 categoryPublic = 0
@ -167,6 +163,11 @@ func setPrivateNetwork(ifcGUID *windows.GUID) (bool, error) {
categoryDomain = 2 categoryDomain = 2
) )
ifcGUID, err := ifcLUID.GUID()
if err != nil {
return false, fmt.Errorf("ifcLUID.GUID: %v", err)
}
// Lock OS thread when using OLE, which seems to be a requirement // Lock OS thread when using OLE, which seems to be a requirement
// from the Microsoft docs. go-ole doesn't seem to handle it automatically. // from the Microsoft docs. go-ole doesn't seem to handle it automatically.
// https://github.com/tailscale/tailscale/issues/921#issuecomment-727526807 // https://github.com/tailscale/tailscale/issues/921#issuecomment-727526807
@ -222,12 +223,8 @@ func setPrivateNetwork(ifcGUID *windows.GUID) (bool, error) {
return false, nil return false, nil
} }
// interfaceFromGUID returns IPAdapterAddresses with specified GUID. // interfaceFromLUID returns IPAdapterAddresses with specified LUID.
func interfaceFromGUID(guid *windows.GUID, flags winipcfg.GAAFlags) (*winipcfg.IPAdapterAddresses, error) { func interfaceFromLUID(luid winipcfg.LUID, flags winipcfg.GAAFlags) (*winipcfg.IPAdapterAddresses, error) {
luid, err := winipcfg.LUIDFromGUID(guid)
if err != nil {
return nil, err
}
addresses, err := winipcfg.GetAdaptersAddresses(windows.AF_UNSPEC, flags) addresses, err := winipcfg.GetAdaptersAddresses(windows.AF_UNSPEC, flags)
if err != nil { if err != nil {
return nil, err return nil, err
@ -237,13 +234,13 @@ func interfaceFromGUID(guid *windows.GUID, flags winipcfg.GAAFlags) (*winipcfg.I
return addr, nil return addr, nil
} }
} }
return nil, fmt.Errorf("interfaceFromGUID: interface with LUID %v (from GUID %v) not found", luid, guid) return nil, fmt.Errorf("interfaceFromLUID: interface with LUID %v not found", luid)
} }
func configureInterface(cfg *Config, tun *tun.NativeTun) error { func configureInterface(cfg *Config, tun *tun.NativeTun) error {
const mtu = 0 const mtu = 0
guid := tun.GUID() luid := winipcfg.LUID(tun.LUID())
iface, err := interfaceFromGUID(&guid, iface, err := interfaceFromLUID(luid,
// Issue 474: on early boot, when the network is still // Issue 474: on early boot, when the network is still
// coming up, if the Tailscale service comes up first, // coming up, if the Tailscale service comes up first,
// the Tailscale adapter it finds might not have the // the Tailscale adapter it finds might not have the
@ -260,7 +257,7 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) error {
// does. // does.
const tries = 20 const tries = 20
for i := 0; i < tries; i++ { for i := 0; i < tries; i++ {
found, err := setPrivateNetwork(&guid) found, err := setPrivateNetwork(luid)
if err != nil { if err != nil {
log.Printf("setPrivateNetwork(try=%d): %v", i, err) log.Printf("setPrivateNetwork(try=%d): %v", i, err)
} else { } else {
@ -271,7 +268,7 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) error {
} }
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
} }
log.Printf("setPrivateNetwork: adapter %v not found after %d tries, giving up", guid, tries) log.Printf("setPrivateNetwork: adapter LUID %v not found after %d tries, giving up", luid, tries)
}() }()
var firstGateway4 *net.IP var firstGateway4 *net.IP
@ -353,7 +350,7 @@ func configureInterface(cfg *Config, tun *tun.NativeTun) error {
} }
// Re-read interface after syncAddresses. // Re-read interface after syncAddresses.
iface, err = interfaceFromGUID(&guid, iface, err = interfaceFromLUID(luid,
// Issue 474: on early boot, when the network is still // Issue 474: on early boot, when the network is still
// coming up, if the Tailscale service comes up first, // coming up, if the Tailscale service comes up first,
// the Tailscale adapter it finds might not have the // the Tailscale adapter it finds might not have the

@ -37,10 +37,15 @@ func newUserspaceRouter(logf logger.Logf, wgdev *device.Device, tundev tun.Devic
} }
nativeTun := tundev.(*tun.NativeTun) nativeTun := tundev.(*tun.NativeTun)
guid := nativeTun.GUID().String() luid := winipcfg.LUID(nativeTun.LUID())
guid, err := luid.GUID()
if err != nil {
return nil, err
}
mconfig := dns.ManagerConfig{ mconfig := dns.ManagerConfig{
Logf: logf, Logf: logf,
InterfaceName: guid, InterfaceName: guid.String(),
} }
return &winRouter{ return &winRouter{

@ -515,6 +515,13 @@ func (p *pinger) run(ctx context.Context, peerKey wgcfg.Key, ips []wgcfg.IP, src
start := time.Now() start := time.Now()
var dstIPs []packet.IP4 var dstIPs []packet.IP4
for _, ip := range ips { for _, ip := range ips {
if ip.Is6() {
// This code is only used for legacy (pre-discovery)
// peers. They're not going to work right with IPv6 on the
// overlay anyway, so don't bother trying to make ping
// work.
continue
}
dstIPs = append(dstIPs, packet.IP4FromNetaddr(netaddr.IPFrom16(ip.Addr))) dstIPs = append(dstIPs, packet.IP4FromNetaddr(netaddr.IPFrom16(ip.Addr)))
} }

Loading…
Cancel
Save