Compare commits

..

2 Commits

Author SHA1 Message Date
Xe
2716667c9d cmd/gitops-pusher: things i forgot to push last PR
Signed-off-by: Xe <xe@tailscale.com>
2022-07-22 15:14:39 -04:00
Xe
affa25097c cmd/gitops-pusher: add etag cache file for the three version problem
This allows gitops-pusher to detect external ACL changes. I'm not
sure what to call this problem, so I've been calling it the "three
version problem" in my notes. The basic problem is that at any given
time we only have two versions of the ACL file at any given point:
the version in CONTROL and the one in the git repo. In order to
check if there has been tampering of the ACL files in the admin
panel, we need to have a _third_ version to compare against.

In this case I am not storing the old ACL entirely (though that could
be a reasonable thing to add in the future), but only its sha256sum.
This allows us to detect if the shasum in control matches the shasum
we expect, and if that expectation fails, then we can react
accordingly.

This will require additional configuration in CI, but I'm sure that
can be done.

Signed-off-by: Xe <xe@tailscale.com>
2022-07-22 13:53:20 -04:00
269 changed files with 2982 additions and 5767 deletions

View File

@@ -31,11 +31,6 @@ jobs:
GOARCH: wasm
run: go build ./cmd/tsconnect/wasm
- name: tsconnect static build
# Use our custom Go toolchain, we set build tags (to control binary size)
# that depend on it.
run: ./tool/go run ./cmd/tsconnect build
- uses: k0kubun/action-slack@v2.0.0
with:
payload: |

View File

@@ -28,11 +28,6 @@ jobs:
- name: Basic build
run: go build ./cmd/...
- name: Build variants
run: |
go install --tags=ts_include_cli ./cmd/tailscaled
go install --tags=ts_omit_aws ./cmd/tailscaled
- name: Get QEMU
run: |
# The qemu in Ubuntu 20.04 (Focal) is too old; we need 5.x something

View File

@@ -21,9 +21,6 @@ jobs:
- name: Check out code
uses: actions/checkout@v3
- name: Run gofmt (goimports)
run: go run golang.org/x/tools/cmd/goimports -d --format-only .
- name: Run go vet
run: go vet ./...

View File

@@ -45,25 +45,4 @@ EOF
exit 0
fi
tags=""
ldflags="-X tailscale.com/version.Long=${LONG} -X tailscale.com/version.Short=${SHORT} -X tailscale.com/version.GitCommit=${GIT_HASH}"
# build_dist.sh arguments must precede go build arguments.
while [ "$#" -gt 1 ]; do
case "$1" in
--extra-small)
shift
ldflags="$ldflags -w -s"
tags="${tags:+$tags,}ts_omit_aws"
;;
--box)
shift
tags="${tags:+$tags,}ts_include_cli"
;;
*)
break
;;
esac
done
exec ./tool/go build ${tags:+-tags=$tags} -ldflags "$ldflags" "$@"
exec ./tool/go build -ldflags "-X tailscale.com/version.Long=${LONG} -X tailscale.com/version.Short=${SHORT} -X tailscale.com/version.GitCommit=${GIT_HASH}" "$@"

View File

