2025-11-06

This commit is contained in:
Qin tian
2025-11-06 06:31:01 +08:00
parent 8220474f90
commit 191a092cd5
18 changed files with 1012 additions and 2237 deletions

View File

@@ -1,10 +0,0 @@
@echo off
set CGO_ENABLED=1
set GOOS=windows
set GOARCH=386
set tmpPath=%~dp0
cd %tmpPath:~0,1%:
for %%I in ("%tmpPath%..\") do set "parentPath=%%~fI"
cd %parentPath%
go build -trimpath -buildmode=c-shared -tags mini -ldflags "-s -w" -o "G:\Sunny\SunnyNet\BuildLibrary\SunnyNet.dll"
@echo on

View File

@@ -0,0 +1,20 @@
echo "" > /dev/null
echo "正在编译..."
set CGO_ENABLED=1
set GOOS=darwin
set GOARCH=amd64
tmpPath=$(dirname "$(readlink -f "$0")")
parentPath=$(dirname "$tmpPath")
cd "$parentPath"
go build -trimpath -buildmode=c-shared -ldflags "-s -w" -o "$tmpPath/Library/darwin/amd64/SunnyNet.dylib"
# 检查命令的退出状态码
if [ $? -ne 0 ]; then
echo ""
echo ""
echo "编译失败!"
else
echo ""
echo ""
echo "编译完成!"
fi

View File

@@ -0,0 +1,20 @@
echo "" > /dev/null
echo "正在编译..."
set CGO_ENABLED=1
set GOOS=darwin
set GOARCH=arm64
tmpPath=$(dirname "$(readlink -f "$0")")
parentPath=$(dirname "$tmpPath")
cd "$parentPath"
go build -trimpath -buildmode=c-shared -ldflags "-s -w" -o "$tmpPath/Library/darwin/arm64/SunnyNet.dylib"
# 检查命令的退出状态码
if [ $? -ne 0 ]; then
echo ""
echo ""
echo "编译失败!"
else
echo ""
echo ""
echo "编译完成!"
fi

Binary file not shown.

File diff suppressed because it is too large Load Diff

295
README.md
View File

@@ -1,205 +1,120 @@
# Sunny网络中间件 SDK 文档
<div style="text-align: center;"><h3><a style="color: red;">请注意:由于本仓库历史记录太大</a></div></h3></center>
<div style="text-align: center;"><h3><a style="color: red;">本仓库于 2025-04-24 删除重建</a></div></h3></center>
<div align="center">
<svg xmlns="http://www.w3.org/2000/svg" width="210" height="20" role="img" aria-label="Platform: Windows | Linux | macOS"><title>Platform: Windows | Linux | macOS</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="210" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="57" height="20" fill="#555"/><rect x="57" width="153" height="20" fill="#007ec6"/><rect width="210" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="295" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="470">Platform</text><text x="295" y="140" transform="scale(.1)" fill="#fff" textLength="470">Platform</text><text aria-hidden="true" x="1325" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="1430">Windows | Linux | macOS</text><text x="1325" y="140" transform="scale(.1)" fill="#fff" textLength="1430">Windows | Linux | macOS</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="78" height="20" role="img" aria-label="Go: >=1.16"><title>Go: >=1.16</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="78" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="25" height="20" fill="#555"/><rect x="25" width="53" height="20" fill="#97ca00"/><rect width="78" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="135" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="150">Go</text><text x="135" y="140" transform="scale(.1)" fill="#fff" textLength="150">Go</text><text aria-hidden="true" x="505" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="430">>=1.16</text><text x="505" y="140" transform="scale(.1)" fill="#fff" textLength="430">>=1.16</text></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" width="82" height="20" role="img" aria-label="License: MIT"><title>License: MIT</title><linearGradient id="s" x2="0" y2="100%"><stop offset="0" stop-color="#bbb" stop-opacity=".1"/><stop offset="1" stop-opacity=".1"/></linearGradient><clipPath id="r"><rect width="82" height="20" rx="3" fill="#fff"/></clipPath><g clip-path="url(#r)"><rect width="51" height="20" fill="#555"/><rect x="51" width="31" height="20" fill="#dfb317"/><rect width="82" height="20" fill="url(#s)"/></g><g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" text-rendering="geometricPrecision" font-size="110"><text aria-hidden="true" x="265" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="410">License</text><text x="265" y="140" transform="scale(.1)" fill="#fff" textLength="410">License</text><text aria-hidden="true" x="655" y="150" fill="#010101" fill-opacity=".3" transform="scale(.1)" textLength="210">MIT</text><text x="655" y="140" transform="scale(.1)" fill="#fff" textLength="210">MIT</text></g></svg>
</div>
<div align="center">
<h3>跨平台网络分析组件 SDK</h3>
<p>类似 Fiddler 的网络中间件,支持 HTTP/HTTPS/WS/WSS/TCP/UDP 网络分析</p>
</div>
## 📌 重要通知
<div align="center">
<h3><span style="color: red;">请注意: 由于本仓库历史记录太大</span></h3>
<h3><span style="color: red;">本仓库于 2025-04-24 删除重建</span></h3>
</div>
## 🌟 项目简介
Sunny网络中间件是一个功能强大的跨平台网络分析组件专为二次开发而设计。它提供了完整的网络流量捕获和修改功能支持多种协议类型。
## 🚀 主要特性
-**多协议支持**: HTTP/HTTPS/WS/WSS/TCP/UDP 网络分析
-**数据获取与修改**: 可获取和修改所有协议的发送及返回数据
-**代理设置**: 可为指定连接设置独立代理
-**连接重定向**: 支持 HTTP/HTTPS/WS/WSS/TCP/TLS-TCP 链接重定向
-**数据解码**: 支持 gzip, deflate, br, zstd 解码
-**主动发送**: 支持 WS/WSS/TCP/TLS-TCP/UDP 主动发送数据
-**跨平台**: 支持 Windows、Linux 和 macOS
-**脚本支持**: 支持通过Go脚本自定义处理逻辑
## 🚦 多驱动支持
| 驱动名称 | 平台 | 127.0.0.1捕获 | 内网捕获 | 兼容性 |
|---------|------|-------------|------|-------|
| Netfilter | Windows | ✅ | ✅ | 一般 |
| Proxifier | Windows | ✅ | ✅ | 一般 |
| Tun(WinDivert) | Windows | ❌ | ✅ | 较好 |
| Tun(VPN) | Android | ✅ | ✅ | 较好 |
| Tun(utun) | MacOs | ❌ | ❌ | 较好 |
| Tun(tun) | Linux | ❌ | ❌ | 较好 |
## 📚 SDK API 参考
有关Go语言环境下使用SunnyNet的详细示例请参考 [Go语言使用示例](README_go.md) 文档。
完整的API参考文档请查看 [API参考文档](README_api.md)。
## ⚙️ 使用说明
### 系统要求
- Windows 7 及以上版本(使用 Go 1.21 以下版本编译)
- Windows 10/11 推荐(支持最新 Go 版本)
- Linux / macOS 最新稳定版本
# <center><h3>Sunny网络中间件</center></h3></center>
---
> Sunny网络中间件 和 Fiddler 类似。 是可跨平台的网络分析组件
```log
可用于HTTP/HTTPS/WS/WSS/TCP/UDP网络分析 为二次开发量身制作
支持 获取/修改 HTTP/HTTPS/WS/WSS/TCP/TLS-TCP/UDP 发送及返回数据
## 🛠 编译说明
### Windows 编译步骤
1. 安装 [TDM-GCC](https://github.com/jmeubank/tdm-gcc/releases/download/v10.3.0-tdm64-2/tdm64-gcc-10.3.0-2.exe)
2. 进入到 SunnyNet 目录
3. 执行命令 `.\BuildLibrary\BuildALL.bat`
### Linux 编译步骤
1. 确保已安装 GCC 工具链
2. 进入到 SunnyNet 目录
3. 执行命令 `.\BuildLibrary\Linux64.sh`
4. 或 执行命令 `.\BuildLibrary\Linux32.sh`
### macOS 编译步骤
1. 确保已安装 GCC 工具链
2. 进入到 SunnyNet 目录
3. 执行命令 `.\BuildLibrary\MacOs_amd64.sh`
4. 或 执行命令 `.\BuildLibrary\MacOs_arm64.sh`
支持 对 HTTP/HTTPS/WS/WSS 指定连接使用指定代理
## 📨 BUG 反馈与技术支持
<p>项目网站: <a href="https://esunny.vip/">https://esunny.vip/</a></p>
支持 对 HTTP/HTTPS/WS/WSS/TCP/TLS-TCP 链接重定向
支持 gzip, deflate, br, zstd 解码
支持 WS/WSS/TCP/TLS-TCP/UDP 主动发送数据
```
<p><strong>QQ群:</strong></p>
---
* # 由于代码主要是做DLL使用,部分功能未封装给Go使用请自行探索
* # 如需支持Win7系统
* # 请使用Go1.21以下版本编译,例如 go 1.20.4版本
* # <a href="https://github.com/jmeubank/tdm-gcc/releases/download/v10.3.0-tdm64-2/tdm64-gcc-10.3.0-2.exe">编译请使用 TDM-GCC</a>
<div style="text-align: center;"><h2><a style="color: red;">BUG 反馈</a></h2></div>
<div style="text-align: center;"><h3>QQ群:</h3></div>
<div style="text-align: center;"><h3>一群751406884</h3></div>
<div style="text-align: center;"><h3>二群545120699</h3></div>
<div style="text-align: center;"><h3>三群170902713</h3></div>
<div style="text-align: center;"><h3>四群1070797457</h3></div>
<div style="text-align: center;"><h3>网址:<a href="https://esunny.vip/">https://esunny.vip/</a></h3></div>
<ul>
---
<li>一群751406884</li>
### <center><h3>各语言,示例文件以及抓包工具 下载地址 </center>
<div style="text-align: center;"><h3>https://wwxa.lanzouu.com/b02p4aet8j</h3></div>
<div style="text-align: center;"><h3>密码:4h7r</h3></div>
<div style="text-align: center;"><h3></h3></div>
<li>二群545120699</li>
<li>三群170902713</li>
<li>四群1070797457</li>
</ul>
## 📦 下载资源
---
- > GoLang使用示例代码
```golang
package main
<p>各语言示例文件以及抓包工具下载地址:</p>
import (
"github.com/qtgolang/SunnyNet/SunnyNet"
"github.com/qtgolang/SunnyNet/src/public"
"time"
"log"
"fmt"
)
func main() {
var Sunny = SunnyNet.NewSunny()
/*
//载入自定义证书
cert := SunnyNet.NewCertManager()
ok := cert.LoadP12Certificate("C:\\Users\\Qin\\Desktop\\Cert\\ca6afc5aa40fcbd3.p12", "GXjc75IRAO0T")
fmt.Println("载入P12:", ok)
fmt.Println("证书名称:", cert.GetCommonName())
<p>🔗 <a href="https://wwxa.lanzouu.com/b02p4aet8j">https://wwxa.lanzouu.com/b02p4aet8j</a></p>
//给指定域名使用这个证书
Sunny.AddHttpCertificate("api.vlightv.com", cert, SunnyNet.HTTPCertRules_Request)
<p><strong>密码:</strong> 4h7r</p>
*/
## ⚠️ 注意事项
/*
log := func(Context int, info ...any) {
fmt.Println("x脚本日志", fmt.Sprintf("%v", info))
}
save := func(Context int, code []byte) {
//在这里将code代码 储存到文件,下次启动时,载入恢复
}
Sunny.SetScriptCall(log, save)
//载入上次保存的脚本代码
Sunny.SetScriptCode(string(GoScriptCode.DefaultCode))
*/
/*
//设置全局上游代理
Sunny.SetGlobalProxy("socket://192.168.31.1:4321", 60000)
//指定IP或域名不使用全局的上游代理
Sunny.CompileProxyRegexp("127.0.0.1;[::1];192.168.*;*.baidu.com")
*/
/*
//开启强制走TCP,开启后 https 将不会解密 直接转发数据流量
Sunny.MustTcp(true)
*/
/*
//禁止TCP所有TCP流量将直接断开连接
Sunny.DisableTCP(true)
*/
/*
//设置强制走TCP规则使用这个函数后 就不要使用 Sunny.MustTcp(true) 否则这个函数无效
Sunny.SetMustTcpRegexp("tpstelemetry.tencent.com", true)
*/
/*
//使用驱动抓包 (两个驱动各有特点自行尝试,哪个能用/好用 用哪个)
Sunny.OpenDrive(true) // 使用 NFAPI 驱动
Sunny.OpenDrive(false) // 使用 Proxifier 驱动 不支持32位操作系统不支持UDP数据捕获
Sunny.ProcessAddName("gamemon.des") //添加指定进程名称
Sunny.ProcessDelName("gamemon.des") //删除已添加的指定进程名称
Sunny.ProcessAddPid(1122) //添加指定进程PID
Sunny.ProcessDelPid(1122) //删除已添加的指定进程PID
Sunny.ProcessCancelAll() //删除已添加的所有进程名称/PID
Sunny.ProcessALLName(true, false) //捕获全部进程开始后,添加进程名称-PID无效
*/
//设置回调地址
Sunny.SetGoCallback(HttpCallback, TcpCallback, WSCallback, UdpCallback)
Port := 2025
Sunny.SetPort(Port).Start()
err := Sunny.Error
if err != nil {
panic(err)
}
fmt.Println("Run Port=", Port)
//阻止程序退出
select {}
}
func HttpCallback(Conn SunnyNet.ConnHTTP) {
if Conn.Type() == public.HttpSendRequest {
//fmt.Println(Conn.URL())
//发起请求
//直接响应,不让其发送请求
//Conn.StopRequest(200, "Hello Word")
} else if Conn.Type() == public.HttpResponseOK {
//请求完成
//log.Println("Call", Conn.URL())
} else if Conn.Type() == public.HttpRequestFail {
//请求错误
/* fmt.Println(Conn.Request.URL.String(), Conn.GetError())
*/
}
}
func WSCallback(Conn SunnyNet.ConnWebSocket) {
log.Println("WebSocket", Conn.URL())
}
func TcpCallback(Conn SunnyNet.ConnTCP) {
if Conn.Type() == public.SunnyNetMsgTypeTCPAboutToConnect {
//即将连接
mode := string(Conn.Body())
log.Println("PID", Conn.PID(), "TCP 即将连接到:", mode, Conn.LocalAddress(), "->", Conn.RemoteAddress())
//修改目标连接地址
//Conn.SetNewAddress("8.8.8.8:8080")
return
}
if Conn.Type() == public.SunnyNetMsgTypeTCPConnectOK {
log.Println("PID", Conn.PID(), "TCP 连接到:", Conn.LocalAddress(), "->", Conn.RemoteAddress(), "成功")
return
}
if Conn.Type() == public.SunnyNetMsgTypeTCPClose {
log.Println("PID", Conn.PID(), "TCP 断开连接:", Conn.LocalAddress(), "->", Conn.RemoteAddress())
return
}
if Conn.Type() == public.SunnyNetMsgTypeTCPClientSend {
log.Println("PID", Conn.PID(), "发送数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), Conn.Body())
return
}
if Conn.Type() == public.SunnyNetMsgTypeTCPClientReceive {
log.Println("PID", Conn.PID(), "收到数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), Conn.Body())
return
}
}
func UdpCallback(Conn SunnyNet.ConnUDP) {
if Conn.Type() == public.SunnyNetUDPTypeSend {
//客户端向服务器端发送数据
log.Println("PID", Conn.PID(), "发送UDP", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.BodyLen())
//修改发送的数据
//Conn.SetBody([]byte("Hello Word"))
return
}
if Conn.Type() == public.SunnyNetUDPTypeReceive {
//服务器端向客户端发送数据
log.Println("PID", Conn.PID(), "接收UDP", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.BodyLen())
//修改响应的数据
//Conn.SetBody([]byte("Hello Word"))
return
}
if Conn.Type() == public.SunnyNetUDPTypeClosed {
log.Println("PID", Conn.PID(), "关闭UDP", Conn.LocalAddress(), Conn.RemoteAddress())
return
}
}
```
1. 如需支持 Win7 系统,请使用 Go 1.21 以下版本编译,例如 go 1.20.4 版本
2. <a href="https://github.com/jmeubank/tdm-gcc/releases/download/v10.3.0-tdm64-2/tdm64-gcc-10.3.0-2.exe">编译请使用 TDM-GCC</a>

281
README_api.md Normal file
View File

@@ -0,0 +1,281 @@
# Sunny网络中间件 SDK API 参考
## 目录
- [SunnyNet核心类](#sunnynet核心类)
- [创建和管理SunnyNet实例](#创建和管理sunnynet实例)
- [证书管理](#证书管理)
- [回调设置](#回调设置)
- [代理设置](#代理设置)
- [TCP/UDP控制](#tcpudp控制)
- [Socks5代理验证](#socks5代理验证)
- [进程代理](#进程代理)
- [网络设置](#网络设置)
- [脚本支持](#脚本支持)
- [HTTP/HTTPS处理](#httphttps处理)
- [请求信息获取与修改](#请求信息获取与修改)
- [响应信息获取与修改](#响应信息获取与修改)
- [HTTP客户端](#http客户端)
- [WebSocket处理](#websocket处理)
- [TCP处理](#tcp处理)
- [UDP处理](#udp处理)
- [证书管理器](#证书管理器)
- [其他工具函数](#其他工具函数)
- [数据压缩/解压缩](#数据压缩解压缩)
- [图片处理](#图片处理)
- [Protobuf处理](#protobuf处理)
- [队列操作](#队列操作)
- [TCP客户端](#tcp客户端)
- [WebSocket客户端](#websocket客户端)
- [Redis客户端](#redis客户端)
- [Go Map操作](#go-map操作)
## SunnyNet核心类
### 创建和管理SunnyNet实例
- `CreateSunnyNet() int` - 创建Sunny中间件对象
- `ReleaseSunnyNet(SunnyContext int) bool` - 释放SunnyNet
- `SunnyNetStart(SunnyContext int) bool` - 启动Sunny中间件
- `SunnyNetClose(SunnyContext int) bool` - 关闭停止指定Sunny中间件
- `SunnyNetSetPort(SunnyContext, Port int) bool` - 设置指定端口
- `SunnyNetError(SunnyContext int) uintptr` - 获取中间件启动时的错误信息
### 证书管理
- `SunnyNetSetCert(SunnyContext, CertificateManagerId int) bool` - 设置自定义证书
- `SunnyNetInstallCert(SunnyContext int) uintptr` - 安装证书到Windows系统内
- `ExportCert(SunnyContext int) uintptr` - 导出已设置的证书
### 回调设置
- `SunnyNetSetCallback(SunnyContext, httpCallback, tcpCallback, wsCallback, udpCallback int) bool` - 设置中间件回调地址
### 代理设置
- `SetGlobalProxy(SunnyContext int, ProxyAddress *C.char, outTime int) bool` - 设置全局上游代理
- `CompileProxyRegexp(SunnyContext int, Regexp *C.char) bool` - 设置中间件上游代理使用规则
### TCP/UDP控制
- `SunnyNetMustTcp(SunnyContext int, open bool)` - 设置中间件是否开启强制走TCP
- `SetMustTcpRegexp(SunnyContext int, Regexp *C.char, RulesAllow bool) bool` - 设置强制走TCP规则
- `DisableTCP(SunnyContext int, Disable bool) bool` - 禁用TCP
- `DisableUDP(SunnyContext int, Disable bool) bool` - 禁用UDP
### Socks5代理验证
- `SunnyNetSocket5AddUser(SunnyContext int, User, Pass *C.char) bool` - 添加S5代理需要验证的用户名
- `SunnyNetVerifyUser(SunnyContext int, open bool) bool` - 开启身份验证模式
- `SunnyNetSocket5DelUser(SunnyContext int, User *C.char) bool` - 删除S5需要验证的用户名
- `SunnyNetGetSocket5User(Theology int) uintptr` - 获取授权的S5账号
### 进程代理
- `OpenDrive(SunnyContext int, devMode int) bool` - 开始进程代理/打开驱动
- `UnDrive(SunnyContext int)` - 卸载驱动
- `ProcessAddName(SunnyContext int, Name *C.char)` - 进程代理添加进程名
- `ProcessDelName(SunnyContext int, Name *C.char)` - 进程代理删除进程名
- `ProcessAddPid(SunnyContext, pid int)` - 进程代理添加PID
- `ProcessDelPid(SunnyContext, pid int)` - 进程代理删除PID
- `ProcessCancelAll(SunnyContext int)` - 进程代理取消全部已设置的进程名
- `ProcessALLName(SunnyContext int, open, StopNetwork bool)` - 进程代理设置是否全部进程通过
### 网络设置
- `SetOutRouterIP(SunnyContext int, value *C.char) bool` - 设置数据出口IP
- `SetIeProxy(SunnyContext int) bool` - 设置IE代理
- `CancelIEProxy(SunnyContext int) bool` - 取消设置的IE代理
- `SetDnsServer(ServerName *C.char)` - 设置Dns解析服务器
### 脚本支持
- `SetScriptCode(SunnyContext int, code uintptr, length int) uintptr` - 加载用户的脚本代码
- `SetScriptCall(SunnyContext int, LOG, SAVE uintptr)` - 设置脚本代码的回调函数
- `SetScriptPage(SunnyContext int, Page *C.char) uintptr` - 设置脚本编辑器页面
## HTTP/HTTPS处理
### 请求信息获取与修改
- `GetRequestProto(MessageId int) uintptr` - 获取HTTPS请求的协议版本
- `SetRequestUrl(MessageId int, URI *C.char) bool` - 修改HTTP/S当前请求的URL
- `GetRequestHeader(MessageId int, name *C.char) uintptr` - 获取HTTP/S当前请求数据中的指定协议头
- `SetRequestHeader(MessageId int, name, val *C.char)` - 设置HTTP/S请求体中的协议头
- `DelRequestHeader(MessageId int, name *C.char)` - 删除HTTP/S请求数据中指定的协议头
- `GetRequestAllHeader(MessageId int) uintptr` - 获取HTTP/S当前请求数据全部协议头
- `SetRequestALLHeader(MessageId int, val *C.char)` - 设置HTTP/S请求体中的全部协议头
- `GetRequestCookie(MessageId int, name *C.char) uintptr` - 获取HTTP/S当前请求数据中指定的Cookie
- `GetRequestALLCookie(MessageId int) uintptr` - 获取HTTP/S当前请求全部Cookie
- `SetRequestCookie(MessageId int, name, val *C.char)` - 修改、设置HTTP/S当前请求数据中指定Cookie
- `SetRequestAllCookie(MessageId int, val *C.char)` - 修改、设置HTTP/S当前请求数据中的全部Cookie
- `GetRequestBodyLen(MessageId int) int` - 获取HTTP/S当前请求POST提交数据长度
- `GetRequestBody(MessageId int) uintptr` - 获取HTTP/S当前POST提交数据
- `SetRequestData(MessageId int, data uintptr, dataLen int) bool` - 设置、修改HTTP/S当前请求POST提交数据
- `IsRequestRawBody(MessageId int) bool` - 此请求是否为原始body
- `RawRequestDataToFile(MessageId int, saveFileName uintptr, len int) bool` - 获取HTTP/S当前POST提交数据原始Data
### 响应信息获取与修改
- `GetResponseProto(MessageId int) uintptr` - 获取HTTPS响应的协议版本
- `GetResponseHeader(MessageId int, name *C.char) uintptr` - 获取HTTP/S当前返回数据中指定的协议头
- `SetResponseHeader(MessageId int, name *C.char, val *C.char)` - 修改、设置HTTP/S当前返回数据中的指定协议头
- `DelResponseHeader(MessageId int, name *C.char)` - 删除HTTP/S返回数据中指定的协议头
- `GetResponseAllHeader(MessageId int) uintptr` - 获取HTTP/S当前返回全部协议头
- `SetResponseAllHeader(MessageId int, value *C.char)` - 修改、设置HTTP/S当前返回数据中的全部协议头
- `GetResponseBodyLen(MessageId int) int` - 获取HTTP/S当前返回数据长度
- `GetResponseBody(MessageId int) uintptr` - 获取HTTP/S当前返回数据
- `SetResponseData(MessageId int, data uintptr, dataLen int) bool` - 设置、修改HTTP/S当前请求返回数据
- `GetResponseStatusCode(MessageId int) int` - 获取HTTP/S返回的状态码
- `GetResponseStatus(MessageId int) uintptr` - 获取HTTP/S返回的状态文本
- `SetResponseStatus(MessageId, code int)` - 修改HTTP/S返回的状态码
### HTTP客户端
- `CreateHTTPClient() int` - 创建HTTP客户端
- `RemoveHTTPClient(Context int)` - 释放HTTP客户端
- `HTTPOpen(Context int, Method, URL *C.char)` - HTTP客户端Open
- `HTTPSetHeader(Context int, name, value *C.char)` - HTTP客户端设置协议头
- `HTTPSetProxyIP(Context int, ProxyUrl *C.char) bool` - HTTP客户端设置代理IP
- `HTTPSetServerIP(Context int, ServerIP *C.char)` - HTTP客户端设置真实连接IP地址
- `HTTPSetTimeouts(Context int, t1 int)` - HTTP客户端设置超时
- `HTTPSendBin(Context int, body uintptr, bodyLength int)` - HTTP客户端发送Body
- `HTTPGetHeads(Context int) uintptr` - HTTP客户端返回响应全部Heads
- `HTTPGetHeader(Context int, name *C.char) uintptr` - HTTP客户端返回响应HTTPGetHeader
- `HTTPGetRequestHeader(Context int) uintptr` - HTTP客户端添加的全部协议头
- `HTTPGetBodyLen(Context int) int` - HTTP客户端返回响应长度
- `HTTPGetBody(Context int) uintptr` - HTTP客户端返回响应内容
- `HTTPGetCode(Context int) int` - HTTP客户端返回响应状态码
- `HTTPSetCertManager(Context, CertManagerContext int) bool` - HTTP客户端设置证书管理器
- `HTTPSetRedirect(Context int, Redirect bool) bool` - HTTP客户端设置重定向
- `HTTPSetRandomTLS(Context int, RandomTLS bool) bool` - HTTP客户端设置随机使用TLS指纹
- `HTTPSetH2Config(Context int, config *C.char) bool` - HTTP客户端设置HTTP2指纹
## WebSocket处理
- `GetWebsocketBodyLen(MessageId int) int` - 获取WebSocket消息长度
- `GetWebsocketBody(MessageId int) uintptr` - 获取WebSocket消息
- `SetWebsocketBody(MessageId int, data uintptr, dataLen int) bool` - 修改WebSocket消息
- `SendWebsocketBody(Theology, MessageType int, data uintptr, dataLen int) bool` - 主动向Websocket服务器发送消息
- `SendWebsocketClientBody(Theology, MessageType int, data uintptr, dataLen int) bool` - 主动向Websocket客户端发送消息
- `CloseWebsocket(Theology int) bool` - 主动关闭Websocket
## TCP处理
- `SetTcpBody(MessageId, MsgType int, data uintptr, dataLen int) bool` - 修改TCP消息数据
- `SetTcpAgent(MessageId int, ProxyUrl *C.char, outTime int) bool` - 给当前TCP连接设置代理
- `TcpCloseClient(theology int) bool` - 根据唯一ID关闭指定的TCP连接
- `SetTcpConnectionIP(MessageId int, address *C.char) bool` - 给指定的TCP连接修改目标连接地址
- `TcpSendMsg(theology int, data uintptr, dataLen int) int` - 指定的TCP连接模拟客户端向服务器端主动发送数据
- `TcpSendMsgClient(theology int, data uintptr, dataLen int) int` - 指定的TCP连接模拟服务器端向客户端主动发送数据
## UDP处理
- `GetUdpData(MessageId int) uintptr` - 获取UDP数据
- `SetUdpData(MessageId int, val uintptr, valLen int) bool` - 设置修改UDP数据
- `UdpSendToClient(theology int, data uintptr, dataLen int) bool` - 指定的UDP连接模拟服务器端向客户端主动发送数据
- `UdpSendToServer(theology int, data uintptr, dataLen int) bool` - 指定的UDP连接模拟客户端向服务器端主动发送数据
## 证书管理器
- `CreateCertificate() int` - 创建证书管理器对象
- `RemoveCertificate(Context int)` - 释放证书管理器对象
- `LoadX509Certificate(Context int, Host, CA, KEY *C.char) bool` - 载入X509证书
- `LoadX509KeyPair(Context int, CaPath, KeyPath *C.char) bool` - 载入X509证书2
- `LoadP12Certificate(Context int, Name, Password *C.char) bool` - 载入p12证书
- `SetInsecureSkipVerify(Context int, b bool) bool` - 设置跳过主机验证
- `SetServerName(Context int, name *C.char) bool` - 设置ServerName
- `GetServerName(Context int) uintptr` - 取ServerName
- `AddCertPoolPath(Context int, cer *C.char) bool` - 设置信任的证书从文件
- `AddCertPoolText(Context int, cer *C.char) bool` - 设置信任的证书从文本
- `SetCipherSuites(Context int, val *C.char) bool` - 设置CipherSuites
- `AddClientAuth(Context, val int) bool` - 设置ClientAuth
- `CreateCA(Context int, Country, Organization, OrganizationalUnit, Province, CommonName, Locality *C.char, bits, NotAfter int) bool` - 创建证书
- `ExportCA(Context int) uintptr` - 导出证书
- `ExportKEY(Context int) uintptr` - 导出私钥
- `ExportPub(Context int) uintptr` - 导出公钥
- `ExportP12(Context int, path, pass *C.char) bool` - 导出为P12
- `GetCommonName(Context int) uintptr` - 获取证书CommonName字段
## 其他工具函数
- `GetSunnyVersion() uintptr` - 获取SunnyNet版本
- `Free(ptr uintptr)` - 释放指针
- `SetHTTPRequestMaxUpdateLength(SunnyContext int, i int64) bool` - 设置HTTP请求提交数据的最大长度
- `RandomRequestCipherSuites(MessageId int) bool` - 随机设置请求CipherSuites
- `SetRequestHTTP2Config(MessageId int, h2Config *C.char) bool` - 设置HTTP 2.0请求指纹配置
- `GetRequestClientIp(MessageId int) uintptr` - 获取当前HTTP/S请求由哪个IP发起
- `GetResponseServerAddress(MessageId int) uintptr` - 获取HTTP/S相应的服务器地址
- `SetRequestOutTime(MessageId int, times int)` - 请求设置超时-毫秒
- `GetMessageNote(MessageId int) uintptr` - 获取请求中的注释
- `BytesToInt(data uintptr, dataLen int) int` - 将Go int的Bytes转为int
### 数据压缩/解压缩
- `GzipUnCompress(data uintptr, dataLen int) uintptr` - Gzip解压缩
- `GzipCompress(data uintptr, dataLen int) uintptr` - Gzip压缩
- `BrUnCompress(data uintptr, dataLen int) uintptr` - br解压缩
- `BrCompress(data uintptr, dataLen int) uintptr` - br压缩
- `BrotliCompress(data uintptr, dataLen int) uintptr` - br压缩(别名)
- `ZSTDDecompress(data uintptr, dataLen int) uintptr` - ZSTD解压缩
- `ZSTDCompress(data uintptr, dataLen int) uintptr` - ZSTD压缩
- `ZlibUnCompress(data uintptr, dataLen int) uintptr` - Zlib解压缩
- `ZlibCompress(data uintptr, dataLen int) uintptr` - Zlib压缩
- `DeflateUnCompress(data uintptr, dataLen int) uintptr` - Deflate解压缩
- `DeflateCompress(data uintptr, dataLen int) uintptr` - Deflate压缩
### 图片处理
- `WebpToJpegBytes(data uintptr, dataLen int, SaveQuality int) uintptr` - Webp图片转JEG图片字节数组
- `WebpToPngBytes(data uintptr, dataLen int) uintptr` - Webp图片转Png图片字节数组
- `WebpToJpeg(webpPath, savePath *C.char, SaveQuality int) bool` - Webp图片转JEG图片根据文件名
- `WebpToPng(webpPath, savePath *C.char) bool` - Webp图片转Png图片根据文件名
### Protobuf处理
- `JsonToPB(bin uintptr, binLen int) uintptr` - JSON格式的protobuf数据转为protobuf二进制数据
- `PbToJson(bin uintptr, binLen int) uintptr` - protobuf数据转为JSON格式
### 队列操作
- `CreateQueue(name *C.char)` - 创建队列
- `QueueIsEmpty(name *C.char) bool` - 队列是否为空
- `QueueRelease(name *C.char)` - 清空销毁队列
- `QueueLength(name *C.char) int` - 取队列长度
- `QueuePush(name *C.char, val uintptr, valLen int)` - 加入队列
- `QueuePull(name *C.char) uintptr` - 队列弹出
### TCP客户端
- `CreateSocketClient() int` - 创建TCP客户端
- `RemoveSocketClient(Context int)` - 释放TCP客户端
- `SocketClientDial(Context int, addr *C.char, call int, isTls, synchronous bool, ProxyUrl *C.char, CertificateConText int, OutTime int, OutRouterIP *C.char) bool` - TCP客户端连接
- `SocketClientReceive(Context, OutTimes int) uintptr` - TCP客户端同步模式下接收数据
- `SocketClientWrite(Context, OutTimes int, val uintptr, valLen int) int` - TCP客户端发送数据
- `SocketClientClose(Context int)` - TCP客户端断开连接
- `SocketClientSetBufferSize(Context, BufferSize int) bool` - TCP客户端置缓冲区大小
- `SocketClientGetErr(Context int) uintptr` - TCP客户端取错误
### WebSocket客户端
- `CreateWebsocket() int` - 创建WebSocket客户端对象
- `RemoveWebsocket(Context int)` - 释放WebSocket客户端对象
- `WebsocketDial(Context int, URL, Heads *C.char, call int, synchronous bool, ProxyUrl *C.char, CertificateConText, outTime int, OutRouterIP *C.char) bool` - Websocket客户端连接
- `WebsocketReceive(Context, OutTimes int) uintptr` - Websocket客户端同步模式下接收数据
- `WebsocketReadWrite(Context int, val uintptr, valLen int, messageType int) bool` - Websocket客户端发送数据
- `WebsocketClose(Context int)` - Websocket客户端断开
- `WebsocketHeartbeat(Context, HeartbeatTime, call int)` - Websocket客户端心跳设置
- `WebsocketGetErr(Context int) uintptr` - Websocket客户端获取错误
### Redis客户端
- `CreateRedis() int` - 创建Redis对象
- `RemoveRedis(Context int)` - 释放Redis对象
- `RedisDial(Context int, host, pass *C.char, db, PoolSize, MinIdleCons, DialTimeout, ReadTimeout, WriteTimeout, PoolTimeout, IdleCheckFrequency, IdleTimeout int, error uintptr) bool` - Redis连接
- `RedisSetBytes(Context int, key *C.char, val uintptr, valLen int, expr int) bool` - Redis设置Bytes值
- `RedisSet(Context int, key, val *C.char, expr int) bool` - Redis设置值
- `RedisSetNx(Context int, key, val *C.char, expr int) bool` - Redis设置NX
- `RedisExists(Context int, key *C.char) bool` - Redis检查指定key是否存在
- `RedisGetBytes(Context int, key *C.char) uintptr` - Redis取Bytes值
- `RedisGetStr(Context int, key *C.char) uintptr` - Redis取文本值
- `RedisDo(Context int, args *C.char, error uintptr) uintptr` - Redis自定义执行和查询命令
- `RedisGetInt(Context int, key *C.char) int64` - Redis取整数值
- `RedisGetKeys(Context int, key *C.char) uintptr` - Redis取指定条件键名
- `RedisDelete(Context int, key *C.char) bool` - Redis删除
- `RedisFlushDB(Context int)` - Redis清空当前数据库
- `RedisFlushAll(Context int)` - Redis清空redis服务器
- `RedisClose(Context int)` - Redis关闭
- `RedisSubscribe(Context int, scribe *C.char, call int, nc bool) bool` - Redis订阅消息
### Go Map操作
- `CreateKeys() int` - GoMap创建
- `RemoveKeys(KeysHandle int)` - GoMap删除GoMap
- `KeysDelete(KeysHandle int, name *C.char)` - GoMap删除
- `KeysRead(KeysHandle int, name *C.char) uintptr` - GoMap读取字符串/字节数组
- `KeysWrite(KeysHandle int, name *C.char, val uintptr, length int)` - GoMap写字节数组
- `KeysWriteFloat(KeysHandle int, name *C.char, val float64)` - GoMap写浮点数
- `KeysReadFloat(KeysHandle int, name *C.char) float64` - GoMap读浮点数
- `KeysWriteLong(KeysHandle int, name *C.char, val int64)` - GoMap写长整数
- `KeysReadLong(KeysHandle int, name *C.char) int64` - GoMap读长整数
- `KeysWriteInt(KeysHandle int, name *C.char, val int)` - GoMap写整数
- `KeysReadInt(KeysHandle int, name *C.char) int` - GoMap读整数
- `KeysEmpty(KeysHandle int)` - GoMap清空
- `KeysGetCount(KeysHandle int) int` - GoMap取数量
- `KeysGetJson(KeysHandle int) uintptr` - GoMap转为JSON字符串
- `KeysWriteStr(KeysHandle int, name *C.char, val uintptr, len int)` - GoMap写字符串

514
README_go.md Normal file
View File

@@ -0,0 +1,514 @@
# SunnyNet Go语言使用示例
本文档提供了SunnyNet网络中间件在Go语言环境下的各种使用场景示例基于test.go文件中的实际代码示例。
## 目录
1. [基础HTTP代理](#基础http代理)
2. [双向认证处理](#双向认证处理)
3. [WebSocket处理](#websocket处理)
4. [TCP连接处理](#tcp连接处理)
5. [UDP数据处理](#udp数据处理)
6. [证书管理](#证书管理)
7. [代理设置](#代理设置)
8. [驱动模式](#驱动模式)
9. [DNS解析模式](#dns解析模式)
10. [其他功能](#其他功能)
## 基础HTTP代理
### 简单HTTP代理服务器
```go
package main
import (
"fmt"
"log"
"github.com/qtgolang/SunnyNet/SunnyNet"
"github.com/qtgolang/SunnyNet/src/public"
)
func main() {
var Sunny = SunnyNet.NewSunny()
// 设置回调地址
Sunny.SetGoCallback(HttpCallback, TcpCallback, WSCallback, UdpCallback)
Port := 2021
Sunny.SetPort(Port).Start()
err := Sunny.Error
if err != nil {
panic(err)
return
}
fmt.Println("Run Port=", Port)
// 阻止程序退出
select {}
}
func HttpCallback(Conn SunnyNet.ConnHTTP) {
switch Conn.Type() {
case public.HttpSendRequest: // 发起请求
fmt.Println("发起请求", Conn.URL(), Conn.Proto(), Conn.GetProcessName())
// 直接响应,不让其发送请求
// Conn.StopRequest(200, "Hello Word")
return
case public.HttpResponseOK: // 请求完成
bs := Conn.GetResponseBody()
log.Println("请求完成", Conn.GetResponseProto(), Conn.URL(), len(bs), Conn.GetResponseHeader())
return
case public.HttpRequestFail: // 请求错误
// fmt.Println(time.Now(), Conn.URL(), Conn.Error())
return
}
}
func WSCallback(Conn SunnyNet.ConnWebSocket) {
switch Conn.Type() {
case public.WebsocketConnectionOK: // 连接成功
log.Println("PID", Conn.PID(), "Websocket 连接成功:", Conn.URL())
return
case public.WebsocketUserSend: // 发送数据
log.Println("PID", Conn.PID(), "Websocket 发送数据:", Conn.MessageType(), "->", hex.EncodeToString(Conn.Body()))
return
case public.WebsocketServerSend: // 收到数据
log.Println("PID", Conn.PID(), "Websocket 收到数据:", Conn.MessageType(), "->", hex.EncodeToString(Conn.Body()))
return
case public.WebsocketDisconnect: // 连接关闭
log.Println("PID", Conn.PID(), "Websocket 连接关闭", Conn.URL())
return
default:
return
}
}
func TcpCallback(Conn SunnyNet.ConnTCP) {
switch Conn.Type() {
case public.SunnyNetMsgTypeTCPAboutToConnect: // 即将连接
mode := string(Conn.Body())
log.Println("PID", Conn.PID(), "TCP 即将连接到:", mode, Conn.LocalAddress(), "->", Conn.RemoteAddress())
// 修改目标连接地址
// Conn.SetNewAddress("8.8.8.8:8080")
return
case public.SunnyNetMsgTypeTCPConnectOK: // 连接成功
// log.Println("PID", Conn.PID(), "TCP 连接到:", Conn.LocalAddress(), "->", Conn.RemoteAddress(), "成功")
return
case public.SunnyNetMsgTypeTCPClose: // 连接关闭
// log.Println("PID", Conn.PID(), "TCP 断开连接:", Conn.LocalAddress(), "->", Conn.RemoteAddress())
return
case public.SunnyNetMsgTypeTCPClientSend: // 客户端发送数据
// log.Println("PID", Conn.PID(), "TCP 发送数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
return
case public.SunnyNetMsgTypeTCPClientReceive: // 客户端收到数据
// log.Println("PID", Conn.PID(), "收到数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
return
default:
return
}
}
func UdpCallback(Conn SunnyNet.ConnUDP) {
switch Conn.Type() {
case public.SunnyNetUDPTypeSend: // 客户端向服务器端发送数据
log.Println("PID", Conn.PID(), Conn.GetProcessName(), "发送UDP", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.BodyLen())
// 修改发送的数据
// Conn.SetBody([]byte("Hello Word"))
return
case public.SunnyNetUDPTypeReceive: // 服务器端向客户端发送数据
log.Println("PID", Conn.PID(), Conn.GetProcessName(), "接收UDP", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.BodyLen())
// 修改响应的数据
// Conn.SetBody([]byte("Hello Word"))
return
case public.SunnyNetUDPTypeClosed: // 关闭会话
log.Println("PID", Conn.PID(), Conn.GetProcessName(), "关闭UDP", Conn.LocalAddress(), Conn.RemoteAddress())
return
}
}
```
### 双向认证处理
```go
package main
import (
"fmt"
"log"
"github.com/qtgolang/SunnyNet/SunnyNet"
"github.com/qtgolang/SunnyNet/src/public"
)
func main() {
var Sunny = SunnyNet.NewSunny()
// 载入自定义证书
cert := SunnyNet.NewCertManager()
ok := cert.LoadP12Certificate("./test.p12", "password")
if ok {
fmt.Println("证书名称:", cert.GetCommonName())
// 给指定域名使用这个证书
// SunnyNet.HTTPCertRules_ResponseAndRequest 按需调整 SunnyNet.HTTPCertRules_Request、SunnyNet.HTTPCertRules_Response
Sunny.AddHttpCertificate("api.test.com", cert, SunnyNet.HTTPCertRules_ResponseAndRequest)
}else{
panic("载入P12证书失败")
}
// 设置回调地址
Sunny.SetGoCallback(HttpCallback, nil, nil, nil)
Port := 2021
Sunny.SetPort(Port).Start()
err := Sunny.Error
if err != nil {
panic(err)
return
}
fmt.Println("Run Port=", Port)
// 阻止程序退出
select {}
}
func HttpCallback(Conn SunnyNet.ConnHTTP) {
switch Conn.Type() {
case public.HttpSendRequest: // 发起请求
fmt.Println("发起请求", Conn.URL(), Conn.Proto(), Conn.GetProcessName())
// 直接响应,不让其发送请求
// Conn.StopRequest(200, "Hello Word")
return
case public.HttpResponseOK: // 请求完成
bs := Conn.GetResponseBody()
log.Println("请求完成", Conn.GetResponseProto(), Conn.URL(), len(bs), Conn.GetResponseHeader())
return
case public.HttpRequestFail: // 请求错误
// fmt.Println(time.Now(), Conn.URL(), Conn.Error())
return
}
}
```
## WebSocket处理
### WebSocket连接监控
```go
func WSCallback(Conn SunnyNet.ConnWebSocket) {
switch Conn.Type() {
case public.WebsocketConnectionOK: // 连接成功
log.Println("PID", Conn.PID(), "Websocket 连接成功:", Conn.URL())
// 连接建立时可以主动发送消息
// Conn.SendToClient(1, []byte("Hello from proxy!"))
return
case public.WebsocketUserSend: // 发送数据
log.Println("PID", Conn.PID(), "Websocket 发送数据:", Conn.MessageType(), "->", hex.EncodeToString(Conn.Body()))
// 可以修改发送的消息
// if Conn.MessageType() == 1 { // 文本消息
// modifiedMsg := []byte("modified: " + string(Conn.Body()))
// Conn.SetBody(modifiedMsg)
// }
// 拦截此消息,不让此消息发出
// Conn.SetBody(nil)
// 主动向客户端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToClient([]byte("模拟服务端发送到客户端的消息"))
// 主动向服务端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToServer([]byte("模拟客户端发送到服务端的消息"))
return
case public.WebsocketServerSend: // 收到数据
log.Println("PID", Conn.PID(), "Websocket 收到数据:", Conn.MessageType(), "->", hex.EncodeToString(Conn.Body()))
// 可以修改接收的消息
// if Conn.MessageType() == 1 { // 文本消息
// modifiedMsg := []byte("modified: " + string(Conn.Body()))
// Conn.SetBody(modifiedMsg)
// }
// 拦截此消息,不让此消息发出
// Conn.SetBody(nil)
// 主动向客户端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToClient([]byte("模拟服务端发送到客户端的消息"))
// 主动向服务端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToServer([]byte("模拟客户端发送到服务端的消息"))
return
case public.WebsocketDisconnect: // 连接关闭
log.Println("PID", Conn.PID(), "Websocket 连接关闭", Conn.URL())
return
default:
return
}
}
```
## TCP连接处理
### TCP连接监控
```go
func TcpCallback(Conn SunnyNet.ConnTCP) {
switch Conn.Type() {
case public.SunnyNetMsgTypeTCPAboutToConnect: // 即将连接
mode := string(Conn.Body())
log.Println("PID", Conn.PID(), "TCP 即将连接到:", mode, Conn.LocalAddress(), "->", Conn.RemoteAddress())
// 修改目标连接地址
// Conn.SetNewAddress("8.8.8.8:8080")
// 设置TCP代理
// Conn.SetAgent("socks5://user:pass@127.0.0.1:1080", 30)
return
case public.SunnyNetMsgTypeTCPConnectOK: // 连接成功
// log.Println("PID", Conn.PID(), "TCP 连接到:", Conn.LocalAddress(), "->", Conn.RemoteAddress(), "成功")
return
case public.SunnyNetMsgTypeTCPClose: // 连接关闭
// log.Println("PID", Conn.PID(), "TCP 断开连接:", Conn.LocalAddress(), "->", Conn.RemoteAddress())
return
case public.SunnyNetMsgTypeTCPClientSend: // 客户端发送数据
// log.Println("PID", Conn.PID(), "TCP 发送数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
// 修改发送的数据
// Conn.SetBody([]byte("modified data"))
// 拦截此消息,不让此消息发出
// Conn.SetBody(nil)
// 主动向客户端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToClient([]byte("模拟服务端发送到客户端的消息"))
// 主动向服务端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToServer([]byte("模拟客户端发送到服务端的消息"))
return
case public.SunnyNetMsgTypeTCPClientReceive: // 客户端收到数据
// log.Println("PID", Conn.PID(), "收到数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
// 修改接收的数据
// Conn.SetBody([]byte("modified response"))
// 拦截此消息,不让此消息发出
// Conn.SetBody(nil)
// 主动向客户端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToClient([]byte("模拟服务端发送到客户端的消息"))
// 主动向服务端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToServer([]byte("模拟客户端发送到服务端的消息"))
return
default:
return
}
}
```
## UDP数据处理
### UDP监控
```go
func UdpCallback(Conn SunnyNet.ConnUDP) {
switch Conn.Type() {
case public.SunnyNetUDPTypeSend: // 客户端向服务器端发送数据
log.Println("PID", Conn.PID(), Conn.GetProcessName(), "发送UDP", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.BodyLen())
// 修改发送的数据
// Conn.SetBody([]byte("Hello Word"))
// 拦截此消息,不让此消息发出
// Conn.SetBody(nil)
// 主动向客户端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToClient([]byte("模拟服务端发送到客户端的消息"))
// 主动向服务端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToServer([]byte("模拟客户端发送到服务端的消息"))
return
case public.SunnyNetUDPTypeReceive: // 服务器端向客户端发送数据
log.Println("PID", Conn.PID(), Conn.GetProcessName(), "接收UDP", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.BodyLen())
// 修改响应的数据
// Conn.SetBody([]byte("Hello Word"))
// 拦截此消息,不让此消息发出
// Conn.SetBody(nil)
// 主动向客户端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToClient([]byte("模拟服务端发送到客户端的消息"))
// 主动向服务端发送数据(可将Conn储存到全局随时调用)
// Conn.SendToServer([]byte("模拟客户端发送到服务端的消息"))
return
case public.SunnyNetUDPTypeClosed: // 关闭会话
log.Println("PID", Conn.PID(), Conn.GetProcessName(), "关闭UDP", Conn.LocalAddress(), Conn.RemoteAddress())
return
}
}
```
## 证书管理
### 创建自定义证书
```go
var Sunny = SunnyNet.NewSunny()
Cert := SunnyNet.NewCertManager()
//从文件载入证书并且设置为SunnyNet根证书
if Cert.LoadP12Certificate("./SunnyNet.p12", "1234567890") {
Sunny.SetCert(Cert.Context())
}
success := Cert.CreateCA(
"CN", // Country
"My Organization", // Organization
"IT", // Organizational Unit
"Beijing", // Province
"My Custom CA", // Common Name
"Beijing", // Locality
2048, // bits
365, // NotAfter (days)
)
if success {
fmt.Println("CA证书创建成功")
// 导出证书
caPem := Cert.ExportCA()
keyPem := Cert.ExportKEY()
fmt.Println("CA证书:", caPem)
fmt.Println("私钥:", keyPem)
//导出到文件
Cert.ExportP12("./SunnyNet.p12", "1234567890")
}
```
## 代理设置
### 全局上游代理
```go
// 设置全局上游代理
Sunny.SetGlobalProxy("socket://192.168.31.1:4321", 60000)
// 指定IP或域名不使用全局的上游代理
Sunny.CompileProxyRegexp("127.0.0.1;[::1];192.168.*;*.baidu.com")
```
### 强制TCP模式
```go
// 开启强制走TCP,开启后 https 将不会解密 直接转发数据流量
Sunny.MustTcp(true)
// 或者设置强制走TCP规则使用这个函数后 就不要使用 Sunny.MustTcp(true) 否则这个函数无效
Sunny.SetMustTcpRegexp("tpstelemetry.tencent.com", true)
// 设置TCP规则模式 (true: 规则内走TCP, false: 规则外走TCP)
Sunny.SetMustTcpRegexp("*.google.com;*.github.com", true)
```
### 禁用TCP/UDP
```go
// 禁止TCP所有TCP流量将直接断开连接
Sunny.DisableTCP(true)
// 禁止UDP所有UDP流量将直接断开连接
Sunny.DisableUDP(true)
```
## 驱动模式
### 使用驱动抓包
```go
// 使用驱动抓包 (两个驱动各有特点自行尝试,哪个能用/好用 用哪个)
// 0=Proxifier,1=NFAPI,2=Tun
success := Sunny.OpenDrive(2)
if success {
fmt.Println("驱动启动成功")
} else {
fmt.Println("驱动启动失败")
}
// 添加指定进程名称进行抓包
Sunny.ProcessAddName("chrome.exe")
Sunny.ProcessAddName("firefox.exe")
// 删除已添加的指定进程名称
Sunny.ProcessDelName("firefox.exe")
// 添加指定进程PID进行抓包
Sunny.ProcessAddPid(1234)
// 删除已添加的指定进程PID
Sunny.ProcessDelPid(1234)
// 删除已添加的所有进程名称/PID
Sunny.ProcessCancelAll()
// 捕获全部进程开始后,添加进程名称-PID无效
Sunny.ProcessALLName(true, false)
// 设置数据出口IP
Sunny.SetOutRouterIP("192.168.1.100")
```
## DNS解析模式
### DNS使用的3种模式
```go
/*
DNS使用的3种模式
当你设置了全局上游代理或对请求单独设置了代理的情况下使用DNS模式视情况而定,来选择设置
情况1.你没有使用全局上游代理,也没有对请求单独设置代理,这种情况下没什么好说的无论你设置的是那种模式都只会使用本机的DNS进行解析!
情况2.你使用了全局上游代理或请求单独设置代理,这种情况下你设置以下3种模式会有区别
1.使用 本地解析 模式你要访问的目标地址通过你本地DNS解析出的IP可能会被服务器拒绝连接。这时候你需要尝试 远程解析/远程服务器解析
2.使用 远程解析 模式,你所使用的代理服务器可能存在无法解析的情况。这时你应该尝试 远程服务器解析
3.使用 远程服务器解析 模式,远程服务器解析 会使用你设置的代理连接到远程DNS服务器进行查询并且解析可能会导致首次访问变慢
Sunny.SetDnsServer("local") //本地解析
Sunny.SetDnsServer("remote") //远程解析
Sunny.SetDnsServer("223.5.5.5:853") //远程服务器解析
*/
// 设置DNS解析模式示例
Sunny.SetDnsServer("223.5.5.5:853") // 使用远程DNS服务器解析
```
## 其他功能
### 设置出口IP
```go
// 设置数据出口IP
Sunny.SetOutRouterIP("192.168.31.154")
// 为请求单独设置出口IP(在回调中设置)
// Conn.SetOutRouterIP("192.168.31.154")
```
### 脚本功能
```go
log := func(Context int, info ...any) {
fmt.Println("x脚本日志", fmt.Sprintf("%v", info))
}
save := func(Context int, code []byte) {
// 在这里将code代码 储存到文件,下次启动时,载入恢复
}
Sunny.SetScriptCall(log, save)
// 载入上次保存的脚本代码
Sunny.SetScriptCode(string(GoScriptCode.DefaultCode))
// 设置脚本页面
Sunny.SetScriptPage("MyScriptEditor")
//设置后 使用浏览器访问 http://127.0.0.1:{prot}/MyScriptEditor 来使用脚本
```
### 数据压缩/解压缩
```go
// Gzip压缩
originalData := []byte("Hello, World!")
//压缩 仅支持 gzip、br、deflate、zstd、zlib 算法
compressedData := Compress.CompressAuto("gzip", originalData)
if compressedData != nil {
fmt.Printf("压缩后长度: %d\n", len(compressedData))
// 解压缩 仅支持 gzip、br、deflate、zstd、zlib 算法
decompressedData := Compress.UnCompressAuto("gzip", compressedData)
if decompressedData != nil {
fmt.Printf("解压后数据: %s\n", string(decompressedData))
}
}
```
以上示例展示了SunnyNet在各种场景下的使用方法开发者可以根据具体需求选择合适的示例进行参考和修改。[有任何Bug和示例错误可以随时反馈](README.md)

Binary file not shown.

View File

@@ -2450,13 +2450,12 @@ func (s *Sunny) InstallCert() string {
}
// SetCert 设置证书
func (s *Sunny) SetCert(ManagerId int) *Sunny {
Manager := Certificate.LoadCertificateContext(ManagerId)
func (s *Sunny) SetCert(ManagerContext int) *Sunny {
Manager := Certificate.LoadCertificateContext(ManagerContext)
if Manager == nil {
s.Error = errors.New("CertificateManager invalid ")
return s
}
var err error
s.initCertOK = false
p, _ := pem.Decode([]byte(Manager.ExportCA()))

View File

@@ -26,6 +26,7 @@ type CertManager struct {
PrivateKey string
Certificates string
Cert string
context int
}
var Lock sync.Mutex
@@ -37,6 +38,7 @@ func CreateCertificate() int {
w := &CertManager{Tls: &tls.Config{}}
Context := NewMessageId()
Map[Context] = w
w.context = Context
return Context
}
@@ -51,7 +53,14 @@ func RemoveCertificate(Context int) {
c = nil
delete(Map, Context)
}
func (c *CertManager) Context() int {
Lock.Lock()
defer Lock.Unlock()
if c.context == 0 {
c.context = NewMessageId()
}
return c.context
}
func LoadCertificateContext(Context int) *CertManager {
s := Map[Context]
if s == nil {

View File

@@ -9,6 +9,7 @@ import (
"github.com/klauspost/compress/zstd"
"io"
"io/ioutil"
"strings"
)
var _null_bytes = make([]byte, 0)
@@ -111,3 +112,38 @@ func ZSTDDecompress(input []byte) []byte {
a, _ := decoder.DecodeAll(input, nil)
return a
}
var decompressors = map[string]func([]byte) []byte{
"gzip": GzipUnCompress,
"br": BrUnCompress,
"deflate": DeflateUnCompress,
"zstd": ZSTDDecompress,
"zlib": ZlibUnCompress,
}
var encompressors = map[string]func([]byte) []byte{
"gzip": GzipCompress,
"br": BrCompress,
"deflate": DeflateCompress,
"zstd": ZSTDCompress,
"zlib": ZlibCompress,
}
// CompressAuto 自动压缩 仅支持 gzip、br、deflate、zstd、zlib 算法
func CompressAuto(encoding string, data []byte) []byte {
if Compress, ok := encompressors[strings.ToLower(encoding)]; ok {
if compressed := Compress(data); len(compressed) > 0 {
return compressed
}
}
return nil
}
// UnCompressAuto 自动解压缩 仅支持 gzip、br、deflate、zstd、zlib 算法
func UnCompressAuto(encoding string, data []byte) []byte {
if decompressor, ok := decompressors[strings.ToLower(encoding)]; ok {
if uncompressed := decompressor(data); len(uncompressed) > 0 {
return uncompressed
}
}
return nil
}

View File

@@ -109,7 +109,7 @@ done
}
// startWatchdog 启动路由恢复监控脚本(防止主进程异常退出导致网络不恢复)
func startWatchdog() { // 定义 startWatchdog 函数
func startWatchdog() { // 定义 startWatchdog 函数
if watchdogStarted { // 如果已启动
return // 直接返回
}

View File

@@ -222,6 +222,9 @@ func (d *DevConn) SetWriteDeadline(t time.Time) error {
}
func (d *DevConn) PushClientPayload(payload []byte, seq uint32) {
if seq != d.clientNext {
return
}
defer func() {
d.mu.Lock()
clientNext := d.clientNext

View File

@@ -4,7 +4,6 @@
package WinDivert
import (
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/ProcessCheck"
@@ -19,11 +18,7 @@ var (
sessionsMu sync.Mutex
sessions = make(map[uint16]*DevConn)
)
func sessKey(ip net.IP, port uint16) string {
return ip.String() + ":" + fmt.Sprint(port)
}
type expiry struct {
pid int32
name string
@@ -68,8 +63,11 @@ func getPidByPort(kind string, port uint16) (int32, string) {
var loopbackV4 = net.IPv4(127, 0, 0, 1)
var loopbackV6 = net.IPv6loopback
var mm sync.Mutex
func (d *Divert) handleCommand(h *Handle, data []byte, addr *Address, tcp *layers.TCP, clientIP, serverIP net.IP, clientPort, serverPort uint16, v4 bool) {
mm.Lock()
defer mm.Unlock()
if !addr.Outbound() || (clientIP.Equal(serverIP) && (serverIP.Equal(loopbackV4) || serverIP.Equal(loopbackV6))) {
_, _ = h.Send(data, addr)
return
@@ -130,19 +128,17 @@ func (d *Divert) handleCommand(h *Handle, data []byte, addr *Address, tcp *layer
return
}
if tcp.ACK && !tcp.SYN && len(tcp.Payload) == 0 {
return
}
// 处理 payload写入 devConn 并向内核注入 ACK告知 we've consumed bytes
if len(tcp.Payload) > 0 {
fmt.Println(string(tcp.Payload))
// 写入 session buffer 并更新 clientNext
sess.PushClientPayload(tcp.Payload, tcp.Seq)
return
}
sess.mu.Lock()
sess.clientNext = tcp.Seq
sess.mu.Unlock()
// 其他情况原样放行
_, _ = h.Send(data, addr)
//_, _ = h.Send(data, addr)
return
}
func (d *Divert) handleIPv4(h *Handle, data []byte, addr *Address, ip4 *layers.IPv4, pkt gopacket.Packet) bool {

View File

@@ -16,7 +16,7 @@ import (
"time"
)
const SunnyVersion = "2025-10-24"
const SunnyVersion = "2025-11-06"
const Information = `
------------------------------------------------------
欢迎使用 SunnyNet 网络中间件 - V` + SunnyVersion + `

View File

@@ -801,7 +801,9 @@ func (c *Conn) WriteMessage(messageType int, data []byte) error {
return w.Close()
}
func (c *Conn) WriteFullMessage(messageType int, data []byte) error {
if len(data) < cap(c.writeBuf) || (c.newCompressionWriter == nil || !c.enableWriteCompression) {
a1 := len(data) < cap(c.writeBuf)
a2 := !c.enableWriteCompression
if a1 || a2 {
return c.WriteMessage(messageType, data)
}
var mw messageWriter

37
test.go
View File

@@ -3,27 +3,12 @@ package main
import "C"
import (
"fmt"
"github.com/qtgolang/SunnyNet/Api"
"github.com/qtgolang/SunnyNet/SunnyNet"
"github.com/qtgolang/SunnyNet/src/dns"
"github.com/qtgolang/SunnyNet/src/encoding/hex"
"github.com/qtgolang/SunnyNet/src/public"
"log"
"os"
)
func wss() {
defer os.Exit(0)
dns.SetDnsServer("remote")
id := Api.CreateHTTPClient()
Api.HTTPSetProxyIP(id, "http://:@183.146.240.245:13477")
Api.HTTPOpen(id, "GET", "https://www.baidu.com/s")
Api.HTTPSetHeader(id, "host", "www.baidu.com")
Api.HTTPSendBin(id, []byte(""))
fmt.Println(Api.HTTPGetCode(id))
}
func Test() {
//wss()
var Sunny = SunnyNet.NewSunny()
@@ -38,7 +23,6 @@ func Test() {
Sunny.AddHttpCertificate("api.vlightv.com", cert, SunnyNet.HTTPCertRules_Request)
*/
/*
log := func(Context int, info ...any) {
fmt.Println("x脚本日志", fmt.Sprintf("%v", info))
@@ -122,8 +106,8 @@ func Test() {
//Sunny.SetGlobalProxy("http://abc9068377_mdse-zone-abc:11223344@b062e1016fa4e9c4.abcproxy.vip:4950", 60000)
//if Sunny.OpenDrive(2) {
Sunny.ProcessAddName("as5.exe")
//Sunny.ProcessAddName("com.apple.WebKit.Networking")
Sunny.ProcessAddName("go_build_VxOCR.exe")
Sunny.ProcessAddName("chrome.exe")
//Sunny.ProcessAddName("WeChatAppEx.exe")
//Sunny.ProcessALLName(true, false)
//}
//fmt.Println(Sunny.SetIEProxy())
@@ -137,13 +121,8 @@ func Test() {
//阻止程序退出
select {}
}
func updateLog() {
//2025-07-16 修复 GET 请求可能导致服务器出现响应501https://c1-nuwa.lefile.cn/t_/cn_zh/version/css/aaa13d3c3f1d2708624c179cbba4e8bb.css
//2025-07-19 修复 WSS 可能异常断开
//2025-07-26 修复 开启强制TCP 无法访问脚本编辑
//2025-07-26 优化 脚本编辑
}
func HttpCallback(Conn SunnyNet.ConnHTTP) {
return
switch Conn.Type() {
case public.HttpSendRequest: //发起请求
fmt.Println("发起请求", Conn.URL(), Conn.Proto(), Conn.GetProcessName())
@@ -184,7 +163,6 @@ func WSCallback(Conn SunnyNet.ConnWebSocket) {
}
}
func TcpCallback(Conn SunnyNet.ConnTCP) {
return
switch Conn.Type() {
case public.SunnyNetMsgTypeTCPAboutToConnect: //即将连接
mode := string(Conn.Body())
@@ -193,23 +171,24 @@ func TcpCallback(Conn SunnyNet.ConnTCP) {
//Conn.SetNewAddress("8.8.8.8:8080")
return
case public.SunnyNetMsgTypeTCPConnectOK: //连接成功
log.Println("PID", Conn.PID(), "TCP 连接到:", Conn.LocalAddress(), "->", Conn.RemoteAddress(), "成功")
//log.Println("PID", Conn.PID(), "TCP 连接到:", Conn.LocalAddress(), "->", Conn.RemoteAddress(), "成功")
return
case public.SunnyNetMsgTypeTCPClose: //连接关闭
log.Println("PID", Conn.PID(), "TCP 断开连接:", Conn.LocalAddress(), "->", Conn.RemoteAddress())
//log.Println("PID", Conn.PID(), "TCP 断开连接:", Conn.LocalAddress(), "->", Conn.RemoteAddress())
return
case public.SunnyNetMsgTypeTCPClientSend: //客户端发送数据
log.Println("PID", Conn.PID(), "TCP 发送数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
//log.Println("PID", Conn.PID(), "TCP 发送数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
return
case public.SunnyNetMsgTypeTCPClientReceive: //客户端收到数据
log.Println("PID", Conn.PID(), "收到数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
//log.Println("PID", Conn.PID(), "收到数据", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.Type(), Conn.BodyLen(), len(Conn.Body()))
return
default:
return
}
}
func UdpCallback(Conn SunnyNet.ConnUDP) {
return
switch Conn.Type() {
case public.SunnyNetUDPTypeSend: //客户端向服务器端发送数据
log.Println("PID", Conn.PID(), Conn.GetProcessName(), "发送UDP", Conn.LocalAddress(), Conn.RemoteAddress(), Conn.BodyLen())