2019-08-26 19:00:17 +02:00
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 ( ) {
2020-02-25 15:20:57 +01:00
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
}
}
}
2019-08-26 19:00:17 +02:00
2020-02-25 15:20:57 +01:00
if proxy == "" && ! autoDetect {
// Fall back to IE registry or manual detection if nothing is found there..
2019-08-26 19:00:17 +02:00
regedit , _ := readRegedit ( ) // If the syscall fails, backup to manual detection.
windowsProxyConf = parseRegedit ( regedit )
return
}
2020-02-25 15:20:57 +01:00
// Setting the proxy settings.
2019-08-26 19:00:17 +02:00
windowsProxyConf = ProxyConf {
Static : StaticProxyConf {
2020-02-25 15:20:57 +01:00
Active : len ( proxy ) > 0 ,
2019-08-26 19:00:17 +02:00
} ,
Automatic : ProxyScriptConf {
2020-02-25 15:20:57 +01:00
Active : len ( autoConfigUrl ) > 0 || autoDetect ,
2019-08-26 19:00:17 +02:00
} ,
}
if windowsProxyConf . Static . Active {
protocol := make ( map [ string ] string )
2020-02-25 15:20:57 +01:00
for _ , s := range strings . Split ( proxy , ";" ) {
2019-08-26 19:00:17 +02:00
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
2020-02-25 15:20:57 +01:00
if len ( proxyByPass ) > 0 {
windowsProxyConf . Static . NoProxy = strings . Replace ( proxyByPass , ";" , "," , - 1 )
2019-08-26 19:00:17 +02:00
}
}
if windowsProxyConf . Automatic . Active {
2020-02-25 15:20:57 +01:00
windowsProxyConf . Automatic . PreConfiguredURL = autoConfigUrl
2019-08-26 19:00:17 +02:00
}
}
func getUserConfigFromWindowsSyscall ( ) ( * tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG , error ) {
2020-02-25 15:20:57 +01:00
if err := winHttpGetIEProxyConfigForCurrentUser . Find ( ) ; err != nil {
return nil , err
2019-08-26 19:00:17 +02:00
}
2020-02-25 15:20:57 +01:00
p := new ( tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG )
r , _ , err := winHttpGetIEProxyConfigForCurrentUser . Call ( uintptr ( unsafe . Pointer ( p ) ) )
if rTrue ( r ) {
return p , nil
2019-08-26 19:00:17 +02:00
}
2020-02-25 15:20:57 +01:00
return nil , err
}
2019-08-26 19:00:17 +02:00
2020-02-25 15:20:57 +01:00
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
2019-08-26 19:00:17 +02:00
}
// 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 ) {
2020-02-25 15:20:57 +01:00
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 )
2019-08-26 19:00:17 +02:00
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
}