220 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			220 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
 | |
| }
 | |
| 
 | |
| // reloadConf forces a reload of the proxy configuration from the Windows registry
 | |
| func reloadConf() ProxyConf {
 | |
| 	writeConf()
 | |
| 	return getConf()
 | |
| }
 | |
| 
 | |
| 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 == "" && !autoDetect {
 | |
| 		// Try WinHTTP default proxy.
 | |
| 		if defaultCfg, err := getDefaultProxyConfiguration(); err == nil {
 | |
| 			defer globalFreeWrapper(defaultCfg.lpszProxy)
 | |
| 			defer globalFreeWrapper(defaultCfg.lpszProxyBypass)
 | |
| 
 | |
| 			// 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)
 | |
| 			proxy = StringFromUTF16Ptr(defaultCfg.lpszProxy)
 | |
| 			proxyByPass = StringFromUTF16Ptr(defaultCfg.lpszProxyBypass)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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
 | |
| }
 |