139 lines
3.0 KiB
Go
139 lines
3.0 KiB
Go
//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.*",
|
|
"<local>",
|
|
}, ";")
|
|
|
|
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
|
|
}
|