2026-05-26

This commit is contained in:
Qin tian
2026-05-26 22:08:09 +08:00
parent cb4b81eec1
commit 85245992f5
34 changed files with 782 additions and 131 deletions

View File

@@ -4,17 +4,19 @@ import "C"
import (
"bytes"
"fmt"
"io/ioutil"
"net/url"
"runtime"
"sort"
"strconv"
"strings"
"time"
"github.com/qtgolang/SunnyNet/SunnyNet"
"github.com/qtgolang/SunnyNet/src/Call"
"github.com/qtgolang/SunnyNet/src/SunnyProxy"
"github.com/qtgolang/SunnyNet/src/http"
"github.com/qtgolang/SunnyNet/src/public"
"io/ioutil"
"net/url"
"sort"
"strconv"
"strings"
"time"
)
// GetSunnyVersion 获取SunnyNet版本
@@ -171,6 +173,9 @@ func GetRequestClientIp(MessageId int) string {
}
k.Lock.Lock()
defer k.Lock.Unlock()
if k.Global.Drive().Name() == "tun" && runtime.GOOS == "android" {
return k.Conn.LocalAddr().String()
}
return k.Conn.RemoteAddr().String()
}

View File

@@ -0,0 +1,32 @@
@echo off
set NDK=E:\init\windows-ndk-x86_64
set CGO_ENABLED=1
set tmpPath=G:\AndroidProject\SunnyNet\app\src\main\jniLibs\
set GOOS=android
set GOARCH=arm64
set CC=%NDK%\bin\aarch64-linux-android21-clang
echo [Full]_Build_Android_arm64-v8a.so
go build -trimpath -buildmode=c-shared -ldflags "-s -w" -o "%tmpPath%arm64-v8a/libSunnyNet.so"
set GOOS=android
set GOARCH=arm
set CC=%NDK%\bin\armv7a-linux-androideabi21-clang
echo [Full]_Build_Android_armeabi-v7a.so
go build -trimpath -buildmode=c-shared -ldflags "-s -w" -o "%tmpPath%armeabi-v7a/libSunnyNet.so"
set GOOS=android
set GOARCH=386
set CC=%NDK%\bin\x86_64-linux-android21-clang
echo [Full]_Build_Android_x86.so
go build -trimpath -buildmode=c-shared -ldflags "-s -w" -o "%tmpPath%x86/libSunnyNet.so"
set GOOS=android
set GOARCH=amd64
set CC=%NDK%\bin\x86_64-linux-android21-clang
echo [Full]_Build_Android_x86_64.so
go build -trimpath -buildmode=c-shared -ldflags "-s -w" -o "%tmpPath%x86_64/libSunnyNet.so"

View File

@@ -0,0 +1,15 @@
@echo off
set NDK=E:\init\windows-ndk-x86_64
set CGO_ENABLED=1
set tmpPath=G:\AndroidProject\SunnyNet\app\src\main\jniLibs\
set GOOS=android
set GOARCH=arm64
set CC=%NDK%\bin\aarch64-linux-android21-clang
echo [Full]_Build_Android_arm64-v8a.so
go build -trimpath -buildmode=c-shared -ldflags "-s -w" -o "%tmpPath%arm64-v8a/libSunnyNet.so"

View File

