all: gofmt for Go 1.19

Updates #5210

Change-Id: Ib02cd5e43d0a8db60c1f09755a8ac7b140b670be
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/5264/head
Brad Fitzpatrick 2 years ago committed by Brad Fitzpatrick
parent a029989aff
commit 116f55ff66

@ -14,14 +14,14 @@
// //
// Use this Grafana configuration to enable the auth proxy: // Use this Grafana configuration to enable the auth proxy:
// //
// [auth.proxy] // [auth.proxy]
// enabled = true // enabled = true
// header_name = X-WEBAUTH-USER // header_name = X-WEBAUTH-USER
// header_property = username // header_property = username
// auto_sign_up = true // auto_sign_up = true
// whitelist = 127.0.0.1 // whitelist = 127.0.0.1
// headers = Name:X-WEBAUTH-NAME // headers = Name:X-WEBAUTH-NAME
// enable_login_token = true // enable_login_token = true
package main package main
import ( import (

@ -178,15 +178,16 @@ var upArgs upArgsT
// JSON block will be output. The AuthURL and QR fields will not be present, the // JSON block will be output. The AuthURL and QR fields will not be present, the
// BackendState and Error fields will give the result of the authentication. // BackendState and Error fields will give the result of the authentication.
// Ex: // Ex:
// {
// "AuthURL": "https://login.tailscale.com/a/0123456789abcdef",
// "QR": "data:image/png;base64,0123...cdef"
// "BackendState": "NeedsLogin"
// }
// {
// "BackendState": "Running"
// }
// //
// {
// "AuthURL": "https://login.tailscale.com/a/0123456789abcdef",
// "QR": "data:image/png;base64,0123...cdef"
// "BackendState": "NeedsLogin"
// }
//
// {
// "BackendState": "Running"
// }
type upOutputJSON struct { type upOutputJSON struct {
AuthURL string `json:",omitempty"` // Authentication URL of the form https://login.tailscale.com/a/0123456789 AuthURL string `json:",omitempty"` // Authentication URL of the form https://login.tailscale.com/a/0123456789
QR string `json:",omitempty"` // a DataURL (base64) PNG of a QR code AuthURL QR string `json:",omitempty"` // a DataURL (base64) PNG of a QR code AuthURL

@ -4,10 +4,10 @@
// The tsconnect command builds and serves the static site that is generated for // The tsconnect command builds and serves the static site that is generated for
// the Tailscale Connect JS/WASM client. Can be run in 3 modes: // the Tailscale Connect JS/WASM client. Can be run in 3 modes:
// - dev: builds the site and serves it. JS and CSS changes can be picked up // - dev: builds the site and serves it. JS and CSS changes can be picked up
// with a reload. // with a reload.
// - build: builds the site and writes it to dist/ // - build: builds the site and writes it to dist/
// - serve: serves the site from dist/ (embedded in the binary) // - serve: serves the site from dist/ (embedded in the binary)
package main // import "tailscale.com/cmd/tsconnect" package main // import "tailscale.com/cmd/tsconnect"
import ( import (

@ -342,8 +342,7 @@ func main() {
it := codegen.NewImportTracker(pkg.Types) it := codegen.NewImportTracker(pkg.Types)
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
fmt.Fprintf(buf, `//go:generate go run tailscale.com/cmd/cloner %s`, strings.Join(flagArgs, " ")) fmt.Fprintf(buf, "//go:generate go run tailscale.com/cmd/cloner %s\n\n", strings.Join(flagArgs, " "))
fmt.Fprintln(buf)
runCloner := false runCloner := false
for _, typeName := range typeNames { for _, typeName := range typeNames {
typ, ok := namedTypes[typeName] typ, ok := namedTypes[typeName]

@ -40,8 +40,8 @@ const (
) )
// ProtocolVersion is bumped whenever there's a wire-incompatible change. // ProtocolVersion is bumped whenever there's a wire-incompatible change.
// * version 1 (zero on wire): consistent box headers, in use by employee dev nodes a bit // - version 1 (zero on wire): consistent box headers, in use by employee dev nodes a bit
// * version 2: received packets have src addrs in frameRecvPacket at beginning // - version 2: received packets have src addrs in frameRecvPacket at beginning
const ProtocolVersion = 2 const ProtocolVersion = 2
// frameType is the one byte frame type at the beginning of the frame // frameType is the one byte frame type at the beginning of the frame

@ -7,16 +7,17 @@
// A discovery message is: // A discovery message is:
// //
// Header: // Header:
// magic [6]byte // “TS💬” (0x54 53 f0 9f 92 ac) //
// senderDiscoPub [32]byte // nacl public key // magic [6]byte // “TS💬” (0x54 53 f0 9f 92 ac)
// nonce [24]byte // senderDiscoPub [32]byte // nacl public key
// nonce [24]byte
// //
// The recipient then decrypts the bytes following (the nacl secretbox) // The recipient then decrypts the bytes following (the nacl secretbox)
// and then the inner payload structure is: // and then the inner payload structure is:
// //
// messageType byte (the MessageType constants below) // messageType byte (the MessageType constants below)
// messageVersion byte (0 for now; but always ignore bytes at the end) // messageVersion byte (0 for now; but always ignore bytes at the end)
// message-paylod [...]byte // message-paylod [...]byte
package disco package disco
import ( import (

@ -162,12 +162,12 @@ type PartialFile struct {
// //
// Various platforms currently set StateKey in different ways: // Various platforms currently set StateKey in different ways:
// //
// * the macOS/iOS GUI apps set it to "ipn-go-bridge" // - the macOS/iOS GUI apps set it to "ipn-go-bridge"
// * the Android app sets it to "ipn-android" // - the Android app sets it to "ipn-android"
// * on Windows, it's the empty string (in client mode) or, via // - on Windows, it's the empty string (in client mode) or, via
// LocalBackend.userID, a string like "user-$USER_ID" (used in // LocalBackend.userID, a string like "user-$USER_ID" (used in
// server mode). // server mode).
// * on Linux/etc, it's always "_daemon" (ipn.GlobalDaemonStateKey) // - on Linux/etc, it's always "_daemon" (ipn.GlobalDaemonStateKey)
type StateKey string type StateKey string
type Options struct { type Options struct {

@ -883,11 +883,11 @@ func (b *LocalBackend) getNewControlClientFunc() clientGen {
// startIsNoopLocked reports whether a Start call on this LocalBackend // startIsNoopLocked reports whether a Start call on this LocalBackend
// with the provided Start Options would be a useless no-op. // with the provided Start Options would be a useless no-op.
// //
// TODO(apenwarr): we shouldn't need this. // TODO(apenwarr): we shouldn't need this. The state machine is now
// The state machine is now nearly clean enough where it can accept a new // nearly clean enough where it can accept a new connection while in
// connection while in any state, not just Running, and on any platform. // any state, not just Running, and on any platform. We'd want to add
// We'd want to add a few more tests to state_test.go to ensure this continues // a few more tests to state_test.go to ensure this continues to work
// to work as expected. // as expected.
// //
// b.mu must be held. // b.mu must be held.
func (b *LocalBackend) startIsNoopLocked(opts ipn.Options) bool { func (b *LocalBackend) startIsNoopLocked(opts ipn.Options) bool {
@ -2926,7 +2926,7 @@ func (b *LocalBackend) RequestEngineStatus() {
// feed events into LocalBackend. // feed events into LocalBackend.
// //
// TODO(apenwarr): use a channel or something to prevent re-entrancy? // TODO(apenwarr): use a channel or something to prevent re-entrancy?
// Or maybe just call the state machine from fewer places. // Or maybe just call the state machine from fewer places.
func (b *LocalBackend) stateMachine() { func (b *LocalBackend) stateMachine() {
b.enterState(b.nextState()) b.enterState(b.nextState())
} }

@ -545,7 +545,7 @@ func (h *Handler) serveFileTargets(w http.ResponseWriter, r *http.Request) {
// //
// URL format: // URL format:
// //
// * PUT /localapi/v0/file-put/:stableID/:escaped-filename // - PUT /localapi/v0/file-put/:stableID/:escaped-filename
func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) { func (h *Handler) serveFilePut(w http.ResponseWriter, r *http.Request) {
if !h.PermitWrite { if !h.PermitWrite {
http.Error(w, "file access denied", http.StatusForbidden) http.Error(w, "file access denied", http.StatusForbidden)

@ -305,8 +305,8 @@ func (bc *BackendClient) RequestStatus() {
// MaxMessageSize is the maximum message size, in bytes. // MaxMessageSize is the maximum message size, in bytes.
const MaxMessageSize = 10 << 20 const MaxMessageSize = 10 << 20
// TODO(apenwarr): incremental json decode? // TODO(apenwarr): incremental json decode? That would let us avoid
// That would let us avoid storing the whole byte array uselessly in RAM. // storing the whole byte array uselessly in RAM.
func ReadMsg(r io.Reader) ([]byte, error) { func ReadMsg(r io.Reader) ([]byte, error) {
cb := make([]byte, 4) cb := make([]byte, 4)
_, err := io.ReadFull(r, cb) _, err := io.ReadFull(r, cb)
@ -328,10 +328,11 @@ func ReadMsg(r io.Reader) ([]byte, error) {
return b, nil return b, nil
} }
// TODO(apenwarr): incremental json encode?
// That would save RAM, at the expense of having to encode once so that
// we can produce the initial byte count.
func WriteMsg(w io.Writer, b []byte) error { func WriteMsg(w io.Writer, b []byte) error {
// TODO(apenwarr): incremental json encode? That would save RAM, at the
// expense of having to encode once so that we can produce the initial byte
// count.
// TODO(bradfitz): this does two writes to w, which likely // TODO(bradfitz): this does two writes to w, which likely
// does two writes on the wire, two frame generations, etc. We // does two writes on the wire, two frame generations, etc. We
// should take a concrete buffered type, or use a sync.Pool to // should take a concrete buffered type, or use a sync.Pool to

@ -49,13 +49,13 @@ var knownStores map[string]Provider
// //
// By default the following stores are registered: // By default the following stores are registered:
// //
// * if the string begins with "mem:", the suffix // - if the string begins with "mem:", the suffix
// is ignored and an in-memory store is used. // is ignored and an in-memory store is used.
// * (Linux-only) if the string begins with "arn:", // - (Linux-only) if the string begins with "arn:",
// the suffix an AWS ARN for an SSM. // the suffix an AWS ARN for an SSM.
// * (Linux-only) if the string begins with "kube:", // - (Linux-only) if the string begins with "kube:",
// the suffix is a Kubernetes secret name // the suffix is a Kubernetes secret name
// * In all other cases, the path is treated as a filepath. // - In all other cases, the path is treated as a filepath.
func New(logf logger.Logf, path string) (ipn.StateStore, error) { func New(logf logger.Logf, path string) (ipn.StateStore, error) {
regOnce.Do(registerDefaultStores) regOnce.Do(registerDefaultStores)
for prefix, sf := range knownStores { for prefix, sf := range knownStores {

@ -31,7 +31,9 @@ import (
// In other cases, resolved may be managing the system DNS configuration directly. // In other cases, resolved may be managing the system DNS configuration directly.
// Then the nameserver list will be a concatenation of those for all // Then the nameserver list will be a concatenation of those for all
// the interfaces that register their interest in being a default resolver with // the interfaces that register their interest in being a default resolver with
// SetLinkDomains([]{{"~.", true}, ...}) //
// SetLinkDomains([]{{"~.", true}, ...})
//
// which includes at least the interface with the default route, i.e. not us. // which includes at least the interface with the default route, i.e. not us.
// This does not work for us: there is a possibility of getting NXDOMAIN // This does not work for us: there is a possibility of getting NXDOMAIN
// from the other nameservers before we are asked or get a chance to respond. // from the other nameservers before we are asked or get a chance to respond.

@ -1025,11 +1025,11 @@ const (
// https://tools.ietf.org/html/rfc6763 lists // https://tools.ietf.org/html/rfc6763 lists
// "five special RR names" for Bonjour service discovery: // "five special RR names" for Bonjour service discovery:
// //
// b._dns-sd._udp.<domain>. // b._dns-sd._udp.<domain>.
// db._dns-sd._udp.<domain>. // db._dns-sd._udp.<domain>.
// r._dns-sd._udp.<domain>. // r._dns-sd._udp.<domain>.
// dr._dns-sd._udp.<domain>. // dr._dns-sd._udp.<domain>.
// lb._dns-sd._udp.<domain>. // lb._dns-sd._udp.<domain>.
func hasRDNSBonjourPrefix(name dnsname.FQDN) bool { func hasRDNSBonjourPrefix(name dnsname.FQDN) bool {
s := name.WithTrailingDot() s := name.WithTrailingDot()
base, rest, ok := strings.Cut(s, ".") base, rest, ok := strings.Cut(s, ".")
@ -1063,9 +1063,12 @@ func rawNameToLower(name []byte) string {
// ptrNameToIPv4 transforms a PTR name representing an IPv4 address to said address. // ptrNameToIPv4 transforms a PTR name representing an IPv4 address to said address.
// Such names are IPv4 labels in reverse order followed by .in-addr.arpa. // Such names are IPv4 labels in reverse order followed by .in-addr.arpa.
// For example, // For example,
// 4.3.2.1.in-addr.arpa //
// 4.3.2.1.in-addr.arpa
//
// is transformed to // is transformed to
// 1.2.3.4 //
// 1.2.3.4
func rdnsNameToIPv4(name dnsname.FQDN) (ip netip.Addr, ok bool) { func rdnsNameToIPv4(name dnsname.FQDN) (ip netip.Addr, ok bool) {
s := strings.TrimSuffix(name.WithTrailingDot(), rdnsv4Suffix) s := strings.TrimSuffix(name.WithTrailingDot(), rdnsv4Suffix)
ip, err := netip.ParseAddr(s) ip, err := netip.ParseAddr(s)
@ -1082,9 +1085,12 @@ func rdnsNameToIPv4(name dnsname.FQDN) (ip netip.Addr, ok bool) {
// ptrNameToIPv6 transforms a PTR name representing an IPv6 address to said address. // ptrNameToIPv6 transforms a PTR name representing an IPv6 address to said address.
// Such names are dot-separated nibbles in reverse order followed by .ip6.arpa. // Such names are dot-separated nibbles in reverse order followed by .ip6.arpa.
// For example, // For example,
// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa. //
// b.a.9.8.7.6.5.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa.
//
// is transformed to // is transformed to
// 2001:db8::567:89ab //
// 2001:db8::567:89ab
func rdnsNameToIPv6(name dnsname.FQDN) (ip netip.Addr, ok bool) { func rdnsNameToIPv6(name dnsname.FQDN) (ip netip.Addr, ok bool) {
var b [32]byte var b [32]byte
var ipb [16]byte var ipb [16]byte

@ -39,7 +39,6 @@ default link#14 UCSI utun2
10/16 link#4 UCS en0 ! 10/16 link#4 UCS en0 !
10.0.0.1/32 link#4 UCS en0 ! 10.0.0.1/32 link#4 UCS en0 !
... ...
*/ */
func likelyHomeRouterIPDarwinExec() (ret netip.Addr, ok bool) { func likelyHomeRouterIPDarwinExec() (ret netip.Addr, ok bool) {
if version.IsMobile() { if version.IsMobile() {

@ -25,13 +25,13 @@ import (
// TCP RST, this includes a reason. // TCP RST, this includes a reason.
// //
// On the wire, after the IP header, it's currently 7 or 8 bytes: // On the wire, after the IP header, it's currently 7 or 8 bytes:
// * '!' // - '!'
// * IPProto byte (IANA protocol number: TCP or UDP) // - IPProto byte (IANA protocol number: TCP or UDP)
// * 'A' or 'S' (RejectedDueToACLs, RejectedDueToShieldsUp) // - 'A' or 'S' (RejectedDueToACLs, RejectedDueToShieldsUp)
// * srcPort big endian uint16 // - srcPort big endian uint16
// * dstPort big endian uint16 // - dstPort big endian uint16
// * [optional] byte of flag bits: // - [optional] byte of flag bits:
// lowest bit (0x1): MaybeBroken // lowest bit (0x1): MaybeBroken
// //
// In the future it might also accept 16 byte IP flow src/dst IPs // In the future it might also accept 16 byte IP flow src/dst IPs
// after the header, if they're different than the IP-level ones. // after the header, if they're different than the IP-level ones.
@ -205,8 +205,8 @@ func (pp *Parsed) AsTailscaleRejectedHeader() (h TailscaleRejectedHeader, ok boo
// TSMPPingRequest is a TSMP message that's like an ICMP ping request. // TSMPPingRequest is a TSMP message that's like an ICMP ping request.
// //
// On the wire, after the IP header, it's currently 9 bytes: // On the wire, after the IP header, it's currently 9 bytes:
// * 'p' (TSMPTypePing) // - 'p' (TSMPTypePing)
// * 8 opaque ping bytes to copy back in the response // - 8 opaque ping bytes to copy back in the response
type TSMPPingRequest struct { type TSMPPingRequest struct {
Data [8]byte Data [8]byte
} }

@ -164,19 +164,20 @@ func SetConfigExpectedCert(c *tls.Config, certDNSName string) {
letsEncryptX1 is the LetsEncrypt X1 root: letsEncryptX1 is the LetsEncrypt X1 root:
Certificate: Certificate:
Data:
Version: 3 (0x2) Data:
Serial Number: Version: 3 (0x2)
82:10:cf:b0:d2:40:e3:59:44:63:e0:bb:63:82:8b:00 Serial Number:
Signature Algorithm: sha256WithRSAEncryption 82:10:cf:b0:d2:40:e3:59:44:63:e0:bb:63:82:8b:00
Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1 Signature Algorithm: sha256WithRSAEncryption
Validity Issuer: C = US, O = Internet Security Research Group, CN = ISRG Root X1
Not Before: Jun 4 11:04:38 2015 GMT Validity
Not After : Jun 4 11:04:38 2035 GMT Not Before: Jun 4 11:04:38 2015 GMT
Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1 Not After : Jun 4 11:04:38 2035 GMT
Subject Public Key Info: Subject: C = US, O = Internet Security Research Group, CN = ISRG Root X1
Public Key Algorithm: rsaEncryption Subject Public Key Info:
RSA Public-Key: (4096 bit) Public Key Algorithm: rsaEncryption
RSA Public-Key: (4096 bit)
We bake it into the binary as a fallback verification root, We bake it into the binary as a fallback verification root,
in case the system we're running on doesn't have it. in case the system we're running on doesn't have it.
@ -189,7 +190,6 @@ $ sudo update-ca-certificates
Then restart tailscaled. To also test dnsfallback's use of it, nuke Then restart tailscaled. To also test dnsfallback's use of it, nuke
your /etc/resolv.conf and it should still start & run fine. your /etc/resolv.conf and it should still start & run fine.
*/ */
const letsEncryptX1 = ` const letsEncryptX1 = `
-----BEGIN CERTIFICATE----- -----BEGIN CERTIFICATE-----

@ -13,11 +13,11 @@ import (
// TryConfigFileMigration carefully copies the contents of oldFile to // TryConfigFileMigration carefully copies the contents of oldFile to
// newFile, returning the path which should be used to read the config. // newFile, returning the path which should be used to read the config.
// - if newFile already exists, don't modify it just return its path // - if newFile already exists, don't modify it just return its path
// - if neither oldFile nor newFile exist, return newFile for a fresh // - if neither oldFile nor newFile exist, return newFile for a fresh
// default config to be written to. // default config to be written to.
// - if oldFile exists but copying to newFile fails, return oldFile so // - if oldFile exists but copying to newFile fails, return oldFile so
// there will at least be some config to work with. // there will at least be some config to work with.
func TryConfigFileMigration(logf logger.Logf, oldFile, newFile string) string { func TryConfigFileMigration(logf logger.Logf, oldFile, newFile string) string {
_, err := os.Stat(newFile) _, err := os.Stat(newFile)
if err == nil { if err == nil {

@ -18,12 +18,15 @@ import (
// Owner: The user for the current process; // Owner: The user for the current process;
// Primary Group: The primary group for the current process; // Primary Group: The primary group for the current process;
// DACL: Full control to the current user and to the Administrators group. // DACL: Full control to the current user and to the Administrators group.
// (We include Administrators so that admin users may still access logs; //
// granting access exclusively to LocalSystem would require admins to use // (We include Administrators so that admin users may still access logs;
// special tools to access the Log directory) // granting access exclusively to LocalSystem would require admins to use
// special tools to access the Log directory)
//
// Inheritance: The directory does not inherit the ACL from its parent. // Inheritance: The directory does not inherit the ACL from its parent.
// However, any directories and/or files created within this //
// directory *do* inherit the ACL that we are setting. // However, any directories and/or files created within this
// directory *do* inherit the ACL that we are setting.
func ensureStateDirPerms(dirPath string) error { func ensureStateDirPerms(dirPath string) error {
fi, err := os.Stat(dirPath) fi, err := os.Stat(dirPath)
if err != nil { if err != nil {

@ -239,13 +239,13 @@ func (v varExporter) String() string {
// WritePrometheus writes the the state of all probes to w. // WritePrometheus writes the the state of all probes to w.
// //
// For each probe, WritePrometheus exports 5 variables: // For each probe, WritePrometheus exports 5 variables:
// - <prefix>_interval_secs, how frequently the probe runs. // - <prefix>_interval_secs, how frequently the probe runs.
// - <prefix>_start_secs, when the probe last started running, in seconds since epoch. // - <prefix>_start_secs, when the probe last started running, in seconds since epoch.
// - <prefix>_end_secs, when the probe last finished running, in seconds since epoch. // - <prefix>_end_secs, when the probe last finished running, in seconds since epoch.
// - <prefix>_latency_millis, how long the last probe cycle took, in // - <prefix>_latency_millis, how long the last probe cycle took, in
// milliseconds. This is just (end_secs-start_secs) in an easier to // milliseconds. This is just (end_secs-start_secs) in an easier to
// graph form. // graph form.
// - <prefix>_result, 1 if the last probe succeeded, 0 if it failed. // - <prefix>_result, 1 if the last probe succeeded, 0 if it failed.
// //
// Each probe has a set of static key/value labels (defined once at // Each probe has a set of static key/value labels (defined once at
// probe creation), which are added as Prometheus metric labels to // probe creation), which are added as Prometheus metric labels to

@ -27,11 +27,12 @@ func setFlags(network, address string, c syscall.RawConn) error {
} }
// TODO(apenwarr): use named pipes instead of sockets? // TODO(apenwarr): use named pipes instead of sockets?
// I tried to use winio.ListenPipe() here, but that code is a disaster, //
// built on top of an API that's a disaster. So for now we'll hack it by // I tried to use winio.ListenPipe() here, but that code is a disaster,
// just always using a TCP session on a fixed port on localhost. As a // built on top of an API that's a disaster. So for now we'll hack it by
// result, on Windows we ignore the vendor and name strings. // just always using a TCP session on a fixed port on localhost. As a
// NOTE(bradfitz): Jason did a new pipe package: https://go-review.googlesource.com/c/sys/+/299009 // result, on Windows we ignore the vendor and name strings.
// NOTE(bradfitz): Jason did a new pipe package: https://go-review.googlesource.com/c/sys/+/299009
func listen(path string, port uint16) (_ net.Listener, gotPort uint16, _ error) { func listen(path string, port uint16) (_ net.Listener, gotPort uint16, _ error) {
lc := net.ListenConfig{ lc := net.ListenConfig{
Control: setFlags, Control: setFlags,

@ -26,8 +26,9 @@ func init() {
// from /Library/Tailscale. // from /Library/Tailscale.
// //
// In that case the files are: // In that case the files are:
// /Library/Tailscale/ipnport => $port (symlink with localhost port number target) //
// /Library/Tailscale/sameuserproof-$port is a file with auth // /Library/Tailscale/ipnport => $port (symlink with localhost port number target)
// /Library/Tailscale/sameuserproof-$port is a file with auth
func localTCPPortAndTokenMacsys() (port int, token string, err error) { func localTCPPortAndTokenMacsys() (port int, token string, err error) {
const dir = "/Library/Tailscale" const dir = "/Library/Tailscale"

@ -118,11 +118,11 @@ func socketPermissionsForOS() os.FileMode {
// homebrew or go install). This little dance to connect a regular user binary // homebrew or go install). This little dance to connect a regular user binary
// to the sandboxed network extension is: // to the sandboxed network extension is:
// //
// * the sandboxed IPNExtension picks a random localhost:0 TCP port // - the sandboxed IPNExtension picks a random localhost:0 TCP port
// to listen on // to listen on
// * it also picks a random hex string that acts as an auth token // - it also picks a random hex string that acts as an auth token
// * the CLI looks on disk for that TCP port + auth token (see localTCPPortAndTokenDarwin) // - the CLI looks on disk for that TCP port + auth token (see localTCPPortAndTokenDarwin)
// * we send it upon TCP connect to prove to the Tailscale daemon that // - we send it upon TCP connect to prove to the Tailscale daemon that
// we're a suitably privileged user to have access the files on disk // we're a suitably privileged user to have access the files on disk
// which the Network/App Extension wrote. // which the Network/App Extension wrote.
func connectMacOSAppSandbox() (net.Conn, error) { func connectMacOSAppSandbox() (net.Conn, error) {

@ -20,7 +20,7 @@ type DERPMap struct {
OmitDefaultRegions bool `json:"omitDefaultRegions,omitempty"` OmitDefaultRegions bool `json:"omitDefaultRegions,omitempty"`
} }
/// RegionIDs returns the sorted region IDs. // / RegionIDs returns the sorted region IDs.
func (m *DERPMap) RegionIDs() []int { func (m *DERPMap) RegionIDs() []int {
ret := make([]int, 0, len(m.Regions)) ret := make([]int, 0, len(m.Regions))
for rid := range m.Regions { for rid := range m.Regions {

@ -37,37 +37,38 @@ type CapabilityVersion int
// CurrentCapabilityVersion is the current capability version of the codebase. // CurrentCapabilityVersion is the current capability version of the codebase.
// //
// History of versions: // History of versions:
// 3: implicit compression, keep-alives //
// 4: opt-in keep-alives via KeepAlive field, opt-in compression via Compress // 3: implicit compression, keep-alives
// 5: 2020-10-19, implies IncludeIPv6, delta Peers/UserProfiles, supports MagicDNS // 4: opt-in keep-alives via KeepAlive field, opt-in compression via Compress
// 6: 2020-12-07: means MapResponse.PacketFilter nil means unchanged // 5: 2020-10-19, implies IncludeIPv6, delta Peers/UserProfiles, supports MagicDNS
// 7: 2020-12-15: FilterRule.SrcIPs accepts CIDRs+ranges, doesn't warn about 0.0.0.0/:: // 6: 2020-12-07: means MapResponse.PacketFilter nil means unchanged
// 8: 2020-12-19: client can buggily receive IPv6 addresses and routes if beta enabled server-side // 7: 2020-12-15: FilterRule.SrcIPs accepts CIDRs+ranges, doesn't warn about 0.0.0.0/::
// 9: 2020-12-30: client doesn't auto-add implicit search domains from peers; only DNSConfig.Domains // 8: 2020-12-19: client can buggily receive IPv6 addresses and routes if beta enabled server-side
// 10: 2021-01-17: client understands MapResponse.PeerSeenChange // 9: 2020-12-30: client doesn't auto-add implicit search domains from peers; only DNSConfig.Domains
// 11: 2021-03-03: client understands IPv6, multiple default routes, and goroutine dumping // 10: 2021-01-17: client understands MapResponse.PeerSeenChange
// 12: 2021-03-04: client understands PingRequest // 11: 2021-03-03: client understands IPv6, multiple default routes, and goroutine dumping
// 13: 2021-03-19: client understands FilterRule.IPProto // 12: 2021-03-04: client understands PingRequest
// 14: 2021-04-07: client understands DNSConfig.Routes and DNSConfig.Resolvers // 13: 2021-03-19: client understands FilterRule.IPProto
// 15: 2021-04-12: client treats nil MapResponse.DNSConfig as meaning unchanged // 14: 2021-04-07: client understands DNSConfig.Routes and DNSConfig.Resolvers
// 16: 2021-04-15: client understands Node.Online, MapResponse.OnlineChange // 15: 2021-04-12: client treats nil MapResponse.DNSConfig as meaning unchanged
// 17: 2021-04-18: MapResponse.Domain empty means unchanged // 16: 2021-04-15: client understands Node.Online, MapResponse.OnlineChange
// 18: 2021-04-19: MapResponse.Node nil means unchanged (all fields now omitempty) // 17: 2021-04-18: MapResponse.Domain empty means unchanged
// 19: 2021-04-21: MapResponse.Debug.SleepSeconds // 18: 2021-04-19: MapResponse.Node nil means unchanged (all fields now omitempty)
// 20: 2021-06-11: MapResponse.LastSeen used even less (https://github.com/tailscale/tailscale/issues/2107) // 19: 2021-04-21: MapResponse.Debug.SleepSeconds
// 21: 2021-06-15: added MapResponse.DNSConfig.CertDomains // 20: 2021-06-11: MapResponse.LastSeen used even less (https://github.com/tailscale/tailscale/issues/2107)
// 22: 2021-06-16: added MapResponse.DNSConfig.ExtraRecords // 21: 2021-06-15: added MapResponse.DNSConfig.CertDomains
// 23: 2021-08-25: DNSConfig.Routes values may be empty (for ExtraRecords support in 1.14.1+) // 22: 2021-06-16: added MapResponse.DNSConfig.ExtraRecords
// 24: 2021-09-18: MapResponse.Health from control to node; node shows in "tailscale status" // 23: 2021-08-25: DNSConfig.Routes values may be empty (for ExtraRecords support in 1.14.1+)
// 25: 2021-11-01: MapResponse.Debug.Exit // 24: 2021-09-18: MapResponse.Health from control to node; node shows in "tailscale status"
// 26: 2022-01-12: (nothing, just bumping for 1.20.0) // 25: 2021-11-01: MapResponse.Debug.Exit
// 27: 2022-02-18: start of SSHPolicy being respected // 26: 2022-01-12: (nothing, just bumping for 1.20.0)
// 28: 2022-03-09: client can communicate over Noise. // 27: 2022-02-18: start of SSHPolicy being respected
// 29: 2022-03-21: MapResponse.PopBrowserURL // 28: 2022-03-09: client can communicate over Noise.
// 30: 2022-03-22: client can request id tokens. // 29: 2022-03-21: MapResponse.PopBrowserURL
// 31: 2022-04-15: PingRequest & PingResponse TSMP & disco support // 30: 2022-03-22: client can request id tokens.
// 32: 2022-04-17: client knows FilterRule.CapMatch // 31: 2022-04-15: PingRequest & PingResponse TSMP & disco support
// 33: 2022-07-20: added MapResponse.PeersChangedPatch (DERPRegion + Endpoints) // 32: 2022-04-17: client knows FilterRule.CapMatch
// 33: 2022-07-20: added MapResponse.PeersChangedPatch (DERPRegion + Endpoints)
const CurrentCapabilityVersion CapabilityVersion = 33 const CurrentCapabilityVersion CapabilityVersion = 33
type StableID string type StableID string
@ -746,6 +747,7 @@ func (st SignatureType) String() string {
// RegisterRequest is sent by a client to register the key for a node. // RegisterRequest is sent by a client to register the key for a node.
// It is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box, // It is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box,
// using the local machine key, and sent to: // using the local machine key, and sent to:
//
// https://login.tailscale.com/machine/<mkey hex> // https://login.tailscale.com/machine/<mkey hex>
type RegisterRequest struct { type RegisterRequest struct {
_ structs.Incomparable _ structs.Incomparable
@ -864,6 +866,7 @@ type Endpoint struct {
// //
// The request is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box, // The request is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box,
// using the local machine key, and sent to: // using the local machine key, and sent to:
//
// https://login.tailscale.com/machine/<mkey hex>/map // https://login.tailscale.com/machine/<mkey hex>/map
type MapRequest struct { type MapRequest struct {
// Version is incremented whenever the client code changes enough that // Version is incremented whenever the client code changes enough that
@ -1514,6 +1517,7 @@ const (
// //
// The request is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box, // The request is encoded to JSON, encrypted with golang.org/x/crypto/nacl/box,
// using the local machine key, and sent to: // using the local machine key, and sent to:
//
// https://login.tailscale.com/machine/<mkey hex>/set-dns // https://login.tailscale.com/machine/<mkey hex>/set-dns
type SetDNSRequest struct { type SetDNSRequest struct {
// Version is the client's capabilities // Version is the client's capabilities

@ -10,29 +10,29 @@ use crypto/ssh for building SSH clients.
ListenAndServe starts an SSH server with a given address, handler, and options. The ListenAndServe starts an SSH server with a given address, handler, and options. The
handler is usually nil, which means to use DefaultHandler. Handle sets DefaultHandler: handler is usually nil, which means to use DefaultHandler. Handle sets DefaultHandler:
ssh.Handle(func(s ssh.Session) { ssh.Handle(func(s ssh.Session) {
io.WriteString(s, "Hello world\n") io.WriteString(s, "Hello world\n")
}) })
log.Fatal(ssh.ListenAndServe(":2222", nil)) log.Fatal(ssh.ListenAndServe(":2222", nil))
If you don't specify a host key, it will generate one every time. This is convenient If you don't specify a host key, it will generate one every time. This is convenient
except you'll have to deal with clients being confused that the host key is different. except you'll have to deal with clients being confused that the host key is different.
It's a better idea to generate or point to an existing key on your system: It's a better idea to generate or point to an existing key on your system:
log.Fatal(ssh.ListenAndServe(":2222", nil, ssh.HostKeyFile("/Users/progrium/.ssh/id_rsa"))) log.Fatal(ssh.ListenAndServe(":2222", nil, ssh.HostKeyFile("/Users/progrium/.ssh/id_rsa")))
Although all options have functional option helpers, another way to control the Although all options have functional option helpers, another way to control the
server's behavior is by creating a custom Server: server's behavior is by creating a custom Server:
s := &ssh.Server{ s := &ssh.Server{
Addr: ":2222", Addr: ":2222",
Handler: sessionHandler, Handler: sessionHandler,
PublicKeyHandler: authHandler, PublicKeyHandler: authHandler,
} }
s.AddHostKey(hostKeySigner) s.AddHostKey(hostKeySigner)
log.Fatal(s.ListenAndServe()) log.Fatal(s.ListenAndServe())
This package automatically handles basic SSH requests like setting environment This package automatically handles basic SSH requests like setting environment
variables, requesting PTY, and changing window size. These requests are variables, requesting PTY, and changing window size. These requests are

@ -582,17 +582,17 @@ func writePromExpVar(w io.Writer, prefix string, kv expvar.KeyValue) {
// VarzHandler is an HTTP handler to write expvar values into the // VarzHandler is an HTTP handler to write expvar values into the
// prometheus export format: // prometheus export format:
// //
// https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md // https://github.com/prometheus/docs/blob/master/content/docs/instrumenting/exposition_formats.md
// //
// It makes the following assumptions: // It makes the following assumptions:
// //
// * *expvar.Int are counters (unless marked as a gauge_; see below) // - *expvar.Int are counters (unless marked as a gauge_; see below)
// * a *tailscale/metrics.Set is descended into, joining keys with // - a *tailscale/metrics.Set is descended into, joining keys with
// underscores. So use underscores as your metric names. // underscores. So use underscores as your metric names.
// * an expvar named starting with "gauge_" or "counter_" is of that // - an expvar named starting with "gauge_" or "counter_" is of that
// Prometheus type, and has that prefix stripped. // Prometheus type, and has that prefix stripped.
// * anything else is untyped and thus not exported. // - anything else is untyped and thus not exported.
// * expvar.Func can return an int or int64 (for now) and anything else // - expvar.Func can return an int or int64 (for now) and anything else
// is not exported. // is not exported.
// //
// This will evolve over time, or perhaps be replaced. // This will evolve over time, or perhaps be replaced.

@ -12,10 +12,11 @@ import (
// It's similar in function to golang.org/x/time/rate.Limiter, which we // It's similar in function to golang.org/x/time/rate.Limiter, which we
// can't use because: // can't use because:
// - It doesn't give access to the number of accumulated tokens, which we // - It doesn't give access to the number of accumulated tokens, which we
// need for implementing hysteresis; // need for implementing hysteresis;
// - It doesn't let us provide our own time function, which we need for // - It doesn't let us provide our own time function, which we need for
// implementing proper unit tests. // implementing proper unit tests.
//
// rate.Limiter is also much more complex than necessary, but that wouldn't // rate.Limiter is also much more complex than necessary, but that wouldn't
// be enough to disqualify it on its own. // be enough to disqualify it on its own.
// //

@ -216,11 +216,11 @@ const (
// without further escaping. // without further escaping.
// //
// The current encoding is: // The current encoding is:
// * name immediately following metric: // - name immediately following metric:
// 'N' + hex(varint(len(name))) + name // 'N' + hex(varint(len(name))) + name
// * set value of a metric: // - set value of a metric:
// 'S' + hex(varint(wireid)) + hex(varint(value)) // 'S' + hex(varint(wireid)) + hex(varint(value))
// * increment a metric: (decrements if negative) // - increment a metric: (decrements if negative)
// 'I' + hex(varint(wireid)) + hex(varint(value)) // 'I' + hex(varint(wireid)) + hex(varint(value))
func EncodeLogTailMetricsDelta() string { func EncodeLogTailMetricsDelta() string {
mu.Lock() mu.Lock()

@ -9,11 +9,11 @@
// Hash(x) == Hash(y) is an appropriate replacement for x == y. // Hash(x) == Hash(y) is an appropriate replacement for x == y.
// //
// The definition of equality is identical to reflect.DeepEqual except: // The definition of equality is identical to reflect.DeepEqual except:
// * Floating-point values are compared based on the raw bits, // - Floating-point values are compared based on the raw bits,
// which means that NaNs (with the same bit pattern) are treated as equal. // which means that NaNs (with the same bit pattern) are treated as equal.
// * Types which implement interface { AppendTo([]byte) []byte } use // - Types which implement interface { AppendTo([]byte) []byte } use
// the AppendTo method to produce a textual representation of the value. // the AppendTo method to produce a textual representation of the value.
// Thus, two values are equal if AppendTo produces the same bytes. // Thus, two values are equal if AppendTo produces the same bytes.
// //
// WARNING: This package, like most of the tailscale.com Go module, // WARNING: This package, like most of the tailscale.com Go module,
// should be considered Tailscale-internal; we make no API promises. // should be considered Tailscale-internal; we make no API promises.

@ -34,8 +34,9 @@ func (e Error) Errors() []error {
// New returns an error composed from errs. // New returns an error composed from errs.
// Some errors in errs get special treatment: // Some errors in errs get special treatment:
// * nil errors are discarded // - nil errors are discarded
// * errors of type Error are expanded into the top level // - errors of type Error are expanded into the top level
//
// If the resulting slice has length 0, New returns nil. // If the resulting slice has length 0, New returns nil.
// If the resulting slice has length 1, New returns that error. // If the resulting slice has length 1, New returns that error.
// If the resulting slice has length > 1, New returns that slice as an Error. // If the resulting slice has length > 1, New returns that slice as an Error.

@ -12,9 +12,9 @@
// This is a Tailscale fork of Go's singleflight package which has had several // This is a Tailscale fork of Go's singleflight package which has had several
// homes in the past: // homes in the past:
// //
// * https://github.com/golang/go/commit/61d3b2db6292581fc07a3767ec23ec94ad6100d1 // - https://github.com/golang/go/commit/61d3b2db6292581fc07a3767ec23ec94ad6100d1
// * https://github.com/golang/groupcache/tree/master/singleflight // - https://github.com/golang/groupcache/tree/master/singleflight
// * https://pkg.go.dev/golang.org/x/sync/singleflight // - https://pkg.go.dev/golang.org/x/sync/singleflight
// //
// This fork adds generics. // This fork adds generics.
package singleflight // import "tailscale.com/util/singleflight" package singleflight // import "tailscale.com/util/singleflight"

@ -59,18 +59,18 @@ func Ready() {
// Status sends a single line status update to systemd so that information shows up // Status sends a single line status update to systemd so that information shows up
// in systemctl output. For example: // in systemctl output. For example:
// //
// $ systemctl status tailscale // $ systemctl status tailscale
// ● tailscale.service - Tailscale client daemon // ● tailscale.service - Tailscale client daemon
// Loaded: loaded (/nix/store/qc312qcy907wz80fqrgbbm8a9djafmlg-unit-tailscale.service/tailscale.service; enabled; vendor preset: enabled) // Loaded: loaded (/nix/store/qc312qcy907wz80fqrgbbm8a9djafmlg-unit-tailscale.service/tailscale.service; enabled; vendor preset: enabled)
// Active: active (running) since Tue 2020-11-24 17:54:07 EST; 13h ago // Active: active (running) since Tue 2020-11-24 17:54:07 EST; 13h ago
// Main PID: 26741 (.tailscaled-wra) // Main PID: 26741 (.tailscaled-wra)
// Status: "Connected; user@host.domain.tld; 100.101.102.103" // Status: "Connected; user@host.domain.tld; 100.101.102.103"
// IP: 0B in, 0B out // IP: 0B in, 0B out
// Tasks: 22 (limit: 4915) // Tasks: 22 (limit: 4915)
// Memory: 30.9M // Memory: 30.9M
// CPU: 2min 38.469s // CPU: 2min 38.469s
// CGroup: /system.slice/tailscale.service // CGroup: /system.slice/tailscale.service
// └─26741 /nix/store/sv6cj4mw2jajm9xkbwj07k29dj30lh0n-tailscale-date.20200727/bin/tailscaled --port 41641 // └─26741 /nix/store/sv6cj4mw2jajm9xkbwj07k29dj30lh0n-tailscale-date.20200727/bin/tailscaled --port 41641
func Status(format string, args ...any) { func Status(format string, args ...any) {
err := notifier().Notify(sdnotify.Statusf(format, args...)) err := notifier().Notify(sdnotify.Statusf(format, args...))
if err != nil { if err != nil {

@ -245,17 +245,17 @@ func maybeHexdump(flag RunFlags, b []byte) string {
} }
// TODO(apenwarr): use a bigger bucket for specifically TCP SYN accept logging? // TODO(apenwarr): use a bigger bucket for specifically TCP SYN accept logging?
// Logging is a quick way to record every newly opened TCP connection, but // Logging is a quick way to record every newly opened TCP connection, but
// we have to be cautious about flooding the logs vs letting people use // we have to be cautious about flooding the logs vs letting people use
// flood protection to hide their traffic. We could use a rate limiter in // flood protection to hide their traffic. We could use a rate limiter in
// the actual *filter* for SYN accepts, perhaps. // the actual *filter* for SYN accepts, perhaps.
var acceptBucket = rate.NewLimiter(rate.Every(10*time.Second), 3) var acceptBucket = rate.NewLimiter(rate.Every(10*time.Second), 3)
var dropBucket = rate.NewLimiter(rate.Every(5*time.Second), 10) var dropBucket = rate.NewLimiter(rate.Every(5*time.Second), 10)
// NOTE(Xe): This func init is used to detect // NOTE(Xe): This func init is used to detect
// TS_DEBUG_FILTER_RATE_LIMIT_LOGS=all, and if it matches, to // TS_DEBUG_FILTER_RATE_LIMIT_LOGS=all, and if it matches, to
// effectively disable the limits on the log rate by setting the limit // effectively disable the limits on the log rate by setting the limit
// to 1 millisecond. This should capture everything. // to 1 millisecond. This should capture everything.
func init() { func init() {
if envknob.String("TS_DEBUG_FILTER_RATE_LIMIT_LOGS") != "all" { if envknob.String("TS_DEBUG_FILTER_RATE_LIMIT_LOGS") != "all" {
return return

@ -102,10 +102,10 @@ var (
// parseIPSet parses arg as one: // parseIPSet parses arg as one:
// //
// * an IP address (IPv4 or IPv6) // - an IP address (IPv4 or IPv6)
// * the string "*" to match everything (both IPv4 & IPv6) // - the string "*" to match everything (both IPv4 & IPv6)
// * a CIDR (e.g. "192.168.0.0/16") // - a CIDR (e.g. "192.168.0.0/16")
// * a range of two IPs, inclusive, separated by hyphen ("2eff::1-2eff::0800") // - a range of two IPs, inclusive, separated by hyphen ("2eff::1-2eff::0800")
// //
// bits, if non-nil, is the legacy SrcBits CIDR length to make a IP // bits, if non-nil, is the legacy SrcBits CIDR length to make a IP
// address (without a slash) treated as a CIDR of *bits length. // address (without a slash) treated as a CIDR of *bits length.

@ -1833,10 +1833,10 @@ func (c *Conn) sendDiscoMessage(dst netip.AddrPort, dstKey key.NodePublic, dstDi
// //
// A discovery message has the form: // A discovery message has the form:
// //
// * magic [6]byte // - magic [6]byte
// * senderDiscoPubKey [32]byte // - senderDiscoPubKey [32]byte
// * nonce [24]byte // - nonce [24]byte
// * naclbox of payload (see tailscale.com/disco package for inner payload format) // - naclbox of payload (see tailscale.com/disco package for inner payload format)
// //
// For messages received over DERP, the src.Addr() will be derpMagicIP (with // For messages received over DERP, the src.Addr() will be derpMagicIP (with
// src.Port() being the region ID) and the derpNodeSrc will be the node key // src.Port() being the region ID) and the derpNodeSrc will be the node key

@ -517,8 +517,8 @@ func (e *userspaceEngine) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper)
// pollResolver reads packets from the DNS resolver and injects them inbound. // pollResolver reads packets from the DNS resolver and injects them inbound.
// //
// TODO(tom): Remove this fallback path (via NextPacket()) once // TODO(tom): Remove this fallback path (via NextPacket()) once all
// all platforms use netstack. // platforms use netstack.
func (e *userspaceEngine) pollResolver() { func (e *userspaceEngine) pollResolver() {
for { for {
bs, err := e.dns.NextPacket() bs, err := e.dns.NextPacket()
@ -1503,7 +1503,6 @@ func (e *userspaceEngine) WhoIsIPPort(ipport netip.AddrPort) (tsIP netip.Addr, o
// If none is found in the wireguard config but one is found in // If none is found in the wireguard config but one is found in
// the netmap, it's described in an error. // the netmap, it's described in an error.
// //
//
// peerForIP acquires both e.mu and e.wgLock, but neither at the same // peerForIP acquires both e.mu and e.wgLock, but neither at the same
// time. // time.
func (e *userspaceEngine) PeerForIP(ip netip.Addr) (ret PeerForIP, ok bool) { func (e *userspaceEngine) PeerForIP(ip netip.Addr) (ret PeerForIP, ok bool) {

Loading…
Cancel
Save