@@ -13,7 +13,8 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/netip"
"inet.af/netaddr"
)
// ACLRow defines a rule that grants access by a set of users or groups to a set
@@ -353,7 +354,7 @@ func (c *Client) PreviewACLForUser(ctx context.Context, acl ACL, user string) (r
// Returns ACLPreview on success with matches in a slice. If there are no matches,
// the call is still successful but Matches will be an empty slice.
// Returns error if the provided ACL is invalid.
func (c *Client) PreviewACLForIPPort(ctx context.Context, acl ACL, ipport netip.AddrPort) (res *ACLPreview, err error) {
func (c *Client) PreviewACLForIPPort(ctx context.Context, acl ACL, ipport netaddr.IPPort) (res *ACLPreview, err error) {
// Format return errors to be descriptive.
defer func() {
if err != nil {

View File

@@ -19,7 +19,6 @@ import (
"net"
"net/http"
"net/http/httptrace"
"net/netip"
"net/url"
"os/exec"
"runtime"
@@ -29,6 +28,7 @@ import (
"time"
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
@@ -665,7 +665,7 @@ func (lc *LocalClient) ExpandSNIName(ctx context.Context, name string) (fqdn str
// Ping sends a ping of the provided type to the provided IP and waits
// for its response.
func (lc *LocalClient) Ping(ctx context.Context, ip netip.Addr, pingtype tailcfg.PingType) (*ipnstate.PingResult, error) {
func (lc *LocalClient) Ping(ctx context.Context, ip netaddr.IP, pingtype tailcfg.PingType) (*ipnstate.PingResult, error) {
v := url.Values{}
v.Set("ip", ip.String())
v.Set("type", string(pingtype))

View File

@@ -13,14 +13,15 @@ import (
"encoding/json"
"fmt"
"net/http"
"net/netip"
"inet.af/netaddr"
)
// Routes contains the lists of subnet routes that are currently advertised by a device,
// as well as the subnets that are enabled to be routed by the device.
type Routes struct {
AdvertisedRoutes []netip.Prefix `json:"advertisedRoutes"`
EnabledRoutes []netip.Prefix `json:"enabledRoutes"`
AdvertisedRoutes []netaddr.IPPrefix `json:"advertisedRoutes"`
EnabledRoutes []netaddr.IPPrefix `json:"enabledRoutes"`
}
// Routes retrieves the list of subnet routes that have been enabled for a device.
@@ -55,14 +56,14 @@ func (c *Client) Routes(ctx context.Context, deviceID string) (routes *Routes, e
}
type postRoutesParams struct {
Routes []netip.Prefix `json:"routes"`
Routes []netaddr.IPPrefix `json:"routes"`
}
// SetRoutes updates the list of subnets that are enabled for a device.
// Subnets must be parsable by net/netip.ParsePrefix.
// Subnets must be parsable by inet.af/netaddr.ParseIPPrefix.
// Subnets do not have to be currently advertised by a device, they may be pre-enabled.
// Returns the updated list of enabled and advertised subnet routes in a *Routes object.
func (c *Client) SetRoutes(ctx context.Context, deviceID string, subnets []netip.Prefix) (routes *Routes, err error) {
func (c *Client) SetRoutes(ctx context.Context, deviceID string, subnets []netaddr.IPPrefix) (routes *Routes, err error) {
defer func() {
if err != nil {
err = fmt.Errorf("tailscale.SetRoutes: %w", err)

View File

@@ -29,6 +29,7 @@ import (
"tailscale.com/atomicfile"
"tailscale.com/derp"
"tailscale.com/derp/derphttp"
"tailscale.com/logpolicy"
"tailscale.com/metrics"
"tailscale.com/net/stun"
"tailscale.com/tsweb"
@@ -36,15 +37,16 @@ import (
)
var (
dev = flag.Bool("dev", false, "run in localhost development mode")
addr = flag.String("a", ":443", "server HTTPS listen address, in form \":port\", \"ip:port\", or for IPv6 \"[ip]:port\". If the IP is omitted, it defaults to all interfaces.")
httpPort = flag.Int("http-port", 80, "The port on which to serve HTTP. Set to -1 to disable. The listener is bound to the same IP (if any) as specified in the -a flag.")
stunPort = flag.Int("stun-port", 3478, "The UDP port on which to serve STUN. The listener is bound to the same IP (if any) as specified in the -a flag.")
configPath = flag.String("c", "", "config file path")
certMode = flag.String("certmode", "letsencrypt", "mode for getting a cert. possible options: manual, letsencrypt")
certDir = flag.String("certdir", tsweb.DefaultCertDir("derper-certs"), "directory to store LetsEncrypt certs, if addr's port is :443")
hostname = flag.String("hostname", "derp.tailscale.com", "LetsEncrypt host name, if addr's port is :443")
runSTUN = flag.Bool("stun", true, "whether to run a STUN server. It will bind to the same IP (if any) as the --addr flag value.")
dev = flag.Bool("dev", false, "run in localhost development mode")
addr = flag.String("a", ":443", "server HTTPS listen address, in form \":port\", \"ip:port\", or for IPv6 \"[ip]:port\". If the IP is omitted, it defaults to all interfaces.")
httpPort = flag.Int("http-port", 80, "The port on which to serve HTTP. Set to -1 to disable. The listener is bound to the same IP (if any) as specified in the -a flag.")
stunPort = flag.Int("stun-port", 3478, "The UDP port on which to serve STUN. The listener is bound to the same IP (if any) as specified in the -a flag.")
configPath = flag.String("c", "", "config file path")
certMode = flag.String("certmode", "letsencrypt", "mode for getting a cert. possible options: manual, letsencrypt")
certDir = flag.String("certdir", tsweb.DefaultCertDir("derper-certs"), "directory to store LetsEncrypt certs, if addr's port is :443")
hostname = flag.String("hostname", "derp.tailscale.com", "LetsEncrypt host name, if addr's port is :443")
logCollection = flag.String("logcollection", "", "If non-empty, logtail collection to log to")
runSTUN = flag.Bool("stun", true, "whether to run a STUN server. It will bind to the same IP (if any) as the --addr flag value.")
meshPSKFile = flag.String("mesh-psk-file", defaultMeshPSKFile(), "if non-empty, path to file containing the mesh pre-shared key file. It should contain some hex string; whitespace is trimmed.")
meshWith = flag.String("mesh-with", "", "optional comma-separated list of hostnames to mesh with; the server's own hostname can be in the list")
@@ -133,6 +135,7 @@ func main() {
flag.Parse()
if *dev {
*logCollection = ""
*addr = ":3340" // above the keys DERP
log.Printf("Running in dev mode.")
tsweb.DevMode = true
@@ -143,6 +146,12 @@ func main() {
log.Fatalf("invalid server address: %v", err)
}
var logPol *logpolicy.Policy
if *logCollection != "" {
logPol = logpolicy.New(*logCollection)
log.SetOutput(logPol.Logtail)
}
cfg := loadConfig()
serveTLS := tsweb.IsProd443(*addr) || *certMode == "manual"

View File

@@ -291,6 +291,12 @@ func testNewACLs(ctx context.Context, tailnet, apiKey, policyFname string) error
}
defer resp.Body.Close()
got := resp.StatusCode
want := http.StatusOK
if got != want {
return fmt.Errorf("wanted HTTP status code %d but got %d", want, got)
}
var ate ACLTestError
err = json.NewDecoder(resp.Body).Decode(&ate)
if err != nil {
@@ -301,12 +307,6 @@ func testNewACLs(ctx context.Context, tailnet, apiKey, policyFname string) error
return ate
}
got := resp.StatusCode
want := http.StatusOK
if got != want {
return fmt.Errorf("wanted HTTP status code %d but got %d", want, got)
}
return nil
}

View File

@@ -135,13 +135,13 @@ func tailscaleIP(who *apitype.WhoIsResponse) string {
return ""
}
for _, nodeIP := range who.Node.Addresses {
if nodeIP.Addr().Is4() && nodeIP.IsSingleIP() {
return nodeIP.Addr().String()
if nodeIP.IP().Is4() && nodeIP.IsSingleIP() {
return nodeIP.IP().String()
}
}
for _, nodeIP := range who.Node.Addresses {
if nodeIP.IsSingleIP() {
return nodeIP.Addr().String()
return nodeIP.IP().String()
}
}
return ""

View File

@@ -7,7 +7,6 @@ package cli
import (
"context"
"errors"
"fmt"
"github.com/peterbourgon/ff/v3/ffcli"
)
@@ -32,6 +31,6 @@ func runBugReport(ctx context.Context, args []string) error {
if err != nil {
return err
}
fmt.Println(logMarker)
outln(logMarker)
return nil
}

View File

@@ -27,7 +27,7 @@ var certCmd = &ffcli.Command{
ShortHelp: "get TLS certs",
ShortUsage: "cert [flags] <domain>",
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("cert", flag.ExitOnError)
fs := newFlagSet("cert")
fs.StringVar(&certArgs.certFile, "cert-file", "", "output cert file or \"-\" for stdout; defaults to DOMAIN.crt if --cert-file and --key-file are both unset")
fs.StringVar(&certArgs.keyFile, "key-file", "", "output cert file or \"-\" for stdout; defaults to DOMAIN.key if --cert-file and --key-file are both unset")
fs.BoolVar(&certArgs.serve, "serve-demo", false, "if true, serve on port :443 using the cert as a demo, instead of writing out the files to disk")
@@ -79,7 +79,7 @@ func runCert(ctx context.Context, args []string) error {
domain := args[0]
printf := func(format string, a ...any) {
fmt.Printf(format, a...)
printf(format, a...)
}
if certArgs.certFile == "-" || certArgs.keyFile == "-" {
printf = log.Printf
@@ -138,7 +138,7 @@ func runCert(ctx context.Context, args []string) error {
func writeIfChanged(filename string, contents []byte, mode os.FileMode) (changed bool, err error) {
if filename == "-" {
os.Stdout.Write(contents)
Stdout.Write(contents)
return false, nil
}
if old, err := os.ReadFile(filename); err == nil && bytes.Equal(contents, old) {

View File

@@ -33,6 +33,22 @@ import (
"tailscale.com/version/distro"
)
var Stderr io.Writer = os.Stderr
var Stdout io.Writer = os.Stdout
func printf(format string, a ...any) {
fmt.Fprintf(Stdout, format, a...)
}
// outln is like fmt.Println in the common case, except when Stdout is
// changed (as in js/wasm).
//
// It's not named println because that looks like the Go built-in
// which goes to stderr and formats slightly differently.
func outln(a ...any) {
fmt.Fprintln(Stdout, a...)
}
// ActLikeCLI reports whether a GUI application should act like the
// CLI based on os.Args, GOOS, the context the process is running in
// (pty, parent PID), etc.
@@ -79,6 +95,16 @@ func ActLikeCLI() bool {
return false
}
func newFlagSet(name string) *flag.FlagSet {
onError := flag.ExitOnError
if runtime.GOOS == "js" {
onError = flag.ContinueOnError
}
fs := flag.NewFlagSet(name, onError)
fs.SetOutput(Stderr)
return fs
}
// CleanUpArgs rewrites command line arguments for simplicity and backwards compatibility.
// In particular, it rewrites --authkey to --auth-key.
func CleanUpArgs(args []string) []string {
@@ -112,11 +138,11 @@ func Run(args []string) (err error) {
var warnOnce sync.Once
tailscale.SetVersionMismatchHandler(func(clientVer, serverVer string) {
warnOnce.Do(func() {
fmt.Fprintf(os.Stderr, "Warning: client version %q != tailscaled server version %q\n", clientVer, serverVer)
fmt.Fprintf(Stderr, "Warning: client version %q != tailscaled server version %q\n", clientVer, serverVer)
})
})
rootfs := flag.NewFlagSet("tailscale", flag.ExitOnError)
rootfs := newFlagSet("tailscale")
rootfs.StringVar(&rootArgs.socket, "socket", paths.DefaultTailscaledSocket(), "path to tailscaled's unix socket")
rootCmd := &ffcli.Command{

View File

@@ -9,13 +9,13 @@ import (
"encoding/json"
"flag"
"fmt"
"net/netip"
"reflect"
"strings"
"testing"
qt "github.com/frankban/quicktest"
"github.com/google/go-cmp/cmp"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tstest"
@@ -56,7 +56,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
flags []string // argv to be parsed by FlagSet
curPrefs *ipn.Prefs
curExitNodeIP netip.Addr
curExitNodeIP netaddr.IP
curUser string // os.Getenv("USER") on the client side
goos string // empty means "linux"
distro distro.Distro
@@ -152,10 +152,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.42.0/24"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.42.0/24"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
},
},
want: accidentalUpPrefix + " --advertise-routes=10.0.42.0/24 --advertise-exit-node",
@@ -168,10 +168,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.42.0/24"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.42.0/24"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
},
},
want: "",
@@ -184,10 +184,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.42.0/24"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.42.0/24"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
},
},
want: "",
@@ -212,8 +212,8 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("1.2.0.0/16"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --advertise-exit-node --advertise-routes=1.2.0.0/16",
@@ -226,10 +226,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
netip.MustParsePrefix("1.2.0.0/16"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
netaddr.MustParseIPPrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --advertise-exit-node --advertise-routes=1.2.0.0/16",
@@ -255,16 +255,16 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
ControlURL: ipn.DefaultControlURL,
RouteAll: true,
AllowSingleHosts: false,
ExitNodeIP: netip.MustParseAddr("100.64.5.6"),
ExitNodeIP: netaddr.MustParseIP("100.64.5.6"),
CorpDNS: false,
ShieldsUp: true,
AdvertiseTags: []string{"tag:foo", "tag:bar"},
Hostname: "myhostname",
ForceDaemon: true,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/16"),
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.0.0/16"),
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
},
NetfilterMode: preftype.NetfilterNoDivert,
OperatorUser: "alice",
@@ -280,14 +280,14 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
ControlURL: ipn.DefaultControlURL,
RouteAll: true,
AllowSingleHosts: false,
ExitNodeIP: netip.MustParseAddr("100.64.5.6"),
ExitNodeIP: netaddr.MustParseIP("100.64.5.6"),
CorpDNS: false,
ShieldsUp: true,
AdvertiseTags: []string{"tag:foo", "tag:bar"},
Hostname: "myhostname",
ForceDaemon: true,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/16"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.0.0/16"),
},
NetfilterMode: preftype.NetfilterNoDivert,
OperatorUser: "alice",
@@ -344,10 +344,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
netip.MustParsePrefix("1.2.0.0/16"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
netaddr.MustParseIPPrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --operator=expbits --advertise-exit-node --advertise-routes=1.2.0.0/16",
@@ -360,10 +360,10 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
AllowSingleHosts: true,
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
netip.MustParsePrefix("1.2.0.0/16"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
netaddr.MustParseIPPrefix("1.2.0.0/16"),
},
},
want: accidentalUpPrefix + " --advertise-routes=1.2.0.0/16 --operator=expbits --advertise-exit-node",
@@ -391,14 +391,14 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
CorpDNS: true,
NetfilterMode: preftype.NetfilterOn,
ExitNodeIP: netip.MustParseAddr("100.64.5.4"),
ExitNodeIP: netaddr.MustParseIP("100.64.5.4"),
},
want: accidentalUpPrefix + " --hostname=foo --exit-node=100.64.5.4",
},
{
name: "error_exit_node_omit_with_id_pref",
flags: []string{"--hostname=foo"},
curExitNodeIP: netip.MustParseAddr("100.64.5.7"),
curExitNodeIP: netaddr.MustParseIP("100.64.5.7"),
curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
@@ -412,7 +412,7 @@ func TestCheckForAccidentalSettingReverts(t *testing.T) {
{
name: "error_exit_node_and_allow_lan_omit_with_id_pref", // Isue 3480
flags: []string{"--hostname=foo"},
curExitNodeIP: netip.MustParseAddr("100.2.3.4"),
curExitNodeIP: netaddr.MustParseIP("100.2.3.4"),
curPrefs: &ipn.Prefs{
ControlURL: ipn.DefaultControlURL,
AllowSingleHosts: true,
@@ -562,9 +562,9 @@ func TestPrefsFromUpArgs(t *testing.T) {
WantRunning: true,
AllowSingleHosts: true,
CorpDNS: true,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
},
NetfilterMode: preftype.NetfilterOn,
},
@@ -631,7 +631,7 @@ func TestPrefsFromUpArgs(t *testing.T) {
exitNodeIP: "100.105.106.107",
},
st: &ipnstate.Status{
TailscaleIPs: []netip.Addr{netip.MustParseAddr("100.105.106.107")},
TailscaleIPs: []netaddr.IP{netaddr.MustParseIP("100.105.106.107")},
},
wantErr: `cannot use 100.105.106.107 as an exit node as it is a local IP address to this machine; did you mean --advertise-exit-node?`,
},
@@ -671,8 +671,8 @@ func TestPrefsFromUpArgs(t *testing.T) {
want: &ipn.Prefs{
WantRunning: true,
NoSNAT: true,
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("fd7a:115c:a1e0:b1a::bb:10.0.0.0/112"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("fd7a:115c:a1e0:b1a::bb:10.0.0.0/112"),
},
},
},
@@ -956,7 +956,7 @@ func TestUpdatePrefs(t *testing.T) {
}
}
var cmpIP = cmp.Comparer(func(a, b netip.Addr) bool {
var cmpIP = cmp.Comparer(func(a, b netaddr.IP) bool {
return a == b
})

View File

@@ -31,7 +31,7 @@ permission to use it.
See: https://tailscale.com/kb/1152/synology-outbound/
`),
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("configure-host", flag.ExitOnError)
fs := newFlagSet("configure-host")
return fs
})(),
}

View File

@@ -17,7 +17,6 @@ import (
"log"
"net"
"net/http"
"net/netip"
"os"
"runtime"
"strconv"
@@ -25,6 +24,7 @@ import (
"time"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/control/controlhttp"
"tailscale.com/hostinfo"
"tailscale.com/ipn"
@@ -40,7 +40,7 @@ var debugCmd = &ffcli.Command{
Exec: runDebug,
LongHelp: `"tailscale debug" contains misc debug facilities; it is not a stable interface.`,
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("debug", flag.ExitOnError)
fs := newFlagSet("debug")
fs.StringVar(&debugArgs.file, "file", "", "get, delete:NAME, or NAME")
fs.StringVar(&debugArgs.cpuFile, "cpu-profile", "", "if non-empty, grab a CPU profile for --profile-sec seconds and write it to this file; - for stdout")
fs.StringVar(&debugArgs.memFile, "mem-profile", "", "if non-empty, grab a memory profile and write it to this file; - for stdout")
@@ -63,7 +63,7 @@ var debugCmd = &ffcli.Command{
Exec: runDaemonMetrics,
ShortHelp: "print tailscaled's metrics",
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("metrics", flag.ExitOnError)
fs := newFlagSet("metrics")
fs.BoolVar(&metricsArgs.watch, "watch", false, "print JSON dump of delta values")
return fs
})(),
@@ -103,7 +103,7 @@ var debugCmd = &ffcli.Command{
Exec: runPrefs,
ShortHelp: "print prefs",
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("prefs", flag.ExitOnError)
fs := newFlagSet("prefs")
fs.BoolVar(&prefsArgs.pretty, "pretty", false, "If true, pretty-print output")
return fs
})(),
@@ -113,7 +113,7 @@ var debugCmd = &ffcli.Command{
Exec: runWatchIPN,
ShortHelp: "subscribe to IPN message bus",
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("watch-ipn", flag.ExitOnError)
fs := newFlagSet("watch-ipn")
fs.BoolVar(&watchIPNArgs.netmap, "netmap", true, "include netmap in messages")
return fs
})(),
@@ -128,7 +128,7 @@ var debugCmd = &ffcli.Command{
Exec: runTS2021,
ShortHelp: "debug ts2021 protocol connectivity",
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("ts2021", flag.ExitOnError)
fs := newFlagSet("ts2021")
fs.StringVar(&ts2021Args.host, "host", "controlplane.tailscale.com", "hostname of control plane")
fs.IntVar(&ts2021Args.version, "version", int(tailcfg.CurrentCapabilityVersion), "protocol version")
return fs
@@ -146,7 +146,7 @@ var debugArgs struct {
func writeProfile(dst string, v []byte) error {
if dst == "-" {
_, err := os.Stdout.Write(v)
_, err := Stdout.Write(v)
return err
}
return os.WriteFile(dst, v, 0600)
@@ -198,7 +198,7 @@ func runDebug(ctx context.Context, args []string) error {
if err != nil {
fatalf("%v\n", err)
}
e := json.NewEncoder(os.Stdout)
e := json.NewEncoder(Stdout)
e.SetIndent("", "\t")
e.Encode(wfs)
return nil
@@ -212,7 +212,7 @@ func runDebug(ctx context.Context, args []string) error {
return err
}
log.Printf("Size: %v\n", size)
io.Copy(os.Stdout, rc)
io.Copy(Stdout, rc)
return nil
}
if usedFlag {
@@ -226,14 +226,14 @@ func runDebug(ctx context.Context, args []string) error {
func runLocalCreds(ctx context.Context, args []string) error {
port, token, err := safesocket.LocalTCPPortAndToken()
if err == nil {
fmt.Printf("curl -u:%s http://localhost:%d/localapi/v0/status\n", token, port)
printf("curl -u:%s http://localhost:%d/localapi/v0/status\n", token, port)
return nil
}
if runtime.GOOS == "windows" {
fmt.Printf("curl http://localhost:%v/localapi/v0/status\n", safesocket.WindowsLocalPort)
printf("curl http://localhost:%v/localapi/v0/status\n", safesocket.WindowsLocalPort)
return nil
}
fmt.Printf("curl --unix-socket %s http://foo/localapi/v0/status\n", paths.DefaultTailscaledSocket())
printf("curl --unix-socket %s http://foo/localapi/v0/status\n", paths.DefaultTailscaledSocket())
return nil
}
@@ -247,10 +247,10 @@ func runPrefs(ctx context.Context, args []string) error {
return err
}
if prefsArgs.pretty {
fmt.Println(prefs.Pretty())
outln(prefs.Pretty())
} else {
j, _ := json.MarshalIndent(prefs, "", "\t")
fmt.Println(string(j))
outln(string(j))
}
return nil
}
@@ -268,7 +268,7 @@ func runWatchIPN(ctx context.Context, args []string) error {
n.NetMap = nil
}
j, _ := json.MarshalIndent(n, "", "\t")
fmt.Printf("%s\n", j)
printf("%s\n", j)
})
bc.RequestEngineStatus()
pump(ctx, bc, c)
@@ -282,7 +282,7 @@ func runDERPMap(ctx context.Context, args []string) error {
"failed to get local derp map, instead `curl %s/derpmap/default`: %w", ipn.DefaultControlURL, err,
)
}
enc := json.NewEncoder(os.Stdout)
enc := json.NewEncoder(Stdout)
enc.SetIndent("", "\t")
enc.Encode(dm)
return nil
@@ -299,7 +299,7 @@ func localAPIAction(action string) func(context.Context, []string) error {
func runEnv(ctx context.Context, args []string) error {
for _, e := range os.Environ() {
fmt.Println(e)
outln(e)
}
return nil
}
@@ -338,7 +338,7 @@ func runDaemonGoroutines(ctx context.Context, args []string) error {
if err != nil {
return err
}
os.Stdout.Write(goroutines)
Stdout.Write(goroutines)
return nil
}
@@ -354,7 +354,7 @@ func runDaemonMetrics(ctx context.Context, args []string) error {
return err
}
if !metricsArgs.watch {
os.Stdout.Write(out)
Stdout.Write(out)
return nil
}
bs := bufio.NewScanner(bytes.NewReader(out))
@@ -391,9 +391,9 @@ func runDaemonMetrics(ctx context.Context, args []string) error {
if len(changes) > 0 {
format := fmt.Sprintf("%%-%ds %%+5d => %%v\n", maxNameLen)
for _, c := range changes {
fmt.Fprintf(os.Stdout, format, c.name, c.to-c.from, c.to)
fmt.Fprintf(Stdout, format, c.name, c.to-c.from, c.to)
}
io.WriteString(os.Stdout, "\n")
io.WriteString(Stdout, "\n")
}
time.Sleep(time.Second)
}
@@ -404,23 +404,23 @@ func runVia(ctx context.Context, args []string) error {
default:
return errors.New("expect either <site-id> <v4-cidr> or <v6-route>")
case 1:
ipp, err := netip.ParsePrefix(args[0])
ipp, err := netaddr.ParseIPPrefix(args[0])
if err != nil {
return err
}
if !ipp.Addr().Is6() {
if !ipp.IP().Is6() {
return errors.New("with one argument, expect an IPv6 CIDR")
}
if !tsaddr.TailscaleViaRange().Contains(ipp.Addr()) {
if !tsaddr.TailscaleViaRange().Contains(ipp.IP()) {
return errors.New("not a via route")
}
if ipp.Bits() < 96 {
return errors.New("short length, want /96 or more")
}
v4 := tsaddr.UnmapVia(ipp.Addr())
a := ipp.Addr().As16()
v4 := tsaddr.UnmapVia(ipp.IP())
a := ipp.IP().As16()
siteID := binary.BigEndian.Uint32(a[8:12])
fmt.Printf("site %v (0x%x), %v\n", siteID, siteID, netip.PrefixFrom(v4, ipp.Bits()-96))
fmt.Printf("site %v (0x%x), %v\n", siteID, siteID, netaddr.IPPrefixFrom(v4, ipp.Bits()-96))
case 2:
siteID, err := strconv.ParseUint(args[0], 0, 32)
if err != nil {
@@ -429,7 +429,7 @@ func runVia(ctx context.Context, args []string) error {
if siteID > 0xff {
return fmt.Errorf("site-id values over 255 are currently reserved")
}
ipp, err := netip.ParsePrefix(args[1])
ipp, err := netaddr.ParseIPPrefix(args[1])
if err != nil {
return err
}

View File

@@ -8,7 +8,6 @@ import (
"context"
"flag"
"fmt"
"os"
"github.com/peterbourgon/ff/v3/ffcli"
"tailscale.com/ipn"
@@ -24,7 +23,7 @@ var downCmd = &ffcli.Command{
}
func newDownFlagSet() *flag.FlagSet {
downf := flag.NewFlagSet("down", flag.ExitOnError)
downf := newFlagSet("down")
registerAcceptRiskFlag(downf)
return downf
}
@@ -45,7 +44,7 @@ func runDown(ctx context.Context, args []string) error {
return fmt.Errorf("error fetching current status: %w", err)
}
if st.BackendState == "Stopped" {
fmt.Fprintf(os.Stderr, "Tailscale was already stopped.\n")
fmt.Fprintf(Stderr, "Tailscale was already stopped.\n")
return nil
}
_, err = localClient.EditPrefs(ctx, &ipn.MaskedPrefs{

View File

@@ -14,7 +14,6 @@ import (
"log"
"mime"
"net/http"
"net/netip"
"os"
"path"
"path/filepath"
@@ -24,6 +23,7 @@ import (
"github.com/peterbourgon/ff/v3/ffcli"
"golang.org/x/time/rate"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/envknob"
"tailscale.com/ipn"
@@ -54,7 +54,7 @@ var fileCpCmd = &ffcli.Command{
ShortHelp: "Copy file(s) to a host",
Exec: runCp,
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("cp", flag.ExitOnError)
fs := newFlagSet("cp")
fs.StringVar(&cpArgs.name, "name", "", "alternate filename to use, especially useful when <file> is \"-\" (stdin)")
fs.BoolVar(&cpArgs.verbose, "verbose", false, "verbose output")
fs.BoolVar(&cpArgs.targets, "targets", false, "list possible file cp targets")
@@ -85,7 +85,7 @@ func runCp(ctx context.Context, args []string) error {
hadBrackets = true
target = strings.TrimSuffix(strings.TrimPrefix(target, "["), "]")
}
if ip, err := netip.ParseAddr(target); err == nil && ip.Is6() && !hadBrackets {
if ip, err := netaddr.ParseIP(target); err == nil && ip.Is6() && !hadBrackets {
return fmt.Errorf("an IPv6 literal must be written as [%s]", ip)
} else if hadBrackets && (err != nil || !ip.Is6()) {
return errors.New("unexpected brackets around target")
@@ -100,7 +100,7 @@ func runCp(ctx context.Context, args []string) error {
return fmt.Errorf("can't send to %s: %v", target, err)
}
if isOffline {
fmt.Fprintf(os.Stderr, "# warning: %s is offline\n", target)
fmt.Fprintf(Stderr, "# warning: %s is offline\n", target)
}
if len(files) > 1 {
@@ -168,7 +168,7 @@ func runCp(ctx context.Context, args []string) error {
}
func getTargetStableID(ctx context.Context, ipStr string) (id tailcfg.StableNodeID, isOffline bool, err error) {
ip, err := netip.ParseAddr(ipStr)
ip, err := netaddr.ParseIP(ipStr)
if err != nil {
return "", false, err
}
@@ -179,7 +179,7 @@ func getTargetStableID(ctx context.Context, ipStr string) (id tailcfg.StableNode
for _, ft := range fts {
n := ft.Node
for _, a := range n.Addresses {
if a.Addr() != ip {
if a.IP() != ip {
continue
}
isOffline = n.Online != nil && !*n.Online
@@ -191,7 +191,7 @@ func getTargetStableID(ctx context.Context, ipStr string) (id tailcfg.StableNode
// fileTargetErrorDetail returns a non-nil error saying why ip is an
// invalid file sharing target.
func fileTargetErrorDetail(ctx context.Context, ip netip.Addr) error {
func fileTargetErrorDetail(ctx context.Context, ip netaddr.IP) error {
found := false
if st, err := localClient.Status(ctx); err == nil && st.Self != nil {
for _, peer := range st.Peer {
@@ -281,7 +281,7 @@ func runCpTargets(ctx context.Context, args []string) error {
if detail != "" {
detail = "\t" + detail
}
fmt.Printf("%s\t%s%s\n", n.Addresses[0].Addr(), n.ComputedName, detail)
printf("%s\t%s%s\n", n.Addresses[0].IP(), n.ComputedName, detail)
}
return nil
}
@@ -315,7 +315,7 @@ var fileGetCmd = &ffcli.Command{
ShortHelp: "Move files out of the Tailscale file inbox",
Exec: runFileGet,
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("get", flag.ExitOnError)
fs := newFlagSet("get")
fs.BoolVar(&getArgs.wait, "wait", false, "wait for a file to arrive if inbox is empty")
fs.BoolVar(&getArgs.loop, "loop", false, "run get in a loop, receiving files as they come in")
fs.BoolVar(&getArgs.verbose, "verbose", false, "verbose output")
@@ -415,7 +415,7 @@ func runFileGetOneBatch(ctx context.Context, dir string) []error {
break
}
if getArgs.verbose {
fmt.Printf("waiting for file...")
printf("waiting for file...")
}
if err := waitForFile(ctx); err != nil {
errs = append(errs, err)
@@ -436,7 +436,7 @@ func runFileGetOneBatch(ctx context.Context, dir string) []error {
continue
}
if getArgs.verbose {
fmt.Printf("wrote %v as %v (%d bytes)\n", wf.Name, writtenFile, size)
printf("wrote %v as %v (%d bytes)\n", wf.Name, writtenFile, size)
}
if err = localClient.DeleteWaitingFile(ctx, wf.Name); err != nil {
errs = append(errs, fmt.Errorf("deleting %q from inbox: %v", wf.Name, err))
@@ -448,7 +448,7 @@ func runFileGetOneBatch(ctx context.Context, dir string) []error {
// persistently stuck files are basically an error
errs = append(errs, fmt.Errorf("moved %d/%d files", deleted, len(wfs)))
} else if getArgs.verbose {
fmt.Printf("moved %d/%d files\n", deleted, len(wfs))
printf("moved %d/%d files\n", deleted, len(wfs))
}
return errs
}
@@ -471,7 +471,7 @@ func runFileGet(ctx context.Context, args []string) error {
for {
errs := runFileGetOneBatch(ctx, dir)
for _, err := range errs {
fmt.Println(err)
outln(err)
}
if len(errs) > 0 {
// It's possible whatever caused the error(s) (e.g. conflicting target file,
@@ -493,7 +493,7 @@ func runFileGet(ctx context.Context, args []string) error {
return nil
}
for _, err := range errs[:len(errs)-1] {
fmt.Println(err)
outln(err)
}
return errs[len(errs)-1]
}

View File

@@ -9,9 +9,9 @@ import (
"errors"
"flag"
"fmt"
"net/netip"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/ipn/ipnstate"
)
@@ -22,7 +22,7 @@ var ipCmd = &ffcli.Command{
LongHelp: "Show Tailscale IP addresses for peer. Peer defaults to the current machine.",
Exec: runIP,
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("ip", flag.ExitOnError)
fs := newFlagSet("ip")
fs.BoolVar(&ipArgs.want1, "1", false, "only print one IP address")
fs.BoolVar(&ipArgs.want4, "4", false, "only print IPv4 address")
fs.BoolVar(&ipArgs.want6, "6", false, "only print IPv6 address")
@@ -85,7 +85,7 @@ func runIP(ctx context.Context, args []string) error {
for _, ip := range ips {
if ip.Is4() && v4 || ip.Is6() && v6 {
match = true
fmt.Println(ip)
outln(ip)
}
}
if !match {
@@ -100,7 +100,7 @@ func runIP(ctx context.Context, args []string) error {
}
func peerMatchingIP(st *ipnstate.Status, ipStr string) (ps *ipnstate.PeerStatus, ok bool) {
ip, err := netip.ParseAddr(ipStr)
ip, err := netaddr.ParseIP(ipStr)
if err != nil {
return
}

View File

@@ -29,7 +29,7 @@ func runNC(ctx context.Context, args []string) error {
}
description, ok := isRunningOrStarting(st)
if !ok {
fmt.Printf("%s\n", description)
printf("%s\n", description)
os.Exit(1)
}

View File

@@ -13,7 +13,6 @@ import (
"io/ioutil"
"log"
"net/http"
"os"
"sort"
"strings"
"time"
@@ -33,7 +32,7 @@ var netcheckCmd = &ffcli.Command{
ShortHelp: "Print an analysis of local network conditions",
Exec: runNetcheck,
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("netcheck", flag.ExitOnError)
fs := newFlagSet("netcheck")
fs.StringVar(&netcheckArgs.format, "format", "", `output format; empty (for human-readable), "json" or "json-line"`)
fs.DurationVar(&netcheckArgs.every, "every", 0, "if non-zero, do an incremental report with the given frequency")
fs.BoolVar(&netcheckArgs.verbose, "verbose", false, "verbose logs")
@@ -60,7 +59,7 @@ func runNetcheck(ctx context.Context, args []string) error {
}
if strings.HasPrefix(netcheckArgs.format, "json") {
fmt.Fprintln(os.Stderr, "# Warning: this JSON format is not yet considered a stable interface")
fmt.Fprintln(Stderr, "# Warning: this JSON format is not yet considered a stable interface")
}
dm, err := localClient.CurrentDERPMap(ctx)
@@ -112,38 +111,38 @@ func printReport(dm *tailcfg.DERPMap, report *netcheck.Report) error {
}
if j != nil {
j = append(j, '\n')
os.Stdout.Write(j)
Stdout.Write(j)
return nil
}
fmt.Printf("\nReport:\n")
fmt.Printf("\t* UDP: %v\n", report.UDP)
printf("\nReport:\n")
printf("\t* UDP: %v\n", report.UDP)
if report.GlobalV4 != "" {
fmt.Printf("\t* IPv4: yes, %v\n", report.GlobalV4)
printf("\t* IPv4: yes, %v\n", report.GlobalV4)
} else {
fmt.Printf("\t* IPv4: (no addr found)\n")
printf("\t* IPv4: (no addr found)\n")
}
if report.GlobalV6 != "" {
fmt.Printf("\t* IPv6: yes, %v\n", report.GlobalV6)
printf("\t* IPv6: yes, %v\n", report.GlobalV6)
} else if report.IPv6 {
fmt.Printf("\t* IPv6: (no addr found)\n")
printf("\t* IPv6: (no addr found)\n")
} else if report.OSHasIPv6 {
fmt.Printf("\t* IPv6: no, but OS has support\n")
printf("\t* IPv6: no, but OS has support\n")
} else {
fmt.Printf("\t* IPv6: no, unavailable in OS\n")
printf("\t* IPv6: no, unavailable in OS\n")
}
fmt.Printf("\t* MappingVariesByDestIP: %v\n", report.MappingVariesByDestIP)
fmt.Printf("\t* HairPinning: %v\n", report.HairPinning)
fmt.Printf("\t* PortMapping: %v\n", portMapping(report))
printf("\t* MappingVariesByDestIP: %v\n", report.MappingVariesByDestIP)
printf("\t* HairPinning: %v\n", report.HairPinning)
printf("\t* PortMapping: %v\n", portMapping(report))
// When DERP latency checking failed,
// magicsock will try to pick the DERP server that
// most of your other nodes are also using
if len(report.RegionLatency) == 0 {
fmt.Printf("\t* Nearest DERP: unknown (no response to latency probes)\n")
printf("\t* Nearest DERP: unknown (no response to latency probes)\n")
} else {
fmt.Printf("\t* Nearest DERP: %v\n", dm.Regions[report.PreferredDERP].RegionName)
fmt.Printf("\t* DERP latency:\n")
printf("\t* Nearest DERP: %v\n", dm.Regions[report.PreferredDERP].RegionName)
printf("\t* DERP latency:\n")
var rids []int
for rid := range dm.Regions {
rids = append(rids, rid)
@@ -170,7 +169,7 @@ func printReport(dm *tailcfg.DERPMap, report *netcheck.Report) error {
if netcheckArgs.verbose {
derpNum = fmt.Sprintf("derp%d, ", rid)
}
fmt.Printf("\t\t- %3s: %-7s (%s%s)\n", r.RegionCode, latency, derpNum, r.RegionName)
printf("\t\t- %3s: %-7s (%s%s)\n", r.RegionCode, latency, derpNum, r.RegionName)
}
}
return nil

View File

@@ -11,12 +11,12 @@ import (
"fmt"
"log"
"net"
"net/netip"
"os"
"strings"
"time"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
)
@@ -46,7 +46,7 @@ relay node.
`),
Exec: runPing,
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("ping", flag.ExitOnError)
fs := newFlagSet("ping")
fs.BoolVar(&pingArgs.verbose, "verbose", false, "verbose output")
fs.BoolVar(&pingArgs.untilDirect, "until-direct", true, "stop once a direct path is established")
fs.BoolVar(&pingArgs.tsmp, "tsmp", false, "do a TSMP-level ping (through WireGuard, but not either host OS stack)")
@@ -88,7 +88,7 @@ func runPing(ctx context.Context, args []string) error {
}
description, ok := isRunningOrStarting(st)
if !ok {
fmt.Printf("%s\n", description)
printf("%s\n", description)
os.Exit(1)
}
@@ -103,7 +103,7 @@ func runPing(ctx context.Context, args []string) error {
return err
}
if self {
fmt.Printf("%v is local Tailscale IP\n", ip)
printf("%v is local Tailscale IP\n", ip)
return nil
}
@@ -116,11 +116,11 @@ func runPing(ctx context.Context, args []string) error {
for {
n++
ctx, cancel := context.WithTimeout(ctx, pingArgs.timeout)
pr, err := localClient.Ping(ctx, netip.MustParseAddr(ip), pingType())
pr, err := localClient.Ping(ctx, netaddr.MustParseIP(ip), pingType())
cancel()
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
fmt.Printf("ping %q timed out\n", ip)
printf("ping %q timed out\n", ip)
if n == pingArgs.num {
if !anyPong {
return errors.New("no reply")
@@ -133,7 +133,7 @@ func runPing(ctx context.Context, args []string) error {
}
if pr.Err != "" {
if pr.IsLocalIP {
fmt.Println(pr.Err)
outln(pr.Err)
return nil
}
return errors.New(pr.Err)
@@ -149,7 +149,7 @@ func runPing(ctx context.Context, args []string) error {
via = string(pingType())
}
if pingArgs.peerAPI {
fmt.Printf("hit peerapi of %s (%s) at %s in %s\n", pr.NodeIP, pr.NodeName, pr.PeerAPIURL, latency)
printf("hit peerapi of %s (%s) at %s in %s\n", pr.NodeIP, pr.NodeName, pr.PeerAPIURL, latency)
return nil
}
anyPong = true
@@ -157,7 +157,7 @@ func runPing(ctx context.Context, args []string) error {
if pr.PeerAPIPort != 0 {
extra = fmt.Sprintf(", %d", pr.PeerAPIPort)
}
fmt.Printf("pong from %s (%s%s) via %v in %v\n", pr.NodeName, pr.NodeIP, extra, via, latency)
printf("pong from %s (%s%s) via %v in %v\n", pr.NodeName, pr.NodeIP, extra, via, latency)
if pingArgs.tsmp || pingArgs.icmp {
return nil
}

View File

@@ -10,7 +10,6 @@ import (
"errors"
"fmt"
"log"
"net/netip"
"os"
"os/user"
"path/filepath"
@@ -18,6 +17,7 @@ import (
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsaddr"
@@ -163,10 +163,10 @@ func nodeDNSNameFromArg(st *ipnstate.Status, arg string) (dnsName string, ok boo
if arg == "" {
return
}
argIP, _ := netip.ParseAddr(arg)
argIP, _ := netaddr.ParseIP(arg)
for _, ps := range st.Peer {
dnsName = ps.DNSName
if argIP.IsValid() {
if !argIP.IsZero() {
for _, ip := range ps.TailscaleIPs {
if ip == argIP {
return dnsName, true
@@ -202,7 +202,7 @@ func isSSHOverTailscale() bool {
if !ok {
return false
}
ip, err := netip.ParseAddr(ipStr)
ip, err := netaddr.ParseIP(ipStr)
if err != nil {
return false
}

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !windows
// +build !windows
//go:build !js && !windows
// +build !js,!windows
package cli

View File

@@ -0,0 +1,17 @@
// Copyright (c) 2022 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 cli
import (
"errors"
)
func findSSH() (string, error) {
return "", errors.New("Not implemented")
}
func execSSH(ssh string, argv []string) error {
return errors.New("Not implemented")
}

View File

@@ -13,12 +13,12 @@ import (
"fmt"
"net"
"net/http"
"net/netip"
"os"
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"github.com/toqueteos/webbrowser"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/interfaces"
@@ -46,7 +46,7 @@ https://github.com/tailscale/tailscale/blob/main/ipn/ipnstate/ipnstate.go
`),
Exec: runStatus,
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("status", flag.ExitOnError)
fs := newFlagSet("status")
fs.BoolVar(&statusArgs.json, "json", false, "output in JSON format (WARNING: format subject to change)")
fs.BoolVar(&statusArgs.web, "web", false, "run webserver with HTML showing status")
fs.BoolVar(&statusArgs.active, "active", false, "filter output to only peers with active sessions (not applicable to web mode)")
@@ -92,7 +92,7 @@ func runStatus(ctx context.Context, args []string) error {
if err != nil {
return err
}
fmt.Printf("%s", j)
printf("%s", j)
return nil
}
if statusArgs.web {
@@ -101,7 +101,7 @@ func runStatus(ctx context.Context, args []string) error {
return err
}
statusURL := interfaces.HTTPOfListener(ln)
fmt.Printf("Serving Tailscale status at %v ...\n", statusURL)
printf("Serving Tailscale status at %v ...\n", statusURL)
go func() {
<-ctx.Done()
ln.Close()
@@ -131,16 +131,16 @@ func runStatus(ctx context.Context, args []string) error {
// print health check information prior to checking LocalBackend state as
// it may provide an explanation to the user if we choose to exit early
if len(st.Health) > 0 {
fmt.Printf("# Health check:\n")
printf("# Health check:\n")
for _, m := range st.Health {
fmt.Printf("# - %s\n", m)
printf("# - %s\n", m)
}
fmt.Println()
outln()
}
description, ok := isRunningOrStarting(st)
if !ok {
fmt.Println(description)
outln(description)
os.Exit(1)
}
@@ -213,7 +213,7 @@ func runStatus(ctx context.Context, args []string) error {
printPS(ps)
}
}
os.Stdout.Write(buf.Bytes())
Stdout.Write(buf.Bytes())
return nil
}
@@ -260,7 +260,7 @@ func ownerLogin(st *ipnstate.Status, ps *ipnstate.PeerStatus) string {
return u.LoginName
}
func firstIPString(v []netip.Addr) string {
func firstIPString(v []netaddr.IP) string {
if len(v) == 0 {
return ""
}

View File

@@ -13,7 +13,6 @@ import (
"flag"
"fmt"
"log"
"net/netip"
"os"
"reflect"
"runtime"
@@ -25,6 +24,7 @@ import (
shellquote "github.com/kballard/go-shellquote"
"github.com/peterbourgon/ff/v3/ffcli"
qrcode "github.com/skip2/go-qrcode"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsaddr"
@@ -85,7 +85,7 @@ var upFlagSet = newUpFlagSet(effectiveGOOS(), &upArgs)
func inTest() bool { return flag.Lookup("test.v") != nil }
func newUpFlagSet(goos string, upArgs *upArgsT) *flag.FlagSet {
upf := flag.NewFlagSet("up", flag.ExitOnError)
upf := newFlagSet("up")
upf.BoolVar(&upArgs.qr, "qr", false, "show QR code for login URLs")
upf.BoolVar(&upArgs.json, "json", false, "output in JSON format (WARNING: format subject to change)")
@@ -195,22 +195,22 @@ type upOutputJSON struct {
}
func warnf(format string, args ...any) {
fmt.Printf("Warning: "+format+"\n", args...)
printf("Warning: "+format+"\n", args...)
}
var (
ipv4default = netip.MustParsePrefix("0.0.0.0/0")
ipv6default = netip.MustParsePrefix("::/0")
ipv4default = netaddr.MustParseIPPrefix("0.0.0.0/0")
ipv6default = netaddr.MustParseIPPrefix("::/0")
)
func validateViaPrefix(ipp netip.Prefix) error {
func validateViaPrefix(ipp netaddr.IPPrefix) error {
if !tsaddr.IsViaPrefix(ipp) {
return fmt.Errorf("%v is not a 4-in-6 prefix", ipp)
}
if ipp.Bits() < (128 - 32) {
return fmt.Errorf("%v 4-in-6 prefix must be at least a /%v", ipp, 128-32)
}
a := ipp.Addr().As16()
a := ipp.IP().As16()
// The first 64 bits of a are the via prefix.
// The next 32 bits are the "site ID".
// The last 32 bits are the IPv4.
@@ -223,13 +223,13 @@ func validateViaPrefix(ipp netip.Prefix) error {
return nil
}
func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]netip.Prefix, error) {
routeMap := map[netip.Prefix]bool{}
func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]netaddr.IPPrefix, error) {
routeMap := map[netaddr.IPPrefix]bool{}
if advertiseRoutes != "" {
var default4, default6 bool
advroutes := strings.Split(advertiseRoutes, ",")
for _, s := range advroutes {
ipp, err := netip.ParsePrefix(s)
ipp, err := netaddr.ParseIPPrefix(s)
if err != nil {
return nil, fmt.Errorf("%q is not a valid IP address or CIDR prefix", s)
}
@@ -255,10 +255,10 @@ func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
}
}
if advertiseDefaultRoute {
routeMap[netip.MustParsePrefix("0.0.0.0/0")] = true
routeMap[netip.MustParsePrefix("::/0")] = true
routeMap[netaddr.MustParseIPPrefix("0.0.0.0/0")] = true
routeMap[netaddr.MustParseIPPrefix("::/0")] = true
}
routes := make([]netip.Prefix, 0, len(routeMap))
routes := make([]netaddr.IPPrefix, 0, len(routeMap))
for r := range routeMap {
routes = append(routes, r)
}
@@ -266,7 +266,7 @@ func calcAdvertiseRoutes(advertiseRoutes string, advertiseDefaultRoute bool) ([]
if routes[i].Bits() != routes[j].Bits() {
return routes[i].Bits() < routes[j].Bits()
}
return routes[i].Addr().Less(routes[j].Addr())
return routes[i].IP().Less(routes[j].IP())
})
return routes, nil
}
@@ -538,7 +538,7 @@ func runUp(ctx context.Context, args []string) (retErr error) {
if env.upArgs.json {
printUpDoneJSON(ipn.NeedsMachineAuth, "")
} else {
fmt.Fprintf(os.Stderr, "\nTo authorize your machine, visit (as admin):\n\n\t%s\n\n", prefs.AdminPageURL())
fmt.Fprintf(Stderr, "\nTo authorize your machine, visit (as admin):\n\n\t%s\n\n", prefs.AdminPageURL())
}
case ipn.Running:
// Done full authentication process
@@ -546,7 +546,7 @@ func runUp(ctx context.Context, args []string) (retErr error) {
printUpDoneJSON(ipn.Running, "")
} else if printed {
// Only need to print an update if we printed the "please click" message earlier.
fmt.Fprintf(os.Stderr, "Success.\n")
fmt.Fprintf(Stderr, "Success.\n")
}
select {
case running <- true:
@@ -575,13 +575,13 @@ func runUp(ctx context.Context, args []string) (retErr error) {
fmt.Println(string(data))
}
} else {
fmt.Fprintf(os.Stderr, "\nTo authenticate, visit:\n\n\t%s\n\n", *url)
fmt.Fprintf(Stderr, "\nTo authenticate, visit:\n\n\t%s\n\n", *url)
if upArgs.qr {
q, err := qrcode.New(*url, qrcode.Medium)
if err != nil {
log.Printf("QR code error: %v", err)
} else {
fmt.Fprintf(os.Stderr, "%s\n", q.ToString(false))
fmt.Fprintf(Stderr, "%s\n", q.ToString(false))
}
}
}
@@ -695,12 +695,12 @@ func checkSSHUpWarnings(ctx context.Context) {
return
}
if len(st.Health) == 1 && strings.Contains(st.Health[0], "SSH") {
fmt.Printf("%s\n", st.Health[0])
printf("%s\n", st.Health[0])
return
}
fmt.Printf("# Health check:\n")
printf("# Health check:\n")
for _, m := range st.Health {
fmt.Printf(" - %s\n", m)
printf(" - %s\n", m)
}
}
@@ -790,7 +790,7 @@ type upCheckEnv struct {
flagSet *flag.FlagSet
upArgs upArgsT
backendState string
curExitNodeIP netip.Addr
curExitNodeIP netaddr.IP
distro distro.Distro
}
@@ -913,10 +913,10 @@ func prefsToFlags(env upCheckEnv, prefs *ipn.Prefs) (flagVal map[string]any) {
ret := make(map[string]any)
exitNodeIPStr := func() string {
if prefs.ExitNodeIP.IsValid() {
if !prefs.ExitNodeIP.IsZero() {
return prefs.ExitNodeIP.String()
}
if prefs.ExitNodeID.IsZero() || !env.curExitNodeIP.IsValid() {
if prefs.ExitNodeID.IsZero() || env.curExitNodeIP.IsZero() {
return ""
}
return env.curExitNodeIP.String()
@@ -991,13 +991,13 @@ func fmtFlagValueArg(flagName string, val any) string {
return fmt.Sprintf("--%s=%v", flagName, shellquote.Join(fmt.Sprint(val)))
}
func hasExitNodeRoutes(rr []netip.Prefix) bool {
func hasExitNodeRoutes(rr []netaddr.IPPrefix) bool {
var v4, v6 bool
for _, r := range rr {
if r.Bits() == 0 {
if r.Addr().Is4() {
if r.IP().Is4() {
v4 = true
} else if r.Addr().Is6() {
} else if r.IP().Is6() {
v6 = true
}
}
@@ -1008,11 +1008,11 @@ func hasExitNodeRoutes(rr []netip.Prefix) bool {
// withoutExitNodes returns rr unchanged if it has only 1 or 0 /0
// routes. If it has both IPv4 and IPv6 /0 routes, then it returns
// a copy with all /0 routes removed.
func withoutExitNodes(rr []netip.Prefix) []netip.Prefix {
func withoutExitNodes(rr []netaddr.IPPrefix) []netaddr.IPPrefix {
if !hasExitNodeRoutes(rr) {
return rr
}
var out []netip.Prefix
var out []netaddr.IPPrefix
for _, r := range rr {
if r.Bits() > 0 {
out = append(out, r)
@@ -1023,11 +1023,11 @@ func withoutExitNodes(rr []netip.Prefix) []netip.Prefix {
// exitNodeIP returns the exit node IP from p, using st to map
// it from its ID form to an IP address if needed.
func exitNodeIP(p *ipn.Prefs, st *ipnstate.Status) (ip netip.Addr) {
func exitNodeIP(p *ipn.Prefs, st *ipnstate.Status) (ip netaddr.IP) {
if p == nil {
return
}
if p.ExitNodeIP.IsValid() {
if !p.ExitNodeIP.IsZero() {
return p.ExitNodeIP
}
id := p.ExitNodeID

View File

@@ -18,7 +18,7 @@ var versionCmd = &ffcli.Command{
ShortUsage: "version [flags]",
ShortHelp: "Print Tailscale version",
FlagSet: (func() *flag.FlagSet {
fs := flag.NewFlagSet("version", flag.ExitOnError)
fs := newFlagSet("version")
fs.BoolVar(&versionArgs.daemon, "daemon", false, "also print local node's daemon version")
return fs
})(),
@@ -34,16 +34,16 @@ func runVersion(ctx context.Context, args []string) error {
return fmt.Errorf("too many non-flag arguments: %q", args)
}
if !versionArgs.daemon {
fmt.Println(version.String())
outln(version.String())
return nil
}
fmt.Printf("Client: %s\n", version.String())
printf("Client: %s\n", version.String())
st, err := localClient.StatusWithoutPeers(ctx)
if err != nil {
return err
}
fmt.Printf("Daemon: %s\n", st.Version)
printf("Daemon: %s\n", st.Version)
return nil
}

View File

@@ -20,7 +20,6 @@ import (
"net"
"net/http"
"net/http/cgi"
"net/netip"
"net/url"
"os"
"os/exec"
@@ -28,6 +27,7 @@ import (
"strings"
"github.com/peterbourgon/ff/v3/ffcli"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/tailcfg"
"tailscale.com/types/preftype"
@@ -75,7 +75,7 @@ Tailscale, as opposed to a CLI or a native app.
`),
FlagSet: (func() *flag.FlagSet {
webf := flag.NewFlagSet("web", flag.ExitOnError)
webf := newFlagSet("web")
webf.StringVar(&webArgs.listen, "listen", "localhost:8088", "listen address; use port 0 for automatic")
webf.BoolVar(&webArgs.cgi, "cgi", false, "run as CGI script")
return webf
@@ -393,8 +393,8 @@ func webHandler(w http.ResponseWriter, r *http.Request) {
Status: st.BackendState,
DeviceName: deviceName,
}
exitNodeRouteV4 := netip.MustParsePrefix("0.0.0.0/0")
exitNodeRouteV6 := netip.MustParsePrefix("::/0")
exitNodeRouteV4 := netaddr.MustParseIPPrefix("0.0.0.0/0")
exitNodeRouteV6 := netaddr.MustParseIPPrefix("::/0")
for _, r := range prefs.AdvertiseRoutes {
if r == exitNodeRouteV4 || r == exitNodeRouteV6 {
data.AdvertiseExitNode = true

View File

@@ -1,13 +1,9 @@
tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/depaware)
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/negotiate+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/josharian/native from github.com/mdlayher/netlink+
L 💣 github.com/jsimonetti/rtnetlink from tailscale.com/net/interfaces
L github.com/jsimonetti/rtnetlink/internal/unix from github.com/jsimonetti/rtnetlink
@@ -30,10 +26,11 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
github.com/tailscale/goupnp/ssdp from github.com/tailscale/goupnp
github.com/tcnksm/go-httpstat from tailscale.com/net/netcheck
github.com/toqueteos/webbrowser from tailscale.com/cmd/tailscale/cli
github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/intern from inet.af/netaddr
💣 go4.org/mem from tailscale.com/derp+
go4.org/netipx from tailscale.com/wgengine/filter
go4.org/unsafe/assume-no-moving-gc from go4.org/intern
W 💣 golang.zx2c4.com/wireguard/windows/tunnel/winipcfg from tailscale.com/net/interfaces+
inet.af/netaddr from tailscale.com/cmd/tailscale/cli+
nhooyr.io/websocket from tailscale.com/derp/derphttp+
nhooyr.io/websocket/internal/errd from nhooyr.io/websocket
nhooyr.io/websocket/internal/xsync from nhooyr.io/websocket
@@ -57,7 +54,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/net/dnsfallback from tailscale.com/control/controlhttp
tailscale.com/net/flowtrack from tailscale.com/wgengine/filter+
💣 tailscale.com/net/interfaces from tailscale.com/cmd/tailscale/cli+
tailscale.com/net/netaddr from tailscale.com/disco+
tailscale.com/net/netcheck from tailscale.com/cmd/tailscale/cli
tailscale.com/net/neterror from tailscale.com/net/netcheck+
tailscale.com/net/netknob from tailscale.com/net/netns
@@ -73,7 +69,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
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+
tailscale.com/tka from tailscale.com/types/key
W tailscale.com/tsconst from tailscale.com/net/interfaces
💣 tailscale.com/tstime/mono from tailscale.com/tstime/rate
tailscale.com/tstime/rate from tailscale.com/wgengine/filter
@@ -83,7 +78,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/types/key from tailscale.com/derp+
tailscale.com/types/logger from tailscale.com/cmd/tailscale/cli+
tailscale.com/types/netmap from tailscale.com/ipn
tailscale.com/types/nettype from tailscale.com/net/netcheck+
tailscale.com/types/opt from tailscale.com/net/netcheck+
tailscale.com/types/pad32 from tailscale.com/derp
tailscale.com/types/persist from tailscale.com/ipn
@@ -102,9 +96,8 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
tailscale.com/version from tailscale.com/cmd/tailscale/cli+
tailscale.com/version/distro from tailscale.com/cmd/tailscale/cli+
tailscale.com/wgengine/filter from tailscale.com/types/netmap
golang.org/x/crypto/argon2 from tailscale.com/tka
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box+
golang.org/x/crypto/blake2s from tailscale.com/control/controlbase+
golang.org/x/crypto/blake2b from golang.org/x/crypto/nacl/box
golang.org/x/crypto/blake2s from tailscale.com/control/controlbase
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305
golang.org/x/crypto/chacha20poly1305 from crypto/tls+
golang.org/x/crypto/cryptobyte from crypto/ecdsa+
@@ -162,7 +155,6 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
embed from tailscale.com/cmd/tailscale/cli+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from tailscale.com/tka
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/hex from crypto/x509+
@@ -183,7 +175,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
image/color from github.com/skip2/go-qrcode+
image/png from github.com/skip2/go-qrcode
io from bufio+
io/fs from crypto/x509+
io/fs from crypto/rand+
io/ioutil from golang.org/x/sys/cpu+
log from expvar+
math from compress/flate+
@@ -198,7 +190,7 @@ tailscale.com/cmd/tailscale dependencies: (generated by github.com/tailscale/dep
net/http/cgi from tailscale.com/cmd/tailscale/cli
net/http/httptrace from github.com/tcnksm/go-httpstat+
net/http/internal from net/http
net/netip from net+
net/netip from net
net/textproto from golang.org/x/net/http/httpguts+
net/url from crypto/x509+
os from crypto/rand+

View File

@@ -20,12 +20,12 @@ import (
"net"
"net/http"
"net/http/httptrace"
"net/netip"
"net/url"
"os"
"strings"
"time"
"inet.af/netaddr"
"tailscale.com/derp/derphttp"
"tailscale.com/envknob"
"tailscale.com/ipn"
@@ -266,11 +266,11 @@ func debugPortmap(ctx context.Context) error {
return err
}
gatewayAndSelfIP := func() (gw, self netip.Addr, ok bool) {
gatewayAndSelfIP := func() (gw, self netaddr.IP, ok bool) {
if v := os.Getenv("TS_DEBUG_GW_SELF"); strings.Contains(v, "/") {
i := strings.Index(v, "/")
gw = netip.MustParseAddr(v[:i])
self = netip.MustParseAddr(v[i+1:])
gw = netaddr.MustParseIP(v[:i])
self = netaddr.MustParseIP(v[i+1:])
return gw, self, true
}
return linkMon.GatewayAndSelfIP()

View File

@@ -1,7 +1,5 @@
tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/depaware)
filippo.io/edwards25519 from github.com/hdevalence/ed25519consensus
filippo.io/edwards25519/field from filippo.io/edwards25519
W 💣 github.com/alexbrainman/sspi from github.com/alexbrainman/sspi/internal/common+
W github.com/alexbrainman/sspi/internal/common from github.com/alexbrainman/sspi/negotiate
W 💣 github.com/alexbrainman/sspi/negotiate from tailscale.com/net/tshttpproxy
@@ -64,13 +62,11 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/aws/smithy-go/waiter from github.com/aws/aws-sdk-go-v2/service/ssm
L github.com/coreos/go-iptables/iptables from tailscale.com/wgengine/router
LD 💣 github.com/creack/pty from tailscale.com/ssh/tailssh
github.com/fxamacker/cbor/v2 from tailscale.com/tka
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/net/dns+
github.com/golang/groupcache/lru from tailscale.com/net/dnscache
github.com/google/btree from gvisor.dev/gvisor/pkg/tcpip/header+
github.com/hdevalence/ed25519consensus from tailscale.com/tka
L github.com/insomniacslk/dhcp/dhcpv4 from tailscale.com/net/tstun
L github.com/insomniacslk/dhcp/iana from github.com/insomniacslk/dhcp/dhcpv4
L github.com/insomniacslk/dhcp/interfaces from github.com/insomniacslk/dhcp/dhcpv4
@@ -117,9 +113,9 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
L github.com/u-root/uio/uio from github.com/insomniacslk/dhcp/dhcpv4+
L 💣 github.com/vishvananda/netlink/nl from github.com/tailscale/netlink
L github.com/vishvananda/netns from github.com/tailscale/netlink+
github.com/x448/float16 from github.com/fxamacker/cbor/v2
💣 go4.org/intern from inet.af/netaddr
💣 go4.org/mem from tailscale.com/control/controlbase+
go4.org/netipx from tailscale.com/ipn/ipnlocal+
go4.org/unsafe/assume-no-moving-gc from go4.org/intern
W 💣 golang.zx2c4.com/wintun from golang.zx2c4.com/wireguard/tun
💣 golang.zx2c4.com/wireguard/conn from golang.zx2c4.com/wireguard/device+
W 💣 golang.zx2c4.com/wireguard/conn/winrio from golang.zx2c4.com/wireguard/conn
@@ -172,6 +168,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
gvisor.dev/gvisor/pkg/tcpip/transport/tcpconntrack from gvisor.dev/gvisor/pkg/tcpip/stack
gvisor.dev/gvisor/pkg/tcpip/transport/udp from tailscale.com/net/tstun+
gvisor.dev/gvisor/pkg/waiter from gvisor.dev/gvisor/pkg/context+
inet.af/netaddr from tailscale.com/control/controlclient+
inet.af/peercred from tailscale.com/ipn/ipnserver
W 💣 inet.af/wf from tailscale.com/wf
nhooyr.io/websocket from tailscale.com/derp/derphttp+
@@ -219,7 +216,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/net/dnsfallback from tailscale.com/control/controlclient+
tailscale.com/net/flowtrack from tailscale.com/net/packet+
💣 tailscale.com/net/interfaces from tailscale.com/control/controlclient+
tailscale.com/net/netaddr from tailscale.com/disco+
tailscale.com/net/netcheck from tailscale.com/wgengine/magicsock
tailscale.com/net/neterror from tailscale.com/net/dns/resolver+
tailscale.com/net/netknob from tailscale.com/net/netns+
@@ -244,7 +240,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/syncs from tailscale.com/control/controlknobs+
tailscale.com/tailcfg from tailscale.com/client/tailscale/apitype+
LD tailscale.com/tempfork/gliderlabs/ssh from tailscale.com/ssh/tailssh
tailscale.com/tka from tailscale.com/types/key
W tailscale.com/tsconst from tailscale.com/net/interfaces
tailscale.com/tstime from tailscale.com/wgengine/magicsock
💣 tailscale.com/tstime/mono from tailscale.com/net/tstun+
@@ -257,7 +252,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/types/key from tailscale.com/control/controlbase+
tailscale.com/types/logger from tailscale.com/control/controlclient+
tailscale.com/types/netmap from tailscale.com/control/controlclient+
tailscale.com/types/nettype from tailscale.com/wgengine/magicsock+
tailscale.com/types/nettype from tailscale.com/wgengine/magicsock
tailscale.com/types/opt from tailscale.com/control/controlclient+
tailscale.com/types/pad32 from tailscale.com/derp
tailscale.com/types/persist from tailscale.com/control/controlclient+
@@ -274,6 +269,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/util/lineread from tailscale.com/hostinfo+
tailscale.com/util/mak from tailscale.com/control/controlclient+
tailscale.com/util/multierr from tailscale.com/control/controlclient+
tailscale.com/util/netconv from tailscale.com/wgengine/magicsock
tailscale.com/util/osshare from tailscale.com/ipn/ipnlocal+
tailscale.com/util/pidowner from tailscale.com/ipn/ipnserver
tailscale.com/util/racebuild from tailscale.com/logpolicy
@@ -295,8 +291,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
tailscale.com/wgengine/wglog from tailscale.com/wgengine
W 💣 tailscale.com/wgengine/winnet from tailscale.com/wgengine/router
golang.org/x/crypto/acme from tailscale.com/ipn/localapi
golang.org/x/crypto/argon2 from tailscale.com/tka
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 golang.zx2c4.com/wireguard/device+
LD golang.org/x/crypto/blowfish from golang.org/x/crypto/ssh/internal/bcrypt_pbkdf+
golang.org/x/crypto/chacha20 from golang.org/x/crypto/chacha20poly1305+
@@ -367,7 +362,6 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
embed from tailscale.com+
encoding from encoding/json+
encoding/asn1 from crypto/x509+
encoding/base32 from tailscale.com/tka
encoding/base64 from encoding/json+
encoding/binary from compress/gzip+
encoding/hex from crypto/x509+
@@ -384,7 +378,7 @@ tailscale.com/cmd/tailscaled dependencies: (generated by github.com/tailscale/de
hash/maphash from go4.org/mem
html from tailscale.com/ipn/ipnlocal+
io from bufio+
io/fs from crypto/x509+
io/fs from crypto/rand+
io/ioutil from github.com/godbus/dbus/v5+
log from expvar+
LD log/syslog from tailscale.com/ssh/tailssh

View File

@@ -21,7 +21,6 @@ import (
"net"
"net/http"
"net/http/pprof"
"net/netip"
"os"
"os/signal"
"path/filepath"
@@ -30,6 +29,7 @@ import (
"syscall"
"time"
"inet.af/netaddr"
"tailscale.com/cmd/tailscaled/childproc"
"tailscale.com/control/controlclient"
"tailscale.com/envknob"
@@ -128,8 +128,6 @@ var subCommands = map[string]*func([]string) error{
"be-child": &beChildFunc,
}
var beCLI func() // non-nil if CLI is linked in
func main() {
printVersion := false
flag.IntVar(&args.verbose, "verbose", 0, "log verbosity level; 0 is default, 1 or higher are increasingly verbose")
@@ -145,11 +143,6 @@ func main() {
flag.StringVar(&args.birdSocketPath, "bird-socket", "", "path of the bird unix socket")
flag.BoolVar(&printVersion, "version", false, "print version information and exit")
if len(os.Args) > 0 && filepath.Base(os.Args[0]) == "tailscale" && beCLI != nil {
beCLI()
return
}
if len(os.Args) > 1 {
sub := os.Args[1]
if fp, ok := subCommands[sub]; ok {
@@ -274,6 +267,13 @@ func ipnServerOpts() (o ipnserver.Options) {
}
switch goos {
case "js":
// The js/wasm client has no state storage so for now
// treat all interactive logins as ephemeral.
// TODO(bradfitz): if we start using browser LocalStorage
// or something, then rethink this.
o.LoginFlags = controlclient.LoginEphemeral
fallthrough
default:
o.SurviveDisconnects = true
o.AutostartStateKey = ipn.GlobalDaemonStateKey
@@ -366,11 +366,11 @@ func run() error {
ns.ProcessSubnets = useNetstack || wrapNetstack
if useNetstack {
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
dialer.UseNetstackForIP = func(ip netaddr.IP) bool {
_, ok := e.PeerForIP(ip)
return ok
}
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
dialer.NetstackDialTCP = func(ctx context.Context, dst netaddr.IPPort) (net.Conn, error) {
return ns.DialContextTCP(ctx, dst)
}
}

View File

@@ -25,7 +25,6 @@ import (
"encoding/json"
"fmt"
"log"
"net/netip"
"os"
"time"
@@ -33,6 +32,7 @@ import (
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
"golang.zx2c4.com/wireguard/windows/tunnel/winipcfg"
"inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/ipn/ipnserver"
"tailscale.com/ipn/store"
@@ -245,7 +245,7 @@ func beFirewallKillswitch() bool {
// is passed in via stdin encoded in json.
dcd := json.NewDecoder(os.Stdin)
for {
var routes []netip.Prefix
var routes []netaddr.IPPrefix
if err := dcd.Decode(&routes); err != nil {
log.Fatalf("parent process died or requested exit, exiting (%v)", err)
}

View File

@@ -1,25 +0,0 @@
// Copyright (c) 2022 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.
//go:build ts_include_cli
// +build ts_include_cli
package main
import (
"fmt"
"os"
"tailscale.com/cmd/tailscale/cli"
)
func init() {
beCLI = func() {
args := os.Args[1:]
if err := cli.Run(args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
}

View File

@@ -28,11 +28,6 @@ func runBuild() {
log.Fatalf("Cannot setup: %v", err)
}
log.Printf("Linting...\n")
if err := runYarn("lint"); err != nil {
log.Fatalf("Linting failed: %v", err)
}
if err := cleanDist(); err != nil {
log.Fatalf("Cannot clean %s: %v", *distDir, err)
}

View File

@@ -13,7 +13,6 @@ import (
"path/filepath"
"runtime"
"strconv"
"time"
esbuild "github.com/evanw/esbuild/pkg/api"
)
@@ -37,7 +36,7 @@ func commonSetup(dev bool) (*esbuild.BuildOptions, error) {
}
return &esbuild.BuildOptions{
EntryPoints: []string{"src/index.ts", "src/index.css"},
EntryPoints: []string{"src/index.js", "src/index.css"},
Loader: map[string]esbuild.Loader{".wasm": esbuild.LoaderFile},
Outdir: *distDir,
Bundle: true,
@@ -45,12 +44,6 @@ func commonSetup(dev bool) (*esbuild.BuildOptions, error) {
LogLevel: esbuild.LogLevelInfo,
Define: map[string]string{"DEBUG": strconv.FormatBool(dev)},
Target: esbuild.ES2017,
Plugins: []esbuild.Plugin{{
Name: "tailscale-tailwind",
Setup: func(build esbuild.PluginBuild) {
setupEsbuildTailwind(build, dev)
},
}},
}, nil
}
@@ -104,14 +97,11 @@ func buildWasm(dev bool) error {
// installJSDeps installs the JavaScript dependencies specified by package.json
func installJSDeps() error {
log.Printf("Installing JS deps...\n")
return runYarn()
}
func runYarn(args ...string) error {
cmd := exec.Command(*yarnPath, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
stdoutStderr, err := exec.Command("yarn").CombinedOutput()
if err != nil {
log.Printf("yarn failed: %s", stdoutStderr)
}
return err
}
// EsbuildMetadata is the subset of metadata struct (described by
@@ -122,30 +112,3 @@ type EsbuildMetadata struct {
EntryPoint string `json:"entryPoint,omitempty"`
} `json:"outputs,omitempty"`
}
func setupEsbuildTailwind(build esbuild.PluginBuild, dev bool) {
build.OnLoad(esbuild.OnLoadOptions{
Filter: "./src/index.css$",
}, func(args esbuild.OnLoadArgs) (esbuild.OnLoadResult, error) {
start := time.Now()
yarnArgs := []string{"--silent", "tailwind", "-i", args.Path}
if !dev {
yarnArgs = append(yarnArgs, "--minify")
}
cmd := exec.Command(*yarnPath, yarnArgs...)
tailwindOutput, err := cmd.Output()
log.Printf("Ran tailwind in %v\n", time.Since(start))
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
log.Printf("Tailwind stderr: %s", exitErr.Stderr)
}
return esbuild.OnLoadResult{}, fmt.Errorf("Cannot run tailwind: %w", err)
}
tailwindOutputStr := string(tailwindOutput)
return esbuild.OnLoadResult{
Contents: &tailwindOutputStr,
Loader: esbuild.LoaderCSS,
}, nil
})
}

View File

@@ -5,34 +5,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="dist/index.css" />
</head>
<body class="flex flex-col min-h-screen">
<div class="bg-gray-100 border-b border-gray-200 pt-4 pb-2 mb-6">
<header class="container mx-auto px-4 flex flex-row items-center">
<h1 class="text-3xl font-bold grow">Tailscale Connect</h1>
<div class="text-gray-600" id="state">Loading…</div>
</header>
</div>
<form
id="ssh-form"
class="container mx-auto px-4 hidden flex justify-center"
>
<input type="text" class="input username" placeholder="Username" />
<div class="select-with-arrow mx-2">
<select class="select"></select>
</div>
<input
type="submit"
class="button bg-green-500 border-green-500 text-white hover:bg-green-600 hover:border-green-600"
value="SSH"
/>
</form>
<div id="no-ssh" class="container mx-auto px-4 hidden text-center">
None of your machines have
<a href="https://tailscale.com/kb/1193/tailscale-ssh/" class="link"
>Tailscale SSH</a
>
enabled. Give it a try!
<body>
<div id="header">
<h1>Tailscale Connect</h1>
<div id="state">Loading…</div>
</div>
<div id="peers"></div>
<script src="dist/index.js"></script>
</body>
</html>

View File

@@ -1,18 +1,10 @@
{
"name": "tsconnect",
"name": "@tailscale/ssh",
"version": "0.0.1",
"license": "BSD-3-Clause",
"devDependencies": {
"@types/golang-wasm-exec": "^1.15.0",
"@types/qrcode": "^1.4.2",
"qrcode": "^1.5.0",
"tailwindcss": "^3.1.6",
"typescript": "^4.7.4",
"xterm": "^4.18.0"
},
"scripts": {
"lint": "tsc --noEmit"
},
"prettier": {
"semi": false,
"printWidth": 80

View File

@@ -102,7 +102,7 @@ func generateServeIndex(distFS fs.FS) ([]byte, error) {
var entryPointsToDefaultDistPaths = map[string]string{
"src/index.css": "dist/index.css",
"src/index.ts": "dist/index.js",
"src/index.js": "dist/index.js",
}
func handleServeDist(w http.ResponseWriter, r *http.Request, distFS fs.FS) {

View File

@@ -1,15 +0,0 @@
// Copyright (c) 2022 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.
/**
* @fileoverview Type definitions for types generated by the esbuild build
* process.
*/
declare module "*.wasm" {
const path: string
export default path
}
declare const DEBUG: boolean

View File

@@ -4,72 +4,88 @@
@import "xterm/css/xterm.css";
@tailwind base;
@tailwind components;
@tailwind utilities;
.link {
@apply text-blue-600;
html {
background: #fff;
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
.link:hover {
@apply underline;
body {
margin: 0;
}
.button {
@apply font-medium py-1 px-2 rounded-md border border-transparent text-center cursor-pointer;
transition-property: background-color, border-color, color, box-shadow;
transition-duration: 120ms;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
min-width: 80px;
}
.button:focus {
@apply outline-none ring;
}
.button:disabled {
@apply pointer-events-none select-none;
button {
font-family: inherit;
border: solid 1px #ccc;
background: #fff;
color: #000;
padding: 4px 8px;
border-radius: 4px;
}
.input {
@apply appearance-none leading-tight rounded-md bg-white border border-gray-300 hover:border-gray-400 transition-colors px-3;
height: 2.375rem;
#header {
background: #f7f5f4;
border-bottom: 1px solid #eeebea;
padding: 12px;
display: flex;
align-items: center;
}
.input::placeholder {
@apply text-gray-400;
#header h1 {
margin: 0;
flex-grow: 1;
}
.input:disabled {
@apply border-gray-200;
@apply bg-gray-50;
@apply cursor-not-allowed;
#header #state {
padding: 0 8px;
color: #444342;
}
.input:focus {
@apply outline-none ring border-transparent;
#peers {
box-sizing: border-box;
width: 100%;
padding: 12px;
}
.select {
@apply appearance-none py-2 px-3 leading-tight rounded-md bg-white border border-gray-300;
.login {
text-align: center;
}
.select-with-arrow {
@apply relative;
.logout {
font-weight: bold;
}
.select-with-arrow .select {
width: 100%;
.peer {
display: flex;
justify-content: space-between;
padding: 2px;
}
.select-with-arrow::after {
@apply absolute;
content: "";
top: 50%;
right: 0.5rem;
transform: translate(-0.3em, -0.15em);
width: 0.6em;
height: 0.4em;
opacity: 0.6;
background-color: currentColor;
clip-path: polygon(100% 0%, 0 0%, 50% 100%);
.peer:hover {
background: #eee;
}
.peer .name {
font-family: monospace;
}
.peer .ssh {
background-color: #cbf4c9;
}
.term-container {
padding: 12px;
}
.xterm-viewport.xterm-viewport {
scrollbar-width: thin;
}
.xterm-viewport::-webkit-scrollbar {
width: 10px;
}
.xterm-viewport::-webkit-scrollbar-track {
opacity: 0;
}
.xterm-viewport::-webkit-scrollbar-thumb {
min-height: 20px;
background-color: #ffffff20;
}

View File

@@ -0,0 +1,26 @@
// Copyright (c) 2022 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.
import "./wasm_exec"
import wasmUrl from "./main.wasm"
import { notifyState, notifyNetMap, notifyBrowseToURL } from "./notifier"
import { sessionStateStorage } from "./js-state-store"
const go = new window.Go()
WebAssembly.instantiateStreaming(
fetch(`./dist/${wasmUrl}`),
go.importObject
).then((result) => {
go.run(result.instance)
const ipn = newIPN({
// Persist IPN state in sessionStorage in development, so that we don't need
// to re-authorize every time we reload the page.
stateStorage: DEBUG ? sessionStateStorage : undefined,
})
ipn.run({
notifyState: notifyState.bind(null, ipn),
notifyNetMap: notifyNetMap.bind(null, ipn),
notifyBrowseToURL: notifyBrowseToURL.bind(null, ipn),
})
})

View File

@@ -1,54 +0,0 @@
// Copyright (c) 2022 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.
import "./wasm_exec"
import wasmUrl from "./main.wasm"
import { notifyState, notifyNetMap, notifyBrowseToURL } from "./notifier"
import { sessionStateStorage } from "./js-state-store"
const go = new Go()
WebAssembly.instantiateStreaming(
fetch(`./dist/${wasmUrl}`),
go.importObject
).then((result) => {
// The Go process should never exit, if it does then it's an unhandled panic.
go.run(result.instance).then(() => handleGoPanic())
const ipn = newIPN({
// Persist IPN state in sessionStorage in development, so that we don't need
// to re-authorize every time we reload the page.
stateStorage: DEBUG ? sessionStateStorage : undefined,
})
ipn.run({
notifyState: notifyState.bind(null, ipn),
notifyNetMap: notifyNetMap.bind(null, ipn),
notifyBrowseToURL: notifyBrowseToURL.bind(null, ipn),
notifyPanicRecover: handleGoPanic,
})
})
function handleGoPanic(err?: string) {
if (DEBUG && err) {
console.error("Go panic", err)
}
if (panicNode) {
panicNode.remove()
}
panicNode = document.createElement("div")
panicNode.className =
"rounded bg-red-500 p-2 absolute top-2 right-2 text-white font-bold text-right cursor-pointer"
panicNode.textContent = "Tailscale has encountered an error."
const panicDetailNode = document.createElement("div")
panicDetailNode.className = "text-sm font-normal"
panicDetailNode.textContent = "Click to reload"
panicNode.appendChild(panicDetailNode)
panicNode.addEventListener("click", () => location.reload(), {
once: true,
})
document.body.appendChild(panicNode)
setTimeout(() => {
panicNode!.remove()
}, 10000)
}
let panicNode: HTMLDivElement | undefined

View File

@@ -2,9 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/** @fileoverview Callbacks used by jsStateStore to persist IPN state. */
/**
* @fileoverview Callbacks used by jsStateStore to persist IPN state.
*/
export const sessionStateStorage: IPNStateStorage = {
export const sessionStateStorage = {
setState(id, value) {
window.sessionStorage[`ipn-state-${id}`] = value
},

View File

@@ -2,32 +2,32 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
import * as qrcode from "qrcode"
import QRCode from "qrcode"
export async function showLoginURL(url: string) {
export async function showLoginURL(url) {
if (loginNode) {
loginNode.remove()
}
loginNode = document.createElement("div")
loginNode.className = "flex flex-col items-center justify-items-center"
loginNode.className = "login"
const linkNode = document.createElement("a")
linkNode.className = "link"
linkNode.href = url
linkNode.target = "_blank"
loginNode.appendChild(linkNode)
try {
const dataURL = await qrcode.toDataURL(url, { width: 512 })
const dataURL = await QRCode.toDataURL(url, { width: 512 })
const imageNode = document.createElement("img")
imageNode.className = "mx-auto"
imageNode.src = dataURL
imageNode.width = 256
imageNode.height = 256
imageNode.border = "0"
linkNode.appendChild(imageNode)
} catch (err) {
console.error("Could not generate QR code:", err)
}
linkNode.appendChild(document.createElement("br"))
linkNode.appendChild(document.createTextNode(url))
document.body.appendChild(loginNode)
@@ -41,15 +41,14 @@ export function hideLoginURL() {
loginNode = undefined
}
let loginNode: HTMLDivElement | undefined
let loginNode
export function showLogoutButton(ipn: IPN) {
export function showLogoutButton(ipn) {
if (logoutButtonNode) {
logoutButtonNode.remove()
}
logoutButtonNode = document.createElement("button")
logoutButtonNode.className =
"button bg-gray-500 border-gray-500 text-white hover:bg-gray-600 hover:border-gray-600 ml-2 font-bold"
logoutButtonNode.className = "logout"
logoutButtonNode.textContent = "Logout"
logoutButtonNode.addEventListener(
"click",
@@ -58,8 +57,7 @@ export function showLogoutButton(ipn: IPN) {
},
{ once: true }
)
const headerNode = document.getElementsByTagName("header")[0]!
headerNode.appendChild(logoutButtonNode)
document.getElementById("header").appendChild(logoutButtonNode)
}
export function hideLogoutButton() {
@@ -70,4 +68,4 @@ export function hideLogoutButton() {
logoutButtonNode = undefined
}
let logoutButtonNode: HTMLButtonElement | undefined
let logoutButtonNode

View File

@@ -8,58 +8,68 @@ import {
showLogoutButton,
hideLogoutButton,
} from "./login"
import { showSSHForm, hideSSHForm } from "./ssh"
import { IPNState } from "./wasm_js"
import { showSSHPeers, hideSSHPeers } from "./ssh"
/**
* @fileoverview Notification callback functions (bridged from ipn.Notify)
*/
export function notifyState(ipn: IPN, state: IPNState) {
/** Mirrors values from ipn/backend.go */
const State = {
NoState: 0,
InUseOtherUser: 1,
NeedsLogin: 2,
NeedsMachineAuth: 3,
Stopped: 4,
Starting: 5,
Running: 6,
}
export function notifyState(ipn, state) {
let stateLabel
switch (state) {
case IPNState.NoState:
case State.NoState:
stateLabel = "Initializing…"
break
case IPNState.InUseOtherUser:
case State.InUseOtherUser:
stateLabel = "In-use by another user"
break
case IPNState.NeedsLogin:
case State.NeedsLogin:
stateLabel = "Needs Login"
hideLogoutButton()
hideSSHForm()
hideSSHPeers()
ipn.login()
break
case IPNState.NeedsMachineAuth:
case State.NeedsMachineAuth:
stateLabel = "Needs authorization"
break
case IPNState.Stopped:
case State.Stopped:
stateLabel = "Stopped"
hideLogoutButton()
hideSSHForm()
hideSSHPeers()
break
case IPNState.Starting:
case State.Starting:
stateLabel = "Starting…"
break
case IPNState.Running:
case State.Running:
stateLabel = "Running"
hideLoginURL()
showLogoutButton(ipn)
break
}
const stateNode = document.getElementById("state") as HTMLDivElement
const stateNode = document.getElementById("state")
stateNode.textContent = stateLabel ?? ""
}
export function notifyNetMap(ipn: IPN, netMapStr: string) {
const netMap = JSON.parse(netMapStr) as IPNNetMap
export function notifyNetMap(ipn, netMapStr) {
const netMap = JSON.parse(netMapStr)
if (DEBUG) {
console.log("Received net map: " + JSON.stringify(netMap, null, 2))
}
showSSHForm(netMap.peers, ipn)
showSSHPeers(netMap.peers, ipn)
}
export function notifyBrowseToURL(ipn: IPN, url: string) {
export function notifyBrowseToURL(ipn, url) {
showLoginURL(url)
}

77
cmd/tsconnect/src/ssh.js Normal file
View File

@@ -0,0 +1,77 @@
// Copyright (c) 2022 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.
import { Terminal } from "xterm"
export function showSSHPeers(peers, ipn) {
const peersNode = document.getElementById("peers")
peersNode.innerHTML = ""
const sshPeers = peers.filter((p) => p.tailscaleSSHEnabled)
if (!sshPeers.length) {
peersNode.textContent = "No machines have Tailscale SSH installed."
return
}
for (const peer of sshPeers) {
const peerNode = document.createElement("div")
peerNode.className = "peer"
const nameNode = document.createElement("div")
nameNode.className = "name"
nameNode.textContent = peer.name
peerNode.appendChild(nameNode)
const sshButtonNode = document.createElement("button")
sshButtonNode.className = "ssh"
sshButtonNode.addEventListener("click", function () {
ssh(peer.name, ipn)
})
sshButtonNode.textContent = "SSH"
peerNode.appendChild(sshButtonNode)
peersNode.appendChild(peerNode)
}
}
export function hideSSHPeers() {
const peersNode = document.getElementById("peers")
peersNode.innerHTML = ""
}
function ssh(hostname, ipn) {
const termContainerNode = document.createElement("div")
termContainerNode.className = "term-container"
document.body.appendChild(termContainerNode)
const term = new Terminal({
cursorBlink: true,
})
term.open(termContainerNode)
// Cancel wheel events from scrolling the page if the terminal has scrollback
termContainerNode.addEventListener("wheel", (e) => {
if (term.buffer.active.baseY > 0) {
e.preventDefault()
}
})
let onDataHook
term.onData((e) => {
onDataHook?.(e)
})
term.focus()
ipn.ssh(
hostname,
(input) => term.write(input),
(hook) => (onDataHook = hook),
term.rows,
term.cols,
() => {
term.dispose()
termContainerNode.remove()
}
)
}

View File

@@ -1,80 +0,0 @@
// Copyright (c) 2022 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.
import { Terminal } from "xterm"
export function showSSHForm(peers: IPNNetMapPeerNode[], ipn: IPN) {
const formNode = document.getElementById("ssh-form") as HTMLDivElement
const noSSHNode = document.getElementById("no-ssh") as HTMLDivElement
const sshPeers = peers.filter(
(p) => p.tailscaleSSHEnabled && p.online !== false
)
if (sshPeers.length == 0) {
formNode.classList.add("hidden")
noSSHNode.classList.remove("hidden")
return
}
sshPeers.sort((a, b) => a.name.localeCompare(b.name))
const selectNode = formNode.querySelector("select")!
selectNode.innerHTML = ""
for (const p of sshPeers) {
const option = document.createElement("option")
option.textContent = p.name.split(".")[0]
option.value = p.name
selectNode.appendChild(option)
}
const usernameNode = formNode.querySelector(".username") as HTMLInputElement
formNode.onsubmit = (e) => {
e.preventDefault()
const hostname = selectNode.value
ssh(hostname, usernameNode.value, ipn)
}
noSSHNode.classList.add("hidden")
formNode.classList.remove("hidden")
}
export function hideSSHForm() {
const formNode = document.getElementById("ssh-form") as HTMLDivElement
formNode.classList.add("hidden")
}
function ssh(hostname: string, username: string, ipn: IPN) {
const termContainerNode = document.createElement("div")
termContainerNode.className = "p-3"
document.body.appendChild(termContainerNode)
const term = new Terminal({
cursorBlink: true,
})
term.open(termContainerNode)
// Cancel wheel events from scrolling the page if the terminal has scrollback
termContainerNode.addEventListener("wheel", (e) => {
if (term.buffer.active.baseY > 0) {
e.preventDefault()
}
})
let onDataHook: ((data: string) => void) | undefined
term.onData((e) => {
onDataHook?.(e)
})
term.focus()
ipn.ssh(hostname, username, {
writeFn: (input) => term.write(input),
setReadFn: (hook) => (onDataHook = hook),
rows: term.rows,
cols: term.cols,
onDone: () => {
term.dispose()
termContainerNode.remove()
},
})
}

View File

@@ -1,86 +0,0 @@
// Copyright (c) 2022 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.
/**
* @fileoverview Type definitions for types exported by the wasm_js.go Go
* module. Not actually a .d.ts file so that we can use enums from it in
* esbuild's simplified TypeScript compiler (see https://github.com/evanw/esbuild/issues/2298#issuecomment-1146378367)
*/
declare global {
function newIPN(config: IPNConfig): IPN
interface IPN {
run(callbacks: IPNCallbacks): void
login(): void
logout(): void
ssh(
host: string,
username: string,
termConfig: {
writeFn: (data: string) => void
setReadFn: (readFn: (data: string) => void) => void
rows: number
cols: number
onDone: () => void
}
): void
}
interface IPNStateStorage {
setState(id: string, value: string): void
getState(id: string): string
}
type IPNConfig = {
stateStorage?: IPNStateStorage
}
type IPNCallbacks = {
notifyState: (state: IPNState) => void
notifyNetMap: (netMapStr: string) => void
notifyBrowseToURL: (url: string) => void
notifyPanicRecover: (err: string) => void
}
type IPNNetMap = {
self: IPNNetMapSelfNode
peers: IPNNetMapPeerNode[]
}
type IPNNetMapNode = {
name: string
addresses: string[]
machineKey: string
nodeKey: string
}
type IPNNetMapSelfNode = IPNNetMapNode & {
machineStatus: IPNMachineStatus
}
type IPNNetMapPeerNode = IPNNetMapNode & {
online?: boolean
tailscaleSSHEnabled: boolean
}
}
/** Mirrors values from ipn/backend.go */
export const enum IPNState {
NoState = 0,
InUseOtherUser = 1,
NeedsLogin = 2,
NeedsMachineAuth = 3,
Stopped = 4,
Starting = 5,
Running = 6,
}
/** Mirrors values from MachineStatus in tailcfg.go */
export const enum IPNMachineStatus {
MachineUnknown = 0,
MachineUnauthorized = 1,
MachineAuthorized = 2,
MachineInvalid = 3,
}

View File

@@ -1,8 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: ["./index.html", "./src/**/*.ts"],
theme: {
extend: {},
},
plugins: [],
}

View File

@@ -1,13 +0,0 @@
{
"compilerOptions": {
"target": "ES2017",
"module": "ES2020",
"moduleResolution": "node",
"isolatedModules": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}

View File

@@ -18,9 +18,8 @@ import (
)
var (
addr = flag.String("addr", ":9090", "address to listen on")
distDir = flag.String("distdir", "./dist", "path of directory to place build output in")
yarnPath = flag.String("yarnpath", "../../tool/yarn", "path yarn executable used to install JavaScript dependencies")
addr = flag.String("addr", ":9090", "address to listen on")
distDir = flag.String("distdir", "./dist", "path of directory to place build output in")
)
func main() {

View File

@@ -19,12 +19,12 @@ import (
"log"
"math/rand"
"net"
"net/netip"
"strings"
"syscall/js"
"time"
"golang.org/x/crypto/ssh"
"inet.af/netaddr"
"tailscale.com/control/controlclient"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
@@ -78,10 +78,10 @@ func newIPN(jsConfig js.Value) map[string]any {
if err := ns.Start(); err != nil {
log.Fatalf("failed to start netstack: %v", err)
}
dialer.UseNetstackForIP = func(ip netip.Addr) bool {
dialer.UseNetstackForIP = func(ip netaddr.IP) bool {
return true
}
dialer.NetstackDialTCP = func(ctx context.Context, dst netip.AddrPort) (net.Conn, error) {
dialer.NetstackDialTCP = func(ctx context.Context, dst netaddr.IPPort) (net.Conn, error) {
return ns.DialContextTCP(ctx, dst)
}
@@ -114,7 +114,6 @@ func newIPN(jsConfig js.Value) map[string]any {
notifyState(state: int): void,
notifyNetMap(netMap: object): void,
notifyBrowseToURL(url: string): void,
notifyPanicRecover(err: string): void,
})`)
return nil
}
@@ -138,14 +137,17 @@ func newIPN(jsConfig js.Value) map[string]any {
return nil
}),
"ssh": js.FuncOf(func(this js.Value, args []js.Value) interface{} {
if len(args) != 3 {
log.Printf("Usage: ssh(hostname, userName, termConfig)")
if len(args) != 6 {
log.Printf("Usage: ssh(hostname, writeFn, readFn, rows, cols, onDone)")
return nil
}
go jsIPN.ssh(
args[0].String(),
args[1].String(),
args[2])
args[1],
args[2],
args[3].Int(),
args[4].Int(),
args[5])
return nil
}),
}
@@ -164,45 +166,30 @@ func (i *jsIPN) run(jsCallbacks js.Value) {
notifyState(ipn.NoState)
i.lb.SetNotifyCallback(func(n ipn.Notify) {
// Panics in the notify callback are likely due to be due to bugs in
// this bridging module (as opposed to actual bugs in Tailscale) and
// thus may be recoverable. Let the UI know, and allow the user to
// choose if they want to reload the page.
defer func() {
if r := recover(); r != nil {
fmt.Println("Panic recovered:", r)
jsCallbacks.Call("notifyPanicRecover", fmt.Sprint(r))
}
}()
log.Printf("NOTIFY: %+v", n)
if n.State != nil {
notifyState(*n.State)
}
if nm := n.NetMap; nm != nil && i.lb.State() == ipn.Running {
if nm := n.NetMap; nm != nil {
jsNetMap := jsNetMap{
Self: jsNetMapSelfNode{
jsNetMapNode: jsNetMapNode{
Name: nm.Name,
Addresses: mapSlice(nm.Addresses, func(a netip.Prefix) string { return a.Addr().String() }),
Addresses: mapSlice(nm.Addresses, func(a netaddr.IPPrefix) string { return a.IP().String() }),
NodeKey: nm.NodeKey.String(),
MachineKey: nm.MachineKey.String(),
},
MachineStatus: int(nm.MachineStatus),
},
Peers: mapSlice(nm.Peers, func(p *tailcfg.Node) jsNetMapPeerNode {
name := p.Name
if name == "" {
// In practice this should only happen for Hello.
name = p.Hostinfo.Hostname()
}
return jsNetMapPeerNode{
jsNetMapNode: jsNetMapNode{
Name: name,
Addresses: mapSlice(p.Addresses, func(a netip.Prefix) string { return a.Addr().String() }),
Name: p.Name,
Addresses: mapSlice(p.Addresses, func(a netaddr.IPPrefix) string { return a.IP().String() }),
MachineKey: p.Machine.String(),
NodeKey: p.Key.String(),
},
Online: p.Online,
Online: *p.Online,
TailscaleSSHEnabled: p.Hostinfo.TailscaleSSHEnabled(),
}
}),
@@ -256,13 +243,7 @@ func (i *jsIPN) logout() {
go i.lb.Logout()
}
func (i *jsIPN) ssh(host, username string, termConfig js.Value) {
writeFn := termConfig.Get("writeFn")
setReadFn := termConfig.Get("setReadFn")
rows := termConfig.Get("rows").Int()
cols := termConfig.Get("cols").Int()
onDone := termConfig.Get("onDone")
func (i *jsIPN) ssh(host string, writeFn js.Value, setReadFn js.Value, rows, cols int, onDone js.Value) {
defer onDone.Invoke()
write := func(s string) {
@@ -283,7 +264,6 @@ func (i *jsIPN) ssh(host, username string, termConfig js.Value) {
config := &ssh.ClientConfig{
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
User: username,
}
sshConn, _, _, err := ssh.NewClientConn(c, host, config)
@@ -359,10 +339,11 @@ type jsNetMap struct {
}
type jsNetMapNode struct {
Name string `json:"name"`
Addresses []string `json:"addresses"`
MachineKey string `json:"machineKey"`
NodeKey string `json:"nodeKey"`
Name string `json:"name"`
Addresses []string `json:"addresses"`
MachineStatus int `json:"machineStatus"`
MachineKey string `json:"machineKey"`
NodeKey string `json:"nodeKey"`
}
type jsNetMapSelfNode struct {
@@ -372,8 +353,8 @@ type jsNetMapSelfNode struct {
type jsNetMapPeerNode struct {
jsNetMapNode
Online *bool `json:"online,omitempty"`
TailscaleSSHEnabled bool `json:"tailscaleSSHEnabled"`
Online bool `json:"online"`
TailscaleSSHEnabled bool `json:"tailscaleSSHEnabled"`
}
type jsStateStore struct {

View File

@@ -2,63 +2,6 @@
# yarn lockfile v1
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
dependencies:
"@nodelib/fs.stat" "2.0.5"
run-parallel "^1.1.9"
"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
version "2.0.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
"@nodelib/fs.walk@^1.2.3":
version "1.2.8"
resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
dependencies:
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"
"@types/golang-wasm-exec@^1.15.0":
version "1.15.0"
resolved "https://registry.yarnpkg.com/@types/golang-wasm-exec/-/golang-wasm-exec-1.15.0.tgz#d0aafbb2b0dc07eaf45dfb83bfb6cdd5b2b3c55c"
integrity sha512-FrL97mp7WW8LqNinVkzTVKOIQKuYjQqgucnh41+1vRQ+bf1LT8uh++KRf9otZPXsa6H1p8ruIGz1BmCGttOL6Q==
"@types/node@*":
version "18.6.1"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.1.tgz#828e4785ccca13f44e2fb6852ae0ef11e3e20ba5"
integrity sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg==
"@types/qrcode@^1.4.2":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@types/qrcode/-/qrcode-1.4.2.tgz#7d7142d6fa9921f195db342ed08b539181546c74"
integrity sha512-7uNT9L4WQTNJejHTSTdaJhfBSCN73xtXaHFyBJ8TSwiLhe4PRuTue7Iph0s2nG9R/ifUaSnGhLUOZavlBEqDWQ==
dependencies:
"@types/node" "*"
acorn-node@^1.8.2:
version "1.8.2"
resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8"
integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A==
dependencies:
acorn "^7.0.0"
acorn-walk "^7.0.0"
xtend "^4.0.2"
acorn-walk@^7.0.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
acorn@^7.0.0:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
@@ -71,56 +14,11 @@ ansi-styles@^4.0.0:
dependencies:
color-convert "^2.0.1"
anymatch@~3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
dependencies:
normalize-path "^3.0.0"
picomatch "^2.0.4"
arg@^5.0.2:
version "5.0.2"
resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c"
integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
binary-extensions@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
dependencies:
fill-range "^7.0.1"
camelcase-css@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5"
integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
camelcase@^5.0.0:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
dependencies:
anymatch "~3.1.2"
braces "~3.0.2"
glob-parent "~5.1.2"
is-binary-path "~2.1.0"
is-glob "~4.0.1"
normalize-path "~3.0.0"
readdirp "~3.6.0"
optionalDependencies:
fsevents "~2.3.2"
cliui@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
@@ -137,50 +35,21 @@ color-convert@^2.0.1:
dependencies:
color-name "~1.1.4"
color-name@^1.1.4, color-name@~1.1.4:
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
cssesc@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
decamelize@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
defined@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693"
integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ==
detective@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034"
integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==
dependencies:
acorn-node "^1.8.2"
defined "^1.0.0"
minimist "^1.2.6"
didyoumean@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037"
integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
dijkstrajs@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/dijkstrajs/-/dijkstrajs-1.0.2.tgz#2e48c0d3b825462afe75ab4ad5e829c8ece36257"
integrity sha512-QV6PMaHTCNmKSeP6QoXhVTw9snc9VD8MulTT0Bd99Pacp4SS1cjcrYPgBPmibqKVtMJJfqC6XvOXgPMEEPH/fg==
dlv@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79"
integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
@@ -191,31 +60,6 @@ encode-utf8@^1.0.3:
resolved "https://registry.yarnpkg.com/encode-utf8/-/encode-utf8-1.0.3.tgz#f30fdd31da07fb596f281beb2f6b027851994cda"
integrity sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==
fast-glob@^3.2.11:
version "3.2.11"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
dependencies:
"@nodelib/fs.stat" "^2.0.2"
"@nodelib/fs.walk" "^1.2.3"
glob-parent "^5.1.2"
merge2 "^1.3.0"
micromatch "^4.0.4"
fastq@^1.6.0:
version "1.13.0"
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
dependencies:
reusify "^1.0.4"
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
dependencies:
to-regex-range "^5.0.1"
find-up@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19"
@@ -224,83 +68,16 @@ find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
fsevents@~2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
function-bind@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
get-caller-file@^2.0.1:
version "2.0.5"
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
glob-parent@^5.1.2, glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies:
is-glob "^4.0.1"
glob-parent@^6.0.2:
version "6.0.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
dependencies:
is-glob "^4.0.3"
has@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
dependencies:
function-bind "^1.1.1"
is-binary-path@~2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
dependencies:
binary-extensions "^2.0.0"
is-core-module@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69"
integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==
dependencies:
has "^1.0.3"
is-extglob@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
dependencies:
is-extglob "^2.1.1"
is-number@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
lilconfig@^2.0.5:
version "2.0.6"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4"
integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==
locate-path@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0"
@@ -308,39 +85,6 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
merge2@^1.3.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
dependencies:
braces "^3.0.2"
picomatch "^2.3.1"
minimist@^1.2.6:
version "1.2.6"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
nanoid@^3.3.4:
version "3.3.4"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab"
integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
object-hash@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@@ -365,84 +109,11 @@ path-exists@^4.0.0:
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
path-parse@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
picocolors@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
pify@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c"
integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
pngjs@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
postcss-import@^14.1.0:
version "14.1.0"
resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0"
integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==
dependencies:
postcss-value-parser "^4.0.0"
read-cache "^1.0.0"
resolve "^1.1.7"
postcss-js@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00"
integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ==
dependencies:
camelcase-css "^2.0.1"
postcss-load-config@^3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855"
integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
dependencies:
lilconfig "^2.0.5"
yaml "^1.10.2"
postcss-nested@5.0.6:
version "5.0.6"
resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.6.tgz#466343f7fc8d3d46af3e7dba3fcd47d052a945bc"
integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA==
dependencies:
postcss-selector-parser "^6.0.6"
postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6:
version "6.0.10"
resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d"
integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==
dependencies:
cssesc "^3.0.0"
util-deprecate "^1.0.2"
postcss-value-parser@^4.0.0, postcss-value-parser@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
postcss@^8.4.14:
version "8.4.14"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf"
integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==
dependencies:
nanoid "^3.3.4"
picocolors "^1.0.0"
source-map-js "^1.0.2"
qrcode@^1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.0.tgz#95abb8a91fdafd86f8190f2836abbfc500c72d1b"
@@ -453,30 +124,6 @@ qrcode@^1.5.0:
pngjs "^5.0.0"
yargs "^15.3.1"
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
quick-lru@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
read-cache@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774"
integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==
dependencies:
pify "^2.3.0"
readdirp@~3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
dependencies:
picomatch "^2.2.1"
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -487,37 +134,11 @@ require-main-filename@^2.0.0:
resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b"
integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==
resolve@^1.1.7, resolve@^1.22.1:
version "1.22.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
dependencies:
is-core-module "^2.9.0"
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
run-parallel@^1.1.9:
version "1.2.0"
resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
dependencies:
queue-microtask "^1.2.2"
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
string-width@^4.1.0, string-width@^4.2.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
@@ -534,56 +155,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
dependencies:
ansi-regex "^5.0.1"
supports-preserve-symlinks-flag@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
tailwindcss@^3.1.6:
version "3.1.6"
resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.6.tgz#bcb719357776c39e6376a8d84e9834b2b19a49f1"
integrity sha512-7skAOY56erZAFQssT1xkpk+kWt2NrO45kORlxFPXUt3CiGsVPhH1smuH5XoDH6sGPXLyBv+zgCKA2HWBsgCytg==
dependencies:
arg "^5.0.2"
chokidar "^3.5.3"
color-name "^1.1.4"
detective "^5.2.1"
didyoumean "^1.2.2"
dlv "^1.1.3"
fast-glob "^3.2.11"
glob-parent "^6.0.2"
is-glob "^4.0.3"
lilconfig "^2.0.5"
normalize-path "^3.0.0"
object-hash "^3.0.0"
picocolors "^1.0.0"
postcss "^8.4.14"
postcss-import "^14.1.0"
postcss-js "^4.0.0"
postcss-load-config "^3.1.4"
postcss-nested "5.0.6"
postcss-selector-parser "^6.0.10"
postcss-value-parser "^4.2.0"
quick-lru "^5.1.1"
resolve "^1.22.1"
to-regex-range@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
dependencies:
is-number "^7.0.0"
typescript@^4.7.4:
version "4.7.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
util-deprecate@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
@@ -598,11 +169,6 @@ wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"
xtend@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==
xterm@^4.18.0:
version "4.18.0"
resolved "https://registry.yarnpkg.com/xterm/-/xterm-4.18.0.tgz#a1f6ab2c330c3918fb094ae5f4c2562987398ea1"
@@ -613,11 +179,6 @@ y18n@^4.0.0:
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
yaml@^1.10.2:
version "1.10.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yargs-parser@^18.1.2:
version "18.1.3"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"

View File

@@ -11,4 +11,3 @@
// Its functionality moved into tailscaled.
//
// See https://github.com/tailscale/tailscale/issues/3802
package main

View File

@@ -7,14 +7,15 @@ package tests
import (
"fmt"
"net/netip"
"inet.af/netaddr"
)
//go:generate go run tailscale.com/cmd/viewer --type=StructWithPtrs,StructWithoutPtrs,Map,StructWithSlices
type StructWithoutPtrs struct {
Int int
Pfx netip.Prefix
Pfx netaddr.IPPrefix
}
type Map struct {
@@ -53,6 +54,6 @@ type StructWithSlices struct {
Ints []*int
Slice []string
Prefixes []netip.Prefix
Prefixes []netaddr.IPPrefix
Data []byte
}

View File

@@ -7,7 +7,7 @@
package tests
import (
"net/netip"
"inet.af/netaddr"
)
// Clone makes a deep copy of StructWithPtrs.
@@ -50,7 +50,7 @@ func (src *StructWithoutPtrs) Clone() *StructWithoutPtrs {
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _StructWithoutPtrsCloneNeedsRegeneration = StructWithoutPtrs(struct {
Int int
Pfx netip.Prefix
Pfx netaddr.IPPrefix
}{})
// Clone makes a deep copy of Map.
@@ -178,6 +178,6 @@ var _StructWithSlicesCloneNeedsRegeneration = StructWithSlices(struct {
Structs []StructWithPtrs
Ints []*int
Slice []string
Prefixes []netip.Prefix
Prefixes []netaddr.IPPrefix
Data []byte
}{})

View File

@@ -9,9 +9,9 @@ package tests
import (
"encoding/json"
"errors"
"net/netip"
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/types/views"
)
@@ -134,13 +134,13 @@ func (v *StructWithoutPtrsView) UnmarshalJSON(b []byte) error {
return nil
}
func (v StructWithoutPtrsView) Int() int { return v.ж.Int }
func (v StructWithoutPtrsView) Pfx() netip.Prefix { return v.ж.Pfx }
func (v StructWithoutPtrsView) Int() int { return v.ж.Int }
func (v StructWithoutPtrsView) Pfx() netaddr.IPPrefix { return v.ж.Pfx }
// A compilation failure here means this code must be regenerated, with the command at the top of this file.
var _StructWithoutPtrsViewNeedsRegeneration = StructWithoutPtrs(struct {
Int int
Pfx netip.Prefix
Pfx netaddr.IPPrefix
}{})
// View returns a readonly view of Map.
@@ -311,6 +311,6 @@ var _StructWithSlicesViewNeedsRegeneration = StructWithSlices(struct {
Structs []StructWithPtrs
Ints []*int
Slice []string
Prefixes []netip.Prefix
Prefixes []netaddr.IPPrefix
Data []byte
}{})

View File

@@ -177,7 +177,7 @@ func genView(buf *bytes.Buffer, it *codegen.ImportTracker, typ *types.Named, thi
case "byte":
it.Import("go4.org/mem")
writeTemplate("byteSliceField")
case "inet.af/netip.Prefix", "net/netip.Prefix":
case "inet.af/netaddr.IPPrefix":
it.Import("tailscale.com/types/views")
writeTemplate("ipPrefixSliceField")
default:

View File

@@ -16,7 +16,6 @@ import (
"io/ioutil"
"log"
"net/http"
"net/netip"
"net/url"
"os"
"reflect"
@@ -27,6 +26,7 @@ import (
"time"
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/control/controlknobs"
"tailscale.com/envknob"
"tailscale.com/health"
@@ -129,7 +129,7 @@ type Options struct {
// Pinger is the LocalBackend.Ping method.
type Pinger interface {
// Ping is a request to do a ping with the peer handling the given IP.
Ping(ctx context.Context, ip netip.Addr, pingType tailcfg.PingType) (*ipnstate.PingResult, error)
Ping(ctx context.Context, ip netaddr.IP, pingType tailcfg.PingType) (*ipnstate.PingResult, error)
}
type Decompressor interface {
@@ -1167,8 +1167,8 @@ func TrimWGConfig() opt.Bool {
// It should not return false positives.
//
// TODO(bradfitz): Change controlclient.Options.SkipIPForwardingCheck into a
// func([]netip.Prefix) error signature instead.
func ipForwardingBroken(routes []netip.Prefix, state *interfaces.State) bool {
// func([]netaddr.IPPrefix) error signature instead.
func ipForwardingBroken(routes []netaddr.IPPrefix, state *interfaces.State) bool {
warn, err := netutil.CheckIPForwarding(routes, state)
if err != nil {
// Oh well, we tried. This is just for debugging.
@@ -1408,7 +1408,7 @@ func (c *Direct) DoNoiseRequest(req *http.Request) (*http.Response, error) {
// doPingerPing sends a Ping to pr.IP using pinger, and sends an http request back to
// pr.URL with ping response data.
func doPingerPing(logf logger.Logf, c *http.Client, pr *tailcfg.PingRequest, pinger Pinger, pingType tailcfg.PingType) {
if pr.URL == "" || !pr.IP.IsValid() || pinger == nil {
if pr.URL == "" || pr.IP.IsZero() || pinger == nil {
logf("invalid ping request: missing url, ip or pinger")
return
}

View File

@@ -8,10 +8,10 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
"net/netip"
"testing"
"time"
"inet.af/netaddr"
"tailscale.com/hostinfo"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/tsdial"
@@ -86,7 +86,7 @@ func TestNewDirect(t *testing.T) {
func fakeEndpoints(ports ...uint16) (ret []tailcfg.Endpoint) {
for _, port := range ports {
ret = append(ret, tailcfg.Endpoint{
Addr: netip.AddrPortFrom(netip.Addr{}, port),
Addr: netaddr.IPPortFrom(netaddr.IP{}, port),
})
}
return

View File

@@ -7,9 +7,9 @@ package controlclient
import (
"fmt"
"log"
"net/netip"
"sort"
"inet.af/netaddr"
"tailscale.com/envknob"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
@@ -303,13 +303,13 @@ func cloneNodes(v1 []*tailcfg.Node) []*tailcfg.Node {
var debugSelfIPv6Only = envknob.Bool("TS_DEBUG_SELF_V6_ONLY")
func filterSelfAddresses(in []netip.Prefix) (ret []netip.Prefix) {
func filterSelfAddresses(in []netaddr.IPPrefix) (ret []netaddr.IPPrefix) {
switch {
default:
return in
case debugSelfIPv6Only:
for _, a := range in {
if a.Addr().Is6() {
if a.IP().Is6() {
ret = append(ret, a)
}
}

View File

@@ -11,13 +11,13 @@ import (
"errors"
"fmt"
"io"
"net/netip"
"sync"
"sync/atomic"
"time"
"go4.org/mem"
"golang.org/x/time/rate"
"inet.af/netaddr"
"tailscale.com/types/key"
"tailscale.com/types/logger"
)
@@ -597,17 +597,17 @@ func (c *Client) setSendRateLimiter(sm ServerInfoMessage) {
//
// If the client is broken in some previously detectable way, it
// returns an error.
func (c *Client) LocalAddr() (netip.AddrPort, error) {
func (c *Client) LocalAddr() (netaddr.IPPort, error) {
readErr, _ := c.readErr.Load().(error)
if readErr != nil {
return netip.AddrPort{}, readErr
return netaddr.IPPort{}, readErr
}
if c.nc == nil {
return netip.AddrPort{}, errors.New("nil conn")
return netaddr.IPPort{}, errors.New("nil conn")
}
a := c.nc.LocalAddr()
if a == nil {
return netip.AddrPort{}, errors.New("nil addr")
return netaddr.IPPort{}, errors.New("nil addr")
}
return netip.ParseAddrPort(a.String())
return netaddr.ParseIPPort(a.String())
}

View File

@@ -25,7 +25,6 @@ import (
"math/rand"
"net"
"net/http"
"net/netip"
"os/exec"
"runtime"
"strconv"
@@ -37,6 +36,7 @@ import (
"go4.org/mem"
"golang.org/x/sync/errgroup"
"golang.org/x/time/rate"
"inet.af/netaddr"
"tailscale.com/client/tailscale"
"tailscale.com/disco"
"tailscale.com/envknob"
@@ -162,8 +162,8 @@ type Server struct {
// src.
sentTo map[key.NodePublic]map[key.NodePublic]int64 // src => dst => dst's latest sclient.connNum
// maps from netip.AddrPort to a client's public key
keyOfAddr map[netip.AddrPort]key.NodePublic
// maps from netaddr.IPPort to a client's public key
keyOfAddr map[netaddr.IPPort]key.NodePublic
}
// clientSet represents 1 or more *sclients.
@@ -314,7 +314,7 @@ func NewServer(privateKey key.NodePrivate, logf logger.Logf) *Server {
watchers: map[*sclient]bool{},
sentTo: map[key.NodePublic]map[key.NodePublic]int64{},
avgQueueDuration: new(uint64),
keyOfAddr: map[netip.AddrPort]key.NodePublic{},
keyOfAddr: map[netaddr.IPPort]key.NodePublic{},
}
s.initMetacert()
s.packetsRecvDisco = s.packetsRecvByKind.Get("disco")
@@ -663,7 +663,7 @@ func (s *Server) accept(ctx context.Context, nc Conn, brw *bufio.ReadWriter, rem
ctx, cancel := context.WithCancel(ctx)
defer cancel()
remoteIPPort, _ := netip.ParseAddrPort(remoteAddr)
remoteIPPort, _ := netaddr.ParseIPPort(remoteAddr)
c := &sclient{
connNum: connNum,
@@ -1246,7 +1246,7 @@ type sclient struct {
logf logger.Logf
done <-chan struct{} // closed when connection closes
remoteAddr string // usually ip:port from net.Conn.RemoteAddr().String()
remoteIPPort netip.AddrPort // zero if remoteAddr is not ip:port.
remoteIPPort netaddr.IPPort // zero if remoteAddr is not ip:port.
sendQueue chan pkt // packets queued to this client; never closed
discoSendQueue chan pkt // important packets queued to this client; never closed
sendPongCh chan [8]byte // pong replies to send to the client; never closed
@@ -1759,8 +1759,8 @@ type BytesSentRecv struct {
// parseSSOutput parses the output from the specific call to ss in ServeDebugTraffic.
// Separated out for ease of testing.
func parseSSOutput(raw string) map[netip.AddrPort]BytesSentRecv {
newState := map[netip.AddrPort]BytesSentRecv{}
func parseSSOutput(raw string) map[netaddr.IPPort]BytesSentRecv {
newState := map[netaddr.IPPort]BytesSentRecv{}
// parse every 2 lines and get src and dst ips, and kv pairs
lines := strings.Split(raw, "\n")
for i := 0; i < len(lines); i += 2 {
@@ -1768,7 +1768,7 @@ func parseSSOutput(raw string) map[netip.AddrPort]BytesSentRecv {
if len(ipInfo) < 5 {
continue
}
src, err := netip.ParseAddrPort(ipInfo[4])
src, err := netaddr.ParseIPPort(ipInfo[4])
if err != nil {
continue
}
@@ -1793,7 +1793,7 @@ func parseSSOutput(raw string) map[netip.AddrPort]BytesSentRecv {
}
func (s *Server) ServeDebugTraffic(w http.ResponseWriter, r *http.Request) {
prevState := map[netip.AddrPort]BytesSentRecv{}
prevState := map[netaddr.IPPort]BytesSentRecv{}
enc := json.NewEncoder(w)
for r.Context().Err() == nil {
output, err := exec.Command("ss", "-i", "-H", "-t").Output()

View File

@@ -22,7 +22,6 @@ import (
"io/ioutil"
"net"
"net/http"
"net/netip"
"net/url"
"runtime"
"strings"
@@ -31,6 +30,7 @@ import (
"time"
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/derp"
"tailscale.com/envknob"
"tailscale.com/net/dnscache"
@@ -579,11 +579,11 @@ func (c *Client) dialContext(ctx context.Context, proto, addr string) (net.Conn,
// address (given in s) is valid. An empty value means to dial, but to
// use DNS. The predicate function reports whether the non-empty
// string s contained a valid IP address of the right family.
func shouldDialProto(s string, pred func(netip.Addr) bool) bool {
func shouldDialProto(s string, pred func(netaddr.IP) bool) bool {
if s == "" {
return true
}
ip, _ := netip.ParseAddr(s)
ip, _ := netaddr.ParseIP(s)
return pred(ip)
}
@@ -651,10 +651,10 @@ func (c *Client) dialNode(ctx context.Context, n *tailcfg.DERPNode) (net.Conn, e
}
}()
}
if shouldDialProto(n.IPv4, netip.Addr.Is4) {
if shouldDialProto(n.IPv4, netaddr.IP.Is4) {
startDial(n.IPv4, "tcp4")
}
if shouldDialProto(n.IPv6, netip.Addr.Is6) {
if shouldDialProto(n.IPv6, netaddr.IP.Is6) {
startDial(n.IPv6, "tcp6")
}
if nwait == 0 {
@@ -839,15 +839,15 @@ func (c *Client) SendPing(data [8]byte) error {
// LocalAddr reports c's local TCP address, without any implicit
// connect or reconnect.
func (c *Client) LocalAddr() (netip.AddrPort, error) {
func (c *Client) LocalAddr() (netaddr.IPPort, error) {
c.mu.Lock()
closed, client := c.closed, c.client
c.mu.Unlock()
if closed {
return netip.AddrPort{}, ErrClientClosed
return netaddr.IPPort{}, ErrClientClosed
}
if client == nil {
return netip.AddrPort{}, errors.New("client not connected")
return netaddr.IPPort{}, errors.New("client not connected")
}
return client.LocalAddr()
}

View File

@@ -24,10 +24,9 @@ import (
"errors"
"fmt"
"net"
"net/netip"
"go4.org/mem"
"tailscale.com/net/netaddr"
"inet.af/netaddr"
"tailscale.com/types/key"
)
@@ -173,7 +172,7 @@ type CallMeMaybe struct {
// in this field, but might not yet be in control's endpoints.
// (And in the future, control will stop distributing endpoints
// when clients are suitably new.)
MyNumber []netip.AddrPort
MyNumber []netaddr.IPPort
}
const epLength = 16 + 2 // 16 byte IP address + 2 byte port
@@ -181,7 +180,7 @@ const epLength = 16 + 2 // 16 byte IP address + 2 byte port
func (m *CallMeMaybe) AppendMarshal(b []byte) []byte {
ret, p := appendMsgHeader(b, TypeCallMeMaybe, v0, epLength*len(m.MyNumber))
for _, ipp := range m.MyNumber {
a := ipp.Addr().As16()
a := ipp.IP().As16()
copy(p[:], a[:])
binary.BigEndian.PutUint16(p[16:], ipp.Port())
p = p[epLength:]
@@ -194,11 +193,11 @@ func parseCallMeMaybe(ver uint8, p []byte) (m *CallMeMaybe, err error) {
if len(p)%epLength != 0 || ver != 0 || len(p) == 0 {
return m, nil
}
m.MyNumber = make([]netip.AddrPort, 0, len(p)/epLength)
m.MyNumber = make([]netaddr.IPPort, 0, len(p)/epLength)
for len(p) > 0 {
var a [16]byte
copy(a[:], p)
m.MyNumber = append(m.MyNumber, netip.AddrPortFrom(
m.MyNumber = append(m.MyNumber, netaddr.IPPortFrom(
netaddr.IPFrom16(a),
binary.BigEndian.Uint16(p[16:18])))
p = p[epLength:]
@@ -212,7 +211,7 @@ func parseCallMeMaybe(ver uint8, p []byte) (m *CallMeMaybe, err error) {
// STUN response.
type Pong struct {
TxID [12]byte
Src netip.AddrPort // 18 bytes (16+2) on the wire; v4-mapped ipv6 for IPv4
Src netaddr.IPPort // 18 bytes (16+2) on the wire; v4-mapped ipv6 for IPv4
}
const pongLen = 12 + 16 + 2
@@ -220,7 +219,7 @@ const pongLen = 12 + 16 + 2
func (m *Pong) AppendMarshal(b []byte) []byte {
ret, d := appendMsgHeader(b, TypePong, v0, pongLen)
d = d[copy(d, m.TxID[:]):]
ip16 := m.Src.Addr().As16()
ip16 := m.Src.IP().As16()
d = d[copy(d, ip16[:]):]
binary.BigEndian.PutUint16(d, m.Src.Port())
return ret
@@ -237,7 +236,7 @@ func parsePong(ver uint8, p []byte) (m *Pong, err error) {
srcIP, _ := netaddr.FromStdIP(net.IP(p[:16]))
p = p[16:]
port := binary.BigEndian.Uint16(p)
m.Src = netip.AddrPortFrom(srcIP, port)
m.Src = netaddr.IPPortFrom(srcIP, port)
return m, nil
}

View File

@@ -6,12 +6,12 @@ package disco
import (
"fmt"
"net/netip"
"reflect"
"strings"
"testing"
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/types/key"
)
@@ -60,9 +60,9 @@ func TestMarshalAndParse(t *testing.T) {
{
name: "call_me_maybe_endpoints",
m: &CallMeMaybe{
MyNumber: []netip.AddrPort{
netip.MustParseAddrPort("1.2.3.4:567"),
netip.MustParseAddrPort("[2001::3456]:789"),
MyNumber: []netaddr.IPPort{
netaddr.MustParseIPPort("1.2.3.4:567"),
netaddr.MustParseIPPort("[2001::3456]:789"),
},
},
want: "03 00 00 00 00 00 00 00 00 00 00 00 ff ff 01 02 03 04 02 37 20 01 00 00 00 00 00 00 00 00 00 00 00 00 34 56 03 15",
@@ -93,8 +93,8 @@ func TestMarshalAndParse(t *testing.T) {
}
}
func mustIPPort(s string) netip.AddrPort {
ipp, err := netip.ParseAddrPort(s)
func mustIPPort(s string) netaddr.IPPort {
ipp, err := netaddr.ParseIPPort(s)
if err != nil {
panic(err)
}

View File

@@ -1,11 +1,7 @@
# Overview
There are quite a few ways of running Tailscale inside a Kubernetes Cluster, some of the common ones are covered in this doc.
## Instructions
### Setup
1. (Optional) Create the following secret which will automate login.<br>
You will need to get an [auth key](https://tailscale.com/kb/1085/auth-keys/) from [Tailscale Admin Console](https://login.tailscale.com/admin/authkeys).<br>
If you don't provide the key, you can still authenticate using the url in the logs.
@@ -16,13 +12,12 @@ There are quite a few ways of running Tailscale inside a Kubernetes Cluster, som
metadata:
name: tailscale-auth
stringData:
TS_AUTH_KEY: tskey-...
AUTH_KEY: tskey-...
```
1. Tailscale (v1.16+) supports storing state inside a Kubernetes Secret.
Configure RBAC to allow the Tailscale pod to read/write the `tailscale` secret.
```bash
export SA_NAME=tailscale
export TS_KUBE_SECRET=tailscale-auth
@@ -30,7 +25,6 @@ There are quite a few ways of running Tailscale inside a Kubernetes Cluster, som
```
### Sample Sidecar
Running as a sidecar allows you to directly expose a Kubernetes pod over Tailscale. This is particularly useful if you do not wish to expose a service on the public internet. This method allows bi-directional connectivity between the pod and other devices on the Tailnet. You can use [ACLs](https://tailscale.com/kb/1018/acls/) to control traffic flow.
1. Create and login to the sample nginx pod with a Tailscale sidecar
@@ -46,15 +40,12 @@ Running as a sidecar allows you to directly expose a Kubernetes pod over Tailsca
```bash
curl http://nginx
```
Or, if you have [MagicDNS](https://tailscale.com/kb/1081/magicdns/) disabled:
```bash
curl "http://$(tailscale ip -4 nginx)"
```
#### Userspace Sidecar
You can also run the sidecar in userspace mode. The obvious benefit is reducing the amount of permissions Tailscale needs to run, the downside is that for outbound connectivity from the pod to the Tailnet you would need to use either the [SOCKS proxy](https://tailscale.com/kb/1112/userspace-networking) or HTTP proxy.
1. Create and login to the sample nginx pod with a Tailscale sidecar
@@ -70,29 +61,23 @@ You can also run the sidecar in userspace mode. The obvious benefit is reducing
```bash
curl http://nginx
```
Or, if you have [MagicDNS](https://tailscale.com/kb/1081/magicdns/) disabled:
```bash
curl "http://$(tailscale ip -4 nginx)"
```
### Sample Proxy
Running a Tailscale proxy allows you to provide inbound connectivity to a Kubernetes Service.
1. Provide the `ClusterIP` of the service you want to reach by either:
**Creating a new deployment**
```bash
kubectl create deployment nginx --image nginx
kubectl expose deployment nginx --port 80
export TS_DEST_IP="$(kubectl get svc nginx -o=jsonpath='{.spec.clusterIP}')"
```
**Using an existing service**
```bash
export TS_DEST_IP="$(kubectl get svc <SVC_NAME> -o=jsonpath='{.spec.clusterIP}')"
```
@@ -122,7 +107,7 @@ Running a Tailscale proxy allows you to provide inbound connectivity to a Kubern
Running a Tailscale [subnet router](https://tailscale.com/kb/1019/subnets/) allows you to access
the entire Kubernetes cluster network (assuming NetworkPolicies allow) over Tailscale.
1. Identify the Pod/Service CIDRs that cover your Kubernetes cluster. These will vary depending on [which CNI](https://kubernetes.io/docs/concepts/cluster-administration/networking/) you are using and on the Cloud Provider you are using. Add these to the `TS_ROUTES` variable as comma-separated values.
1. Identify the Pod/Service CIDRs that cover your Kubernetes cluster. These will vary depending on [which CNI](https://kubernetes.io/docs/concepts/cluster-administration/networking/) you are using and on the Cloud Provider you are using. Add these to the `TS_ROUTES` variable as comma-separated values.
```bash
SERVICE_CIDR=10.20.0.0/16
@@ -139,7 +124,7 @@ the entire Kubernetes cluster network (assuming NetworkPolicies allow) over Tail
```
1. In the [Tailscale admin console](https://login.tailscale.com/admin/machines), ensure that the
routes for the subnet-router are enabled.
routes for the subnet-router are enabled.
1. Make sure that any client you want to connect from has `--accept-routes` enabled.
@@ -148,8 +133,8 @@ the entire Kubernetes cluster network (assuming NetworkPolicies allow) over Tail
```bash
# Get the Service IP
INTERNAL_IP="$(kubectl get svc <SVC_NAME> -o=jsonpath='{.spec.clusterIP}')"
# or, the Pod IP
# INTERNAL_IP="$(kubectl get po <POD_NAME> -o=jsonpath='{.status.podIP}')"
INTERNAL_PORT=8080
# or, the Pod IP
# INTERNAL_IP="$(kubectl get po <POD_NAME> -o=jsonpath='{.status.podIP}')"
INTERNAL_PORT=8080
curl http://$INTERNAL_IP:$INTERNAL_PORT
```

8
go.mod
View File

@@ -54,7 +54,6 @@ require (
github.com/u-root/u-root v0.8.0
github.com/vishvananda/netlink v1.1.1-0.20211118161826-650dca95af54
go4.org/mem v0.0.0-20210711025021-927187094b94
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f
golang.org/x/net v0.0.0-20220607020251-c690dde0001d
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f
@@ -64,10 +63,11 @@ require (
golang.org/x/tools v0.1.11
golang.zx2c4.com/wireguard v0.0.0-20220703234212-c31a7b1ab478
golang.zx2c4.com/wireguard/windows v0.4.10
gvisor.dev/gvisor v0.0.0-20220801230058-850e42eb4444
gvisor.dev/gvisor v0.0.0-20220721202624-0b2c11c2773c
honnef.co/go/tools v0.4.0-0.dev.0.20220404092545-59d7a2877f83
inet.af/netaddr v0.0.0-20220617031823-097006376321
inet.af/peercred v0.0.0-20210906144145-0893ea02156a
inet.af/wf v0.0.0-20220728202103-50d96caab2f6
inet.af/wf v0.0.0-20211204062712-86aaea0a7310
nhooyr.io/websocket v1.8.7
)
@@ -263,6 +263,8 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/xanzy/ssh-agent v0.3.1 // indirect
github.com/yeya24/promlinter v0.1.0 // indirect
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
golang.org/x/exp/typeparams v0.0.0-20220328175248-053ad81199eb // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/text v0.3.7 // indirect

21
go.sum
View File

@@ -265,6 +265,7 @@ github.com/docker/docker-credential-helpers v0.6.4 h1:axCks+yV+2MR3/kZhAmy07yC56
github.com/docker/docker-credential-helpers v0.6.4/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/dvyukov/go-fuzz v0.0.0-20210103155950-6a8e9d1f2415/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -920,6 +921,7 @@ github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/peterbourgon/ff/v3 v3.0.0/go.mod h1:UILIFjRH5a/ar8TjXYLTkIvSvekZqPm5Eb/qbGk6CT0=
github.com/peterbourgon/ff/v3 v3.1.2 h1:0GNhbRhO9yHA4CC27ymskOsuRpmX0YQxwxM9UPiP6JM=
github.com/peterbourgon/ff/v3 v3.1.2/go.mod h1:XNJLY8EIl6MjMVjBS4F0+G0LYoAqs0DTa4rmHHukKDE=
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d h1:CdDQnGF8Nq9ocOS/xlSptM1N3BbrA6/kmaep5ggwaIA=
@@ -1241,12 +1243,16 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go4.org/intern v0.0.0-20210108033219-3eb7198706b2/go.mod h1:vLqJ+12kCw61iCWsPto0EOHhBS+o4rO5VIucbc9g2Cc=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4=
go4.org/mem v0.0.0-20210711025021-927187094b94/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf h1:IdwJUzqoIo5lkr2EOyKoe5qipUaEjbOKKY5+fzPBZ3A=
go4.org/netipx v0.0.0-20220725152314-7e7bdc8411bf/go.mod h1:+QXzaoURFd0rGDIjDNpyIkv+F9R7EmeKorvlKRnhqgA=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222175341-b30ae309168e/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
golang.org/x/crypto v0.0.0-20180501155221-613d6eafa307/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1851,8 +1857,8 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
gvisor.dev/gvisor v0.0.0-20220801230058-850e42eb4444 h1:0d3ygmOM5RgQB8rmsZNeAY/7Q98fKt1HrGO2XIp4pDI=
gvisor.dev/gvisor v0.0.0-20220801230058-850e42eb4444/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
gvisor.dev/gvisor v0.0.0-20220721202624-0b2c11c2773c h1:frrINYSQqhraHqy23/dWqdNt7mRlsGJJBwGHvI3Q+/c=
gvisor.dev/gvisor v0.0.0-20220721202624-0b2c11c2773c/go.mod h1:TIvkJD0sxe8pIob3p6T8IzxXunlp6yfgktvTNp+DGNM=
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=
@@ -1867,10 +1873,13 @@ honnef.co/go/tools v0.4.0-0.dev.0.20220404092545-59d7a2877f83/go.mod h1:vlRD9XEr
howett.net/plist v0.0.0-20181124034731-591f970eefbb/go.mod h1:vMygbs4qMhSZSc4lCUl2OEE+rDiIIJAIdR4m7MiMcm0=
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
inet.af/netaddr v0.0.0-20210515010201-ad03edc7c841/go.mod h1:z0nx+Dh+7N7CC8V5ayHtHGpZpxLQZZxkIaaz6HN65Ls=
inet.af/netaddr v0.0.0-20220617031823-097006376321 h1:B4dC8ySKTQXasnjDTMsoCMf1sQG4WsMej0WXaHxunmU=
inet.af/netaddr v0.0.0-20220617031823-097006376321/go.mod h1:OIezDfdzOgFhuw4HuWapWq2e9l0H9tK4F1j+ETRtF3k=
inet.af/peercred v0.0.0-20210906144145-0893ea02156a h1:qdkS8Q5/i10xU2ArJMKYhVa1DORzBfYS/qA2UK2jheg=
inet.af/peercred v0.0.0-20210906144145-0893ea02156a/go.mod h1:FjawnflS/udxX+SvpsMgZfdqx2aykOlkISeAsADi5IU=
inet.af/wf v0.0.0-20220728202103-50d96caab2f6 h1:BfgDtKnWJTeu+xI1aOEweXdPwqOhB3IbQUDj1XuftcY=
inet.af/wf v0.0.0-20220728202103-50d96caab2f6/go.mod h1:bSAQ38BYbY68uwpasXOTZo22dKGy9SNvI6PZFeKomZE=
inet.af/wf v0.0.0-20211204062712-86aaea0a7310 h1:0jKHTf+W75kYRyg5bto1UT+r18QmAz2u/5pAs/fx4zo=
inet.af/wf v0.0.0-20211204062712-86aaea0a7310/go.mod h1:ViGMZRA6+RA318D7GCncrjv5gHUrPYrNDejjU12tikA=
mvdan.cc/gofumpt v0.0.0-20200802201014-ab5a8192947d/go.mod h1:bzrjFmaD6+xqohD3KYP0H2FEuxknnBmyyOxdhLdaIws=
mvdan.cc/gofumpt v0.0.0-20201129102820-5c11c50e9475/go.mod h1:E4LOcu9JQEtnYXtB1Y51drqh2Qr2Ngk9J3YrRCwcbd0=
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=

View File

@@ -1 +1 @@
tailscale.go1.19
tailscale.go1.18

View File

@@ -1 +1 @@
effe2d16a9ed6c4fd97361e8090bb22acc221075
149f7d88f11384083d42ccbcdb31693510385ec6

View File

@@ -48,17 +48,7 @@ func linuxDeviceModel() string {
return ""
}
func getQnapQtsVersion(versionInfo string) string {
for _, field := range strings.Fields(versionInfo) {
if suffix := strings.TrimPrefix(field, "QTSFW_"); suffix != field {
return "QTS " + suffix
}
}
return ""
}
func osVersionLinux() string {
// TODO(bradfitz,dgentry): cache this, or make caller(s) cache it.
dist := distro.Get()
propFile := "/etc/os-release"
switch dist {
@@ -69,9 +59,6 @@ func osVersionLinux() string {
case distro.WDMyCloud:
slurp, _ := ioutil.ReadFile("/etc/version")
return fmt.Sprintf("%s", string(bytes.TrimSpace(slurp)))
case distro.QNAP:
slurp, _ := ioutil.ReadFile("/etc/version_info")
return getQnapQtsVersion(string(slurp))
}
m := map[string]string{}

View File

@@ -1,38 +0,0 @@
// Copyright (c) 2022 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.
//go:build linux && !android
// +build linux,!android
package hostinfo
import (
"testing"
)
func TestQnap(t *testing.T) {
version_info := `commit 2910d3a594b068024ed01a64a0fe4168cb001a12
Date: 2022-05-30 16:08:45 +0800
================================================
* QTSFW_5.0.0
remotes/origin/QTSFW_5.0.0`
got := getQnapQtsVersion(version_info)
want := "QTS 5.0.0"
if got != want {
t.Errorf("got %q; want %q", got, want)
}
got = getQnapQtsVersion("")
want = ""
if got != want {
t.Errorf("got %q; want %q", got, want)
}
got = getQnapQtsVersion("just a bunch of junk")
want = ""
if got != want {
t.Errorf("got %q; want %q", got, want)
}
}

View File

@@ -2,12 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build for_go_mod_tidy_only
// +build for_go_mod_tidy_only
package tooldeps
import (
_ "github.com/tailscale/depaware/depaware"
_ "golang.org/x/tools/cmd/goimports"
)

View File

@@ -5,10 +5,10 @@
package ipn
import (
"net/netip"
"sync"
"time"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/logger"
"tailscale.com/types/netmap"
@@ -126,7 +126,7 @@ func (h *Handle) EngineStatus() EngineStatus {
return h.engineStatusCache
}
func (h *Handle) LocalAddrs() []netip.Prefix {
func (h *Handle) LocalAddrs() []netaddr.IPPrefix {
h.mu.Lock()
defer h.mu.Unlock()
@@ -134,7 +134,7 @@ func (h *Handle) LocalAddrs() []netip.Prefix {
if nm != nil {
return nm.Addresses
}
return []netip.Prefix{}
return []netaddr.IPPrefix{}
}
func (h *Handle) NetMap() *netmap.NetworkMap {

View File

@@ -7,8 +7,7 @@
package ipn
import (
"net/netip"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/persist"
"tailscale.com/types/preftype"
@@ -37,7 +36,7 @@ var _PrefsCloneNeedsRegeneration = Prefs(struct {
RouteAll bool
AllowSingleHosts bool
ExitNodeID tailcfg.StableNodeID
ExitNodeIP netip.Addr
ExitNodeIP netaddr.IP
ExitNodeAllowLANAccess bool
CorpDNS bool
RunSSH bool
@@ -48,7 +47,7 @@ var _PrefsCloneNeedsRegeneration = Prefs(struct {
Hostname string
NotepadURLs bool
ForceDaemon bool
AdvertiseRoutes []netip.Prefix
AdvertiseRoutes []netaddr.IPPrefix
NoSNAT bool
NetfilterMode preftype.NetfilterMode
OperatorUser string

View File

@@ -6,10 +6,10 @@ package ipnlocal
import (
"encoding/json"
"net/netip"
"reflect"
"testing"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/net/dns"
"tailscale.com/tailcfg"
@@ -20,20 +20,20 @@ import (
"tailscale.com/util/dnsname"
)
func ipps(ippStrs ...string) (ipps []netip.Prefix) {
func ipps(ippStrs ...string) (ipps []netaddr.IPPrefix) {
for _, s := range ippStrs {
if ip, err := netip.ParseAddr(s); err == nil {
ipps = append(ipps, netip.PrefixFrom(ip, ip.BitLen()))
if ip, err := netaddr.ParseIP(s); err == nil {
ipps = append(ipps, netaddr.IPPrefixFrom(ip, ip.BitLen()))
continue
}
ipps = append(ipps, netip.MustParsePrefix(s))
ipps = append(ipps, netaddr.MustParseIPPrefix(s))
}
return
}
func ips(ss ...string) (ips []netip.Addr) {
func ips(ss ...string) (ips []netaddr.IP) {
for _, s := range ss {
ips = append(ips, netip.MustParseAddr(s))
ips = append(ips, netaddr.MustParseIP(s))
}
return
}
@@ -54,7 +54,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
prefs: &ipn.Prefs{},
want: &dns.Config{
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
Hosts: map[dnsname.FQDN][]netip.Addr{},
Hosts: map[dnsname.FQDN][]netaddr.IP{},
},
},
{
@@ -80,7 +80,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
prefs: &ipn.Prefs{},
want: &dns.Config{
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
Hosts: map[dnsname.FQDN][]netip.Addr{
Hosts: map[dnsname.FQDN][]netaddr.IP{
"b.net.": ips("100.102.0.1", "100.102.0.2"),
"myname.net.": ips("100.101.101.101"),
"peera.net.": ips("100.102.0.1", "100.102.0.2"),
@@ -115,7 +115,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
want: &dns.Config{
OnlyIPv6: true,
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
Hosts: map[dnsname.FQDN][]netip.Addr{
Hosts: map[dnsname.FQDN][]netaddr.IP{
"b.net.": ips("fe75::2"),
"myname.net.": ips("fe75::1"),
"peera.net.": ips("fe75::1001"),
@@ -139,7 +139,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
prefs: &ipn.Prefs{},
want: &dns.Config{
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
Hosts: map[dnsname.FQDN][]netip.Addr{
Hosts: map[dnsname.FQDN][]netaddr.IP{
"myname.net.": ips("100.101.101.101"),
"foo.com.": ips("1.2.3.4"),
"bar.com.": ips("1::6"),
@@ -159,7 +159,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
CorpDNS: true,
},
want: &dns.Config{
Hosts: map[dnsname.FQDN][]netip.Addr{},
Hosts: map[dnsname.FQDN][]netaddr.IP{},
Routes: map[dnsname.FQDN][]*dnstype.Resolver{
"0.e.1.a.c.5.1.1.a.7.d.f.ip6.arpa.": nil,
"100.100.in-addr.arpa.": nil,
@@ -259,7 +259,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
CorpDNS: true,
},
want: &dns.Config{
Hosts: map[dnsname.FQDN][]netip.Addr{},
Hosts: map[dnsname.FQDN][]netaddr.IP{},
DefaultResolvers: []*dnstype.Resolver{
{Addr: "8.8.8.8"},
},
@@ -282,7 +282,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
ExitNodeID: "some-id",
},
want: &dns.Config{
Hosts: map[dnsname.FQDN][]netip.Addr{},
Hosts: map[dnsname.FQDN][]netaddr.IP{},
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
DefaultResolvers: []*dnstype.Resolver{
{Addr: "8.8.4.4"},
@@ -302,7 +302,7 @@ func TestDNSConfigForNetmap(t *testing.T) {
CorpDNS: true,
},
want: &dns.Config{
Hosts: map[dnsname.FQDN][]netip.Addr{},
Hosts: map[dnsname.FQDN][]netaddr.IP{},
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
},
},

View File

@@ -11,7 +11,6 @@ import (
"io"
"net"
"net/http"
"net/netip"
"os"
"os/user"
"path/filepath"
@@ -23,7 +22,7 @@ import (
"sync/atomic"
"time"
"go4.org/netipx"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/control/controlclient"
"tailscale.com/envknob"
@@ -129,7 +128,7 @@ type LocalBackend struct {
shutdownCalled bool // if Shutdown has been called
filterAtomic atomic.Value // of *filter.Filter
containsViaIPFuncAtomic atomic.Value // of func(netip.Addr) bool
containsViaIPFuncAtomic atomic.Value // of func(netaddr.IP) bool
// The mutex protects the following elements.
mu sync.Mutex
@@ -151,7 +150,7 @@ type LocalBackend struct {
hostinfo *tailcfg.Hostinfo
// netMap is not mutated in-place once set.
netMap *netmap.NetworkMap
nodeByAddr map[netip.Addr]*tailcfg.Node
nodeByAddr map[netaddr.IP]*tailcfg.Node
activeLogin string // last logged LoginName from netMap
engineStatus ipn.EngineStatus
endpoints []tailcfg.Endpoint
@@ -175,11 +174,11 @@ type LocalBackend struct {
// same as the Network Extension lifetime and we can thus avoid
// double-copying files by writing them to the right location
// immediately.
// It's also used on several NAS platforms (Synology, TrueNAS, etc)
// but in that case DoFinalRename is also set true, which moves the
// *.partial file to its final name on completion.
// It's also used on Synology & TrueNAS, but in that case DoFinalRename
// is also set true, which moves the *.partial file to its final
// name on completion.
directFileRoot string
directFileDoFinalRename bool // false on macOS, true on several NAS platforms
directFileDoFinalRename bool // false on macOS, true on Synology & TrueNAS
// statusLock must be held before calling statusChanged.Wait() or
// statusChanged.Broadcast().
@@ -232,7 +231,7 @@ func NewLocalBackend(logf logger.Logf, logid string, store ipn.StateStore, diale
}
// Default filter blocks everything and logs nothing, until Start() is called.
b.setFilter(filter.NewAllowNone(logf, &netipx.IPSet{}))
b.setFilter(filter.NewAllowNone(logf, &netaddr.IPSet{}))
b.statusChanged = sync.NewCond(&b.statusLock)
b.e.SetStatusCallback(b.setWgengineStatus)
@@ -497,13 +496,13 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
if p.LastSeen != nil {
lastSeen = *p.LastSeen
}
var tailscaleIPs = make([]netip.Addr, 0, len(p.Addresses))
var tailscaleIPs = make([]netaddr.IP, 0, len(p.Addresses))
for _, addr := range p.Addresses {
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.Addr()) {
tailscaleIPs = append(tailscaleIPs, addr.Addr())
if addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.IP()) {
tailscaleIPs = append(tailscaleIPs, addr.IP())
}
}
exitNodeOption := tsaddr.PrefixesContainsFunc(p.AllowedIPs, func(r netip.Prefix) bool {
exitNodeOption := tsaddr.PrefixesContainsFunc(p.AllowedIPs, func(r netaddr.IPPrefix) bool {
return r.Bits() == 0
})
var tags *views.Slice[string]
@@ -541,12 +540,12 @@ func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
// WhoIs reports the node and user who owns the node with the given IP:port.
// If the IP address is a Tailscale IP, the provided port may be 0.
// If ok == true, n and u are valid.
func (b *LocalBackend) WhoIs(ipp netip.AddrPort) (n *tailcfg.Node, u tailcfg.UserProfile, ok bool) {
func (b *LocalBackend) WhoIs(ipp netaddr.IPPort) (n *tailcfg.Node, u tailcfg.UserProfile, ok bool) {
b.mu.Lock()
defer b.mu.Unlock()
n, ok = b.nodeByAddr[ipp.Addr()]
n, ok = b.nodeByAddr[ipp.IP()]
if !ok {
var ip netip.Addr
var ip netaddr.IP
if ipp.Port() != 0 {
ip, ok = b.e.WhoIsIPPort(ipp)
}
@@ -567,7 +566,7 @@ func (b *LocalBackend) WhoIs(ipp netip.AddrPort) (n *tailcfg.Node, u tailcfg.Use
// PeerCaps returns the capabilities that remote src IP has to
// ths current node.
func (b *LocalBackend) PeerCaps(src netip.Addr) []string {
func (b *LocalBackend) PeerCaps(src netaddr.IP) []string {
b.mu.Lock()
defer b.mu.Unlock()
if b.netMap == nil {
@@ -581,9 +580,9 @@ func (b *LocalBackend) PeerCaps(src netip.Addr) []string {
if !a.IsSingleIP() {
continue
}
dstIP := a.Addr()
dstIP := a.IP()
if dstIP.BitLen() == src.BitLen() {
return filt.AppendCaps(nil, src, a.Addr())
return filt.AppendCaps(nil, src, a.IP())
}
}
return nil
@@ -751,7 +750,7 @@ func (b *LocalBackend) findExitNodeIDLocked(nm *netmap.NetworkMap) (prefsChanged
// If we have a desired IP on file, try to find the corresponding
// node.
if !b.prefs.ExitNodeIP.IsValid() {
if b.prefs.ExitNodeIP.IsZero() {
return false
}
@@ -763,13 +762,13 @@ func (b *LocalBackend) findExitNodeIDLocked(nm *netmap.NetworkMap) (prefsChanged
for _, peer := range nm.Peers {
for _, addr := range peer.Addresses {
if !addr.IsSingleIP() || addr.Addr() != b.prefs.ExitNodeIP {
if !addr.IsSingleIP() || addr.IP() != b.prefs.ExitNodeIP {
continue
}
// Found the node being referenced, upgrade prefs to
// reference it directly for next time.
b.prefs.ExitNodeID = peer.StableID
b.prefs.ExitNodeIP = netip.Addr{}
b.prefs.ExitNodeIP = netaddr.IP{}
return true
}
}
@@ -1122,10 +1121,10 @@ func (b *LocalBackend) updateFilterLocked(netMap *netmap.NetworkMap, prefs *ipn.
// quite hard to debug, so save yourself the trouble.
var (
haveNetmap = netMap != nil
addrs []netip.Prefix
addrs []netaddr.IPPrefix
packetFilter []filter.Match
localNetsB netipx.IPSetBuilder
logNetsB netipx.IPSetBuilder
localNetsB netaddr.IPSetBuilder
logNetsB netaddr.IPSetBuilder
shieldsUp = prefs == nil || prefs.ShieldsUp // Be conservative when not ready
)
// Log traffic for Tailscale IPs.
@@ -1205,21 +1204,21 @@ func (b *LocalBackend) setFilter(f *filter.Filter) {
b.e.SetFilter(f)
}
var removeFromDefaultRoute = []netip.Prefix{
var removeFromDefaultRoute = []netaddr.IPPrefix{
// RFC1918 LAN ranges
netip.MustParsePrefix("192.168.0.0/16"),
netip.MustParsePrefix("172.16.0.0/12"),
netip.MustParsePrefix("10.0.0.0/8"),
netaddr.MustParseIPPrefix("192.168.0.0/16"),
netaddr.MustParseIPPrefix("172.16.0.0/12"),
netaddr.MustParseIPPrefix("10.0.0.0/8"),
// IPv4 link-local
netip.MustParsePrefix("169.254.0.0/16"),
netaddr.MustParseIPPrefix("169.254.0.0/16"),
// IPv4 multicast
netip.MustParsePrefix("224.0.0.0/4"),
netaddr.MustParseIPPrefix("224.0.0.0/4"),
// Tailscale IPv4 range
tsaddr.CGNATRange(),
// IPv6 Link-local addresses
netip.MustParsePrefix("fe80::/10"),
netaddr.MustParseIPPrefix("fe80::/10"),
// IPv6 multicast
netip.MustParsePrefix("ff00::/8"),
netaddr.MustParseIPPrefix("ff00::/8"),
// Tailscale IPv6 range
tsaddr.TailscaleULARange(),
}
@@ -1231,7 +1230,7 @@ var removeFromDefaultRoute = []netip.Prefix{
//
// Given that "internal" routes don't leave the device, we choose to
// trust them more, allowing access to them when an Exit Node is enabled.
func internalAndExternalInterfaces() (internal, external []netip.Prefix, err error) {
func internalAndExternalInterfaces() (internal, external []netaddr.IPPrefix, err error) {
il, err := interfaces.GetList()
if err != nil {
return nil, nil, err
@@ -1239,12 +1238,12 @@ func internalAndExternalInterfaces() (internal, external []netip.Prefix, err err
return internalAndExternalInterfacesFrom(il, runtime.GOOS)
}
func internalAndExternalInterfacesFrom(il interfaces.List, goos string) (internal, external []netip.Prefix, err error) {
func internalAndExternalInterfacesFrom(il interfaces.List, goos string) (internal, external []netaddr.IPPrefix, err error) {
// We use an IPSetBuilder here to canonicalize the prefixes
// and to remove any duplicate entries.
var internalBuilder, externalBuilder netipx.IPSetBuilder
if err := il.ForeachInterfaceAddress(func(iface interfaces.Interface, pfx netip.Prefix) {
if tsaddr.IsTailscaleIP(pfx.Addr()) {
var internalBuilder, externalBuilder netaddr.IPSetBuilder
if err := il.ForeachInterfaceAddress(func(iface interfaces.Interface, pfx netaddr.IPPrefix) {
if tsaddr.IsTailscaleIP(pfx.IP()) {
return
}
if pfx.IsSingleIP() {
@@ -1285,16 +1284,16 @@ func internalAndExternalInterfacesFrom(il interfaces.List, goos string) (interna
return iSet.Prefixes(), eSet.Prefixes(), nil
}
func interfaceRoutes() (ips *netipx.IPSet, hostIPs []netip.Addr, err error) {
var b netipx.IPSetBuilder
if err := interfaces.ForeachInterfaceAddress(func(_ interfaces.Interface, pfx netip.Prefix) {
if tsaddr.IsTailscaleIP(pfx.Addr()) {
func interfaceRoutes() (ips *netaddr.IPSet, hostIPs []netaddr.IP, err error) {
var b netaddr.IPSetBuilder
if err := interfaces.ForeachInterfaceAddress(func(_ interfaces.Interface, pfx netaddr.IPPrefix) {
if tsaddr.IsTailscaleIP(pfx.IP()) {
return
}
if pfx.IsSingleIP() {
return
}
hostIPs = append(hostIPs, pfx.Addr())
hostIPs = append(hostIPs, pfx.IP())
b.AddPrefix(pfx)
}); err != nil {
return nil, nil, err
@@ -1307,8 +1306,8 @@ func interfaceRoutes() (ips *netipx.IPSet, hostIPs []netip.Addr, err error) {
// shrinkDefaultRoute returns an IPSet representing the IPs in route,
// minus those in removeFromDefaultRoute and localInterfaceRoutes,
// plus the IPs in hostIPs.
func shrinkDefaultRoute(route netip.Prefix, localInterfaceRoutes *netipx.IPSet, hostIPs []netip.Addr) (*netipx.IPSet, error) {
var b netipx.IPSetBuilder
func shrinkDefaultRoute(route netaddr.IPPrefix, localInterfaceRoutes *netaddr.IPSet, hostIPs []netaddr.IP) (*netaddr.IPSet, error) {
var b netaddr.IPSetBuilder
// Add the default route.
b.AddPrefix(route)
// Remove the local interface routes.
@@ -1334,7 +1333,7 @@ func shrinkDefaultRoute(route netip.Prefix, localInterfaceRoutes *netipx.IPSet,
// dnsCIDRsEqual determines whether two CIDR lists are equal
// for DNS map construction purposes (that is, only the first entry counts).
func dnsCIDRsEqual(newAddr, oldAddr []netip.Prefix) bool {
func dnsCIDRsEqual(newAddr, oldAddr []netaddr.IPPrefix) bool {
if len(newAddr) != len(oldAddr) {
return false
}
@@ -1732,7 +1731,7 @@ func (b *LocalBackend) StartLoginInteractive() {
}
}
func (b *LocalBackend) Ping(ctx context.Context, ip netip.Addr, pingType tailcfg.PingType) (*ipnstate.PingResult, error) {
func (b *LocalBackend) Ping(ctx context.Context, ip netaddr.IP, pingType tailcfg.PingType) (*ipnstate.PingResult, error) {
if pingType == tailcfg.PingPeerAPI {
t0 := time.Now()
node, base, err := b.pingPeerAPI(ctx, ip)
@@ -1769,7 +1768,7 @@ func (b *LocalBackend) Ping(ctx context.Context, ip netip.Addr, pingType tailcfg
}
}
func (b *LocalBackend) pingPeerAPI(ctx context.Context, ip netip.Addr) (peer *tailcfg.Node, peerBase string, err error) {
func (b *LocalBackend) pingPeerAPI(ctx context.Context, ip netaddr.IP) (peer *tailcfg.Node, peerBase string, err error) {
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
nm := b.NetMap()
@@ -1878,9 +1877,6 @@ func (b *LocalBackend) checkSSHPrefsLocked(p *ipn.Prefs) error {
if distro.Get() == distro.Synology && !envknob.UseWIPCode() {
return errors.New("The Tailscale SSH server does not run on Synology.")
}
if distro.Get() == distro.QNAP && !envknob.UseWIPCode() {
return errors.New("The Tailscale SSH server does not run on QNAP.")
}
// otherwise okay
case "darwin":
// okay only in tailscaled mode for now.
@@ -2068,7 +2064,7 @@ func (b *LocalBackend) setPrefsLockedOnEntry(caller string, newp *ipn.Prefs) {
// GetPeerAPIPort returns the port number for the peerapi server
// running on the provided IP.
func (b *LocalBackend) GetPeerAPIPort(ip netip.Addr) (port uint16, ok bool) {
func (b *LocalBackend) GetPeerAPIPort(ip netaddr.IP) (port uint16, ok bool) {
b.mu.Lock()
defer b.mu.Unlock()
for _, pln := range b.peerAPIListeners {
@@ -2086,11 +2082,11 @@ func (b *LocalBackend) GetPeerAPIPort(ip netip.Addr) (port uint16, ok bool) {
// or IPv6 IP and the peerapi port for that address).
//
// The connection will be closed by ServePeerAPIConnection.
func (b *LocalBackend) ServePeerAPIConnection(remote, local netip.AddrPort, c net.Conn) {
func (b *LocalBackend) ServePeerAPIConnection(remote, local netaddr.IPPort, c net.Conn) {
b.mu.Lock()
defer b.mu.Unlock()
for _, pln := range b.peerAPIListeners {
if pln.ip == local.Addr() {
if pln.ip == local.IP() {
go pln.ServeConn(remote, c)
return
}
@@ -2288,7 +2284,7 @@ func shouldUseOneCGNATRoute(nm *netmap.NetworkMap, logf logger.Logf, versionOS s
func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs *ipn.Prefs, logf logger.Logf, versionOS string) *dns.Config {
dcfg := &dns.Config{
Routes: map[dnsname.FQDN][]*dnstype.Resolver{},
Hosts: map[dnsname.FQDN][]netip.Addr{},
Hosts: map[dnsname.FQDN][]netaddr.IP{},
}
// selfV6Only is whether we only have IPv6 addresses ourselves.
@@ -2301,7 +2297,7 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs *ipn.Prefs, logf logger.Log
// isn't configured to make MagicDNS resolution truly
// magic. Details in
// https://github.com/tailscale/tailscale/issues/1886.
set := func(name string, addrs []netip.Prefix) {
set := func(name string, addrs []netaddr.IPPrefix) {
if len(addrs) == 0 || name == "" {
return
}
@@ -2310,11 +2306,11 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs *ipn.Prefs, logf logger.Log
return // TODO: propagate error?
}
have4 := tsaddr.PrefixesContainsFunc(addrs, tsaddr.PrefixIs4)
var ips []netip.Addr
var ips []netaddr.IP
for _, addr := range addrs {
if selfV6Only {
if addr.Addr().Is6() {
ips = append(ips, addr.Addr())
if addr.IP().Is6() {
ips = append(ips, addr.IP())
}
continue
}
@@ -2326,10 +2322,10 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs *ipn.Prefs, logf logger.Log
// https://github.com/tailscale/tailscale/issues/1152
// tracks adding the right capability reporting to
// enable AAAA in MagicDNS.
if addr.Addr().Is6() && have4 {
if addr.IP().Is6() && have4 {
continue
}
ips = append(ips, addr.Addr())
ips = append(ips, addr.IP())
}
dcfg.Hosts[fqdn] = ips
}
@@ -2345,7 +2341,7 @@ func dnsConfigForNetmap(nm *netmap.NetworkMap, prefs *ipn.Prefs, logf logger.Log
// TODO: more
continue
}
ip, err := netip.ParseAddr(rec.Value)
ip, err := netaddr.ParseIP(rec.Value)
if err != nil {
// Ignore.
continue
@@ -2521,7 +2517,7 @@ func (b *LocalBackend) initPeerAPIListener() {
if len(b.netMap.Addresses) == len(b.peerAPIListeners) {
allSame := true
for i, pln := range b.peerAPIListeners {
if pln.ip != b.netMap.Addresses[i].Addr() {
if pln.ip != b.netMap.Addresses[i].IP() {
allSame = false
break
}
@@ -2564,20 +2560,20 @@ func (b *LocalBackend) initPeerAPIListener() {
var err error
skipListen := i > 0 && isNetstack
if !skipListen {
ln, err = ps.listen(a.Addr(), b.prevIfState)
ln, err = ps.listen(a.IP(), b.prevIfState)
if err != nil {
if peerAPIListenAsync {
// Expected. But we fix it later in linkChange
// ("peerAPIListeners too low").
continue
}
b.logf("[unexpected] peerapi listen(%q) error: %v", a.Addr(), err)
b.logf("[unexpected] peerapi listen(%q) error: %v", a.IP(), err)
continue
}
}
pln := &peerAPIListener{
ps: ps,
ip: a.Addr(),
ip: a.IP(),
ln: ln, // nil for 2nd+ on netstack
lb: b,
}
@@ -2586,7 +2582,7 @@ func (b *LocalBackend) initPeerAPIListener() {
} else {
pln.port = ln.Addr().(*net.TCPAddr).Port
}
pln.urlStr = "http://" + net.JoinHostPort(a.Addr().String(), strconv.Itoa(pln.port))
pln.urlStr = "http://" + net.JoinHostPort(a.IP().String(), strconv.Itoa(pln.port))
b.logf("peerapi: serving on %s", pln.urlStr)
go pln.serve()
b.peerAPIListeners = append(b.peerAPIListeners, pln)
@@ -2621,30 +2617,30 @@ func magicDNSRootDomains(nm *netmap.NetworkMap) []dnsname.FQDN {
}
var (
ipv4Default = netip.MustParsePrefix("0.0.0.0/0")
ipv6Default = netip.MustParsePrefix("::/0")
ipv4Default = netaddr.MustParseIPPrefix("0.0.0.0/0")
ipv6Default = netaddr.MustParseIPPrefix("::/0")
)
// peerRoutes returns the routerConfig.Routes to access peers.
// If there are over cgnatThreshold CGNAT routes, one big CGNAT route
// is used instead.
func peerRoutes(peers []wgcfg.Peer, cgnatThreshold int) (routes []netip.Prefix) {
func peerRoutes(peers []wgcfg.Peer, cgnatThreshold int) (routes []netaddr.IPPrefix) {
tsULA := tsaddr.TailscaleULARange()
cgNAT := tsaddr.CGNATRange()
var didULA bool
var cgNATIPs []netip.Prefix
var cgNATIPs []netaddr.IPPrefix
for _, peer := range peers {
for _, aip := range peer.AllowedIPs {
aip = unmapIPPrefix(aip)
// Only add the Tailscale IPv6 ULA once, if we see anybody using part of it.
if aip.Addr().Is6() && aip.IsSingleIP() && tsULA.Contains(aip.Addr()) {
if aip.IP().Is6() && aip.IsSingleIP() && tsULA.Contains(aip.IP()) {
if !didULA {
didULA = true
routes = append(routes, tsULA)
}
continue
}
if aip.IsSingleIP() && cgNAT.Contains(aip.Addr()) {
if aip.IsSingleIP() && cgNAT.Contains(aip.IP()) {
cgNATIPs = append(cgNATIPs, aip)
} else {
routes = append(routes, aip)
@@ -2664,11 +2660,11 @@ func peerRoutes(peers []wgcfg.Peer, cgnatThreshold int) (routes []netip.Prefix)
return routes
}
func ipPrefixLess(ri, rj netip.Prefix) bool {
if ri.Addr() == rj.Addr() {
func ipPrefixLess(ri, rj netaddr.IPPrefix) bool {
if ri.IP() == rj.IP() {
return ri.Bits() < rj.Bits()
}
return ri.Addr().Less(rj.Addr())
return ri.IP().Less(rj.IP())
}
// routerConfig produces a router.Config from a wireguard config and IPN prefs.
@@ -2696,7 +2692,7 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs, oneCGNA
// likely to break some functionality, but if the user expressed a
// preference for routing remotely, we want to avoid leaking
// traffic at the expense of functionality.
if prefs.ExitNodeID != "" || prefs.ExitNodeIP.IsValid() {
if prefs.ExitNodeID != "" || !prefs.ExitNodeIP.IsZero() {
var default4, default6 bool
for _, route := range rs.Routes {
switch route {
@@ -2735,17 +2731,17 @@ func (b *LocalBackend) routerConfig(cfg *wgcfg.Config, prefs *ipn.Prefs, oneCGNA
}
if tsaddr.PrefixesContainsFunc(rs.LocalAddrs, tsaddr.PrefixIs4) {
rs.Routes = append(rs.Routes, netip.PrefixFrom(tsaddr.TailscaleServiceIP(), 32))
rs.Routes = append(rs.Routes, netaddr.IPPrefixFrom(tsaddr.TailscaleServiceIP(), 32))
}
return rs
}
func unmapIPPrefix(ipp netip.Prefix) netip.Prefix {
return netip.PrefixFrom(ipp.Addr().Unmap(), ipp.Bits())
func unmapIPPrefix(ipp netaddr.IPPrefix) netaddr.IPPrefix {
return netaddr.IPPrefixFrom(ipp.IP().Unmap(), ipp.Bits())
}
func unmapIPPrefixes(ippsList ...[]netip.Prefix) (ret []netip.Prefix) {
func unmapIPPrefixes(ippsList ...[]netaddr.IPPrefix) (ret []netaddr.IPPrefix) {
for _, ipps := range ippsList {
for _, ipp := range ipps {
ret = append(ret, unmapIPPrefix(ipp))
@@ -2829,7 +2825,7 @@ func (b *LocalBackend) enterState(newState ipn.State) {
case ipn.Running:
var addrs []string
for _, addr := range netMap.Addresses {
addrs = append(addrs, addr.Addr().String())
addrs = append(addrs, addr.IP().String())
}
systemd.Status("Connected; %s; %s", activeLogin, strings.Join(addrs, " "))
default:
@@ -2988,8 +2984,8 @@ func (b *LocalBackend) ShouldRunSSH() bool { return b.sshAtomicBool.Get() && can
// ShouldHandleViaIP reports whether whether ip is an IPv6 address in the
// Tailscale ULA's v6 "via" range embedding an IPv4 address to be forwarded to
// by Tailscale.
func (b *LocalBackend) ShouldHandleViaIP(ip netip.Addr) bool {
if f, ok := b.containsViaIPFuncAtomic.Load().(func(netip.Addr) bool); ok {
func (b *LocalBackend) ShouldHandleViaIP(ip netaddr.IP) bool {
if f, ok := b.containsViaIPFuncAtomic.Load().(func(netaddr.IP) bool); ok {
return f(ip)
}
return false
@@ -3106,7 +3102,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
// Update the nodeByAddr index.
if b.nodeByAddr == nil {
b.nodeByAddr = map[netip.Addr]*tailcfg.Node{}
b.nodeByAddr = map[netaddr.IP]*tailcfg.Node{}
}
// First pass, mark everything unwanted.
for k := range b.nodeByAddr {
@@ -3115,7 +3111,7 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
addNode := func(n *tailcfg.Node) {
for _, ipp := range n.Addresses {
if ipp.IsSingleIP() {
b.nodeByAddr[ipp.Addr()] = n
b.nodeByAddr[ipp.IP()] = n
}
}
}
@@ -3295,9 +3291,9 @@ func peerAPIBase(nm *netmap.NetworkMap, peer *tailcfg.Node) string {
continue
}
switch {
case a.Addr().Is4():
case a.IP().Is4():
have4 = true
case a.Addr().Is6():
case a.IP().Is6():
have6 = true
}
}
@@ -3312,26 +3308,26 @@ func peerAPIBase(nm *netmap.NetworkMap, peer *tailcfg.Node) string {
p6 = s.Port
}
}
var ipp netip.AddrPort
var ipp netaddr.IPPort
switch {
case have4 && p4 != 0:
ipp = netip.AddrPortFrom(nodeIP(peer, netip.Addr.Is4), p4)
ipp = netaddr.IPPortFrom(nodeIP(peer, netaddr.IP.Is4), p4)
case have6 && p6 != 0:
ipp = netip.AddrPortFrom(nodeIP(peer, netip.Addr.Is6), p6)
ipp = netaddr.IPPortFrom(nodeIP(peer, netaddr.IP.Is6), p6)
}
if !ipp.Addr().IsValid() {
if ipp.IP().IsZero() {
return ""
}
return fmt.Sprintf("http://%v", ipp)
}
func nodeIP(n *tailcfg.Node, pred func(netip.Addr) bool) netip.Addr {
func nodeIP(n *tailcfg.Node, pred func(netaddr.IP) bool) netaddr.IP {
for _, a := range n.Addresses {
if a.IsSingleIP() && pred(a.Addr()) {
return a.Addr()
if a.IsSingleIP() && pred(a.IP()) {
return a.IP()
}
}
return netip.Addr{}
return netaddr.IP{}
}
func (b *LocalBackend) CheckIPForwarding() error {
@@ -3370,9 +3366,9 @@ func (b *LocalBackend) OfferingExitNode() bool {
if r.Bits() != 0 {
continue
}
if r.Addr().Is4() {
if r.IP().Is4() {
def4 = true
} else if r.Addr().Is6() {
} else if r.IP().Is6() {
def6 = true
}
}
@@ -3544,7 +3540,7 @@ func (b *LocalBackend) handleQuad100Port80Conn(w http.ResponseWriter, r *http.Re
}
io.WriteString(w, "<p>Local addresses:</p><ul>\n")
for _, ipp := range b.netMap.Addresses {
fmt.Fprintf(w, "<li>%v</li>\n", ipp.Addr())
fmt.Fprintf(w, "<li>%v</li>\n", ipp.IP())
}
io.WriteString(w, "</ul>\n")
}

View File

@@ -8,12 +8,11 @@ import (
"fmt"
"net"
"net/http"
"net/netip"
"reflect"
"testing"
"time"
"go4.org/netipx"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/ipn/store/mem"
"tailscale.com/net/interfaces"
@@ -26,17 +25,17 @@ import (
)
func TestNetworkMapCompare(t *testing.T) {
prefix1, err := netip.ParsePrefix("192.168.0.0/24")
prefix1, err := netaddr.ParseIPPrefix("192.168.0.0/24")
if err != nil {
t.Fatal(err)
}
node1 := &tailcfg.Node{Addresses: []netip.Prefix{prefix1}}
node1 := &tailcfg.Node{Addresses: []netaddr.IPPrefix{prefix1}}
prefix2, err := netip.ParsePrefix("10.0.0.0/8")
prefix2, err := netaddr.ParseIPPrefix("10.0.0.0/8")
if err != nil {
t.Fatal(err)
}
node2 := &tailcfg.Node{Addresses: []netip.Prefix{prefix2}}
node2 := &tailcfg.Node{Addresses: []netaddr.IPPrefix{prefix2}}
tests := []struct {
name string
@@ -132,7 +131,7 @@ func TestNetworkMapCompare(t *testing.T) {
}
}
func inRemove(ip netip.Addr) bool {
func inRemove(ip netaddr.IP) bool {
for _, pfx := range removeFromDefaultRoute {
if pfx.Contains(ip) {
return true
@@ -146,7 +145,7 @@ func TestShrinkDefaultRoute(t *testing.T) {
route string
in []string
out []string
localIPFn func(netip.Addr) bool // true if this machine's local IP address should be "in" after shrinking.
localIPFn func(netaddr.IP) bool // true if this machine's local IP address should be "in" after shrinking.
}{
{
route: "0.0.0.0/0",
@@ -166,7 +165,7 @@ func TestShrinkDefaultRoute(t *testing.T) {
"fe80::",
"2601::1",
},
localIPFn: func(ip netip.Addr) bool { return !inRemove(ip) && ip.Is4() },
localIPFn: func(ip netaddr.IP) bool { return !inRemove(ip) && ip.Is4() },
},
{
route: "::/0",
@@ -174,47 +173,47 @@ func TestShrinkDefaultRoute(t *testing.T) {
out: []string{
"fe80::1",
"ff00::1",
tsaddr.TailscaleULARange().Addr().String(),
tsaddr.TailscaleULARange().IP().String(),
},
localIPFn: func(ip netip.Addr) bool { return !inRemove(ip) && ip.Is6() },
localIPFn: func(ip netaddr.IP) bool { return !inRemove(ip) && ip.Is6() },
},
}
// Construct a fake local network environment to make this test hermetic.
// localInterfaceRoutes and hostIPs would normally come from calling interfaceRoutes,
// and localAddresses would normally come from calling interfaces.LocalAddresses.
var b netipx.IPSetBuilder
var b netaddr.IPSetBuilder
for _, c := range []string{"127.0.0.0/8", "192.168.9.0/24", "fe80::/32"} {
p := netip.MustParsePrefix(c)
p := netaddr.MustParseIPPrefix(c)
b.AddPrefix(p)
}
localInterfaceRoutes, err := b.IPSet()
if err != nil {
t.Fatal(err)
}
hostIPs := []netip.Addr{
netip.MustParseAddr("127.0.0.1"),
netip.MustParseAddr("192.168.9.39"),
netip.MustParseAddr("fe80::1"),
netip.MustParseAddr("fe80::437d:feff:feca:49a7"),
hostIPs := []netaddr.IP{
netaddr.MustParseIP("127.0.0.1"),
netaddr.MustParseIP("192.168.9.39"),
netaddr.MustParseIP("fe80::1"),
netaddr.MustParseIP("fe80::437d:feff:feca:49a7"),
}
localAddresses := []netip.Addr{
netip.MustParseAddr("192.168.9.39"),
localAddresses := []netaddr.IP{
netaddr.MustParseIP("192.168.9.39"),
}
for _, test := range tests {
def := netip.MustParsePrefix(test.route)
def := netaddr.MustParseIPPrefix(test.route)
got, err := shrinkDefaultRoute(def, localInterfaceRoutes, hostIPs)
if err != nil {
t.Fatalf("shrinkDefaultRoute(%q): %v", test.route, err)
}
for _, ip := range test.in {
if !got.Contains(netip.MustParseAddr(ip)) {
if !got.Contains(netaddr.MustParseIP(ip)) {
t.Errorf("shrink(%q).Contains(%v) = false, want true", test.route, ip)
}
}
for _, ip := range test.out {
if got.Contains(netip.MustParseAddr(ip)) {
if got.Contains(netaddr.MustParseIP(ip)) {
t.Errorf("shrink(%q).Contains(%v) = true, want false", test.route, ip)
}
}
@@ -228,22 +227,22 @@ func TestShrinkDefaultRoute(t *testing.T) {
}
func TestPeerRoutes(t *testing.T) {
pp := netip.MustParsePrefix
pp := netaddr.MustParseIPPrefix
tests := []struct {
name string
peers []wgcfg.Peer
want []netip.Prefix
want []netaddr.IPPrefix
}{
{
name: "small_v4",
peers: []wgcfg.Peer{
{
AllowedIPs: []netip.Prefix{
AllowedIPs: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
},
},
},
want: []netip.Prefix{
want: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
},
},
@@ -251,14 +250,14 @@ func TestPeerRoutes(t *testing.T) {
name: "big_v4",
peers: []wgcfg.Peer{
{
AllowedIPs: []netip.Prefix{
AllowedIPs: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
pp("100.101.102.104/32"),
pp("100.101.102.105/32"),
},
},
},
want: []netip.Prefix{
want: []netaddr.IPPrefix{
pp("100.64.0.0/10"),
},
},
@@ -266,12 +265,12 @@ func TestPeerRoutes(t *testing.T) {
name: "has_1_v6",
peers: []wgcfg.Peer{
{
AllowedIPs: []netip.Prefix{
AllowedIPs: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b240/128"),
},
},
},
want: []netip.Prefix{
want: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0::/48"),
},
},
@@ -279,13 +278,13 @@ func TestPeerRoutes(t *testing.T) {
name: "has_2_v6",
peers: []wgcfg.Peer{
{
AllowedIPs: []netip.Prefix{
AllowedIPs: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b240/128"),
pp("fd7a:115c:a1e0:ab12:4843:cd96:6258:b241/128"),
},
},
},
want: []netip.Prefix{
want: []netaddr.IPPrefix{
pp("fd7a:115c:a1e0::/48"),
},
},
@@ -293,7 +292,7 @@ func TestPeerRoutes(t *testing.T) {
name: "big_v4_big_v6",
peers: []wgcfg.Peer{
{
AllowedIPs: []netip.Prefix{
AllowedIPs: []netaddr.IPPrefix{
pp("100.101.102.103/32"),
pp("100.101.102.104/32"),
pp("100.101.102.105/32"),
@@ -302,7 +301,7 @@ func TestPeerRoutes(t *testing.T) {
},
},
},
want: []netip.Prefix{
want: []netaddr.IPPrefix{
pp("100.64.0.0/10"),
pp("fd7a:115c:a1e0::/48"),
},
@@ -311,19 +310,19 @@ func TestPeerRoutes(t *testing.T) {
name: "output-should-be-sorted",
peers: []wgcfg.Peer{
{
AllowedIPs: []netip.Prefix{
AllowedIPs: []netaddr.IPPrefix{
pp("100.64.0.2/32"),
pp("10.0.0.0/16"),
},
},
{
AllowedIPs: []netip.Prefix{
AllowedIPs: []netaddr.IPPrefix{
pp("100.64.0.1/32"),
pp("10.0.0.0/8"),
},
},
},
want: []netip.Prefix{
want: []netaddr.IPPrefix{
pp("10.0.0.0/8"),
pp("10.0.0.0/16"),
pp("100.64.0.1/32"),
@@ -362,14 +361,14 @@ func TestPeerAPIBase(t *testing.T) {
{
name: "self_only_4_them_both",
nm: &netmap.NetworkMap{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.1/32"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.1/32"),
},
},
peer: &tailcfg.Node{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.2/32"),
netip.MustParsePrefix("fe70::2/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
@@ -383,14 +382,14 @@ func TestPeerAPIBase(t *testing.T) {
{
name: "self_only_6_them_both",
nm: &netmap.NetworkMap{
Addresses: []netip.Prefix{
netip.MustParsePrefix("fe70::1/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("fe70::1/128"),
},
},
peer: &tailcfg.Node{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.2/32"),
netip.MustParsePrefix("fe70::2/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
@@ -404,15 +403,15 @@ func TestPeerAPIBase(t *testing.T) {
{
name: "self_both_them_only_4",
nm: &netmap.NetworkMap{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.1/32"),
netip.MustParsePrefix("fe70::1/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.1/32"),
netaddr.MustParseIPPrefix("fe70::1/128"),
},
},
peer: &tailcfg.Node{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.2/32"),
netip.MustParsePrefix("fe70::2/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
@@ -425,15 +424,15 @@ func TestPeerAPIBase(t *testing.T) {
{
name: "self_both_them_only_6",
nm: &netmap.NetworkMap{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.1/32"),
netip.MustParsePrefix("fe70::1/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.1/32"),
netaddr.MustParseIPPrefix("fe70::1/128"),
},
},
peer: &tailcfg.Node{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.2/32"),
netip.MustParsePrefix("fe70::2/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
Hostinfo: (&tailcfg.Hostinfo{
Services: []tailcfg.Service{
@@ -446,15 +445,15 @@ func TestPeerAPIBase(t *testing.T) {
{
name: "self_both_them_no_peerapi_service",
nm: &netmap.NetworkMap{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.1/32"),
netip.MustParsePrefix("fe70::1/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.1/32"),
netaddr.MustParseIPPrefix("fe70::1/128"),
},
},
peer: &tailcfg.Node{
Addresses: []netip.Prefix{
netip.MustParsePrefix("100.64.1.2/32"),
netip.MustParsePrefix("fe70::2/128"),
Addresses: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("100.64.1.2/32"),
netaddr.MustParseIPPrefix("fe70::2/128"),
},
},
want: "",
@@ -542,10 +541,10 @@ func TestFileTargets(t *testing.T) {
func TestInternalAndExternalInterfaces(t *testing.T) {
type interfacePrefix struct {
i interfaces.Interface
pfx netip.Prefix
pfx netaddr.IPPrefix
}
masked := func(ips ...interfacePrefix) (pfxs []netip.Prefix) {
masked := func(ips ...interfacePrefix) (pfxs []netaddr.IPPrefix) {
for _, ip := range ips {
pfxs = append(pfxs, ip.pfx.Masked())
}
@@ -558,11 +557,11 @@ func TestInternalAndExternalInterfaces(t *testing.T) {
return il
}
newInterface := func(name, pfx string, wsl2, loopback bool) interfacePrefix {
ippfx := netip.MustParsePrefix(pfx)
ippfx := netaddr.MustParseIPPrefix(pfx)
ip := interfaces.Interface{
Interface: &net.Interface{},
AltAddrs: []net.Addr{
netipx.PrefixIPNet(ippfx),
ippfx.IPNet(),
},
}
if loopback {
@@ -584,8 +583,8 @@ func TestInternalAndExternalInterfaces(t *testing.T) {
name string
goos string
il interfaces.List
wantInt []netip.Prefix
wantExt []netip.Prefix
wantInt []netaddr.IPPrefix
wantExt []netaddr.IPPrefix
}{
{
name: "single-interface",

View File

@@ -16,7 +16,6 @@ import (
"io/fs"
"net"
"net/http"
"net/netip"
"net/url"
"os"
"path"
@@ -32,6 +31,7 @@ import (
"github.com/kortschak/wol"
"golang.org/x/net/dns/dnsmessage"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/health"
"tailscale.com/hostinfo"
@@ -39,7 +39,6 @@ import (
"tailscale.com/logtail/backoff"
"tailscale.com/net/dns/resolver"
"tailscale.com/net/interfaces"
"tailscale.com/net/netaddr"
"tailscale.com/net/netutil"
"tailscale.com/syncs"
"tailscale.com/tailcfg"
@@ -48,7 +47,7 @@ import (
"tailscale.com/wgengine/filter"
)
var initListenConfig func(*net.ListenConfig, netip.Addr, *interfaces.State, string) error
var initListenConfig func(*net.ListenConfig, netaddr.IP, *interfaces.State, string) error
// addH2C is non-nil on platforms where we want to add H2C
// ("cleartext" HTTP/2) support to the peerAPI.
@@ -387,7 +386,7 @@ func (s *peerAPIServer) OpenFile(baseName string) (rc io.ReadCloser, size int64,
return f, fi.Size(), nil
}
func (s *peerAPIServer) listen(ip netip.Addr, ifState *interfaces.State) (ln net.Listener, err error) {
func (s *peerAPIServer) listen(ip netaddr.IP, ifState *interfaces.State) (ln net.Listener, err error) {
// Android for whatever reason often has problems creating the peerapi listener.
// But since we started intercepting it with netstack, it's not even important that
// we have a real kernel-level listener. So just create a dummy listener on Android
@@ -451,7 +450,7 @@ func (s *peerAPIServer) listen(ip netip.Addr, ifState *interfaces.State) (ln net
type peerAPIListener struct {
ps *peerAPIServer
ip netip.Addr
ip netaddr.IP
lb *LocalBackend
// ln is the Listener. It can be nil in netstack mode if there are more than
@@ -503,7 +502,7 @@ func (pln *peerAPIListener) serve() {
}
}
func (pln *peerAPIListener) ServeConn(src netip.AddrPort, c net.Conn) {
func (pln *peerAPIListener) ServeConn(src netaddr.IPPort, c net.Conn) {
logf := pln.lb.logf
peerNode, peerUser, ok := pln.lb.WhoIs(src)
if !ok {
@@ -530,7 +529,7 @@ func (pln *peerAPIListener) ServeConn(src netip.AddrPort, c net.Conn) {
// peerAPIHandler serves the Peer API for a source specific client.
type peerAPIHandler struct {
ps *peerAPIServer
remoteAddr netip.AddrPort
remoteAddr netaddr.IPPort
isSelf bool // whether peerNode is owned by same user as this node
peerNode *tailcfg.Node // peerNode is who's making the request
peerUser tailcfg.UserProfile // profile of peerNode
@@ -578,7 +577,7 @@ func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
<body>
<h1>Hello, %s (%v)</h1>
This is my Tailscale device. Your device is %v.
`, html.EscapeString(who), h.remoteAddr.Addr(), html.EscapeString(h.peerNode.ComputedName))
`, html.EscapeString(who), h.remoteAddr.IP(), html.EscapeString(h.peerNode.ComputedName))
if h.isSelf {
fmt.Fprintf(w, "<p>You are the owner of this node.\n")
@@ -609,7 +608,7 @@ func (h *peerAPIHandler) handleServeInterfaces(w http.ResponseWriter, r *http.Re
fmt.Fprintf(w, "<th>%v</th> ", v)
}
fmt.Fprint(w, "</tr>\n")
i.ForeachInterface(func(iface interfaces.Interface, ipps []netip.Prefix) {
i.ForeachInterface(func(iface interfaces.Interface, ipps []netaddr.IPPrefix) {
fmt.Fprint(w, "<tr>")
for _, v := range []any{iface.Index, iface.Name, iface.MTU, iface.Flags, ipps} {
fmt.Fprintf(w, "<td>%v</td> ", v)
@@ -694,7 +693,7 @@ func (h *peerAPIHandler) canWakeOnLAN() bool {
}
func (h *peerAPIHandler) peerHasCap(wantCap string) bool {
for _, hasCap := range h.ps.b.PeerCaps(h.remoteAddr.Addr()) {
for _, hasCap := range h.ps.b.PeerCaps(h.remoteAddr.IP()) {
if hasCap == wantCap {
return true
}
@@ -802,7 +801,7 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
}
d := time.Since(t0).Round(time.Second / 10)
h.logf("got put of %s in %v from %v/%v", approxSize(finalSize), d, h.remoteAddr.Addr(), h.peerNode.ComputedName)
h.logf("got put of %s in %v from %v/%v", approxSize(finalSize), d, h.remoteAddr.IP, h.peerNode.ComputedName)
// TODO: set modtime
// TODO: some real response
@@ -926,11 +925,12 @@ func (h *peerAPIHandler) handleWakeOnLAN(w http.ResponseWriter, r *http.Request)
}
for ifName, ips := range st.InterfaceIPs {
for _, ip := range ips {
if ip.Addr().IsLoopback() || ip.Addr().Is6() {
if ip.IP().IsLoopback() || ip.IP().Is6() {
continue
}
ipa := ip.IP().IPAddr()
local := &net.UDPAddr{
IP: ip.Addr().AsSlice(),
IP: ipa.IP,
Port: 0,
}
remote := &net.UDPAddr{
@@ -982,11 +982,11 @@ func (h *peerAPIHandler) replyToDNSQueries() bool {
// arbitrary. DNS runs over TCP and UDP, so sure... we check
// TCP.
dstIP := netaddr.IPv4(0, 0, 0, 0)
remoteIP := h.remoteAddr.Addr()
remoteIP := h.remoteAddr.IP()
if remoteIP.Is6() {
// autogroup:internet for IPv6 is defined to start with 2000::/3,
// so use 2000::0 as the probe "the internet" address.
dstIP = netip.MustParseAddr("2000::")
dstIP = netaddr.MustParseIP("2000::")
}
verdict := f.CheckTCP(remoteIP, dstIP, 53)
return verdict == filter.Accept
@@ -1169,9 +1169,9 @@ func writePrettyDNSReply(w io.Writer, res []byte) (err error) {
// it's listening on the provided IP address and on TCP port 1.
//
// See docs on fakePeerAPIListener.
func newFakePeerAPIListener(ip netip.Addr) net.Listener {
func newFakePeerAPIListener(ip netaddr.IP) net.Listener {
return &fakePeerAPIListener{
addr: net.TCPAddrFromAddrPort(netip.AddrPortFrom(ip, 1)),
addr: netaddr.IPPortFrom(ip, 1).TCPAddr(),
closed: make(chan struct{}),
}
}

View File

@@ -11,8 +11,8 @@ package ipnlocal
import (
"fmt"
"net"
"net/netip"
"inet.af/netaddr"
"tailscale.com/net/interfaces"
"tailscale.com/net/netns"
)
@@ -24,7 +24,7 @@ func init() {
// initListenConfigNetworkExtension configures nc for listening on IP
// through the iOS/macOS Network/System Extension (Packet Tunnel
// Provider) sandbox.
func initListenConfigNetworkExtension(nc *net.ListenConfig, ip netip.Addr, st *interfaces.State, tunIfName string) error {
func initListenConfigNetworkExtension(nc *net.ListenConfig, ip netaddr.IP, st *interfaces.State, tunIfName string) error {
tunIf, ok := st.Interface[tunIfName]
if !ok {
return fmt.Errorf("no interface with name %q", tunIfName)

View File

@@ -13,14 +13,13 @@ import (
"math/rand"
"net/http"
"net/http/httptest"
"net/netip"
"os"
"path/filepath"
"runtime"
"strings"
"testing"
"go4.org/netipx"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/tailcfg"
"tailscale.com/tstest"
@@ -583,7 +582,7 @@ func TestPeerAPIReplyToDNSQueries(t *testing.T) {
t.Errorf("for isSelf = false; want true")
}
h.isSelf = false
h.remoteAddr = netip.MustParseAddrPort("100.150.151.152:12345")
h.remoteAddr = netaddr.MustParseIPPort("100.150.151.152:12345")
eng, _ := wgengine.NewFakeUserspaceEngine(logger.Discard, 0)
h.ps = &peerAPIServer{
@@ -595,9 +594,9 @@ func TestPeerAPIReplyToDNSQueries(t *testing.T) {
t.Fatal("unexpectedly offering exit node")
}
h.ps.b.prefs = &ipn.Prefs{
AdvertiseRoutes: []netip.Prefix{
netip.MustParsePrefix("0.0.0.0/0"),
netip.MustParsePrefix("::/0"),
AdvertiseRoutes: []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("0.0.0.0/0"),
netaddr.MustParseIPPrefix("::/0"),
},
}
if !h.ps.b.OfferingExitNode() {
@@ -608,7 +607,7 @@ func TestPeerAPIReplyToDNSQueries(t *testing.T) {
t.Errorf("unexpectedly doing DNS without filter")
}
h.ps.b.setFilter(filter.NewAllowNone(logger.Discard, new(netipx.IPSet)))
h.ps.b.setFilter(filter.NewAllowNone(logger.Discard, new(netaddr.IPSet)))
if h.replyToDNSQueries() {
t.Errorf("unexpectedly doing DNS without filter")
}
@@ -621,7 +620,7 @@ func TestPeerAPIReplyToDNSQueries(t *testing.T) {
}
// Also test IPv6.
h.remoteAddr = netip.MustParseAddrPort("[fe70::1]:12345")
h.remoteAddr = netaddr.MustParseIPPort("[fe70::1]:12345")
if !h.replyToDNSQueries() {
t.Errorf("unexpectedly IPv6 deny; wanted to be a DNS server")
}

View File

@@ -8,7 +8,6 @@
package ipnlocal
import (
"bytes"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
@@ -26,7 +25,6 @@ import (
"github.com/tailscale/golang-x-crypto/ssh"
"tailscale.com/envknob"
"tailscale.com/util/mak"
)
var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS")
@@ -37,39 +35,34 @@ var useHostKeys = envknob.Bool("TS_USE_SYSTEM_SSH_HOST_KEYS")
var keyTypes = []string{"rsa", "ecdsa", "ed25519"}
func (b *LocalBackend) GetSSH_HostKeys() (keys []ssh.Signer, err error) {
var existing map[string]ssh.Signer
if os.Geteuid() == 0 {
existing = b.getSystemSSH_HostKeys()
keys, err = b.getSystemSSH_HostKeys()
if err != nil || len(keys) > 0 {
return keys, err
}
// Otherwise, perhaps they don't have OpenSSH etc installed.
// Generate our own keys...
}
return b.getTailscaleSSH_HostKeys(existing)
return b.getTailscaleSSH_HostKeys()
}
// getTailscaleSSH_HostKeys returns the three (rsa, ecdsa, ed25519) SSH host
// keys, reusing the provided ones in existing if present in the map.
func (b *LocalBackend) getTailscaleSSH_HostKeys(existing map[string]ssh.Signer) (keys []ssh.Signer, err error) {
var keyDir string // lazily initialized $TAILSCALE_VAR/ssh dir.
func (b *LocalBackend) getTailscaleSSH_HostKeys() (keys []ssh.Signer, err error) {
root := b.TailscaleVarRoot()
if root == "" {
return nil, errors.New("no var root for ssh keys")
}
keyDir := filepath.Join(root, "ssh")
if err := os.MkdirAll(keyDir, 0700); err != nil {
return nil, err
}
for _, typ := range keyTypes {
if s, ok := existing[typ]; ok {
keys = append(keys, s)
continue
}
if keyDir == "" {
root := b.TailscaleVarRoot()
if root == "" {
return nil, errors.New("no var root for ssh keys")
}
keyDir = filepath.Join(root, "ssh")
if err := os.MkdirAll(keyDir, 0700); err != nil {
return nil, err
}
}
hostKey, err := b.hostKeyFileOrCreate(keyDir, typ)
if err != nil {
return nil, fmt.Errorf("error creating SSH host key type %q in %q: %w", typ, keyDir, err)
return nil, err
}
signer, err := ssh.ParsePrivateKey(hostKey)
if err != nil {
return nil, fmt.Errorf("error parsing SSH host key type %q from %q: %w", typ, keyDir, err)
return nil, err
}
keys = append(keys, signer)
}
@@ -121,21 +114,24 @@ func (b *LocalBackend) hostKeyFileOrCreate(keyDir, typ string) ([]byte, error) {
return pemGen, err
}
func (b *LocalBackend) getSystemSSH_HostKeys() (ret map[string]ssh.Signer) {
func (b *LocalBackend) getSystemSSH_HostKeys() (ret []ssh.Signer, err error) {
// TODO(bradfitz): cache this?
for _, typ := range keyTypes {
filename := "/etc/ssh/ssh_host_" + typ + "_key"
hostKey, err := ioutil.ReadFile(filename)
if err != nil || len(bytes.TrimSpace(hostKey)) == 0 {
if os.IsNotExist(err) {
continue
}
if err != nil {
return nil, err
}
signer, err := ssh.ParsePrivateKey(hostKey)
if err != nil {
b.logf("warning: error reading host key %s: %v (generating one instead)", filename, err)
continue
return nil, fmt.Errorf("error reading private key %s: %w", filename, err)
}
mak.Set(&ret, typ, signer)
ret = append(ret, signer)
}
return ret
return ret, nil
}
func (b *LocalBackend) getSSHHostKeyPublicStrings() (ret []string) {

View File

@@ -15,7 +15,7 @@ import (
func TestSSHKeyGen(t *testing.T) {
dir := t.TempDir()
lb := &LocalBackend{varRoot: dir}
keys, err := lb.getTailscaleSSH_HostKeys(nil)
keys, err := lb.getTailscaleSSH_HostKeys()
if err != nil {
t.Fatal(err)
}
@@ -32,7 +32,7 @@ func TestSSHKeyGen(t *testing.T) {
t.Fatalf("keys = %v; want %v", got, want)
}
keys2, err := lb.getTailscaleSSH_HostKeys(nil)
keys2, err := lb.getTailscaleSSH_HostKeys()
if err != nil {
t.Fatal(err)
}

View File

@@ -16,7 +16,6 @@ import (
"log"
"net"
"net/http"
"net/netip"
"os"
"os/exec"
"os/signal"
@@ -30,6 +29,7 @@ import (
"time"
"go4.org/mem"
"inet.af/netaddr"
"inet.af/peercred"
"tailscale.com/control/controlclient"
"tailscale.com/envknob"
@@ -146,15 +146,15 @@ func (s *Server) getConnIdentity(c net.Conn) (ci connIdentity, err error) {
ci.Creds, _ = peercred.Get(c)
return ci, nil
}
la, err := netip.ParseAddrPort(c.LocalAddr().String())
la, err := netaddr.ParseIPPort(c.LocalAddr().String())
if err != nil {
return ci, fmt.Errorf("parsing local address: %w", err)
}
ra, err := netip.ParseAddrPort(c.RemoteAddr().String())
ra, err := netaddr.ParseIPPort(c.RemoteAddr().String())
if err != nil {
return ci, fmt.Errorf("parsing local remote: %w", err)
}
if !la.Addr().IsLoopback() || !ra.Addr().IsLoopback() {
if !la.IP().IsLoopback() || !ra.IP().IsLoopback() {
return ci, errors.New("non-loopback connection")
}
tab, err := netstat.Get()
@@ -772,7 +772,7 @@ func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engi
dg := distro.Get()
switch dg {
case distro.Synology, distro.TrueNAS, distro.QNAP:
case distro.Synology, distro.TrueNAS:
// See if they have a "Taildrop" share.
// See https://github.com/tailscale/tailscale/issues/2179#issuecomment-982821319
path, err := findTaildropDir(dg)
@@ -1065,7 +1065,7 @@ func (s *Server) ServeHTMLStatus(w http.ResponseWriter, r *http.Request) {
st.WriteHTML(w)
}
func peerPid(entries []netstat.Entry, la, ra netip.AddrPort) int {
func peerPid(entries []netstat.Entry, la, ra netaddr.IPPort) int {
for _, e := range entries {
if e.Local == ra && e.Remote == la {
return e.Pid
@@ -1123,8 +1123,6 @@ func findTaildropDir(dg distro.Distro) (string, error) {
return findSynologyTaildropDir(name)
case distro.TrueNAS:
return findTrueNASTaildropDir(name)
case distro.QNAP:
return findQnapTaildropDir(name)
}
return "", fmt.Errorf("%s is an unsupported distro for Taildrop dir", dg)
}
@@ -1165,28 +1163,6 @@ func findTrueNASTaildropDir(name string) (dir string, err error) {
return "", fmt.Errorf("shared folder %q not found", name)
}
// findQnapTaildropDir checks if a Shared Folder named "Taildrop" exists.
func findQnapTaildropDir(name string) (string, error) {
dir := fmt.Sprintf("/share/%s", name)
fi, err := os.Stat(dir)
if err != nil {
return "", fmt.Errorf("shared folder %q not found", name)
}
if fi.IsDir() {
return dir, nil
}
// share/Taildrop is usually a symlink to CACHEDEV1_DATA/Taildrop/ or some such.
fullpath, err := filepath.EvalSymlinks(dir)
if err != nil {
return "", fmt.Errorf("symlink to shared folder %q not found", name)
}
if fi, err = os.Stat(fullpath); err == nil && fi.IsDir() {
return dir, nil // return the symlink, how QNAP set it up
}
return "", fmt.Errorf("shared folder %q not found", name)
}
func loadExtraEnv() (env []string, err error) {
if runtime.GOOS != "windows" {
return nil, nil

View File

@@ -12,12 +12,12 @@ import (
"html"
"io"
"log"
"net/netip"
"sort"
"strings"
"sync"
"time"
"inet.af/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/key"
"tailscale.com/types/views"
@@ -35,7 +35,7 @@ type Status struct {
BackendState string
AuthURL string // current URL provided by control to authorize client
TailscaleIPs []netip.Addr // Tailscale IP(s) assigned to this node
TailscaleIPs []netaddr.IP // Tailscale IP(s) assigned to this node
Self *PeerStatus
// ExitNodeStatus describes the current exit node.
@@ -94,7 +94,7 @@ type ExitNodeStatus struct {
Online bool
// TailscaleIPs are the exit node's IP addresses assigned to the node.
TailscaleIPs []netip.Prefix
TailscaleIPs []netaddr.IPPrefix
}
func (s *Status) Peers() []key.NodePublic {
@@ -124,7 +124,7 @@ type PeerStatus struct {
DNSName string
OS string // HostInfo.OS
UserID tailcfg.UserID
TailscaleIPs []netip.Addr // Tailscale IP(s) assigned to this node
TailscaleIPs []netaddr.IP // Tailscale IP(s) assigned to this node
// Tags are the list of ACL tags applied to this node.
// See tailscale.com/tailcfg#Node.Tags for more information.
@@ -240,7 +240,7 @@ func (sb *StatusBuilder) AddUser(id tailcfg.UserID, up tailcfg.UserProfile) {
}
// AddIP adds a Tailscale IP address to the status.
func (sb *StatusBuilder) AddTailscaleIP(ip netip.Addr) {
func (sb *StatusBuilder) AddTailscaleIP(ip netaddr.IP) {
sb.mu.Lock()
defer sb.mu.Unlock()
if sb.locked {

View File

@@ -16,7 +16,6 @@ import (
"net"
"net/http"
"net/http/httputil"
"net/netip"
"net/url"
"reflect"
"runtime"
@@ -25,6 +24,7 @@ import (
"sync"
"time"
"inet.af/netaddr"
"tailscale.com/client/tailscale/apitype"
"tailscale.com/ipn"
"tailscale.com/ipn/ipnlocal"
@@ -222,10 +222,10 @@ func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) {
return
}
b := h.b
var ipp netip.AddrPort
var ipp netaddr.IPPort
if v := r.FormValue("addr"); v != "" {
var err error
ipp, err = netip.ParseAddrPort(v)
ipp, err = netaddr.ParseIPPort(v)
if err != nil {
http.Error(w, "invalid 'addr' parameter", 400)
return
@@ -242,7 +242,7 @@ func (h *Handler) serveWhoIs(w http.ResponseWriter, r *http.Request) {
res := &apitype.WhoIsResponse{
Node: n,
UserProfile: &u,
Caps: b.PeerCaps(ipp.Addr()),
Caps: b.PeerCaps(ipp.IP()),
}
j, err := json.MarshalIndent(res, "", "\t")
if err != nil {
@@ -667,7 +667,7 @@ func (h *Handler) servePing(w http.ResponseWriter, r *http.Request) {
http.Error(w, "missing 'ip' parameter", 400)
return
}
ip, err := netip.ParseAddr(ipStr)
ip, err := netaddr.ParseIP(ipStr)
if err != nil {
http.Error(w, "invalid IP", 400)
return

View File

@@ -11,16 +11,15 @@ import (
"fmt"
"io/ioutil"
"log"
"net/netip"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"inet.af/netaddr"
"tailscale.com/atomicfile"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/netaddr"
"tailscale.com/net/tsaddr"
"tailscale.com/tailcfg"
"tailscale.com/types/persist"
@@ -99,7 +98,7 @@ type Prefs struct {
// blackhole route will be installed on the local system to
// prevent any traffic escaping to the local network.
ExitNodeID tailcfg.StableNodeID
ExitNodeIP netip.Addr
ExitNodeIP netaddr.IP
// ExitNodeAllowLANAccess indicates whether locally accessible subnets should be
// routed directly or via the exit node.
@@ -168,7 +167,7 @@ type Prefs struct {
// AdvertiseRoutes specifies CIDR prefixes to advertise into the
// Tailscale network as reachable through the current
// node.
AdvertiseRoutes []netip.Prefix
AdvertiseRoutes []netaddr.IPPrefix
// NoSNAT specifies whether to source NAT traffic going to
// destinations in AdvertiseRoutes. The default is to apply source
@@ -309,7 +308,7 @@ func (p *Prefs) pretty(goos string) string {
if p.ShieldsUp {
sb.WriteString("shields=true ")
}
if p.ExitNodeIP.IsValid() {
if !p.ExitNodeIP.IsZero() {
fmt.Fprintf(&sb, "exit=%v lan=%t ", p.ExitNodeIP, p.ExitNodeAllowLANAccess)
} else if !p.ExitNodeID.IsZero() {
fmt.Fprintf(&sb, "exit=%v lan=%t ", p.ExitNodeID, p.ExitNodeAllowLANAccess)
@@ -383,7 +382,7 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
p.Persist.Equals(p2.Persist)
}
func compareIPNets(a, b []netip.Prefix) bool {
func compareIPNets(a, b []netaddr.IPPrefix) bool {
if len(a) != len(b) {
return false
}
@@ -478,13 +477,13 @@ func (p *Prefs) SetAdvertiseExitNode(runExit bool) {
return
}
p.AdvertiseRoutes = append(p.AdvertiseRoutes,
netip.PrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0),
netip.PrefixFrom(netip.IPv6Unspecified(), 0))
netaddr.IPPrefixFrom(netaddr.IPv4(0, 0, 0, 0), 0),
netaddr.IPPrefixFrom(netaddr.IPv6Unspecified(), 0))
}
// peerWithTailscaleIP returns the peer in st with the provided
// Tailscale IP.
func peerWithTailscaleIP(st *ipnstate.Status, ip netip.Addr) (ps *ipnstate.PeerStatus, ok bool) {
func peerWithTailscaleIP(st *ipnstate.Status, ip netaddr.IP) (ps *ipnstate.PeerStatus, ok bool) {
for _, ps := range st.Peer {
for _, ip2 := range ps.TailscaleIPs {
if ip == ip2 {
@@ -495,7 +494,7 @@ func peerWithTailscaleIP(st *ipnstate.Status, ip netip.Addr) (ps *ipnstate.PeerS
return nil, false
}
func isRemoteIP(st *ipnstate.Status, ip netip.Addr) bool {
func isRemoteIP(st *ipnstate.Status, ip netaddr.IP) bool {
for _, selfIP := range st.TailscaleIPs {
if ip == selfIP {
return false
@@ -507,7 +506,7 @@ func isRemoteIP(st *ipnstate.Status, ip netip.Addr) bool {
// ClearExitNode sets the ExitNodeID and ExitNodeIP to their zero values.
func (p *Prefs) ClearExitNode() {
p.ExitNodeID = ""
p.ExitNodeIP = netip.Addr{}
p.ExitNodeIP = netaddr.IP{}
}
// ExitNodeLocalIPError is returned when the requested IP address for an exit
@@ -520,11 +519,11 @@ func (e ExitNodeLocalIPError) Error() string {
return fmt.Sprintf("cannot use %s as an exit node as it is a local IP address to this machine", e.hostOrIP)
}
func exitNodeIPOfArg(s string, st *ipnstate.Status) (ip netip.Addr, err error) {
func exitNodeIPOfArg(s string, st *ipnstate.Status) (ip netaddr.IP, err error) {
if s == "" {
return ip, os.ErrInvalid
}
ip, err = netip.ParseAddr(s)
ip, err = netaddr.ParseIP(s)
if err == nil {
// If we're online already and have a netmap, double check that the IP
// address specified is valid.

View File

@@ -9,7 +9,6 @@ import (
"errors"
"fmt"
"io/ioutil"
"net/netip"
"os"
"reflect"
"strings"
@@ -17,8 +16,8 @@ import (
"time"
"go4.org/mem"
"inet.af/netaddr"
"tailscale.com/ipn/ipnstate"
"tailscale.com/net/netaddr"
"tailscale.com/tailcfg"
"tailscale.com/tstest"
"tailscale.com/types/key"
@@ -63,9 +62,9 @@ func TestPrefsEqual(t *testing.T) {
have, prefsHandles)
}
nets := func(strs ...string) (ns []netip.Prefix) {
nets := func(strs ...string) (ns []netaddr.IPPrefix) {
for _, s := range strs {
n, err := netip.ParsePrefix(s)
n, err := netaddr.ParseIPPrefix(s)
if err != nil {
panic(err)
}
@@ -138,13 +137,13 @@ func TestPrefsEqual(t *testing.T) {
},
{
&Prefs{ExitNodeIP: netip.MustParseAddr("1.2.3.4")},
&Prefs{ExitNodeIP: netaddr.MustParseIP("1.2.3.4")},
&Prefs{},
false,
},
{
&Prefs{ExitNodeIP: netip.MustParseAddr("1.2.3.4")},
&Prefs{ExitNodeIP: netip.MustParseAddr("1.2.3.4")},
&Prefs{ExitNodeIP: netaddr.MustParseIP("1.2.3.4")},
&Prefs{ExitNodeIP: netaddr.MustParseIP("1.2.3.4")},
true,
},
@@ -227,12 +226,12 @@ func TestPrefsEqual(t *testing.T) {
{
&Prefs{AdvertiseRoutes: nil},
&Prefs{AdvertiseRoutes: []netip.Prefix{}},
&Prefs{AdvertiseRoutes: []netaddr.IPPrefix{}},
true,
},
{
&Prefs{AdvertiseRoutes: []netip.Prefix{}},
&Prefs{AdvertiseRoutes: []netip.Prefix{}},
&Prefs{AdvertiseRoutes: []netaddr.IPPrefix{}},
&Prefs{AdvertiseRoutes: []netaddr.IPPrefix{}},
true,
},
{
@@ -416,7 +415,7 @@ func TestPrefsPretty(t *testing.T) {
},
{
Prefs{
ExitNodeIP: netip.MustParseAddr("1.2.3.4"),
ExitNodeIP: netaddr.MustParseIP("1.2.3.4"),
},
"linux",
`Prefs{ra=false mesh=false dns=false want=false exit=1.2.3.4 lan=false routes=[] nf=off Persist=nil}`,
@@ -659,8 +658,8 @@ func TestPrefsExitNode(t *testing.T) {
if p.AdvertisesExitNode() {
t.Errorf("default shouldn't advertise exit node")
}
p.AdvertiseRoutes = []netip.Prefix{
netip.MustParsePrefix("10.0.0.0/16"),
p.AdvertiseRoutes = []netaddr.IPPrefix{
netaddr.MustParseIPPrefix("10.0.0.0/16"),
}
p.SetAdvertiseExitNode(true)
if got, want := len(p.AdvertiseRoutes), 3; got != want {
@@ -683,12 +682,12 @@ func TestPrefsExitNode(t *testing.T) {
}
func TestExitNodeIPOfArg(t *testing.T) {
mustIP := netip.MustParseAddr
mustIP := netaddr.MustParseIP
tests := []struct {
name string
arg string
st *ipnstate.Status
want netip.Addr
want netaddr.IP
wantErr string
}{
{
@@ -714,7 +713,7 @@ func TestExitNodeIPOfArg(t *testing.T) {
BackendState: "Running",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
TailscaleIPs: []netip.Addr{mustIP("1.2.3.4")},
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
},
},
},
@@ -727,7 +726,7 @@ func TestExitNodeIPOfArg(t *testing.T) {
BackendState: "Running",
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
TailscaleIPs: []netip.Addr{mustIP("1.2.3.4")},
TailscaleIPs: []netaddr.IP{mustIP("1.2.3.4")},
ExitNodeOption: true,
},
},
@@ -748,7 +747,7 @@ func TestExitNodeIPOfArg(t *testing.T) {
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
},
@@ -763,7 +762,7 @@ func TestExitNodeIPOfArg(t *testing.T) {
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
},
},
},
@@ -777,12 +776,12 @@ func TestExitNodeIPOfArg(t *testing.T) {
Peer: map[key.NodePublic]*ipnstate.PeerStatus{
key.NewNode().Public(): {
DNSName: "skippy.foo.",
TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
key.NewNode().Public(): {
DNSName: "SKIPPY.foo.",
TailscaleIPs: []netip.Addr{mustIP("1.0.0.2")},
TailscaleIPs: []netaddr.IP{mustIP("1.0.0.2")},
ExitNodeOption: true,
},
},

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build linux && !ts_omit_aws
// +build linux,!ts_omit_aws
//go:build linux
// +build linux
// Package awsstore contains an ipn.StateStore implementation using AWS SSM.
package awsstore

View File

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !linux || ts_omit_aws
// +build !linux ts_omit_aws
//go:build !linux
// +build !linux
package awsstore
@@ -12,9 +12,8 @@ import (
"runtime"
"tailscale.com/ipn"
"tailscale.com/types/logger"
)
func New(logger.Logf, string) (ipn.StateStore, error) {
func NewStore(string) (ipn.StateStore, error) {
return nil, fmt.Errorf("AWS store is not supported on %v", runtime.GOOS)
}

View File

@@ -520,7 +520,7 @@ func New(collection string) *Policy {
}
}
conf := logtail.Config{
c := logtail.Config{
Collection: newc.Collection,
PrivateID: newc.PrivateID,
Stderr: logWriter{console},
@@ -534,16 +534,16 @@ func New(collection string) *Policy {
HTTPC: &http.Client{Transport: NewLogtailTransport(logtail.DefaultHost)},
}
if collection == logtail.CollectionNode {
conf.MetricsDelta = clientmetric.EncodeLogTailMetricsDelta
conf.IncludeProcID = true
conf.IncludeProcSequence = true
c.MetricsDelta = clientmetric.EncodeLogTailMetricsDelta
c.IncludeProcID = true
c.IncludeProcSequence = true
}
if val := getLogTarget(); val != "" {
log.Println("You have enabled a non-default log target. Doing without being told to by Tailscale staff or your network administrator will make getting support difficult.")
conf.BaseURL = val
c.BaseURL = val
u, _ := url.Parse(val)
conf.HTTPC = &http.Client{Transport: NewLogtailTransport(u.Host)}
c.HTTPC = &http.Client{Transport: NewLogtailTransport(u.Host)}
}
filchOptions := filch.Options{
@@ -551,12 +551,12 @@ func New(collection string) *Policy {
}
filchPrefix := filepath.Join(dir, cmdName)
// NAS disks cannot hibernate if we're writing logs to them all the time.
// Synology disks cannot hibernate if we're writing logs to them all the time.
// https://github.com/tailscale/tailscale/issues/3551
if runtime.GOOS == "linux" && (distro.Get() == distro.Synology || distro.Get() == distro.QNAP) {
tmpfsLogs := "/tmp/tailscale-logs"
if err := os.MkdirAll(tmpfsLogs, 0755); err == nil {
filchPrefix = filepath.Join(tmpfsLogs, cmdName)
if runtime.GOOS == "linux" && distro.Get() == distro.Synology {
synologyTmpfsLogs := "/tmp/tailscale-logs"
if err := os.MkdirAll(synologyTmpfsLogs, 0755); err == nil {
filchPrefix = filepath.Join(synologyTmpfsLogs, cmdName)
filchOptions.MaxFileSize = 1 << 20
} else {
// not a fatal error, we can leave the log files on the spinning disk
@@ -566,16 +566,16 @@ func New(collection string) *Policy {
filchBuf, filchErr := filch.New(filchPrefix, filchOptions)
if filchBuf != nil {
conf.Buffer = filchBuf
c.Buffer = filchBuf
if filchBuf.OrigStderr != nil {
conf.Stderr = filchBuf.OrigStderr
c.Stderr = filchBuf.OrigStderr
}
}
lw := logtail.NewLogger(conf, log.Printf)
lw := logtail.NewLogger(c, log.Printf)
var logOutput io.Writer = lw
if runtime.GOOS == "windows" && conf.Collection == logtail.CollectionNode {
if runtime.GOOS == "windows" && c.Collection == logtail.CollectionNode {
logID := newc.PublicID.String()
exe, _ := os.Executable()
if strings.EqualFold(filepath.Base(exe), "tailscaled.exe") {

View File

@@ -21,8 +21,6 @@ type Buffer interface {
TryReadLine() ([]byte, error)
// Write writes a log line into the ring buffer.
//
// Write takes ownership of the provided slice.
Write([]byte) (int, error)
}

View File

@@ -167,9 +167,8 @@ type Logger struct {
procID uint32
includeProcSequence bool
writeLock sync.Mutex // guards increments of procSequence
procSequence uint64
writeLock sync.Mutex // guards increments of procSequence
procSequence uint64
shutdownStart chan struct{} // closed when shutdown begins
shutdownDone chan struct{} // closed when shutdown complete
@@ -455,7 +454,7 @@ func Disable() {
logtailDisabled.Set(true)
}
func (l *Logger) sendLocked(jsonBlob []byte) (int, error) {
func (l *Logger) send(jsonBlob []byte) (int, error) {
if logtailDisabled.Get() {
return len(jsonBlob), nil
}
@@ -579,7 +578,7 @@ func (l *Logger) encodeText(buf []byte, skipClientTime bool, procID uint32, proc
return b
}
func (l *Logger) encodeLocked(buf []byte, level int) []byte {
func (l *Logger) encode(buf []byte, level int) []byte {
if l.includeProcSequence {
l.procSequence++
}
@@ -660,12 +659,10 @@ func (l *Logger) Write(buf []byte) (int, error) {
l.stderr.Write(withNL)
}
}
l.writeLock.Lock()
defer l.writeLock.Unlock()
b := l.encodeLocked(buf, level)
_, err := l.sendLocked(b)
b := l.encode(buf, level)
_, err := l.send(b)
l.writeLock.Unlock()
return len(buf), err
}

View File

@@ -7,9 +7,9 @@ package dns
import (
"bufio"
"fmt"
"net/netip"
"sort"
"inet.af/netaddr"
"tailscale.com/net/dns/resolver"
"tailscale.com/net/tsaddr"
"tailscale.com/types/dnstype"
@@ -40,13 +40,13 @@ type Config struct {
// Adding an entry to Hosts merely creates the record. If you want
// it to resolve, you also need to add appropriate routes to
// Routes.
Hosts map[dnsname.FQDN][]netip.Addr
Hosts map[dnsname.FQDN][]netaddr.IP
// OnlyIPv6, if true, uses the IPv6 service IP (for MagicDNS)
// instead of the IPv4 version (100.100.100.100).
OnlyIPv6 bool
}
func (c *Config) serviceIP() netip.Addr {
func (c *Config) serviceIP() netaddr.IP {
if c.OnlyIPv6 {
return tsaddr.TailscaleServiceIPv6()
}
@@ -143,7 +143,7 @@ func sameResolverNames(a, b []*dnstype.Resolver) bool {
return true
}
func sameIPs(a, b []netip.Addr) bool {
func sameIPs(a, b []netaddr.IP) bool {
if len(a) != len(b) {
return false
}

View File

@@ -13,7 +13,6 @@ import (
"io"
"io/fs"
"io/ioutil"
"net/netip"
"os"
"os/exec"
"path/filepath"
@@ -21,6 +20,7 @@ import (
"strings"
"time"
"inet.af/netaddr"
"tailscale.com/net/dns/resolvconffile"
"tailscale.com/types/logger"
"tailscale.com/util/dnsname"
@@ -33,7 +33,7 @@ const (
)
// writeResolvConf writes DNS configuration in resolv.conf format to the given writer.
func writeResolvConf(w io.Writer, servers []netip.Addr, domains []dnsname.FQDN) error {
func writeResolvConf(w io.Writer, servers []netaddr.IP, domains []dnsname.FQDN) error {
c := &resolvconffile.Config{
Nameservers: servers,
SearchDomains: domains,

View File

@@ -8,7 +8,6 @@ import (
"errors"
"fmt"
"io/fs"
"net/netip"
"os"
"path/filepath"
"strings"
@@ -16,6 +15,7 @@ import (
"testing"
qt "github.com/frankban/quicktest"
"inet.af/netaddr"
"tailscale.com/util/dnsname"
)
@@ -81,7 +81,7 @@ func testDirect(t *testing.T, fs wholeFileFS) {
m := directManager{logf: t.Logf, fs: fs}
if err := m.SetDNS(OSConfig{
Nameservers: []netip.Addr{netip.MustParseAddr("8.8.8.8"), netip.MustParseAddr("8.8.4.4")},
Nameservers: []netaddr.IP{netaddr.MustParseIP("8.8.8.8"), netaddr.MustParseIP("8.8.4.4")},
SearchDomains: []dnsname.FQDN{"ts.net.", "ts-dns.test."},
MatchDomains: []dnsname.FQDN{"ignored."},
}); err != nil {
@@ -108,7 +108,7 @@ search ts.net ts-dns.test
assertBaseState(t)
// Test that Close cleans up resolv.conf.
if err := m.SetDNS(OSConfig{Nameservers: []netip.Addr{netip.MustParseAddr("8.8.8.8")}}); err != nil {
if err := m.SetDNS(OSConfig{Nameservers: []netaddr.IP{netaddr.MustParseIP("8.8.8.8")}}); err != nil {
t.Fatal(err)
}
if err := m.Close(); err != nil {
@@ -149,22 +149,22 @@ func TestReadResolve(t *testing.T) {
}{
{in: `nameserver 192.168.0.100`,
want: OSConfig{
Nameservers: []netip.Addr{
netip.MustParseAddr("192.168.0.100"),
Nameservers: []netaddr.IP{
netaddr.MustParseIP("192.168.0.100"),
},
},
},
{in: `nameserver 192.168.0.100 # comment`,
want: OSConfig{
Nameservers: []netip.Addr{
netip.MustParseAddr("192.168.0.100"),
Nameservers: []netaddr.IP{
netaddr.MustParseIP("192.168.0.100"),
},
},
},
{in: `nameserver 192.168.0.100#`,
want: OSConfig{
Nameservers: []netip.Addr{
netip.MustParseAddr("192.168.0.100"),
Nameservers: []netaddr.IP{
netaddr.MustParseIP("192.168.0.100"),
},
},
},

View File

@@ -11,11 +11,11 @@ import (
"errors"
"io"
"net"
"net/netip"
"runtime"
"sync/atomic"
"time"
"inet.af/netaddr"
"tailscale.com/health"
"tailscale.com/net/dns/resolver"
"tailscale.com/net/packet"
@@ -67,7 +67,7 @@ const reconfigTimeout = time.Second
type response struct {
pkt []byte
to netip.AddrPort // response destination (request source)
to netaddr.IPPort // response destination (request source)
}
// Manager manages system DNS settings.
@@ -175,7 +175,7 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
// through quad-100.
rcfg.Routes = routes
rcfg.Routes["."] = cfg.DefaultResolvers
ocfg.Nameservers = []netip.Addr{cfg.serviceIP()}
ocfg.Nameservers = []netaddr.IP{cfg.serviceIP()}
return rcfg, ocfg, nil
}
@@ -212,7 +212,7 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
// or routes + MagicDNS, or just MagicDNS, or on an OS that cannot
// split-DNS. Install a split config pointing at quad-100.
rcfg.Routes = routes
ocfg.Nameservers = []netip.Addr{cfg.serviceIP()}
ocfg.Nameservers = []netaddr.IP{cfg.serviceIP()}
// If the OS can't do native split-dns, read out the underlying
// resolver config and blend it into our config.
@@ -239,10 +239,10 @@ func (m *Manager) compileConfig(cfg Config) (rcfg resolver.Config, ocfg OSConfig
// toIPsOnly returns only the IP portion of dnstype.Resolver.
// Only safe to use if the resolvers slice has been cleared of
// DoH or custom-port entries with something like hasDefaultIPResolversOnly.
func toIPsOnly(resolvers []*dnstype.Resolver) (ret []netip.Addr) {
func toIPsOnly(resolvers []*dnstype.Resolver) (ret []netaddr.IP) {
for _, r := range resolvers {
if ipp, ok := r.IPPort(); ok && ipp.Port() == 53 {
ret = append(ret, ipp.Addr())
ret = append(ret, ipp.IP())
}
}
return ret
@@ -252,7 +252,7 @@ func toIPsOnly(resolvers []*dnstype.Resolver) (ret []netip.Addr) {
// called with a DNS request payload.
//
// TODO(tom): Rip out once all platforms use netstack.
func (m *Manager) EnqueuePacket(bs []byte, proto ipproto.Proto, from, to netip.AddrPort) error {
func (m *Manager) EnqueuePacket(bs []byte, proto ipproto.Proto, from, to netaddr.IPPort) error {
if to.Port() != 53 || proto != ipproto.UDP {
return nil
}
@@ -299,11 +299,11 @@ func (m *Manager) NextPacket() ([]byte, error) {
var buf []byte
switch {
case resp.to.Addr().Is4():
case resp.to.IP().Is4():
h := packet.UDP4Header{
IP4Header: packet.IP4Header{
Src: magicDNSIP,
Dst: resp.to.Addr(),
Dst: resp.to.IP(),
},
SrcPort: 53,
DstPort: resp.to.Port(),
@@ -312,11 +312,11 @@ func (m *Manager) NextPacket() ([]byte, error) {
buf = make([]byte, offset+hlen+len(resp.pkt))
copy(buf[offset+hlen:], resp.pkt)
h.Marshal(buf[offset:])
case resp.to.Addr().Is6():
case resp.to.IP().Is6():
h := packet.UDP6Header{
IP6Header: packet.IP6Header{
Src: magicDNSIPv6,
Dst: resp.to.Addr(),
Dst: resp.to.IP(),
},
SrcPort: 53,
DstPort: resp.to.Port(),
@@ -334,7 +334,7 @@ func (m *Manager) NextPacket() ([]byte, error) {
// Query executes a DNS query recieved from the given address. The query is
// provided in bs as a wire-encoded DNS query without any transport header.
// This method is called for requests arriving over UDP and TCP.
func (m *Manager) Query(ctx context.Context, bs []byte, from netip.AddrPort) ([]byte, error) {
func (m *Manager) Query(ctx context.Context, bs []byte, from netaddr.IPPort) ([]byte, error) {
select {
case <-m.ctx.Done():
return nil, net.ErrClosed
@@ -368,10 +368,10 @@ type dnsTCPSession struct {
m *Manager
conn net.Conn
srcAddr netip.AddrPort
srcAddr netaddr.IPPort
readClosing chan struct{}
responses chan []byte // DNS replies pending writing
readClosing chan struct{}
responses chan []byte // DNS replies pending writing
ctx context.Context
closeCtx context.CancelFunc
@@ -455,13 +455,13 @@ func (s *dnsTCPSession) handleReads() {
// HandleTCPConn implements magicDNS over TCP, taking a connection and
// servicing DNS requests sent down it.
func (m *Manager) HandleTCPConn(conn net.Conn, srcAddr netip.AddrPort) {
func (m *Manager) HandleTCPConn(conn net.Conn, srcAddr netaddr.IPPort) {
s := dnsTCPSession{
m: m,
conn: conn,
srcAddr: srcAddr,
responses: make(chan []byte),
readClosing: make(chan struct{}),
m: m,
conn: conn,
srcAddr: srcAddr,
responses: make(chan []byte),
readClosing: make(chan struct{}),
}
s.ctx, s.closeCtx = context.WithCancel(m.ctx)
go s.handleReads()

Some files were not shown because too many files have changed in this diff Show More