@@ -168,16 +168,18 @@ func Java_com_SunnyNet_api_SunnyNetSetCallback(envObj uintptr, clazz uintptr, Su
}
defer GlobalVM.DetachCurrentThread()
_PackageName := _env.NewString(Conn.PackageName())
_Method := _env.NewString(Conn.Method())
_url := _env.NewString(Conn.URL())
_er := _env.NewString(Conn.Error())
HTTPEventClass := aliasToClass("HTTPEvent")
EventConstructor := _env.GetMethodID(HTTPEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s%s)%s", sig.Long, sig.Long, sig.Long, sig.Long, sig.String, sig.String, sig.String, sig.Long, sig.Void))
EventObj := _env.NewObjectA(HTTPEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(Conn.Theology()), Jvalue(Conn.MessageId()), Jvalue(Conn.Type()), Jvalue(_Method), Jvalue(_url), Jvalue(_er), Jvalue(Conn.PID()))
EventConstructor := _env.GetMethodID(HTTPEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s%s%s)%s", sig.Long, sig.Long, sig.Long, sig.Long, sig.String, sig.String, sig.String, sig.String, sig.Long, sig.Void))
EventObj := _env.NewObjectA(HTTPEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(Conn.Theology()), Jvalue(Conn.MessageId()), Jvalue(Conn.Type()), Jvalue(_Method), Jvalue(_url), Jvalue(_er), Jvalue(_PackageName), Jvalue(Conn.PID()))
_env.CallVoidMethodA(obj, onHTTPCallbackMethodId, Jvalue(EventObj))
_env.DeleteLocalRef(EventObj)
_env.DeleteLocalRef(_Method)
_env.DeleteLocalRef(_PackageName)
_env.DeleteLocalRef(_url)
_env.DeleteLocalRef(_er)
return
@@ -193,15 +195,17 @@ func Java_com_SunnyNet_api_SunnyNetSetCallback(envObj uintptr, clazz uintptr, Su
defer GlobalVM.DetachCurrentThread()
_LocalAddr := _env.NewString(Conn.LocalAddress())
_RemoteAddr := _env.NewString(Conn.RemoteAddress())
_PackageName := _env.NewString(Conn.PackageName())
_data := _env.NewByteArray(Conn.Body())
TCPEventClass := aliasToClass("TCPEvent")
EventConstructor := _env.GetMethodID(TCPEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s%s)%s", sig.Long, sig.String, sig.String, sig.Long, sig.Long, sig.Long, sig.Long, sig.ByteArray, sig.Void))
EventObj := _env.NewObjectA(TCPEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(_LocalAddr), Jvalue(_RemoteAddr), Jvalue(Conn.Theology()), Jvalue(Conn.MessageId()), Jvalue(Conn.Type()), Jvalue(Conn.PID()), Jvalue(_data))
EventConstructor := _env.GetMethodID(TCPEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s%s%s)%s", sig.Long, sig.String, sig.String, sig.String, sig.Long, sig.Long, sig.Long, sig.Long, sig.ByteArray, sig.Void))
EventObj := _env.NewObjectA(TCPEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(_LocalAddr), Jvalue(_RemoteAddr), Jvalue(_PackageName), Jvalue(Conn.Theology()), Jvalue(Conn.MessageId()), Jvalue(Conn.Type()), Jvalue(Conn.PID()), Jvalue(_data))
_env.CallVoidMethodA(obj, onTCPMethodId, Jvalue(EventObj))
_env.DeleteLocalRef(EventObj)
_env.DeleteLocalRef(_LocalAddr)
_env.DeleteLocalRef(_RemoteAddr)
_env.DeleteLocalRef(_PackageName)
_env.DeleteLocalRef(_data)
}
@@ -214,15 +218,17 @@ func Java_com_SunnyNet_api_SunnyNetSetCallback(envObj uintptr, clazz uintptr, Su
}
defer GlobalVM.DetachCurrentThread()
_PackageName := _env.NewString(Conn.PackageName())
_Method := _env.NewString(Conn.Method())
_url := _env.NewString(Conn.URL())
WebSocketEventClass := aliasToClass("WebSocketEvent")
EventConstructor := _env.GetMethodID(WebSocketEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s%s)%s", sig.Long, sig.Long, sig.Long, sig.Long, sig.String, sig.String, sig.Long, sig.Long, sig.Void))
EventObj := _env.NewObjectA(WebSocketEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(Conn.Theology()), Jvalue(Conn.MessageId()), Jvalue(Conn.Type()), Jvalue(_Method), Jvalue(_url), Jvalue(Conn.PID()), Jvalue(Conn.MessageType()))
EventConstructor := _env.GetMethodID(WebSocketEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s%s%s)%s", sig.Long, sig.Long, sig.Long, sig.Long, sig.String, sig.String, sig.String, sig.Long, sig.Long, sig.Void))
EventObj := _env.NewObjectA(WebSocketEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(Conn.Theology()), Jvalue(Conn.MessageId()), Jvalue(Conn.Type()), Jvalue(_Method), Jvalue(_url), Jvalue(_PackageName), Jvalue(Conn.PID()), Jvalue(Conn.MessageType()))
_env.CallVoidMethodA(obj, onWebSocketMethodId, Jvalue(EventObj))
_env.DeleteLocalRef(_Method)
_env.DeleteLocalRef(_url)
_env.DeleteLocalRef(_PackageName)
_env.DeleteLocalRef(EventObj)
return
}
@@ -237,17 +243,17 @@ func Java_com_SunnyNet_api_SunnyNetSetCallback(envObj uintptr, clazz uintptr, Su
defer GlobalVM.DetachCurrentThread()
MessageId := Conn.MessageId()
SunnyNetUDP.ResetMessage(MessageId, Conn.Body())
_PackageName := _env.NewString(Conn.PackageName())
_LocalAddr := _env.NewString(Conn.LocalAddress())
_RemoteAddr := _env.NewString(Conn.RemoteAddress())
UDPEventClass := aliasToClass("UDPEvent")
EventConstructor := _env.GetMethodID(UDPEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s)%s", sig.Long, sig.String, sig.String, sig.Long, sig.Long, sig.Long, sig.Long, sig.Void))
EventObj := _env.NewObjectA(UDPEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(_LocalAddr), Jvalue(_RemoteAddr), Jvalue(Conn.Theology()), Jvalue(MessageId), Jvalue(Conn.Type()), Jvalue(Conn.PID()))
EventConstructor := _env.GetMethodID(UDPEventClass, "<init>", fmt.Sprintf("(%s%s%s%s%s%s%s%s)%s", sig.Long, sig.String, sig.String, sig.String, sig.Long, sig.Long, sig.Long, sig.Long, sig.Void))
EventObj := _env.NewObjectA(UDPEventClass, EventConstructor, Jvalue(SunnyContext), Jvalue(_LocalAddr), Jvalue(_RemoteAddr), Jvalue(_PackageName), Jvalue(Conn.Theology()), Jvalue(MessageId), Jvalue(Conn.Type()), Jvalue(Conn.PID()))
_env.CallVoidMethodA(obj, onUDPMethodId, Jvalue(EventObj))
_env.DeleteLocalRef(EventObj)
_env.DeleteLocalRef(_LocalAddr)
_env.DeleteLocalRef(_RemoteAddr)
_env.DeleteLocalRef(_PackageName)
Conn.SetBody(SunnyNetUDP.GetMessage(MessageId))
SunnyNetUDP.DelMessage(MessageId)
return
@@ -2047,9 +2053,18 @@ func Java_com_SunnyNet_api_HTTPSetOutRouterIP(envObj uintptr, clazz uintptr, Mes
return Api.HTTPSetOutRouterIP(int(MessageId), env.GetString(value))
}
/*
Java_com_SunnyNet_api_OnTunSetFd 将 VPN TUN fd 交给 Go 侧读取ConnectivityManager 用于 Android 10+ 查询连接归属包名。
对应 Java: public static native boolean OnTunSetFd(long fd, Object ConnectivityManager);
*/
//export Java_com_SunnyNet_api_OnTunSetFd
func Java_com_SunnyNet_api_OnTunSetFd(JavaVM uintptr, reserved uintptr, fd int64) {
tun.SetFd(int(fd))
func Java_com_SunnyNet_api_OnTunSetFd(envObj uintptr, clazz uintptr, fd int64, connectivityManager uintptr) bool {
env := Env(envObj)
InitTunConnectivityManager(env, connectivityManager)
if fd <= 0 {
return false
}
return tun.SetFd(int(fd))
}
type _GlobalRef struct {

322
JavaApi/android_tun.go Normal file
View File

@@ -0,0 +1,322 @@
//go:build android
// +build android
package JavaJni
import (
"runtime"
"strings"
"sync"
)
const (
AndroidSDKQ = 29
IPProtoTCP = 6
IPProtoUDP = 17
invalidUID = -1
)
// TunPackageResult TUN 五元组查询到的包名及是否在 PackageManager 注册。
type TunPackageResult struct {
Name string
Registered bool
}
var (
// AndroidSDKInt 在 InitTunConnectivityManager 时写入当前系统 SDK。
AndroidSDKInt int
// TunPackageLookupEnabled 为 true 表示已保存 ConnectivityManager 且 SDK>=29。
TunPackageLookupEnabled bool
tunCM Jobject
tunCMMu sync.RWMutex
)
// SdkInt 读取 android.os.Build.VERSION.SDK_INT。
func SdkInt(env Env) int {
verClass := env.FindClass("android/os/Build$VERSION")
if verClass == 0 {
return 0
}
defer env.DeleteLocalRef(verClass)
field := env.GetStaticFieldID(verClass, "SDK_INT", "I")
if field == 0 {
return 0
}
return env.GetStaticIntField(verClass, field)
}
// InitTunConnectivityManager 在 OnTunSetFd 中调用:仅 Android 10+ 保存 CM 全局引用并启用包名查询。
func InitTunConnectivityManager(env Env, cm Jobject) bool {
sdk := SdkInt(env)
AndroidSDKInt = sdk
if sdk < AndroidSDKQ || cm == 0 {
clearTunConnectivityManager(env)
return false
}
clearTunConnectivityManager(env)
tunCMMu.Lock()
tunCM = env.NewGlobalRef(cm)
tunCMMu.Unlock()
TunPackageLookupEnabled = true
return true
}
func clearTunConnectivityManager(env Env) {
tunCMMu.Lock()
old := tunCM
tunCM = 0
tunCMMu.Unlock()
if old != 0 {
env.DeleteGlobalRef(old)
}
TunPackageLookupEnabled = false
}
// CurrentApplication 返回 ActivityThread.currentApplication()。
func CurrentApplication(env Env) Jobject {
atClass := env.FindClass("android/app/ActivityThread")
if atClass == 0 {
return 0
}
defer env.DeleteLocalRef(atClass)
method := env.GetStaticMethodID(atClass, "currentApplication", "()Landroid/app/Application;")
if method == 0 {
return 0
}
app := env.CallStaticObjectMethodA(atClass, method)
if app == 0 || env.ExceptionCheck() {
env.ExceptionClear()
return 0
}
return app
}
// NewInetSocketAddress 构造 java.net.InetSocketAddress(InetAddress, port)。
func NewInetSocketAddress(env Env, host string, port int) Jobject {
if host == "" || port <= 0 || port > 65535 {
return 0
}
inetClass := env.FindClass("java/net/InetAddress")
if inetClass == 0 {
return 0
}
defer env.DeleteLocalRef(inetClass)
getByName := env.GetStaticMethodID(inetClass, "getByName", "(Ljava/lang/String;)Ljava/net/InetAddress;")
if getByName == 0 {
return 0
}
hostJ := env.NewString(host)
if hostJ == 0 {
return 0
}
defer env.DeleteLocalRef(hostJ)
inetAddr := env.CallStaticObjectMethodA(inetClass, getByName, Jvalue(hostJ))
if inetAddr == 0 || env.ExceptionCheck() {
env.ExceptionClear()
return 0
}
defer env.DeleteLocalRef(inetAddr)
isaClass := env.FindClass("java/net/InetSocketAddress")
if isaClass == 0 {
return 0
}
defer env.DeleteLocalRef(isaClass)
init := env.GetMethodID(isaClass, "<init>", "(Ljava/net/InetAddress;I)V")
if init == 0 {
return 0
}
sock := env.NewObjectA(isaClass, init, Jvalue(inetAddr), Jvalue(port))
if sock == 0 || env.ExceptionCheck() {
env.ExceptionClear()
return 0
}
return sock
}
// GetConnectionOwnerUid 调用 ConnectivityManager.getConnectionOwnerUid。
func GetConnectionOwnerUid(env Env, cm Jobject, protocol int, local, remote Jobject) int {
cmClass := env.FindClass("android/net/ConnectivityManager")
if cmClass == 0 {
return invalidUID
}
defer env.DeleteLocalRef(cmClass)
method := env.GetMethodID(cmClass, "getConnectionOwnerUid",
"(ILjava/net/InetSocketAddress;Ljava/net/InetSocketAddress;)I")
if method == 0 {
return invalidUID
}
uid := env.CallIntMethodA(cm, method, Jvalue(protocol), Jvalue(local), Jvalue(remote))
if env.ExceptionCheck() {
env.ExceptionClear()
return invalidUID
}
return uid
}
// normalizePackageName 去掉 getNameForUid 返回的 ":appId" 后缀,得到纯包名。
func normalizePackageName(name string) string {
name = strings.TrimSpace(name)
if name == "" {
return ""
}
if i := strings.LastIndex(name, ":"); i > 0 {
suffix := name[i+1:]
if suffix != "" && isDecimalString(suffix) {
return name[:i]
}
}
return name
}
func isDecimalString(s string) bool {
for _, c := range s {
if c < '0' || c > '9' {
return false
}
}
return len(s) > 0
}
// isPackageRegistered 判断包名是否已安装PackageManager.getPackageInfo 能查到)。
func isPackageRegistered(env Env, pm Jobject, pmClass Jclass, pkg string) bool {
if pkg == "" {
return false
}
getPI := env.GetMethodID(pmClass, "getPackageInfo", "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;")
if getPI == 0 {
return false
}
pkgJ := env.NewString(pkg)
if pkgJ == 0 {
return false
}
defer env.DeleteLocalRef(pkgJ)
pi := env.CallObjectMethodA(pm, getPI, Jvalue(pkgJ), Jvalue(0))
if pi == 0 || env.ExceptionCheck() {
env.ExceptionClear()
return false
}
env.DeleteLocalRef(pi)
return true
}
// packageNameFromUid 优先 getPackagesForUid否则 getNameForUid 并规范化;第二返回值表示是否在 PM 注册。
func packageNameFromUid(env Env, pm Jobject, pmClass Jclass, uid int) (string, bool) {
getPkgs := env.GetMethodID(pmClass, "getPackagesForUid", "(I)[Ljava/lang/String;")
if getPkgs != 0 {
arr := env.CallObjectMethodA(pm, getPkgs, Jvalue(uid))
if arr != 0 && !env.ExceptionCheck() {
defer env.DeleteLocalRef(arr)
n := env.GetArrayLength(Jarray(arr))
for i := 0; i < n; i++ {
item := env.GetObjectArrayElement(JobjectArray(arr), i)
if item == 0 {
continue
}
pkg := strings.TrimSpace(env.GetString(Jstring(item)))
env.DeleteLocalRef(item)
if pkg != "" {
return pkg, true
}
}
} else {
env.ExceptionClear()
}
}
getName := env.GetMethodID(pmClass, "getNameForUid", "(I)Ljava/lang/String;")
if getName == 0 {
return "", false
}
nameObj := env.CallObjectMethodA(pm, getName, Jvalue(uid))
if nameObj == 0 || env.ExceptionCheck() {
env.ExceptionClear()
return "", false
}
defer env.DeleteLocalRef(nameObj)
pkg := normalizePackageName(env.GetString(Jstring(nameObj)))
if pkg == "" {
return "", false
}
return pkg, isPackageRegistered(env, pm, pmClass, pkg)
}
// PackageNameForUid 解析 UID 对应纯包名及是否已注册。
func PackageNameForUid(env Env, uid int) TunPackageResult {
ctx := CurrentApplication(env)
if ctx == 0 {
return TunPackageResult{}
}
defer env.DeleteLocalRef(ctx)
getPM := env.GetMethodID(env.GetObjectClass(ctx), "getPackageManager", "()Landroid/content/pm/PackageManager;")
if getPM == 0 {
return TunPackageResult{}
}
pm := env.CallObjectMethodA(ctx, getPM)
if pm == 0 || env.ExceptionCheck() {
env.ExceptionClear()
return TunPackageResult{}
}
defer env.DeleteLocalRef(pm)
pmClass := env.GetObjectClass(pm)
defer env.DeleteLocalRef(pmClass)
name, reg := packageNameFromUid(env, pm, pmClass, uid)
return TunPackageResult{Name: name, Registered: reg}
}
// TunGetRequestPackageName 根据五元组查询来源包名及是否在 PackageManager 注册。
func TunGetRequestPackageName(protocol int, srcIp string, srcPort int, dstIp string, dstPort int) TunPackageResult {
if !TunPackageLookupEnabled || GlobalVM == 0 {
return TunPackageResult{}
}
if protocol != IPProtoTCP && protocol != IPProtoUDP {
return TunPackageResult{}
}
tunCMMu.RLock()
cm := tunCM
tunCMMu.RUnlock()
if cm == 0 {
return TunPackageResult{}
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
env, ret := GlobalVM.AttachCurrentThread()
if ret != JNI_OK {
return TunPackageResult{}
}
defer GlobalVM.DetachCurrentThread()
local := NewInetSocketAddress(env, srcIp, srcPort)
if local == 0 {
return TunPackageResult{}
}
defer env.DeleteLocalRef(local)
remote := NewInetSocketAddress(env, dstIp, dstPort)
if remote == 0 {
return TunPackageResult{}
}
defer env.DeleteLocalRef(remote)
uid := GetConnectionOwnerUid(env, cm, protocol, local, remote)
if uid < 0 {
return TunPackageResult{}
}
return PackageNameForUid(env, uid)
}

43
JavaApi/noAndroid_tun.go Normal file
View File

@@ -0,0 +1,43 @@
//go:build !android
// +build !android
package JavaJni
const (
AndroidSDKQ = 29
IPProtoTCP = 6
IPProtoUDP = 17
)
var (
AndroidSDKInt int
TunPackageLookupEnabled bool
)
func SdkInt(env Env) int { return 0 }
func InitTunConnectivityManager(env Env, cm Jobject) bool {
AndroidSDKInt = 0
TunPackageLookupEnabled = false
return false
}
func CurrentApplication(env Env) Jobject { return 0 }
func NewInetSocketAddress(env Env, host string, port int) Jobject { return 0 }
func GetConnectionOwnerUid(env Env, cm Jobject, protocol int, local, remote Jobject) int {
return -1
}
func PackageNameForUid(env Env, uid int) TunPackageResult { return TunPackageResult{} }
type TunPackageResult struct {
Name string
Registered bool
}
func TunGetRequestPackageName(protocol int, srcIp string, srcPort int, dstIp string, dstPort int) TunPackageResult {
return TunPackageResult{}
}

View File

@@ -88,7 +88,8 @@ func (s *proxyRequest) CallbackTCPRequest(callType int, _msg *public.TcpMsg, Rem
localAddr: LocalAddr,
remoteAddr: hostname,
pid: pid,
sunnyContext: s.Global.SunnyContext,
packageName: s.packageName,
sunnyContext: s.Global.SunnyContext,
_Display: true,
_OutRouterIPFunc: s.SetOutRouterIP,
_note: s._note,
@@ -196,7 +197,8 @@ func (s *proxyRequest) CallbackBeforeRequest() {
_localAddress: s.Conn.LocalAddr().String(),
_OutRouterIPFunc: s.SetOutRouterIP,
updateRawTarget: s.UpdateRawTarget,
_note: s._note,
_note: s._note,
packageName: s.packageName,
}
s.Global.scriptHTTPCall(m)
s._note = m._note
@@ -266,7 +268,8 @@ func (s *proxyRequest) CallbackBeforeResponse() {
_localAddress: s.Conn.LocalAddr().String(),
_OutRouterIPFunc: s.SetOutRouterIP,
updateRawTarget: s.UpdateRawTarget,
_note: s._note,
_note: s._note,
packageName: s.packageName,
}
s.Global.scriptHTTPCall(m)
s._note = m._note
@@ -372,7 +375,8 @@ func (s *proxyRequest) CallbackError(err string) {
_localAddress: s.Conn.LocalAddr().String(),
_OutRouterIPFunc: s.SetOutRouterIP,
updateRawTarget: s.UpdateRawTarget,
_note: s._note,
_note: s._note,
packageName: s.packageName,
}
s.Global.scriptHTTPCall(m)
s._note = m._note
@@ -414,7 +418,8 @@ func (s *proxyRequest) CallbackWssRequest(State int, Method, Url string, msg *pu
_ClientIP: s.Conn.RemoteAddr().String(),
_localAddress: s.Conn.LocalAddr().String(),
_Display: true,
_note: s._note,
_note: s._note,
packageName: s.packageName,
}
s.Global.scriptWebsocketCall(m)
messageIdLock.Lock()

View File

@@ -2,16 +2,17 @@ package SunnyNet
import (
"bytes"
"io"
"net/url"
"strconv"
"strings"
"github.com/qtgolang/SunnyNet/src/CrossCompiled"
"github.com/qtgolang/SunnyNet/src/Interface"
"github.com/qtgolang/SunnyNet/src/SunnyProxy"
"github.com/qtgolang/SunnyNet/src/crypto/tls"
"github.com/qtgolang/SunnyNet/src/http"
"github.com/qtgolang/SunnyNet/src/public"
"io"
"net/url"
"strconv"
"strings"
)
type ConnHTTP Interface.ConnHTTPCall
@@ -37,8 +38,15 @@ type httpConn struct {
_note string
_OutRouterIPFunc func(string) bool
updateRawTarget func(int uint32)
packageName string //安卓模式下 pid 始终为0,但可以获取对应包名
}
func (h *httpConn) PackageName() string {
return h.packageName
}
func (h *httpConn) SetPackageName(packageName string) {
h.packageName = packageName
}
func (h *httpConn) SetNote(s string) {
h._note = s
}

View File

@@ -21,8 +21,15 @@ type tcpConn struct {
_Display bool
_OutRouterIPFunc func(string) bool
_note string
packageName string //安卓模式下 pid 始终为0,但可以获取对应包名
}
func (t *tcpConn) PackageName() string {
return t.packageName
}
func (t *tcpConn) SetPackageName(packageName string) {
t.packageName = packageName
}
func (t *tcpConn) SetNote(s string) {
t._note = s
}

View File

@@ -19,6 +19,11 @@ type udpConn struct {
data []byte
_Display bool
_note string
packageName string //安卓模式下 pid 始终为0,但可以获取对应包名
}
func (u *udpConn) PackageName() string {
return u.packageName
}
func (u *udpConn) SetNote(s string) {

View File

@@ -22,12 +22,19 @@ type wsConn struct {
_Display bool
_localAddress string
_note string
packageName string //安卓模式下 pid 始终为0,但可以获取对应包名
}
func (w *wsConn) SetNote(s string) {
w._note = s
}
func (w *wsConn) PackageName() string {
return w.packageName
}
func (w *wsConn) SetPackageName(packageName string) {
w.packageName = packageName
}
func (w *wsConn) GetNote() string {
return w._note
}

View File

@@ -193,6 +193,7 @@ type proxyRequest struct {
Target *TargetInfo //目标连接信息
ProxyHost string //请求之上的代理
Pid string //s5连接过来的pid
packageName string //安卓 TUN 来源包名
Global *Sunny //继承全局中间件信息
Request *http.Request //要发送的请求体
Response response //HTTP响应体
@@ -2271,6 +2272,11 @@ func (s *Sunny) OpenDrive(DevMode int) bool {
return divert.Run()
}
// Drive 加载的驱动、全局共享
func (s *Sunny) Drive() ProcessDrv.Dev {
return divert
}
// ProcessALLName 是否允许所有进程通过 所有 SunnyNet 通用,
// StopNetwork 是否对所有进程执行一次断网操作
// 请注意GoLang调试时候StopNetwork请不要设置true
@@ -2442,6 +2448,7 @@ func (s *proxyRequest) clone() *proxyRequest {
defaultScheme: s.defaultScheme,
SendTimeout: s.SendTimeout,
rawTarget: s.rawTarget,
packageName: s.packageName,
}
if s.outRouterIP != nil {
req.outRouterIP = &net.TCPAddr{IP: s.outRouterIP.IP}
@@ -2557,6 +2564,7 @@ func (s *Sunny) handleClientConn(conn net.Conn) {
req.setSocket5User("驱动程序")
//如果是 通过 NFapi 驱动进来的数据 对连接信息进行赋值
req.Pid = info.GetPid()
req.packageName = info.GetPackageName()
req.Target.Parse(info.GetRemoteAddress(), info.GetRemotePort(), info.IsV6())
if s.connHijack != nil {
if s.connHijack(&_hijack{req}) {

View File

@@ -3,10 +3,11 @@ package SunnyNet
import (
"bytes"
"fmt"
"sync"
"github.com/qtgolang/SunnyNet/src/Call"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/SunnyNetUDP"
"github.com/qtgolang/SunnyNet/src/public"
"sync"
"net"
"sync/atomic"
@@ -170,7 +171,7 @@ func (s *Sunny) listenUdpGo() {
go s.goUdp(_info, Item.Tid, addr.String(), _info.RemoteAddress, conn, keyHash)
}
if Item.remote != nil {
bs = s.udpSendReceive(public.SunnyNetUDPTypeSend, Item.Tid, 0, addr.String(), _info.RemoteAddress, _info.Data)
bs = s.udpSendReceive(public.SunnyNetUDPTypeSend, Item.Tid, 0, addr.String(), _info.RemoteAddress, _info.Data, "")
if len(bs) > 0 {
_, _ = Item.remote.Write(bs)
}
@@ -191,7 +192,7 @@ func (s *Sunny) goUdp(info *udpInfo, tid int64, Local, Remote string, conn *net.
break
}
// 调用 udpNFSendReceive 方法发送并接收数据,并将返回的数据添加来源信息
bs := s.udpSendReceive(public.SunnyNetUDPTypeReceive, tid, 0, Local, Remote, buff[:nt])
bs := s.udpSendReceive(public.SunnyNetUDPTypeReceive, tid, 0, Local, Remote, buff[:nt], "")
if len(bs) < 1 {
continue
}
@@ -202,18 +203,24 @@ func (s *Sunny) goUdp(info *udpInfo, tid int64, Local, Remote string, conn *net.
_, _ = s.udpSocket.WriteToUDP(data, info.LocalAddress)
}
s.udpSendReceive(public.SunnyNetUDPTypeClosed, tid, 0, Local, Remote, nil)
s.udpSendReceive(public.SunnyNetUDPTypeClosed, tid, 0, Local, Remote, nil, "")
SunnyNetUDP.DelUDPItem(tid)
mu.Lock()
delete(list, keyHash)
mu.Unlock()
}
func (s *Sunny) udpSendReceive(Type int, Theoni int64, pid uint32, LocalAddress, RemoteAddress string, data []byte) []byte {
func (s *Sunny) udpSendReceive(Type int, Theoni int64, pid uint32, LocalAddress, RemoteAddress string, data []byte, packageName string) []byte {
if s.disableUDP {
return nil
}
n := &udpConn{theology: Theoni, messageId: NewMessageId(), _type: Type, sunnyContext: s.SunnyContext, pid: int(pid), localAddress: LocalAddress, remoteAddress: RemoteAddress, data: data, _Display: true}
pkg := packageName
if item := SunnyNetUDP.GetUDPItem(Theoni); item != nil {
if p, ok := item.(interface{ GetPackageName() string }); ok && pkg == "" {
pkg = p.GetPackageName()
}
}
n := &udpConn{theology: Theoni, messageId: NewMessageId(), _type: Type, sunnyContext: s.SunnyContext, pid: int(pid), packageName: pkg, localAddress: LocalAddress, remoteAddress: RemoteAddress, data: data, _Display: true}
s.scriptUDPCall(n)
if !n._Display {
return n.Body()

View File

@@ -1,8 +1,9 @@
package Interface
import (
"github.com/qtgolang/SunnyNet/src/http"
"io"
"github.com/qtgolang/SunnyNet/src/http"
)
/* =============================== 脚本 中 使用的接口 ================================================ */
@@ -478,6 +479,15 @@ type general interface {
*/
PID() int
/*
PackageName
返回当前会话由发起的安卓进程的包名
(仅安卓下有效)
*/
PackageName() string
/*
Theology

View File

@@ -11,6 +11,7 @@ type DrvInfo interface {
GetRemoteAddress() string
GetRemotePort() uint16
GetPid() string
GetPackageName() string
IsV6() bool
ID() uint64
Close() error

View File

@@ -15,14 +15,15 @@ import (
"context"
"encoding/binary"
"fmt"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/ProcessCheck"
"net"
"os"
"path/filepath"
"sync"
"time"
"unsafe"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/ProcessCheck"
)
var HandleClientConn func(net.Conn)
@@ -194,6 +195,9 @@ func (p *proxyProcessInfo) GetRemotePort() uint16 {
func (p *proxyProcessInfo) GetPid() string {
return p.Pid
}
func (p *proxyProcessInfo) GetPackageName() string {
return ""
}
func (p *proxyProcessInfo) IsV6() bool {
return p.V6
}

View File

@@ -6,18 +6,20 @@ package NFapi
import "C"
import (
"fmt"
. "github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/ProcessCheck"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/SunnyNetUDP"
net2 "github.com/qtgolang/SunnyNet/src/iphlpapi/net"
"github.com/qtgolang/SunnyNet/src/public"
"github.com/shirou/gopsutil/process"
"net"
"regexp"
"strconv"
"strings"
"sync/atomic"
"syscall"
. "github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/ProcessCheck"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/SunnyNetUDP"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/tun/Tun"
net2 "github.com/qtgolang/SunnyNet/src/iphlpapi/net"
"github.com/qtgolang/SunnyNet/src/public"
"github.com/shirou/gopsutil/process"
)
func getTcpInfoPID(tcpInfo string) string {
@@ -231,7 +233,7 @@ func udpClosed(id uint64, pConnInfo *NF_UDP_CONN_INFO) {
return
}
if UdpSendReceiveFunc != nil {
UdpSendReceiveFunc(public.SunnyNetUDPTypeClosed, obj.Theoni, pConnInfo.ProcessId.Get(), pConnInfo.LocalAddress.String(), obj.Send.RemoteAddress.String(), nil)
UdpSendReceiveFunc(public.SunnyNetUDPTypeClosed, obj.Theoni, pConnInfo.ProcessId.Get(), pConnInfo.LocalAddress.String(), obj.Send.RemoteAddress.String(), nil, "")
}
mu.Lock()
delete(list, id)
@@ -262,7 +264,7 @@ func udpReceive(id uint64, RemoteAddress *SockaddrInx, buf []byte, options *NF_U
obj.Receive = &NfOPT{Id: id, RemoteAddress: RemoteAddress.Clone(), options: options.Clone()}
}
mu.Unlock()
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeReceive, obj.Theoni, pid, LocalAddress.String(), RemoteAddress.String(), buf)
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeReceive, obj.Theoni, pid, LocalAddress.String(), RemoteAddress.String(), buf, "")
if len(bs) > 0 {
_, _ = Api.NfUdpPostReceive(id, RemoteAddress, bs, options)
}
@@ -294,7 +296,7 @@ func udpSend(id uint64, RemoteAddress *SockaddrInx, buf []byte, options *NF_UDP_
}
mu.Unlock()
//这里因为是接收 所以 RemoteAddress 是本地地址 而 LocalAddress 是远程地址
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeReceive, obj.Theoni, pid, RemoteAddress.String(), LocalAddress.String(), buf)
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeReceive, obj.Theoni, pid, RemoteAddress.String(), LocalAddress.String(), buf, "")
if len(bs) > 0 {
_, _ = Api.NfUdpPostSend(id, RemoteAddress, bs, options)
}
@@ -311,13 +313,13 @@ func udpSend(id uint64, RemoteAddress *SockaddrInx, buf []byte, options *NF_UDP_
mu.Lock()
list[id] = obj
mu.Unlock()
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeSend, obj.Theoni, pid, LocalAddress.String(), RemoteAddress.String(), buf)
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeSend, obj.Theoni, pid, LocalAddress.String(), RemoteAddress.String(), buf, "")
if len(bs) > 0 {
_, _ = Api.NfUdpPostSend(id, RemoteAddress, bs, options)
}
} else {
// 如果连接已建立,则发送数据
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeSend, obj.Theoni, pid, LocalAddress.String(), RemoteAddress.String(), buf)
bs := UdpSendReceiveFunc(public.SunnyNetUDPTypeSend, obj.Theoni, pid, LocalAddress.String(), RemoteAddress.String(), buf, "")
if len(bs) > 0 {
_, _ = Api.NfUdpPostSend(id, RemoteAddress, bs, options)
}
@@ -332,4 +334,4 @@ func udpCanSend(id uint64) {
return
}
var UdpSendReceiveFunc func(Type int, Theoni int64, pid uint32, LocalAddress, RemoteAddress string, data []byte) []byte
var UdpSendReceiveFunc Tun.UdpFunc

View File

@@ -6,11 +6,12 @@ package NFapi
import (
"encoding/binary"
"fmt"
. "github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/nfapi/basetype"
"net"
"reflect"
"unsafe"
. "github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/nfapi/basetype"
)
const (
@@ -99,6 +100,9 @@ func (p *ProcessInfo) GetRemotePort() uint16 {
func (p *ProcessInfo) GetPid() string {
return p.Pid
}
func (p *ProcessInfo) GetPackageName() string {
return ""
}
func (p *ProcessInfo) IsV6() bool {
return p.V6
}

View File

@@ -6,11 +6,12 @@ package NFapi
import (
"encoding/binary"
"fmt"
. "github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/nfapi/basetype"
"net"
"reflect"
"unsafe"
. "github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/nfapi/basetype"
)
const (
@@ -91,6 +92,10 @@ func (p *ProcessInfo) GetRemotePort() uint16 {
func (p *ProcessInfo) GetPid() string {
return p.Pid
}
func (p *ProcessInfo) GetPackageName() string {
return ""
}
func (p *ProcessInfo) IsV6() bool {
return p.V6
}

View File

@@ -9,7 +9,9 @@ type Interface interface {
SetOutRouterIP(RouterIP string) bool
Port() int
}
type UdpFunc func(Type int, Theoni int64, pid uint32, LocalAddress string, RemoteAddress string, data []byte) []byte
// UdpFunc PackageName 为安卓模式下的包名,其他系统下无值
type UdpFunc func(Type int, Theoni int64, pid uint32, LocalAddress string, RemoteAddress string, data []byte, PackageName string) []byte
type TcpFunc func(conn n2.Conn)
var _myPid = int32(os.Getpid())

View File

@@ -14,6 +14,7 @@ import (
"time"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/ProcessCheck"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/tun/getPackageName"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/tun/tunPublic"
)
@@ -24,6 +25,9 @@ type DevConn struct {
serverIP net.IP
serverPort uint16
packageName string
packageRegistered bool
// TCP 序列号跟踪
clientNext uint32 // 客户端下一个期望的 seq
serverISN uint32 // 我们伪造的 server 初始序列号
@@ -62,6 +66,10 @@ func (d *DevConn) GetPid() string {
return strconv.Itoa(int(d.pid))
}
func (d *DevConn) GetPackageName() string {
return d.packageName
}
func (d *DevConn) IsV6() bool {
return !d.v4
}
@@ -70,8 +78,8 @@ func (d *DevConn) ID() uint64 {
return uint64(d.clientPort)
}
// 构造函数
func NewDevConn(h io.ReadWriteCloser, clientIP net.IP, clientPort uint16, serverIP net.IP, serverPort uint16, ipv4 bool, clientSynSeq uint32) *DevConn {
// 构造函数protocol 为 IP 协议号TCP=6UDP=17仅 Android 用于查询来源包名。
func NewDevConn(h io.ReadWriteCloser, clientIP net.IP, clientPort uint16, serverIP net.IP, serverPort uint16, ipv4 bool, protocol int, clientSynSeq uint32) *DevConn {
// 创建 DevConn 基本信息
d := &DevConn{
clientIP: clientIP, // 客户端 IP
@@ -82,6 +90,14 @@ func NewDevConn(h io.ReadWriteCloser, clientIP net.IP, clientPort uint16, server
v4: ipv4, // 是否 IPv4
tun: h, // TUN 句柄
}
// 包名查询走 getPackageName 五元组缓存,减少 JNI 调用
pkgRes := getPackageName.GetRequestPackageName(
protocol,
clientIP.String(), int(clientPort),
serverIP.String(), int(serverPort),
)
d.packageName = pkgRes.Name
d.packageRegistered = pkgRes.Registered
d.ts = time.Now()
// 客户端期望 seq客户端 ISN + 1
// 第一个数据包的 tcp.Seq 必须等于这个值才会被接收

View File

@@ -19,7 +19,7 @@ import (
func (n *NewTun) handleTCPCommand(tcp *layers.TCP, srcIP, dstIP net.IP, clientPort, serverPort uint16, v4 bool) {
if tcp.SYN && !tcp.ACK {
pid, name := getPidByPort("tcp", uint16(tcp.SrcPort))
s := NewDevConn(n.tun, srcIP, clientPort, dstIP, serverPort, v4, tcp.Seq)
s := NewDevConn(n.tun, srcIP, clientPort, dstIP, serverPort, v4, 6, tcp.Seq)
s.pid = uint32(pid)
sessionsMu.Lock()
sessions[clientPort] = s
@@ -35,7 +35,7 @@ func (n *NewTun) handleTCPCommand(tcp *layers.TCP, srcIP, dstIP net.IP, clientPo
sessionsMu.Lock()
call := n.handleTCPCallback
sessionsMu.Unlock()
if n.pidFromCheck(pid, name) {
if (s.packageName != "" && !s.packageRegistered) || n.pidFromCheck(pid, name) {
go func() {
var loader *net.TCPAddr
if defaultGatewayIP != "" {
@@ -79,7 +79,7 @@ func (n *NewTun) handleTCPCommand(tcp *layers.TCP, srcIP, dstIP net.IP, clientPo
sess, ok := sessions[clientPort]
sessionsMu.Unlock()
if !ok {
h2 := NewDevConn(n.tun, srcIP, clientPort, dstIP, serverPort, v4, tcp.Seq)
h2 := NewDevConn(n.tun, srcIP, clientPort, dstIP, serverPort, v4, 6, tcp.Seq)
_ = SendRstToClient(h2)
return
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/SunnyNetUDP"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/tun/getPackageName"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/tun/tunPublic"
"github.com/qtgolang/SunnyNet/src/public"
)
@@ -30,8 +31,13 @@ type connEntry struct {
v4 bool
pid int32
pidFromCheck bool
callback func(Type int, Theoni int64, pid uint32, LocalAddress, RemoteAddress string, data []byte) []byte
callback func(Type int, Theoni int64, pid uint32, LocalAddress, RemoteAddress string, data []byte, packageName string) []byte
mu *sync.Mutex
packageName string
}
func (c *connEntry) GetPackageName() string {
return c.packageName
}
func (c *connEntry) ToClient(payload []byte) bool {
@@ -116,9 +122,16 @@ func (n *NewTun) handleUDP(srcIP, dstIP net.IP, udp *layers.UDP, v4 bool) {
if obj == nil {
var mu sync.Mutex
obj = &connEntry{ClientIP: clientIP, ClientPort: clientPort, ServerIP: serverIP, ServerPort: serverPort, Theology: atomic.AddInt64(&public.Theology, 1), v4: v4, callback: n.handleUDPCallback, mu: &mu, fd: n.tun}
pkgRes := getPackageName.GetRequestPackageName(
getPackageName.ProtocolUDP,
clientIP.String(), int(clientPort),
serverIP.String(), int(serverPort),
)
obj.packageName = pkgRes.Name
pid, name := getPidByPort("udp", clientPort)
obj.pid = pid
obj.pidFromCheck = n.pidFromCheck(obj.pid, name)
obj.pidFromCheck = (pkgRes.Name != "" && !pkgRes.Registered) || n.pidFromCheck(obj.pid, name)
connTable[clientPort] = obj
connMu.Unlock()
mu.Lock()
@@ -159,7 +172,7 @@ func (n *NewTun) handleUDP(srcIP, dstIP net.IP, udp *layers.UDP, v4 bool) {
if tunPublic.IsLocalIp(obj.ServerIP) {
RemoteAddress = net.JoinHostPort("127.0.0.1", strconv.Itoa(int(obj.ServerPort)))
}
bs := obj.callback(public.SunnyNetUDPTypeSend, obj.Theology, uint32(obj.pid), LocalAddress, RemoteAddress, Payload)
bs := obj.callback(public.SunnyNetUDPTypeSend, obj.Theology, uint32(obj.pid), LocalAddress, RemoteAddress, Payload, obj.packageName)
if len(bs) < 1 {
return
}
@@ -202,7 +215,7 @@ func (c *connEntry) loop() {
return
}
bs := c.callback(public.SunnyNetUDPTypeReceive, c.Theology, uint32(c.pid), LocalAddress, RemoteAddress, buff[:nt])
bs := c.callback(public.SunnyNetUDPTypeReceive, c.Theology, uint32(c.pid), LocalAddress, RemoteAddress, buff[:nt], c.packageName)
if len(bs) < 1 {
continue
}
@@ -218,7 +231,7 @@ func (c *connEntry) loop() {
return
}
if c.callback != nil {
c.callback(public.SunnyNetUDPTypeClosed, c.Theology, uint32(c.pid), LocalAddress, RemoteAddress, nil)
c.callback(public.SunnyNetUDPTypeClosed, c.Theology, uint32(c.pid), LocalAddress, RemoteAddress, nil, c.packageName)
}
delete(connTable, c.ClientPort)
}

View File

@@ -67,6 +67,10 @@ func (d *DevConn) GetPid() string {
return strconv.Itoa(int(d.pid))
}
func (d *DevConn) GetPackageName() string {
return ""
}
func (d *DevConn) IsV6() bool {
return !d.v4
}

View File

@@ -126,7 +126,7 @@ func (d *Divert) Run() bool {
func (d *Divert) SetHandle(
callbackTCP func(conn net.Conn),
checkProcess func(int32, string) bool,
udpSendReceiveFunc func(Type int, Theoni int64, pid uint32, LocalAddress, RemoteAddress string, data []byte) []byte) {
udpSendReceiveFunc Tun.UdpFunc) {
sessionsMu.Lock()
defer sessionsMu.Unlock()
d.handleTCP = callbackTCP

View File

@@ -6,14 +6,15 @@ package WinDivert
import (
"bytes"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/SunnyNetUDP"
"github.com/qtgolang/SunnyNet/src/public"
"net"
"strconv"
"sync"
"sync/atomic"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/SunnyNetUDP"
"github.com/qtgolang/SunnyNet/src/public"
)
type expiryUDP struct {
@@ -153,9 +154,9 @@ func (d *Divert) handleCommandUDP(h *Handle, data []byte, addr *Address, udp *la
RemoteAddress := net.JoinHostPort(serverIP.String(), strconv.Itoa(int(serverPort)))
var bs []byte
if addr.Outbound() {
bs = call(public.SunnyNetUDPTypeSend, obj.Theology, uint32(obj.pid), LocalAddress, RemoteAddress, payload)
bs = call(public.SunnyNetUDPTypeSend, obj.Theology, uint32(obj.pid), LocalAddress, RemoteAddress, payload, "")
} else {
bs = call(public.SunnyNetUDPTypeReceive, obj.Theology, uint32(obj.pid), RemoteAddress, LocalAddress, payload)
bs = call(public.SunnyNetUDPTypeReceive, obj.Theology, uint32(obj.pid), RemoteAddress, LocalAddress, payload, "")
}
if len(bs) > 0 {
if bytes.Equal(bs, payload) {
@@ -265,7 +266,7 @@ func (d *Divert) runFlow() bool {
call := d.handleUDP
sessionsMu.Unlock()
if call != nil {
call(public.SunnyNetUDPTypeClosed, obj.Theology, uint32(obj.pid), LocalAddress, RemoteAddress, nil)
call(public.SunnyNetUDPTypeClosed, obj.Theology, uint32(obj.pid), LocalAddress, RemoteAddress, nil, "")
}
SunnyNetUDP.DelUDPItem(obj.Theology)
}

View File

@@ -0,0 +1,23 @@
//go:build android
// +build android
package getPackageName
import JavaJni "github.com/qtgolang/SunnyNet/JavaApi"
const (
ProtocolTCP = 6 // IPPROTO_TCP
ProtocolUDP = 17 // IPPROTO_UDP
)
// SetConnectivityManager 兼容旧接口;实际 CM 由 JavaJni.InitTunConnectivityManager 在 OnTunSetFd 中注入。
// 调用时清空包名缓存,避免 VPN 重建后沿用旧五元组结果。
func SetConnectivityManager(uintptr) {
ClearPackageNameCache()
}
// queryPackageName 执行一次 JNI 查询(无缓存);由 GetRequestPackageName 统一加缓存。
func queryPackageName(protocol int, srcIp string, srcPort int, dstIp string, dstPort int) Result {
r := JavaJni.TunGetRequestPackageName(protocol, srcIp, srcPort, dstIp, dstPort)
return Result{Name: r.Name, Registered: r.Registered}
}

View File

@@ -0,0 +1,116 @@
// Package getPackageName 在 Android TUN 模式下根据五元组查询流量来源包名。
// 查询结果带内存缓存,避免 SYN 重传、UDP 多包等场景反复调用 JNI。
package getPackageName
import (
"fmt"
"sync"
"time"
)
const (
// pkgCacheTTLPositive 命中包名时的缓存时长(连接存活期内五元组通常不变)。
pkgCacheTTLPositive = 2 * time.Minute
// pkgCacheTTLNegative 未查到包名时的缓存时长(较短,便于 socket 登记后尽快重试)。
pkgCacheTTLNegative = 5 * time.Second
// maxCacheEntries 缓存条数上限,防止长时间运行内存无限增长。
maxCacheEntries = 2048
)
// cacheItem 单条缓存记录。
type cacheItem struct {
result Result // 查询结果(包名 + 是否已在 PackageManager 注册)
expire time.Time // 过期时间
}
var (
pkgCacheMu sync.RWMutex
pkgCache = make(map[string]cacheItem) // key 见 cacheKey
)
// cacheKey 生成五元组缓存键protocol|srcIp|srcPort|dstIp|dstPort。
// src 为 TUN 上看到的客户端应用侧dst 为目标服务器。
func cacheKey(protocol int, srcIp string, srcPort int, dstIp string, dstPort int) string {
return fmt.Sprintf("%d|%s|%d|%s|%d", protocol, srcIp, srcPort, dstIp, dstPort)
}
// getFromCache 读取缓存;第二个返回值为 true 表示命中且未过期。
func getFromCache(protocol int, srcIp string, srcPort int, dstIp string, dstPort int) (Result, bool) {
key := cacheKey(protocol, srcIp, srcPort, dstIp, dstPort)
now := time.Now()
pkgCacheMu.RLock()
item, ok := pkgCache[key]
pkgCacheMu.RUnlock()
if !ok {
return Result{}, false
}
if now.After(item.expire) {
// 惰性删除过期项,避免后台清扫协程
pkgCacheMu.Lock()
if cur, still := pkgCache[key]; still && now.After(cur.expire) {
delete(pkgCache, key)
}
pkgCacheMu.Unlock()
return Result{}, false
}
return item.result, true
}
// putCache 写入缓存;有包名与无包名使用不同 TTL。
func putCache(protocol int, srcIp string, srcPort int, dstIp string, dstPort int, r Result) {
ttl := pkgCacheTTLNegative
if r.Name != "" {
ttl = pkgCacheTTLPositive
}
key := cacheKey(protocol, srcIp, srcPort, dstIp, dstPort)
item := cacheItem{result: r, expire: time.Now().Add(ttl)}
pkgCacheMu.Lock()
pkgCache[key] = item
if len(pkgCache) > maxCacheEntries {
evictExpiredLocked(time.Now())
// 仍超限则整表重建(极端流量下的兜底)
if len(pkgCache) > maxCacheEntries {
pkgCache = make(map[string]cacheItem)
}
}
pkgCacheMu.Unlock()
}
// evictExpiredLocked 删除所有已过期项;调用方需已持有 pkgCacheMu 写锁。
func evictExpiredLocked(now time.Time) {
for k, v := range pkgCache {
if now.After(v.expire) {
delete(pkgCache, k)
}
}
}
// ClearPackageNameCache 清空五元组包名查询缓存。
// 建议在 VPN 重启、OnTunSetFd 或切换用户后调用,避免沿用过期归属信息。
func ClearPackageNameCache() {
pkgCacheMu.Lock()
pkgCache = make(map[string]cacheItem)
pkgCacheMu.Unlock()
}
// GetRequestPackageName 按五元组查询来源包名。
//
// 参数:
// - protocolIP 协议号TCP=6UDP=17
// - srcIp/srcPortTUN 包中的源地址VPN 内应用侧,如 10.0.0.2:port
// - dstIp/dstPortTUN 包中的目的地址
//
// 先查缓存,未命中则调用 queryPackageNameAndroid 下走 ConnectivityManager JNI
func GetRequestPackageName(protocol int, srcIp string, srcPort int, dstIp string, dstPort int) Result {
if r, ok := getFromCache(protocol, srcIp, srcPort, dstIp, dstPort); ok {
return r
}
r := queryPackageName(protocol, srcIp, srcPort, dstIp, dstPort)
if r.Name == "" {
return r
}
putCache(protocol, srcIp, srcPort, dstIp, dstPort, r)
return r
}

View File

@@ -0,0 +1,17 @@
//go:build !android
// +build !android
package getPackageName
const (
ProtocolTCP = 6
ProtocolUDP = 17
)
// SetConnectivityManager 非 Android 平台无 TUN 包名查询能力。
func SetConnectivityManager(uintptr) {}
// queryPackageName 非 Android 恒为空结果。
func queryPackageName(protocol int, srcIp string, srcPort int, dstIp string, dstPort int) Result {
return Result{}
}

View File

@@ -0,0 +1,7 @@
package getPackageName
// Result 为 TUN 五元组包名查询结果(与 JavaApi.TunPackageResult 对应)。
type Result struct {
Name string // 来源应用包名(已去掉 getNameForUid 的 ":appId" 后缀)
Registered bool // Name 是否为已安装包pm list packages 可查false 时可能为共享 UID 合成名
}

View File

@@ -32,5 +32,5 @@ func UnInstall() bool {
return true
}
func SetFd(fd int) bool {
return false
return true
}

View File

@@ -6,15 +6,16 @@ package tun
import (
"bufio"
"bytes"
"io"
"os/exec"
"strings"
"syscall"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/Info"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/ProcessCheck"
"github.com/qtgolang/SunnyNet/src/ProcessDrv/tun/Tun"
divert "github.com/qtgolang/SunnyNet/src/ProcessDrv/tun/WinDivert"
"github.com/qtgolang/SunnyNet/src/Resource"
"io"
"os/exec"
"strings"
"syscall"
)
var Divert = divert.NewWinDivert()

View File

@@ -1,54 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build 386 amd64
package cpu_test
import (
. "internal/cpu"
"os"
"runtime"
"testing"
)
func TestX86ifAVX2hasAVX(t *testing.T) {
if X86.HasAVX2 && !X86.HasAVX {
t.Fatalf("HasAVX expected true when HasAVX2 is true, got false")
}
}
func TestDisableSSE2(t *testing.T) {
runDebugOptionsTest(t, "TestSSE2DebugOption", "cpu.sse2=off")
}
func TestSSE2DebugOption(t *testing.T) {
MustHaveDebugOptionsSupport(t)
if os.Getenv("GODEBUG") != "cpu.sse2=off" {
t.Skipf("skipping test: GODEBUG=cpu.sse2=off not set")
}
want := runtime.GOARCH != "386" // SSE2 can only be disabled on 386.
if got := X86.HasSSE2; got != want {
t.Errorf("X86.HasSSE2 on %s expected %v, got %v", runtime.GOARCH, want, got)
}
}
func TestDisableSSE3(t *testing.T) {
runDebugOptionsTest(t, "TestSSE3DebugOption", "cpu.sse3=off")
}
func TestSSE3DebugOption(t *testing.T) {
MustHaveDebugOptionsSupport(t)
if os.Getenv("GODEBUG") != "cpu.sse3=off" {
t.Skipf("skipping test: GODEBUG=cpu.sse3=off not set")
}
want := false
if got := X86.HasSSE3; got != want {
t.Errorf("X86.HasSSE3 expected %v, got %v", want, got)
}
}

View File

@@ -17,7 +17,7 @@ import (
"github.com/qtgolang/SunnyNet/src/websocket"
)
const SunnyVersion = "2026-05-22"
const SunnyVersion = "2026-05-26"
const Information = `
------------------------------------------------------
欢迎使用 SunnyNet 网络中间件 - V` + SunnyVersion + `