1package ieproxy 2 3import ( 4 "strings" 5 "sync" 6 "unsafe" 7 8 "golang.org/x/sys/windows/registry" 9) 10 11type regeditValues struct { 12 ProxyServer string 13 ProxyOverride string 14 ProxyEnable uint64 15 AutoConfigURL string 16} 17 18var once sync.Once 19var windowsProxyConf ProxyConf 20 21// GetConf retrieves the proxy configuration from the Windows Regedit 22func getConf() ProxyConf { 23 once.Do(writeConf) 24 return windowsProxyConf 25} 26 27func writeConf() { 28 proxy := "" 29 proxyByPass := "" 30 autoConfigUrl := "" 31 autoDetect := false 32 33 // Try from IE first. 34 if ieCfg, err := getUserConfigFromWindowsSyscall(); err == nil { 35 defer globalFreeWrapper(ieCfg.lpszProxy) 36 defer globalFreeWrapper(ieCfg.lpszProxyBypass) 37 defer globalFreeWrapper(ieCfg.lpszAutoConfigUrl) 38 39 proxy = StringFromUTF16Ptr(ieCfg.lpszProxy) 40 proxyByPass = StringFromUTF16Ptr(ieCfg.lpszProxyBypass) 41 autoConfigUrl = StringFromUTF16Ptr(ieCfg.lpszAutoConfigUrl) 42 autoDetect = ieCfg.fAutoDetect 43 } 44 45 if proxy == "" && !autoDetect{ 46 // Try WinHTTP default proxy. 47 if defaultCfg, err := getDefaultProxyConfiguration(); err == nil { 48 defer globalFreeWrapper(defaultCfg.lpszProxy) 49 defer globalFreeWrapper(defaultCfg.lpszProxyBypass) 50 51 // 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) 52 proxy = StringFromUTF16Ptr(defaultCfg.lpszProxy) 53 proxyByPass = StringFromUTF16Ptr(defaultCfg.lpszProxyBypass) 54 } 55 } 56 57 if proxy == "" && !autoDetect { 58 // Fall back to IE registry or manual detection if nothing is found there.. 59 regedit, _ := readRegedit() // If the syscall fails, backup to manual detection. 60 windowsProxyConf = parseRegedit(regedit) 61 return 62 } 63 64 // Setting the proxy settings. 65 windowsProxyConf = ProxyConf{ 66 Static: StaticProxyConf{ 67 Active: len(proxy) > 0, 68 }, 69 Automatic: ProxyScriptConf{ 70 Active: len(autoConfigUrl) > 0 || autoDetect, 71 }, 72 } 73 74 if windowsProxyConf.Static.Active { 75 protocol := make(map[string]string) 76 for _, s := range strings.Split(proxy, ";") { 77 s = strings.TrimSpace(s) 78 if s == "" { 79 continue 80 } 81 pair := strings.SplitN(s, "=", 2) 82 if len(pair) > 1 { 83 protocol[pair[0]] = pair[1] 84 } else { 85 protocol[""] = pair[0] 86 } 87 } 88 89 windowsProxyConf.Static.Protocols = protocol 90 if len(proxyByPass) > 0 { 91 windowsProxyConf.Static.NoProxy = strings.Replace(proxyByPass, ";", ",", -1) 92 } 93 } 94 95 if windowsProxyConf.Automatic.Active { 96 windowsProxyConf.Automatic.PreConfiguredURL = autoConfigUrl 97 } 98} 99 100func getUserConfigFromWindowsSyscall() (*tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG, error) { 101 if err := winHttpGetIEProxyConfigForCurrentUser.Find(); err != nil { 102 return nil, err 103 } 104 p := new(tWINHTTP_CURRENT_USER_IE_PROXY_CONFIG) 105 r, _, err := winHttpGetIEProxyConfigForCurrentUser.Call(uintptr(unsafe.Pointer(p))) 106 if rTrue(r) { 107 return p, nil 108 } 109 return nil, err 110} 111 112func getDefaultProxyConfiguration() (*tWINHTTP_PROXY_INFO, error) { 113 pInfo := new(tWINHTTP_PROXY_INFO) 114 if err := winHttpGetDefaultProxyConfiguration.Find(); err != nil { 115 return nil, err 116 } 117 r, _, err := winHttpGetDefaultProxyConfiguration.Call(uintptr(unsafe.Pointer(pInfo))) 118 if rTrue(r) { 119 return pInfo, nil 120 } 121 return nil, err 122} 123 124// OverrideEnvWithStaticProxy writes new values to the 125// http_proxy, https_proxy and no_proxy environment variables. 126// The values are taken from the Windows Regedit (should be called in init() function) 127func overrideEnvWithStaticProxy(conf ProxyConf, setenv envSetter) { 128 if conf.Static.Active { 129 for _, scheme := range []string{"http", "https"} { 130 url := mapFallback(scheme, "", conf.Static.Protocols) 131 setenv(scheme+"_proxy", url) 132 } 133 if conf.Static.NoProxy != "" { 134 setenv("no_proxy", conf.Static.NoProxy) 135 } 136 } 137} 138 139func parseRegedit(regedit regeditValues) ProxyConf { 140 protocol := make(map[string]string) 141 for _, s := range strings.Split(regedit.ProxyServer, ";") { 142 if s == "" { 143 continue 144 } 145 pair := strings.SplitN(s, "=", 2) 146 if len(pair) > 1 { 147 protocol[pair[0]] = pair[1] 148 } else { 149 protocol[""] = pair[0] 150 } 151 } 152 153 return ProxyConf{ 154 Static: StaticProxyConf{ 155 Active: regedit.ProxyEnable > 0, 156 Protocols: protocol, 157 NoProxy: strings.Replace(regedit.ProxyOverride, ";", ",", -1), // to match linux style 158 }, 159 Automatic: ProxyScriptConf{ 160 Active: regedit.AutoConfigURL != "", 161 PreConfiguredURL: regedit.AutoConfigURL, 162 }, 163 } 164} 165 166func readRegedit() (values regeditValues, err error) { 167 var proxySettingsPerUser uint64 = 1 // 1 is the default value to consider current user 168 k, err := registry.OpenKey(registry.LOCAL_MACHINE, `Software\Policies\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE) 169 if err == nil { 170 //We had used the below variable tempPrxUsrSettings, because the Golang method GetIntegerValue 171 //sets the value to zero even it fails. 172 tempPrxUsrSettings, _, err := k.GetIntegerValue("ProxySettingsPerUser") 173 if err == nil { 174 //consider the value of tempPrxUsrSettings if it is a success 175 proxySettingsPerUser = tempPrxUsrSettings 176 } 177 k.Close() 178 } 179 180 var hkey registry.Key 181 if proxySettingsPerUser == 0 { 182 hkey = registry.LOCAL_MACHINE 183 } else { 184 hkey = registry.CURRENT_USER 185 } 186 187 k, err = registry.OpenKey(hkey, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE) 188 if err != nil { 189 return 190 } 191 defer k.Close() 192 193 values.ProxyServer, _, err = k.GetStringValue("ProxyServer") 194 if err != nil && err != registry.ErrNotExist { 195 return 196 } 197 values.ProxyOverride, _, err = k.GetStringValue("ProxyOverride") 198 if err != nil && err != registry.ErrNotExist { 199 return 200 } 201 202 values.ProxyEnable, _, err = k.GetIntegerValue("ProxyEnable") 203 if err != nil && err != registry.ErrNotExist { 204 return 205 } 206 207 values.AutoConfigURL, _, err = k.GetStringValue("AutoConfigURL") 208 if err != nil && err != registry.ErrNotExist { 209 return 210 } 211 err = nil 212 return 213} 214