rclone/vendor/github.com/mattn/go-ieproxy/ieproxy_windows.go
2020-02-26 21:26:46 +00:00

221 lines
5.9 KiB
Go

package ieproxy
import (
"strings"
"sync"
"unsafe"
"golang.org/x/sys/windows/registry"
)
type regeditValues struct {
ProxyServer string
ProxyOverride string
ProxyEnable uint64
AutoConfigURL string
}
var once sync.Once
var windowsProxyConf ProxyConf
// GetConf retrieves the proxy configuration from the Windows Regedit
func getConf() ProxyConf {
once.Do(writeConf)
return windowsProxyConf
}
func writeConf() {
proxy := ""
proxyByPass := ""
autoConfigUrl := ""
autoDetect := false
// Try from IE first.
if ieCfg, err := getUserConfigFromWindowsSyscall(); err == nil {
defer globalFreeWrapper(ieCfg.lpszProxy)
defer globalFreeWrapper(ieCfg.lpszProxyBypass)
defer globalFreeWrapper(ieCfg.lpszAutoConfigUrl)
proxy = StringFromUTF16Ptr(ieCfg.lpszProxy)
proxyByPass = StringFromUTF16Ptr(ieCfg.lpszProxyBypass)
autoConfigUrl = StringFromUTF16Ptr(ieCfg.lpszAutoConfigUrl)
autoDetect = ieCfg.fAutoDetect
}
if proxy == "" { // <- new. Only fallback if we got NO proxy
// Try WinHTTP default proxy.
if defaultCfg, err := getDefaultProxyConfiguration(); err == nil {
defer globalFreeWrapper(defaultCfg.lpszProxy)
defer globalFreeWrapper(defaultCfg.lpszProxyBypass)
// Changed, next 2 lines, so if that if we always set both of these (they are a pair, it doesn't make sense to set one here and keep the value of the other from above)
newProxy := StringFromUTF16Ptr(defaultCfg.lpszProxy)
if proxy == "" {
proxy = newProxy
}
newProxyByPass := StringFromUTF16Ptr(defaultCfg.lpszProxyBypass)
if proxyByPass == "" {
proxyByPass = newProxyByPass
}
}
}
if proxy == "" && !autoDetect {
// Fall back to IE registry or manual detection if nothing is found there..
regedit, _ := readRegedit() // If the syscall fails, backup to manual detection.
windowsProxyConf = parseRegedit(regedit)
return
}
// Setting the proxy settings.
windowsProxyConf = ProxyConf{
Static: StaticProxyConf{
Active: len(proxy) > 0,
},
Automatic: ProxyScriptConf{
Active: len(autoConfigUrl) > 0 || autoDetect,
},
}
if windowsProxyConf.Static.Active {
protocol := make(map[string]string)
for _, s := range strings.Split(proxy, ";") {
s = strings.TrimSpace(s)
if s == "" {
continue
}
pair := strings.SplitN(s, "=", 2)
if len(pair) > 1 {
protocol[pair[0]] = pair[1]
} else {
protocol[""] = pair[0]
}
}
windowsProxyConf.Static.Protocols = protocol
if len(proxyByPass) > 0 {
windowsProxyConf.Static.NoProxy = strings.Replace(proxyByPass, ";", ",", -1)
}
}
if windowsProxyConf.Automatic.Active {
windowsProxyConf.Automatic.PreConfiguredURL = autoConfigUrl
}
}
func getUserConfigFromWindowsSyscall() (*tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG, error) {
if err := winHttpGetIEProxyConfigForCurrentUser.Find(); err != nil {
return nil, err
}
p := new(tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG)
r, _, err := winHttpGetIEProxyConfigForCurrentUser.Call(uintptr(unsafe.Pointer(p)))
if rTrue(r) {
return p, nil
}
return nil, err
}
func getDefaultProxyConfiguration() (*tWINHTTP_PROXY_INFO, error) {
pInfo := new(tWINHTTP_PROXY_INFO)
if err := winHttpGetDefaultProxyConfiguration.Find(); err != nil {
return nil, err
}
r, _, err := winHttpGetDefaultProxyConfiguration.Call(uintptr(unsafe.Pointer(pInfo)))
if rTrue(r) {
return pInfo, nil
}
return nil, err
}
// OverrideEnvWithStaticProxy writes new values to the
// http_proxy, https_proxy and no_proxy environment variables.
// The values are taken from the Windows Regedit (should be called in init() function)
func overrideEnvWithStaticProxy(conf ProxyConf, setenv envSetter) {
if conf.Static.Active {
for _, scheme := range []string{"http", "https"} {
url := mapFallback(scheme, "", conf.Static.Protocols)
setenv(scheme+"_proxy", url)
}
if conf.Static.NoProxy != "" {
setenv("no_proxy", conf.Static.NoProxy)
}
}
}
func parseRegedit(regedit regeditValues) ProxyConf {
protocol := make(map[string]string)
for _, s := range strings.Split(regedit.ProxyServer, ";") {
if s == "" {
continue
}
pair := strings.SplitN(s, "=", 2)
if len(pair) > 1 {
protocol[pair[0]] = pair[1]
} else {
protocol[""] = pair[0]
}
}
return ProxyConf{
Static: StaticProxyConf{
Active: regedit.ProxyEnable > 0,
Protocols: protocol,
NoProxy: strings.Replace(regedit.ProxyOverride, ";", ",", -1), // to match linux style
},
Automatic: ProxyScriptConf{
Active: regedit.AutoConfigURL != "",
PreConfiguredURL: regedit.AutoConfigURL,
},
}
}
func readRegedit() (values regeditValues, err error) {
var proxySettingsPerUser uint64 = 1 // 1 is the default value to consider current user
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE)
if err == nil {
//We had used the below variable tempPrxUsrSettings, because the Golang method GetIntegerValue
//sets the value to zero even it fails.
tempPrxUsrSettings, _, err := k.GetIntegerValue("ProxySettingsPerUser")
if err == nil {
//consider the value of tempPrxUsrSettings if it is a success
proxySettingsPerUser = tempPrxUsrSettings
}
k.Close()
}
var hkey registry.Key
if proxySettingsPerUser == 0 {
hkey = registry.LOCAL_MACHINE
} else {
hkey = registry.CURRENT_USER
}
k, err = registry.OpenKey(hkey, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE)
if err != nil {
return
}
defer k.Close()
values.ProxyServer, _, err = k.GetStringValue("ProxyServer")
if err != nil && err != registry.ErrNotExist {
return
}
values.ProxyOverride, _, err = k.GetStringValue("ProxyOverride")
if err != nil && err != registry.ErrNotExist {
return
}
values.ProxyEnable, _, err = k.GetIntegerValue("ProxyEnable")
if err != nil && err != registry.ErrNotExist {
return
}
values.AutoConfigURL, _, err = k.GetStringValue("AutoConfigURL")
if err != nil && err != registry.ErrNotExist {
return
}
err = nil
return
}