| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 | 
							- //go:build android
 
- package libbox
 
- import (
 
- 	"archive/zip"
 
- 	"bytes"
 
- 	"debug/buildinfo"
 
- 	"io"
 
- 	"runtime/debug"
 
- 	"strings"
 
- 	"github.com/sagernet/sing/common"
 
- )
 
- const (
 
- 	androidVPNCoreTypeOpenVPN     = "OpenVPN"
 
- 	androidVPNCoreTypeShadowsocks = "Shadowsocks"
 
- 	androidVPNCoreTypeClash       = "Clash"
 
- 	androidVPNCoreTypeV2Ray       = "V2Ray"
 
- 	androidVPNCoreTypeWireGuard   = "WireGuard"
 
- 	androidVPNCoreTypeSingBox     = "sing-box"
 
- 	androidVPNCoreTypeUnknown     = "Unknown"
 
- )
 
- type AndroidVPNType struct {
 
- 	CoreType  string
 
- 	CorePath  string
 
- 	GoVersion string
 
- }
 
- func ReadAndroidVPNType(publicSourceDirList StringIterator) (*AndroidVPNType, error) {
 
- 	apkPathList := iteratorToArray[string](publicSourceDirList)
 
- 	var lastError error
 
- 	for _, apkPath := range apkPathList {
 
- 		androidVPNType, err := readAndroidVPNType(apkPath)
 
- 		if androidVPNType == nil {
 
- 			if err != nil {
 
- 				lastError = err
 
- 			}
 
- 			continue
 
- 		}
 
- 		return androidVPNType, nil
 
- 	}
 
- 	return nil, lastError
 
- }
 
- func readAndroidVPNType(publicSourceDir string) (*AndroidVPNType, error) {
 
- 	reader, err := zip.OpenReader(publicSourceDir)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	defer reader.Close()
 
- 	var lastError error
 
- 	for _, file := range reader.File {
 
- 		if !strings.HasPrefix(file.Name, "lib/") {
 
- 			continue
 
- 		}
 
- 		vpnType, err := readAndroidVPNTypeEntry(file)
 
- 		if err != nil {
 
- 			lastError = err
 
- 			continue
 
- 		}
 
- 		return vpnType, nil
 
- 	}
 
- 	for _, file := range reader.File {
 
- 		if !strings.HasPrefix(file.Name, "lib/") {
 
- 			continue
 
- 		}
 
- 		if strings.Contains(file.Name, androidVPNCoreTypeOpenVPN) || strings.Contains(file.Name, "ovpn") {
 
- 			return &AndroidVPNType{CoreType: androidVPNCoreTypeOpenVPN}, nil
 
- 		}
 
- 		if strings.Contains(file.Name, androidVPNCoreTypeShadowsocks) {
 
- 			return &AndroidVPNType{CoreType: androidVPNCoreTypeShadowsocks}, nil
 
- 		}
 
- 	}
 
- 	return nil, lastError
 
- }
 
- func readAndroidVPNTypeEntry(zipFile *zip.File) (*AndroidVPNType, error) {
 
- 	readCloser, err := zipFile.Open()
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	libContent := make([]byte, zipFile.UncompressedSize64)
 
- 	_, err = io.ReadFull(readCloser, libContent)
 
- 	readCloser.Close()
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	buildInfo, err := buildinfo.Read(bytes.NewReader(libContent))
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	var vpnType AndroidVPNType
 
- 	vpnType.GoVersion = buildInfo.GoVersion
 
- 	if !strings.HasPrefix(vpnType.GoVersion, "go") {
 
- 		vpnType.GoVersion = "obfuscated"
 
- 	} else {
 
- 		vpnType.GoVersion = vpnType.GoVersion[2:]
 
- 	}
 
- 	vpnType.CoreType = androidVPNCoreTypeUnknown
 
- 	if len(buildInfo.Deps) == 0 {
 
- 		vpnType.CoreType = "obfuscated"
 
- 		return &vpnType, nil
 
- 	}
 
- 	dependencies := make(map[string]bool)
 
- 	dependencies[buildInfo.Path] = true
 
- 	for _, module := range buildInfo.Deps {
 
- 		dependencies[module.Path] = true
 
- 		if module.Replace != nil {
 
- 			dependencies[module.Replace.Path] = true
 
- 		}
 
- 	}
 
- 	for dependency := range dependencies {
 
- 		pkgType, loaded := determinePkgType(dependency)
 
- 		if loaded {
 
- 			vpnType.CoreType = pkgType
 
- 		}
 
- 	}
 
- 	if vpnType.CoreType == androidVPNCoreTypeUnknown {
 
- 		for dependency := range dependencies {
 
- 			pkgType, loaded := determinePkgTypeSecondary(dependency)
 
- 			if loaded {
 
- 				vpnType.CoreType = pkgType
 
- 				return &vpnType, nil
 
- 			}
 
- 		}
 
- 	}
 
- 	if vpnType.CoreType != androidVPNCoreTypeUnknown {
 
- 		vpnType.CorePath, _ = determineCorePath(buildInfo, vpnType.CoreType)
 
- 		return &vpnType, nil
 
- 	}
 
- 	if dependencies["github.com/golang/protobuf"] && dependencies["github.com/v2fly/ss-bloomring"] {
 
- 		vpnType.CoreType = androidVPNCoreTypeV2Ray
 
- 		return &vpnType, nil
 
- 	}
 
- 	return &vpnType, nil
 
- }
 
