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
- }
|