Jelajahi Sumber

Add PSIPHON_ENABLE_DSL_CLIENT build tag

- Without PSIPHON_ENABLE_DSL_CLIENT, DSL fetches are skipped
- PSIPHON_ENABLE_DSL_CLIENT also controls tactics compression
- Fix JSON tactics marshaling
- Add compression support for in-proxy proxy tactics
Rod Hynes 4 bulan lalu
induk
melakukan
635bb607af

+ 3 - 2
.github/workflows/tests.yml

@@ -96,7 +96,7 @@ jobs:
           go test -v -race ./psiphon/common/values
           go test -v -race ./psiphon/common/wildcard
           go test -v -race ./psiphon/transferstats
-          sudo -E env "PATH=$PATH" go test -v -timeout 30m -race -tags "PSIPHON_ENABLE_INPROXY PSIPHON_RUN_PACKET_MANIPULATOR_TEST" ./psiphon/server
+          sudo -E env "PATH=$PATH" go test -v -timeout 30m -race -tags "PSIPHON_ENABLE_INPROXY PSIPHON_ENABLE_DSL_CLIENT PSIPHON_RUN_PACKET_MANIPULATOR_TEST" ./psiphon/server
           go test -v -race ./psiphon/server/psinet
           go test -v -timeout 30m -race ./psiphon
           go test -v -race ./ClientLibrary/clientlib
@@ -131,7 +131,7 @@ jobs:
           go test -v -covermode=count -coverprofile=values.coverprofile ./psiphon/common/values
           go test -v -covermode=count -coverprofile=wildcard.coverprofile ./psiphon/common/wildcard
           go test -v -covermode=count -coverprofile=transferstats.coverprofile ./psiphon/transferstats
-          sudo -E env "PATH=$PATH" go test -v -timeout 30m -covermode=count -coverprofile=server.coverprofile -tags "PSIPHON_ENABLE_INPROXY PSIPHON_RUN_PACKET_MANIPULATOR_TEST" ./psiphon/server
+          sudo -E env "PATH=$PATH" go test -v -timeout 30m -covermode=count -coverprofile=server.coverprofile -tags "PSIPHON_ENABLE_INPROXY PSIPHON_ENABLE_DSL_CLIENT PSIPHON_RUN_PACKET_MANIPULATOR_TEST" ./psiphon/server
           go test -v -covermode=count -coverprofile=psinet.coverprofile ./psiphon/server/psinet
           go test -v -timeout 30m -covermode=count -coverprofile=psiphon.coverprofile ./psiphon
           go test -v -covermode=count -coverprofile=clientlib.coverprofile ./ClientLibrary/clientlib
@@ -158,6 +158,7 @@ jobs:
         run: |
           cd ${{ github.workspace }}/go/src/github.com/Psiphon-Labs/psiphon-tunnel-core/ConsoleClient
           go build -a -v -tags ""
+          go build -a -v -tags "PSIPHON_ENABLE_DSL_CLIENT"
           go build -a -v -tags "PSIPHON_ENABLE_INPROXY"
           go build -a -v -tags "PSIPHON_DISABLE_QUIC"
           go build -a -v -tags "PSIPHON_DISABLE_GQUIC"

+ 13 - 7
psiphon/common/inproxy/proxy.go

