|
|
|
|
@ -21,6 +21,7 @@ import ( |
|
|
|
|
"runtime" |
|
|
|
|
"strconv" |
|
|
|
|
"strings" |
|
|
|
|
"sync" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"go4.org/mem" |
|
|
|
|
@ -33,30 +34,39 @@ import ( |
|
|
|
|
"tailscale.com/version" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// TailscaledSocket is the tailscaled Unix socket.
|
|
|
|
|
var TailscaledSocket = paths.DefaultTailscaledSocket() |
|
|
|
|
var ( |
|
|
|
|
// TailscaledSocket is the tailscaled Unix socket. It's used by the TailscaledDialer.
|
|
|
|
|
TailscaledSocket = paths.DefaultTailscaledSocket() |
|
|
|
|
|
|
|
|
|
// tsClient does HTTP requests to the local Tailscale daemon.
|
|
|
|
|
var tsClient = &http.Client{ |
|
|
|
|
Transport: &http.Transport{ |
|
|
|
|
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { |
|
|
|
|
if addr != "local-tailscaled.sock:80" { |
|
|
|
|
return nil, fmt.Errorf("unexpected URL address %q", addr) |
|
|
|
|
} |
|
|
|
|
if TailscaledSocket == paths.DefaultTailscaledSocket() { |
|
|
|
|
// On macOS, when dialing from non-sandboxed program to sandboxed GUI running
|
|
|
|
|
// a TCP server on a random port, find the random port. For HTTP connections,
|
|
|
|
|
// we don't send the token. It gets added in an HTTP Basic-Auth header.
|
|
|
|
|
if port, _, err := safesocket.LocalTCPPortAndToken(); err == nil { |
|
|
|
|
var d net.Dialer |
|
|
|
|
return d.DialContext(ctx, "tcp", "localhost:"+strconv.Itoa(port)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return safesocket.Connect(TailscaledSocket, 41112) |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
// TailscaledDialer is the DialContext func that connects to the local machine's
|
|
|
|
|
// tailscaled or equivalent.
|
|
|
|
|
TailscaledDialer = defaultDialer |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func defaultDialer(ctx context.Context, network, addr string) (net.Conn, error) { |
|
|
|
|
if addr != "local-tailscaled.sock:80" { |
|
|
|
|
return nil, fmt.Errorf("unexpected URL address %q", addr) |
|
|
|
|
} |
|
|
|
|
if TailscaledSocket == paths.DefaultTailscaledSocket() { |
|
|
|
|
// On macOS, when dialing from non-sandboxed program to sandboxed GUI running
|
|
|
|
|
// a TCP server on a random port, find the random port. For HTTP connections,
|
|
|
|
|
// we don't send the token. It gets added in an HTTP Basic-Auth header.
|
|
|
|
|
if port, _, err := safesocket.LocalTCPPortAndToken(); err == nil { |
|
|
|
|
var d net.Dialer |
|
|
|
|
return d.DialContext(ctx, "tcp", "localhost:"+strconv.Itoa(port)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return safesocket.Connect(TailscaledSocket, 41112) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var ( |
|
|
|
|
// tsClient does HTTP requests to the local Tailscale daemon.
|
|
|
|
|
// We lazily initialize the client in case the caller wants to
|
|
|
|
|
// override TailscaledDialer.
|
|
|
|
|
tsClient *http.Client |
|
|
|
|
tsClientOnce sync.Once |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// DoLocalRequest makes an HTTP request to the local machine's Tailscale daemon.
|
|
|
|
|
//
|
|
|
|
|
// URLs are of the form http://local-tailscaled.sock/localapi/v0/whois?ip=1.2.3.4.
|
|
|
|
|
@ -67,6 +77,13 @@ var tsClient = &http.Client{ |
|
|
|
|
//
|
|
|
|
|
// DoLocalRequest may mutate the request to add Authorization headers.
|
|
|
|
|
func DoLocalRequest(req *http.Request) (*http.Response, error) { |
|
|
|
|
tsClientOnce.Do(func() { |
|
|
|
|
tsClient = &http.Client{ |
|
|
|
|
Transport: &http.Transport{ |
|
|
|
|
DialContext: TailscaledDialer, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
if _, token, err := safesocket.LocalTCPPortAndToken(); err == nil { |
|
|
|
|
req.SetBasicAuth("", token) |
|
|
|
|
} |
|
|
|
|
|