Updates tailscale/corp#553 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>main
parent
1835bb6f85
commit
c5eb57f4d6
@ -0,0 +1,33 @@ |
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Package tshttpproxy contains Tailscale additions to httpproxy not available
|
||||
// in golang.org/x/net/http/httpproxy. Notably, it aims to support Windows better.
|
||||
package tshttpproxy |
||||
|
||||
import ( |
||||
"net/http" |
||||
"net/url" |
||||
) |
||||
|
||||
// sysProxyFromEnv, if non-nil, specifies a platform-specific ProxyFromEnvironment
|
||||
// func to use if http.ProxyFromEnvironment doesn't return a proxy.
|
||||
// For example, WPAD PAC files on Windows.
|
||||
var sysProxyFromEnv func(*http.Request) (*url.URL, error) |
||||
|
||||
func ProxyFromEnvironment(req *http.Request) (*url.URL, error) { |
||||
u, err := http.ProxyFromEnvironment(req) |
||||
if u != nil && err == nil { |
||||
return u, nil |
||||
} |
||||
|
||||
if sysProxyFromEnv != nil { |
||||
u, err := sysProxyFromEnv(req) |
||||
if u != nil && err == nil { |
||||
return u, nil |
||||
} |
||||
} |
||||
|
||||
return nil, err |
||||
} |
||||
@ -0,0 +1,142 @@ |
||||
// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package tshttpproxy |
||||
|
||||
import ( |
||||
"log" |
||||
"net/http" |
||||
"net/url" |
||||
"strings" |
||||
"syscall" |
||||
"unsafe" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
) |
||||
|
||||
var ( |
||||
winHTTP = windows.NewLazySystemDLL("winhttp.dll") |
||||
httpOpenProc = winHTTP.NewProc("WinHttpOpen") |
||||
closeHandleProc = winHTTP.NewProc("WinHttpCloseHandle") |
||||
getProxyForUrlProc = winHTTP.NewProc("WinHttpGetProxyForUrl") |
||||
) |
||||
|
||||
func init() { |
||||
sysProxyFromEnv = proxyFromWinHTTP |
||||
} |
||||
|
||||
func proxyFromWinHTTP(req *http.Request) (*url.URL, error) { |
||||
if req.URL == nil { |
||||
return nil, nil |
||||
} |
||||
urlStr := req.URL.String() |
||||
|
||||
whi, err := winHTTPOpen() |
||||
if err != nil { |
||||
// Log but otherwise ignore the error.
|
||||
log.Printf("winhttp: Open: %v", err) |
||||
return nil, nil |
||||
} |
||||
defer whi.Close() |
||||
|
||||
v, err := whi.GetProxyForURL(urlStr) |
||||
if err != nil { |
||||
// See https://docs.microsoft.com/en-us/windows/win32/winhttp/error-messages
|
||||
const ERROR_WINHTTP_AUTODETECTION_FAILED = 12180 |
||||
if err == syscall.Errno(ERROR_WINHTTP_AUTODETECTION_FAILED) { |
||||
return nil, nil |
||||
} |
||||
log.Printf("winhttp: GetProxyForURL(%q): %v (%T, %#v)", urlStr, err, err, err) |
||||
return nil, nil |
||||
} |
||||
if v != "" { |
||||
if !strings.HasPrefix(v, "https://") { |
||||
v = "http://" + v |
||||
} |
||||
if u, err := url.Parse(v); err == nil { |
||||
return u, nil |
||||
} |
||||
} |
||||
|
||||
return nil, nil |
||||
} |
||||
|
||||
var userAgent = windows.StringToUTF16Ptr("Tailscale") |
||||
|
||||
const ( |
||||
winHTTP_ACCESS_TYPE_AUTOMATIC_PROXY = 4 |
||||
winHTTP_AUTOPROXY_ALLOW_AUTOCONFIG = 0x00000100 |
||||
winHTTP_AUTOPROXY_AUTO_DETECT = 1 |
||||
winHTTP_AUTO_DETECT_TYPE_DHCP = 0x00000001 |
||||
winHTTP_AUTO_DETECT_TYPE_DNS_A = 0x00000002 |
||||
) |
||||
|
||||
func winHTTPOpen() (winHTTPInternet, error) { |
||||
if err := httpOpenProc.Find(); err != nil { |
||||
return 0, err |
||||
} |
||||
r, _, err := httpOpenProc.Call( |
||||
uintptr(unsafe.Pointer(userAgent)), |
||||
winHTTP_ACCESS_TYPE_AUTOMATIC_PROXY, |
||||
0, /* WINHTTP_NO_PROXY_NAME */ |
||||
0, /* WINHTTP_NO_PROXY_BYPASS */ |
||||
0) |
||||
if r == 0 { |
||||
return 0, err |
||||
} |
||||
return winHTTPInternet(r), nil |
||||
} |
||||
|
||||
type winHTTPInternet windows.Handle |
||||
|
||||
func (hi winHTTPInternet) Close() error { |
||||
if err := closeHandleProc.Find(); err != nil { |
||||
return err |
||||
} |
||||
r, _, err := closeHandleProc.Call(uintptr(hi)) |
||||
if r == 1 { |
||||
return nil |
||||
} |
||||
return err |
||||
} |
||||
|
||||
// WINHTTP_AUTOPROXY_OPTIONS
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winhttp/ns-winhttp-winhttp_autoproxy_options
|
||||
type autoProxyOptions struct { |
||||
DwFlags uint32 |
||||
DwAutoDetectFlags uint32 |
||||
AutoConfigUrl *uint16 |
||||
_ uintptr |
||||
_ uint32 |
||||
FAutoLogonIfChallenged bool |
||||
} |
||||
|
||||
// WINHTTP_PROXY_INFO
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/winhttp/ns-winhttp-winhttp_proxy_info
|
||||
type winHTTPProxyInfo struct { |
||||
AccessType uint16 |
||||
Proxy *uint16 |
||||
ProxyBypass *uint16 |
||||
} |
||||
|
||||
var proxyForURLOpts = &autoProxyOptions{ |
||||
DwFlags: winHTTP_AUTOPROXY_ALLOW_AUTOCONFIG | winHTTP_AUTOPROXY_AUTO_DETECT, |
||||
DwAutoDetectFlags: winHTTP_AUTO_DETECT_TYPE_DHCP, // | winHTTP_AUTO_DETECT_TYPE_DNS_A,
|
||||
} |
||||
|
||||
func (hi winHTTPInternet) GetProxyForURL(urlStr string) (string, error) { |
||||
if err := getProxyForUrlProc.Find(); err != nil { |
||||
return "", err |
||||
} |
||||
var out winHTTPProxyInfo |
||||
r, _, err := getProxyForUrlProc.Call( |
||||
uintptr(hi), |
||||
uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(urlStr))), |
||||
uintptr(unsafe.Pointer(proxyForURLOpts)), |
||||
uintptr(unsafe.Pointer(&out))) |
||||
if r == 1 { |
||||
return windows.UTF16PtrToString(out.Proxy), nil |
||||
} |
||||
return "", err |
||||
} |
||||
Loading…
Reference in new issue