Skip to content

Commit 7022307

Browse files
committed
实现了代理状态查询
1 parent 830c16f commit 7022307

3 files changed

Lines changed: 141 additions & 11 deletions

File tree

README.md

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
# Go System Proxy Windows系统代理配置
22

3+
[![Documentation](https://godoc.org/github.com/Trisia/gosysproxy?status.svg)](https://pkg.go.dev/github.com/Trisia/gosysproxy)
4+
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/Trisia/gosysproxy)
35

4-
通过系统调用的方式实现Windows系统的代理配置。
5-
6-
7-
修改自 C++项目,`sysproxy.exe`
8-
9-
- [Noisyfox/sysproxy](https://github.com/Noisyfox/sysproxy)
10-
6+
通过系统调用的方式实现Windows系统的代理配置、状态查询。
117

128
## Quick Start
139

@@ -32,8 +28,13 @@ func Off() error
3228

3329
// Flush 更新系统配置使生效
3430
func Flush()
31+
32+
// Status 获取当前系统代理配置
33+
func Status() (*ProxyStatus, error)
3534
```
3635

36+
详见: [《Go System Proxy 接口文档》](https://pkg.go.dev/github.com/Trisia/gosysproxy)
37+
3738
## Demo
3839

3940
```go
@@ -58,5 +59,11 @@ func main() {
5859
panic(err)
5960
}
6061
}
62+
```
63+
64+
完整示例详见: [main_test.go](main_test.go)
6165

62-
```
66+
67+
## 致谢
68+
69+
- [Noisyfox/sysproxy](https://github.com/Noisyfox/sysproxy)

main.go

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
//go:build windows
12
// +build windows
23

34
// windows系统代理配置
@@ -15,12 +16,15 @@ import (
1516
var (
1617
wininet, _ = syscall.LoadLibrary("Wininet.dll")
1718
internetSetOption, _ = syscall.GetProcAddress(wininet, "InternetSetOptionW")
19+
// https://learn.microsoft.com/zh-cn/windows/win32/api/wininet/nf-wininet-internetqueryoptionw
20+
internetQueryOption, _ = syscall.GetProcAddress(wininet, "InternetQueryOptionA")
1821
)
1922

2023
const (
2124
_INTERNET_OPTION_PER_CONNECTION_OPTION = 75
2225
_INTERNET_OPTION_PROXY_SETTINGS_CHANGED = 95
2326
_INTERNET_OPTION_REFRESH = 37
27+
_INTERNET_OPTION_PROXY = 38
2428
)
2529

2630
const (
@@ -43,6 +47,12 @@ const (
4347
_INTERNET_PER_CONN_FLAGS_UI = 10
4448
)
4549

50+
const (
51+
INTERNET_OPEN_TYPE_PRECONFIG = 0 // use registry configuration
52+
INTERNET_OPEN_TYPE_DIRECT = 1 // 禁用代理 direct to net
53+
INTERNET_OPEN_TYPE_PROXY = 3 // 启用代理 via named proxy
54+
)
55+
4656
type internetPerConnOptionList struct {
4757
dwSize uint32
4858
pszConnection *uint16
@@ -56,6 +66,30 @@ type internetPreConnOption struct {
5666
value uint64
5767
}
5868

69+
// internetProxyInfo https://learn.microsoft.com/zh-cn/windows/win32/api/wininet/ns-wininet-internet_proxy_info
70+
type internetProxyInfo struct {
71+
dwAccessType uint32
72+
lpszProxy *uint16
73+
lpszProxyBypass *uint16
74+
}
75+
76+
type ProxyStatus struct {
77+
// 代理类型
78+
// - 0: INTERNET_OPEN_TYPE_PRECONFIG: use registry configuration
79+
// - 1: INTERNET_OPEN_TYPE_DIRECT: 不代理 direct to net
80+
// - 3: INTERNET_OPEN_TYPE_PROXY: 使用代理服务器 via named proxy
81+
Type uint32
82+
Proxy string // 代理IP地址与端口,IP:Port,例如:"127.0.0.1:7890"
83+
// 请勿对以下列条目开头的地址使用代理服务器
84+
// 注意:
85+
// - 这里的地址是ASCII编码
86+
// - "<local>" 表示 本地(Intranet)地址,如果包含 "<local>" 则 DisableProxyIntranet 为 true
87+
//
88+
// 例如:["localhost","127.*"],
89+
Bypass []string
90+
DisableProxyIntranet bool // 请勿将代理服务器用于本地(Intranet)地址
91+
}
92+
5993
// stringPtrAddr 获取C字符串(UTF16)的数组第一个位置的地址
6094
func stringPtrAddr(str string) (uint64, error) {
6195
scriptLocPtr, err := syscall.UTF16PtrFromString(str)
@@ -114,8 +148,8 @@ func SetPAC(scriptLoc string) error {
114148
}
115149

116150
// SetGlobalProxy 设置全局代理
117-
// proxyServer: 代理服务器host:port,例如: "127.0.0.1:7890"
118-
// bypass: 忽略代理列表,这些配置项开头的地址不进行代理
151+
// - proxyServer: 代理服务器 host:port,例如: "127.0.0.1:7890"
152+
// - bypass: 忽略代理列表,这些配置项开头的地址不进行代理,若包含 "<local>" 则 ”请勿将代理服务器用于本地(Intranet)地址“ 将勾选。
119153
func SetGlobalProxy(proxyServer string, bypasses ...string) error {
120154
if proxyServer == "" {
121155
return errors.New("代理服务器(proxyServer)配置为空")
@@ -210,3 +244,50 @@ func Flush() error {
210244
}
211245
return nil
212246
}
247+
248+
// Status 获取当前系统代理配置
249+
func Status() (*ProxyStatus, error) {
250+
var bufferLength uint32 = 1024 * 10
251+
buffer := make([]byte, bufferLength)
252+
ret, _, infoPtr := syscall.Syscall6(internetQueryOption,
253+
4,
254+
0,
255+
_INTERNET_OPTION_PROXY,
256+
uintptr(unsafe.Pointer(&buffer[0])), uintptr(unsafe.Pointer(&bufferLength)),
257+
0, 0)
258+
if ret != 1 {
259+
return nil, errors.New(fmt.Sprintf("%s", infoPtr))
260+
}
261+
//fmt.Println(hex.Dump(buffer[:bufferLength]))
262+
proxyInfo := (*internetProxyInfo)(unsafe.Pointer(&buffer[0]))
263+
res := &ProxyStatus{
264+
Type: proxyInfo.dwAccessType,
265+
Proxy: asciiPtrToString(proxyInfo.lpszProxy),
266+
}
267+
bypassArr := asciiPtrToString(proxyInfo.lpszProxyBypass)
268+
res.Bypass = strings.SplitN(bypassArr, " ", -1)
269+
for _, bypass := range res.Bypass {
270+
if bypass == "<local>" {
271+
res.DisableProxyIntranet = true
272+
break
273+
}
274+
}
275+
return res, nil
276+
}
277+
278+
// ASCIIPtrToString 将UTF16指针转换为ASCII字符串
279+
func asciiPtrToString(p *uint16) string {
280+
if p == nil {
281+
return ""
282+
}
283+
res := []byte{}
284+
end := unsafe.Pointer(p)
285+
for {
286+
if *(*uint8)(end) == 0 {
287+
break
288+
}
289+
res = append(res, *(*uint8)(end))
290+
end = unsafe.Pointer(uintptr(end) + 1)
291+
}
292+
return string(res)
293+
}

main_test.go

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
//go:build windows
12
// +build windows
23

34
package gosysproxy
45

5-
import "testing"
6+
import (
7+
"fmt"
8+
"testing"
9+
)
610

711
func TestFlush(t *testing.T) {
812
err := Flush()
@@ -46,3 +50,41 @@ func TestSetGlobalProxy(t *testing.T) {
4650
t.Fatal(err)
4751
}
4852
}
53+
54+
func TestStatus(t *testing.T) {
55+
err := SetGlobalProxy("127.0.0.1:7890")
56+
if err != nil {
57+
t.Fatal(err)
58+
}
59+
60+
err = Off()
61+
if err != nil {
62+
t.Fatal(err)
63+
}
64+
65+
err = SetGlobalProxy("127.0.0.1:7890", "foo", "bar", "<local>")
66+
if err != nil {
67+
t.Fatal(err)
68+
}
69+
res, err := Status()
70+
if err != nil {
71+
t.Fatal(err)
72+
}
73+
switch res.Type {
74+
case INTERNET_OPEN_TYPE_PRECONFIG:
75+
fmt.Println(">> 代理方式:", "use registry configuration")
76+
case INTERNET_OPEN_TYPE_DIRECT:
77+
fmt.Println(">> 代理方式:", "不代理 direct to net")
78+
case INTERNET_OPEN_TYPE_PROXY:
79+
fmt.Println(">> 代理方式:", "使用代理服务器 via named proxy")
80+
}
81+
fmt.Println(">> 代理地址:", res.Proxy)
82+
fmt.Println(">> 请勿对以下列条目开头的地址使用代理服务器:", res.Bypass)
83+
fmt.Println(">> 请勿将代理服务器用于本地(Intranet)地址:", res.DisableProxyIntranet)
84+
85+
err = Off()
86+
if err != nil {
87+
t.Fatal(err)
88+
}
89+
90+
}

0 commit comments

Comments
 (0)