- func determinePkgType(pkgName string) (string, bool) {
 
- 	pkgNameLower := strings.ToLower(pkgName)
 
- 	if strings.Contains(pkgNameLower, "clash") {
 
- 		return androidVPNCoreTypeClash, true
 
- 	}
 
- 	if strings.Contains(pkgNameLower, "v2ray") || strings.Contains(pkgNameLower, "xray") {
 
- 		return androidVPNCoreTypeV2Ray, true
 
- 	}
 
- 	if strings.Contains(pkgNameLower, "sing-box") {
 
- 		return androidVPNCoreTypeSingBox, true
 
- 	}
 
- 	return "", false
 
- }
 
- func determinePkgTypeSecondary(pkgName string) (string, bool) {
 
- 	pkgNameLower := strings.ToLower(pkgName)
 
- 	if strings.Contains(pkgNameLower, "wireguard") {
 
- 		return androidVPNCoreTypeWireGuard, true
 
- 	}
 
- 	return "", false
 
- }
 
- func determineCorePath(pkgInfo *buildinfo.BuildInfo, pkgType string) (string, bool) {
 
- 	switch pkgType {
 
- 	case androidVPNCoreTypeClash:
 
- 		return determineCorePathForPkgs(pkgInfo, []string{"github.com/Dreamacro/clash"}, []string{"clash"})
 
- 	case androidVPNCoreTypeV2Ray:
 
- 		if v2rayVersion, loaded := determineCorePathForPkgs(pkgInfo, []string{
 
- 			"github.com/v2fly/v2ray-core",
 
- 			"github.com/v2fly/v2ray-core/v4",
 
- 			"github.com/v2fly/v2ray-core/v5",
 
- 		}, []string{
 
- 			"v2ray",
 
- 		}); loaded {
 
- 			return v2rayVersion, true
 
- 		}
 
- 		if xrayVersion, loaded := determineCorePathForPkgs(pkgInfo, []string{
 
- 			"github.com/xtls/xray-core",
 
- 		}, []string{
 
- 			"xray",
 
- 		}); loaded {
 
- 			return xrayVersion, true
 
- 		}
 
- 		return "", false
 
- 	case androidVPNCoreTypeSingBox:
 
- 		return determineCorePathForPkgs(pkgInfo, []string{"github.com/sagernet/sing-box"}, []string{"sing-box"})
 
- 	case androidVPNCoreTypeWireGuard:
 
- 		return determineCorePathForPkgs(pkgInfo, []string{"golang.zx2c4.com/wireguard"}, []string{"wireguard"})
 
- 	default:
 
- 		return "", false
 
- 	}
 
- }
 
- func determineCorePathForPkgs(pkgInfo *buildinfo.BuildInfo, pkgs []string, names []string) (string, bool) {
 
- 	for _, pkg := range pkgs {
 
- 		if pkgInfo.Path == pkg {
 
- 			return pkg, true
 
- 		}
 
- 		strictDependency := common.Find(pkgInfo.Deps, func(module *debug.Module) bool {
 
- 			return module.Path == pkg
 
- 		})
 
- 		if strictDependency != nil {
 
- 			if isValidVersion(strictDependency.Version) {
 
- 				return strictDependency.Path + " " + strictDependency.Version, true
 
- 			} else {
 
- 				return strictDependency.Path, true
 
- 			}
 
- 		}
 
- 	}
 
- 	for _, name := range names {
 
- 		if strings.Contains(pkgInfo.Path, name) {
 
- 			return pkgInfo.Path, true
 
- 		}
 
- 		looseDependency := common.Find(pkgInfo.Deps, func(module *debug.Module) bool {
 
- 			return strings.Contains(module.Path, name) || (module.Replace != nil && strings.Contains(module.Replace.Path, name))
 
- 		})
 
- 		if looseDependency != nil {
 
- 			return looseDependency.Path, true
 
- 		}
 
- 	}
 
- 	return "", false
 
- }
 
- func isValidVersion(version string) bool {
 
- 	if version == "(devel)" {
 
- 		return false
 
- 	}
 
- 	if strings.Contains(version, "v0.0.0") {
 
- 		return false
 
- 	}
 
- 	return true
 
- }
 
 
  |