net/tshttpproxy: fix WDAP/PAC proxy detection on Win10 1607 and earlier

Using WINHTTP_AUTOPROXY_ALLOW_AUTOCONFIG on Windows versions older than Windows 10 1703 (build 15063)
is not supported and causes WinHttpGetProxyForUrl to fail with ERROR_INVALID_PARAMETER. This results in failures
reaching the control on environments where a proxy is required.

We use wingoes version detection to conditionally set the WINHTTP_AUTOPROXY_ALLOW_AUTOCONFIG flag
on Windows builds greater than 15063.

While there, we also update proxy detection to use WINHTTP_AUTO_DETECT_TYPE_DNS_A, as DNS-based proxy discovery
might be required with Active Directory and in certain other environments.

Updates tailscale/corp#29168
Fixes #879

Signed-off-by: Nick Khyl <nickk@tailscale.com>
This commit is contained in:
Nick Khyl 2025-05-29 10:41:23 -05:00 committed by Nick Khyl
parent 4cccd15eeb
commit 191afd3390
2 changed files with 21 additions and 9 deletions

View File

@ -12,7 +12,7 @@ tailscale.com/cmd/derper dependencies: (generated by github.com/tailscale/depawa
github.com/coder/websocket/internal/util from github.com/coder/websocket
github.com/coder/websocket/internal/xsync from github.com/coder/websocket
L github.com/coreos/go-iptables/iptables from tailscale.com/util/linuxfw
W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil
W 💣 github.com/dblohm7/wingoes from tailscale.com/util/winutil+
github.com/fxamacker/cbor/v2 from tailscale.com/tka
github.com/go-json-experiment/json from tailscale.com/types/opt+
github.com/go-json-experiment/json/internal from github.com/go-json-experiment/json+

View File

@ -18,6 +18,7 @@ import (
"unsafe"
"github.com/alexbrainman/sspi/negotiate"
"github.com/dblohm7/wingoes"
"golang.org/x/sys/windows"
"tailscale.com/hostinfo"
"tailscale.com/syncs"
@ -97,9 +98,7 @@ func proxyFromWinHTTPOrCache(req *http.Request) (*url.URL, error) {
}
if err == windows.ERROR_INVALID_PARAMETER {
metricErrInvalidParameters.Add(1)
// Seen on Windows 8.1. (https://github.com/tailscale/tailscale/issues/879)
// TODO(bradfitz): figure this out.
setNoProxyUntil(time.Hour)
setNoProxyUntil(10 * time.Second)
proxyErrorf("tshttpproxy: winhttp: GetProxyForURL(%q): ERROR_INVALID_PARAMETER [unexpected]", urlStr)
return nil, nil
}
@ -238,17 +237,30 @@ func (pi *winHTTPProxyInfo) free() {
}
}
var proxyForURLOpts = &winHTTPAutoProxyOptions{
DwFlags: winHTTP_AUTOPROXY_ALLOW_AUTOCONFIG | winHTTP_AUTOPROXY_AUTO_DETECT,
DwAutoDetectFlags: winHTTP_AUTO_DETECT_TYPE_DHCP, // | winHTTP_AUTO_DETECT_TYPE_DNS_A,
}
var getProxyForURLOpts = sync.OnceValue(func() *winHTTPAutoProxyOptions {
opts := &winHTTPAutoProxyOptions{
DwFlags: winHTTP_AUTOPROXY_AUTO_DETECT,
DwAutoDetectFlags: winHTTP_AUTO_DETECT_TYPE_DHCP | winHTTP_AUTO_DETECT_TYPE_DNS_A,
}
// Support for the WINHTTP_AUTOPROXY_ALLOW_AUTOCONFIG flag was added in Windows 10, version 1703.
//
// Using it on earlier versions causes GetProxyForURL to fail with ERROR_INVALID_PARAMETER,
// which prevents proxy detection and can lead to failures reaching the control server
// on environments where a proxy is required.
//
// https://web.archive.org/web/20250529044903/https://learn.microsoft.com/en-us/windows/win32/api/winhttp/ns-winhttp-winhttp_autoproxy_options
if wingoes.IsWin10BuildOrGreater(wingoes.Win10Build1703) {
opts.DwFlags |= winHTTP_AUTOPROXY_ALLOW_AUTOCONFIG
}
return opts
})
func (hi winHTTPInternet) GetProxyForURL(urlStr string) (string, error) {
var out winHTTPProxyInfo
err := winHTTPGetProxyForURL(
hi,
windows.StringToUTF16Ptr(urlStr),
proxyForURLOpts,
getProxyForURLOpts(),
&out,
)
if err != nil {