//go:build windows package sysproxy import ( "fmt" "strings" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" ) const ( internetOptionSettingsChanged = 39 internetOptionRefresh = 37 ) type windowsController struct { enabled bool hadProxy bool oldEnable uint64 oldServer string oldOverride string } func newPlatform() Controller { return &windowsController{} } func (c *windowsController) Enabled() bool { return c.enabled } func (c *windowsController) Enable(httpHostPort string) error { if httpHostPort == "" { return fmt.Errorf("sysproxy: empty http proxy address") } key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE|registry.SET_VALUE) if err != nil { return fmt.Errorf("sysproxy: open registry: %w", err) } defer key.Close() if !c.enabled { c.oldEnable, _, _ = key.GetIntegerValue("ProxyEnable") c.oldServer, _, _ = key.GetStringValue("ProxyServer") c.oldOverride, _, _ = key.GetStringValue("ProxyOverride") c.hadProxy = true } override := strings.Join([]string{ "localhost", "127.*", "10.*", "172.16.*", "172.17.*", "172.18.*", "172.19.*", "172.20.*", "172.21.*", "172.22.*", "172.23.*", "172.24.*", "172.25.*", "172.26.*", "172.27.*", "172.28.*", "172.29.*", "172.30.*", "172.31.*", "192.168.*", "", }, ";") if err := key.SetDWordValue("ProxyEnable", 1); err != nil { return err } if err := key.SetStringValue("ProxyServer", httpHostPort); err != nil { return err } if err := key.SetStringValue("ProxyOverride", override); err != nil { return err } if err := notifyInternetSettingsChanged(); err != nil { return err } // Skip netsh winhttp — spawning netsh is a common AV heuristic; WinINET covers browsers. c.enabled = true return nil } func (c *windowsController) Disable() error { if !c.enabled { return nil } key, err := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Internet Settings`, registry.QUERY_VALUE|registry.SET_VALUE) if err != nil { return err } defer key.Close() if c.hadProxy { _ = key.SetDWordValue("ProxyEnable", uint32(c.oldEnable)) if c.oldServer != "" { _ = key.SetStringValue("ProxyServer", c.oldServer) } else { _ = key.DeleteValue("ProxyServer") } if c.oldOverride != "" { _ = key.SetStringValue("ProxyOverride", c.oldOverride) } } else { _ = key.SetDWordValue("ProxyEnable", 0) } _ = notifyInternetSettingsChanged() c.enabled = false return nil } func notifyInternetSettingsChanged() error { wininet := windows.NewLazySystemDLL("wininet.dll") proc := wininet.NewProc("InternetSetOptionW") r1, _, err := proc.Call(0, uintptr(internetOptionSettingsChanged), 0, 0) if r1 == 0 { return fmt.Errorf("InternetSetOption(SETTINGS_CHANGED): %w", err) } r1, _, err = proc.Call(0, uintptr(internetOptionRefresh), 0, 0) if r1 == 0 { return fmt.Errorf("InternetSetOption(REFRESH): %w", err) } return nil }