@@ -126,7 +126,8 @@ type ProxyConfig struct {
 	// HandleTacticsPayload must return true when the tacticsPayload includes
 	// new tactics, indicating that the proxy should reinitialize components
 	// controlled by tactics parameters.
-	HandleTacticsPayload func(networkID string, tacticsPayload []byte) bool
+	HandleTacticsPayload func(
+		networkID string, compressTactics bool, tacticsPayload []byte) bool
 
 	// MustUpgrade is a callback that is invoked when a MustUpgrade flag is
 	// received from the broker. When MustUpgrade is received, the proxy
@@ -602,7 +603,7 @@ func (p *Proxy) proxyOneClient(
 	// returned in the proxy announcment response are associated and stored
 	// with the original network ID.
 
-	metrics, tacticsNetworkID, err := p.getMetrics(
+	metrics, tacticsNetworkID, compressTactics, err := p.getMetrics(
 		checkTactics, brokerCoordinator, webRTCCoordinator)
 	if err != nil {
 		return backOff, errors.Trace(err)
@@ -684,7 +685,9 @@ func (p *Proxy) proxyOneClient(
 		// response as there may still be a match.
 
 		if p.config.HandleTacticsPayload(
-			tacticsNetworkID, announceResponse.TacticsPayload) {
+			tacticsNetworkID,
+			compressTactics,
+			announceResponse.TacticsPayload) {
 
 			p.resetNetworkDiscovery()
 		}
@@ -1012,7 +1015,8 @@ func (p *Proxy) proxyOneClient(
 func (p *Proxy) getMetrics(
 	includeTacticsParameters bool,
 	brokerCoordinator BrokerDialCoordinator,
-	webRTCCoordinator WebRTCDialCoordinator) (*ProxyMetrics, string, error) {
+	webRTCCoordinator WebRTCDialCoordinator) (
+	*ProxyMetrics, string, bool, error) {
 
 	// tacticsNetworkID records the exact network ID that corresponds to the
 	// tactics tag sent in the base parameters, and is used when applying any
@@ -1020,16 +1024,18 @@ func (p *Proxy) getMetrics(
 	baseParams, tacticsNetworkID, err := p.config.GetBaseAPIParameters(
 		includeTacticsParameters)
 	if err != nil {
-		return nil, "", errors.Trace(err)
+		return nil, "", false, errors.Trace(err)
 	}
 
 	apiParams := common.APIParameters{}
 	apiParams.Add(baseParams)
 	apiParams.Add(common.APIParameters(brokerCoordinator.MetricsForBrokerRequests()))
 
+	compressTactics := protocol.GetCompressTactics(apiParams)
+
 	packedParams, err := protocol.EncodePackedAPIParameters(apiParams)
 	if err != nil {
-		return nil, "", errors.Trace(err)
+		return nil, "", false, errors.Trace(err)
 	}
 
 	return &ProxyMetrics{
@@ -1044,5 +1050,5 @@ func (p *Proxy) getMetrics(
 		LimitDownstreamBytesPerSecond: int64(p.config.LimitDownstreamBytesPerSecond),
 		PeakUpstreamBytesPerSecond:    atomic.LoadInt64(&p.peakBytesUp),
 		PeakDownstreamBytesPerSecond:  atomic.LoadInt64(&p.peakBytesDown),
-	}, tacticsNetworkID, nil
+	}, tacticsNetworkID, compressTactics, nil
 }

+ 2 - 1
psiphon/common/protocol/protocol.go

@@ -21,6 +21,7 @@ package protocol
 
 import (
 	"crypto/sha256"
+	"encoding/json"
 	"fmt"
 	"strings"
 
@@ -805,7 +806,7 @@ type HandshakeResponse struct {
 	ClientAddress             string              `json:"client_address,omitempty" cbor:"7,keyasint,omitempty"`
 	ServerTimestamp           string              `json:"server_timestamp,omitempty" cbor:"8,keyasint,omitempty"`
 	ActiveAuthorizationIDs    []string            `json:"active_authorization_ids,omitempty" cbor:"9,keyasint,omitempty"`
-	TacticsPayload            []byte              `json:"tactics_payload,omitempty" cbor:"10,keyasint,omitempty"`
+	TacticsPayload            json.RawMessage     `json:"tactics_payload,omitempty" cbor:"10,keyasint,omitempty"`
 	TacticsPayloadCompression int32               `json:"tactics_payload_compression,omitempty" cbor:"11,keyasint,omitempty"`
 	UpstreamBytesPerSecond    int64               `json:"upstream_bytes_per_second,omitempty" cbor:"12,keyasint,omitempty"`
 	DownstreamBytesPerSecond  int64               `json:"downstream_bytes_per_second,omitempty" cbor:"13,keyasint,omitempty"`

+ 3 - 3
psiphon/common/tactics/tactics.go

@@ -337,7 +337,7 @@ type Payload struct {
 	Tag string `cbor:"1,keyasint,omitempty"`
 
 	// Tactics is a JSON- or CBOR-encoded Tactics struct and may be nil.
-	Tactics []byte `cbor:"2,keyasint,omitempty"`
+	Tactics json.RawMessage `cbor:"2,keyasint,omitempty"`
 
 	// TacticsCompression specifies how Tactics is compressed.
 	TacticsCompression int32 `cbor:"3,keyasint,omitempty"`
@@ -1631,6 +1631,7 @@ func FetchTactics(
 	params *parameters.Parameters,
 	storer Storer,
 	getNetworkID func() string,
+	compressTacticsEnabled bool,
 	apiParams common.APIParameters,
 	endPointRegion string,
 	endPointProtocol string,
@@ -1641,7 +1642,7 @@ func FetchTactics(
 	p := params.Get()
 	speedTestPaddingMinBytes := p.Int(parameters.SpeedTestPaddingMinBytes)
 	speedTestPaddingMaxBytes := p.Int(parameters.SpeedTestPaddingMaxBytes)
-	compressTactics := p.Bool(parameters.CompressTactics)
+	compressTactics := compressTacticsEnabled && p.Bool(parameters.CompressTactics)
 	p.Close()
 
 	networkID := getNetworkID()
@@ -1748,7 +1749,6 @@ func FetchTactics(
 	// Process and store the response payload.
 
 	var payload *Payload
-
 	_, err = unboxPayload(
 		TACTICS_RESPONSE_NONCE,
 		requestPublicKey,

+ 19 - 2
psiphon/controller.go

@@ -47,6 +47,7 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tun"
 	utls "github.com/Psiphon-Labs/utls"
 	lrucache "github.com/cognusion/go-cache-lru"
+	"github.com/fxamacker/cbor/v2"
 	"golang.org/x/time/rate"
 )
 
@@ -3394,6 +3395,14 @@ func (controller *Controller) inproxyGetProxyAPIParameters(includeTacticsParamet
 		if err != nil {
 			return nil, "", errors.Trace(err)
 		}
+
+		p := controller.config.GetParameters().Get()
+		compressTactics := compressTacticsEnabled && p.Bool(parameters.CompressTactics)
+		p.Close()
+
+		if compressTactics {
+			protocol.SetCompressTactics(params)
+		}
 	}
 
 	return params, networkID, nil
@@ -3429,7 +3438,7 @@ func (controller *Controller) inproxyMakeProxyWebRTCDialCoordinator() (
 // inproxyHandleTacticsPayload duplicates some tactics-handling code from
 // doHandshakeRequest.
 func (controller *Controller) inproxyHandleProxyTacticsPayload(
-	networkID string, tacticsPayload []byte) bool {
+	networkID string, compressTactics bool, tacticsPayload []byte) bool {
 
 	if controller.config.DisableTactics {
 		return false
@@ -3440,8 +3449,14 @@ func (controller *Controller) inproxyHandleProxyTacticsPayload(
 		return false
 	}
 
+	var payloadUnmarshaler func([]byte, any) error
+	payloadUnmarshaler = json.Unmarshal
+	if compressTactics {
+		payloadUnmarshaler = cbor.Unmarshal
+	}
+
 	var payload *tactics.Payload
-	err := json.Unmarshal(tacticsPayload, &payload)
+	err := payloadUnmarshaler(tacticsPayload, &payload)
 	if err != nil {
 		NoticeError("unmarshal tactics payload failed: %v", errors.Trace(err))
 		return false
@@ -3527,3 +3542,5 @@ func (controller *Controller) inproxyHandleProxyTacticsPayload(
 
 	return appliedNewTactics
 }
+
+var compressTacticsEnabled = true

+ 6 - 0
psiphon/dsl.go

@@ -1,3 +1,5 @@
+//go:build PSIPHON_ENABLE_DSL_CLIENT
+
 /*
  * Copyright (c) 2025, Psiphon Inc.
  * All rights reserved.
@@ -31,6 +33,10 @@ import (
 	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
 )
 
+func DSLEnabled() bool {
+	return true
+}
+
 func runUntunneledDSLFetcher(
 	ctx context.Context,
 	config *Config,

+ 48 - 0
psiphon/dsl_disabled.go

@@ -0,0 +1,48 @@
+//go:build !PSIPHON_ENABLE_DSL_CLIENT
+
+/*
+ * Copyright (c) 2025, Psiphon Inc.
+ * All rights reserved.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+package psiphon
+
+import (
+	"context"
+)
+
+func DSLEnabled() bool {
+	return false
+}
+
+func runUntunneledDSLFetcher(
+	_ context.Context,
+	_ *Config,
+	_ *InproxyBrokerClientManager,
+	_ <-chan struct{}) {
+}
+
+func runTunneledDSLFetcher(
+	_ context.Context,
+	_ *Config,
+	_ func() *Tunnel,
+	_ <-chan struct{}) {
+}
+
+func init() {
+	compressTacticsEnabled = false
+}

+ 1 - 0
psiphon/server/meek.go

@@ -2038,6 +2038,7 @@ func (server *MeekServer) inproxyBrokerGetTacticsPayload(
 	responseMarshaler = json.Marshal
 
 	compressTactics := protocol.GetCompressTactics(apiParameters)
+
 	if compressTactics {
 		responseMarshaler = protocol.CBOREncoding.Marshal
 	}

+ 1 - 1
psiphon/server/server_test.go

@@ -910,7 +910,7 @@ func runServer(t *testing.T, runConfig *runServerConfig) {
 	// configureDSLTestServerEntries bootstrap can only perform tactics
 	// requests and not dial a tunnel, so the DSL request must succeed.
 
-	doDSL := doInproxy && inproxyTestConfig.addMeekServerForBroker
+	doDSL := psiphon.DSLEnabled() && doInproxy && inproxyTestConfig.addMeekServerForBroker
 
 	var dslTestConfig *dslTestConfig
 	enableDSLFetcher := "false"

+ 1 - 1
psiphon/serverApi.go

@@ -185,7 +185,7 @@ func (serverContext *ServerContext) doHandshakeRequest(ignoreStatsRegexps bool)
 		}
 
 		p := serverContext.tunnel.config.GetParameters().Get()
-		compressTactics = p.Bool(parameters.CompressTactics)
+		compressTactics = compressTacticsEnabled && p.Bool(parameters.CompressTactics)
 		p.Close()
 	}
 

+ 1 - 0
psiphon/tactics.go

@@ -310,6 +310,7 @@ func fetchTactics(
 		config.GetParameters(),
 		GetTacticsStorer(config),
 		config.GetNetworkID,
+		compressTacticsEnabled,
 		apiParams,
 		serverEntry.Region,
 		dialParams.TunnelProtocol,