فهرست منبع

Avoid issues on older Android versions

Rod Hynes 10 ماه پیش
والد
کامیت
90ad1b17e4
5فایلهای تغییر یافته به همراه91 افزوده شده و 0 حذف شده
  1. 2 0
      MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java
  2. 6 0
      psiphon/config.go
  3. 2 0
      psiphon/controller.go
  4. 4 0
      psiphon/inproxy.go
  5. 77 0
      psiphon/utils.go

+ 2 - 0
MobileLibrary/Android/PsiphonTunnel/PsiphonTunnel.java

@@ -795,6 +795,8 @@ public class PsiphonTunnel {
 
         json.put("ClientPlatform", clientPlatform.toString().replaceAll("[^\\w\\-\\.]", "_"));
 
+        json.put("ClientAPILevel", Build.VERSION.SDK_INT);
+
         return json.toString();
     }
 

+ 6 - 0
psiphon/config.go

@@ -127,6 +127,12 @@ type Config struct {
 	// the client reports to the server.
 	ClientPlatform string
 
+	// ClientAPILevel is the client API or SDK level, such as
+	// Build.VERSION.SDK_INT on Android. This value is used for local
+	// configuration, as required, and not reported to the server.
+	// ClientAPILevel is ignored when 0.
+	ClientAPILevel int
+
 	// ClientFeatures is a list of feature names denoting enabled application
 	// features. Clients report enabled features to the server for stats
 	// purposes.

+ 2 - 0
psiphon/controller.go

@@ -118,6 +118,8 @@ func NewController(config *Config) (controller *Controller, err error) {
 	// Needed by regen, at least
 	rand.Seed(int64(time.Now().Nanosecond()))
 
+	applyClientAPILevel(config)
+
 	// The session ID for the Psiphon server API is used across all
 	// tunnels established by the controller.
 	NoticeSessionId(config.SessionID)

+ 4 - 0
psiphon/inproxy.go

@@ -1658,6 +1658,10 @@ func NewInproxyWebRTCDialInstance(
 		awaitReadyToProxyTimeout = p.Duration(parameters.InproxyClientWebRTCAwaitReadyToProxyTimeout)
 	}
 
+	if clientAPILevelDisableInproxyPortMapping.Load() {
+		disablePortMapping = true
+	}
+
 	// Parameters such as disabling certain operations and operation timeouts
 	// are not replayed, but snapshots are stored in the
 	// InproxyWebRTCDialInstance for efficient lookup.

+ 77 - 0
psiphon/utils.go

@@ -29,6 +29,8 @@ import (
 	"runtime"
 	"runtime/debug"
 	"strings"
+	"sync"
+	"sync/atomic"
 	"syscall"
 	"time"
 
@@ -40,6 +42,7 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/refraction"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/resolver"
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/stacktrace"
+	"github.com/wlynxg/anet"
 )
 
 // MakePsiphonUserAgent constructs a User-Agent value to use for web service
@@ -302,3 +305,77 @@ func GetNetworkType(networkID string) string {
 	}
 	return "UNKNOWN"
 }
+
+var (
+	clientAPILevelDisableInproxyPortMapping atomic.Bool
+	applyClientAPILevelMutex                sync.Mutex
+)
+
+func applyClientAPILevel(config *Config) {
+
+	applyClientAPILevelMutex.Lock()
+	defer applyClientAPILevelMutex.Unlock()
+
+	if config.ClientAPILevel == 0 {
+		return
+	}
+
+	if runtime.GOOS == "android" {
+
+		// When specified, ClientAPILevel should be Build.VERSION.SDK_INT on
+		// Android.
+		//
+		// Avoid android_get_device_api_level call.
+		//
+		// Use ClientAPILevel to explicitly set the Android API level in the
+		// anet packet, which provides a custom alternative to net.Interfaces
+		// on Android. Applying Build.VERSION.SDK_INT here avoids anet
+		// calling the lic function android_get_device_api_level call, which
+		// may not always be available.
+		//
+		// The Android API level is required in order for anet to select its
+		// custom Interfaces on Android >= 11, where the stock net.Interfaces
+		// doesn't work (see Go issue 40569). The stock net.Interfaces should be
+		// selected on Android < 11, as testing shows that anet.Interfaces fails
+		// to enumerate all interfaces, including wlan, on older Android versions.
+		//
+		// anet.SetAndroidVersion must be called before the Controller starts in
+		// order to ensure the explicit version is applied before any call to
+		// android_get_device_api_level. Note that anet doesn't synchronize
+		// access to its internal anet.customAndroidApiLevel value;
+		// applyClientAPILevelMutex prevents concurrent writes by the following
+		// anet.SetAndroidVersion call, but reads by anet remain unsynchronized
+		// apart from scheduling the applyClientAPILevel call before the
+		// Controller starts.
+		//
+		// anet.SetAndroidVersion takes the Android OS version, not API level, but
+		// internally it has only two states: if the specified Android OS version
+		// is >= 11, customAndroidApiLevel is set to 30 and the custom Interfaces
+		// is enabled; otherwise, the stock net.Interfaces is used.
+
+		if config.ClientAPILevel >= 30 {
+
+			anet.SetAndroidVersion(11)
+
+		} else {
+
+			anet.SetAndroidVersion(10)
+		}
+
+		// Disable the tailscale portmapper on Android < 12.
+		//
+		// On Android < 12, the exec.Command calls in tailscale helper
+		// functions, including likelyHomeRouterIPAndroid, will currently hit
+		// Go issue 70508: on Go 1.23, the standard library implementation of
+		// exec.Command calls pidfd_open, which crashes shared libraries with
+		// SIGSYS, "Cause: seccomp prevented call to disallowed arm64 system
+		// call 434". Note that tailscale currently works around this by
+		// patching out the pidfd calls from its fork of Go; see tailscale
+		// issue 13452.
+
+		if config.ClientAPILevel < 31 {
+
+			clientAPILevelDisableInproxyPortMapping.Store(true)
+		}
+	}
+}