Просмотр исходного кода

Fixes: add selectWebRTCMediaStreamQUICVersion and upgrade quic-go

Rod Hynes 1 год назад
Родитель
Сommit
78d00b6a65

+ 1 - 1
go.mod

@@ -36,7 +36,7 @@ require (
 	github.com/Psiphon-Labs/consistent v0.0.0-20240322131436-20aaa4e05737
 	github.com/Psiphon-Labs/goptlib v0.0.0-20200406165125-c0e32a7a3464
 	github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240824224428-ca6969e315a9
-	github.com/Psiphon-Labs/quic-go v0.0.0-20240821052333-b6316b594e39
+	github.com/Psiphon-Labs/quic-go v0.0.0-20250203210204-a4381c68e52f
 	github.com/Psiphon-Labs/utls v1.1.1-0.20241107183331-b18909f8ccaa
 	github.com/armon/go-proxyproto v0.0.0-20180202201750-5b7edb60ff5f
 	github.com/bifurcation/mint v0.0.0-20180306135233-198357931e61

+ 2 - 0
go.sum

@@ -22,6 +22,8 @@ github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240824224428-ca6969e315a9 h1:AJj1cS
 github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240824224428-ca6969e315a9/go.mod h1:AaKKoshr8RI1LZTheeNDtNuZ39qNVPWVK4uir2c2XIs=
 github.com/Psiphon-Labs/quic-go v0.0.0-20240821052333-b6316b594e39 h1:ft0K9EDdBtMl+Q/akZ+qt3SdcmbtnTQOgE3OlWI6uz0=
 github.com/Psiphon-Labs/quic-go v0.0.0-20240821052333-b6316b594e39/go.mod h1:2MTiPsgoOqWs3Bo6Xr3ElMBX6zzfjd3YkDFpQJLwHdQ=
+github.com/Psiphon-Labs/quic-go v0.0.0-20250203210204-a4381c68e52f h1:18J32AZR8UYwmANk4V00UqSwsOus4wbD+0wp4FTW88k=
+github.com/Psiphon-Labs/quic-go v0.0.0-20250203210204-a4381c68e52f/go.mod h1:2MTiPsgoOqWs3Bo6Xr3ElMBX6zzfjd3YkDFpQJLwHdQ=
 github.com/Psiphon-Labs/utls v1.1.1-0.20241107183331-b18909f8ccaa h1:5FszHIhxb7yO267qt47tTfJOtD31k7R80L88EwNm4tc=
 github.com/Psiphon-Labs/utls v1.1.1-0.20241107183331-b18909f8ccaa/go.mod h1:dxmztdV9lf59cq44YY8r21m3b+xSjhg98cgZW8WK1p0=
 github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=

+ 7 - 1
psiphon/common/inproxy/inproxy_test.go

@@ -584,11 +584,17 @@ func runTestInproxy(doMustUpgrade bool) error {
 			relayConn = conn
 
 			if wrapWithQUIC {
+
+				udpAddr, err := net.ResolveUDPAddr("udp", addr)
+				if err != nil {
+					return errors.Trace(err)
+				}
+
 				disablePathMTUDiscovery := true
 				quicConn, err := quic.Dial(
 					dialCtx,
 					conn,
-					&net.UDPAddr{Port: 1}, // This address is ignored, but the zero value is not allowed
+					udpAddr,
 					"test",
 					"QUICv1",
 					nil,

+ 75 - 1
psiphon/dialParameters.go

@@ -1268,7 +1268,8 @@ func MakeDialParameters(
 			}
 		}
 
-		if protocol.TunnelProtocolUsesQUIC(dialParams.TunnelProtocol) &&
+		if (!isReplay || !replayInproxyWebRTC) &&
+			protocol.TunnelProtocolUsesQUIC(dialParams.TunnelProtocol) &&
 			dialParams.InproxyWebRTCDialParameters.UseMediaStreams {
 
 			// In the in-proxy WebRTC media stream mode, QUIC packets are
@@ -1291,6 +1292,38 @@ func MakeDialParameters(
 
 			dialParams.QUICMaxPacketSizeAdjustment = inproxy.GetQUICMaxPacketSizeAdjustment(isIPv6)
 			dialParams.QUICDisablePathMTUDiscovery = true
+
+			// Select a QUIC variant that is compatible with WebRTC media
+			// stream SRTP constraints. This selection overrides the previous
+			// selectQUICVersion. If a compatible QUIC variant cannot be
+			// selected, abort with no error, as is done in the previous
+			// selectQUICVersion case.
+			//
+			// Previous QUICUseObfuscatedPSK/QUICDialEarly parameter
+			// selections are retained, while parameters tied to the QUIC
+			// variant, including QUICClientHelloSeed and
+			// ObfuscatedQUICPaddingSeed/ObfuscatedQUICNonceTransformerParameters
+			// are set or cleared to match the new selection.
+			//
+			// Limitation: replayQUICVersion is ignored and
+			// replayInproxyWebRTC is used for this case, since
+			// UseMediaStreams is determined in the latter case.
+
+			dialParams.QUICVersion = selectWebRTCMediaStreamQUICVersion(serverEntry, p)
+			if dialParams.QUICVersion == "" {
+				return nil, nil
+			}
+			if protocol.QUICVersionHasRandomizedClientHello(dialParams.QUICVersion) {
+				dialParams.QUICClientHelloSeed, err = prng.NewSeed()
+				if err != nil {
+					return nil, errors.Trace(err)
+				}
+			}
+			if protocol.QUICVersionIsObfuscated(dialParams.QUICVersion) {
+				return nil, errors.TraceNew("unexpected obfuscated QUIC version")
+			}
+			dialParams.ObfuscatedQUICPaddingSeed = nil
+			dialParams.ObfuscatedQUICNonceTransformerParameters = nil
 		}
 
 		// dialParams.inproxyConn is left uninitialized until after the dial,
@@ -2054,6 +2087,47 @@ func selectQUICVersion(
 	return quicVersions[choice]
 }
 
+func selectWebRTCMediaStreamQUICVersion(
+	serverEntry *protocol.ServerEntry,
+	p parameters.ParametersAccessor) string {
+
+	// Based on selectQUICVersion. The only supported QUIC versions are the
+	// non-obfuscated IETF QUICv1 versions. Obfuscated versions do not meet
+	// the packet size constraints required for WebRTC SRTP.
+
+	limitQUICVersions := p.QUICVersions(parameters.LimitQUICVersions)
+
+	quicVersions := make([]string, 0)
+
+	supportedQUICVersions := protocol.QUICVersions{
+		protocol.QUIC_VERSION_V1,
+		protocol.QUIC_VERSION_RANDOMIZED_V1,
+	}
+
+	for _, quicVersion := range supportedQUICVersions {
+
+		if len(limitQUICVersions) > 0 &&
+			!common.Contains(limitQUICVersions, quicVersion) {
+			continue
+		}
+
+		if len(serverEntry.LimitQUICVersions) > 0 &&
+			!common.Contains(serverEntry.LimitQUICVersions, quicVersion) {
+			continue
+		}
+
+		quicVersions = append(quicVersions, quicVersion)
+	}
+
+	if len(quicVersions) == 0 {
+		return ""
+	}
+
+	choice := prng.Intn(len(quicVersions))
+
+	return quicVersions[choice]
+}
+
 // selectUserAgentIfUnset selects a User-Agent header if one is not set.
 func selectUserAgentIfUnset(
 	p parameters.ParametersAccessor, headers http.Header) (bool, string) {

+ 25 - 8
vendor/github.com/Psiphon-Labs/quic-go/server.go

@@ -427,14 +427,31 @@ func (s *baseServer) handlePacketImpl(p receivedPacket) bool /* is the buffer st
 	}
 
 	// [Psiphon]
-	// To accomodate additional messages, obfuscated QUIC packets may reserve
-	// significant space in the Initial packet and send less that 1200 QUIC
-	// bytes. In this configuration, the obfuscation layer enforces the
-	// anti-amplification 1200 byte rule, but it must be disabled here.
-	isObfuscated := s.config.ServerMaxPacketSizeAdjustment != nil && s.config.ServerMaxPacketSizeAdjustment(p.remoteAddr) > 0
-
-	if !isObfuscated &&
-		hdr.Type == protocol.PacketTypeInitial && p.Size() < protocol.MinInitialPacketSize {
+	// Both Obfuscated QUIC and QUIC over WebRTC SRTP adjust the max
+	// packet size to allow for additional overhead. Enforce a reduced
+	// minimum initial packet size when the adjustment results in a max
+	// packet size less than protocol.MinInitialPacketSize.
+	minInitialPacketSize := protocol.ByteCount(protocol.MinInitialPacketSize)
+	if s.config.ServerMaxPacketSizeAdjustment != nil {
+
+		maxPacketSize := protocol.ByteCount(protocol.InitialPacketSizeIPv4)
+		if host, _, err := net.SplitHostPort(p.remoteAddr.String()); err == nil {
+			if IP := net.ParseIP(host); IP != nil && IP.To4() == nil {
+				maxPacketSize = protocol.InitialPacketSizeIPv6
+			}
+		}
+		adjustment := protocol.ByteCount(s.config.ServerMaxPacketSizeAdjustment(p.remoteAddr))
+		maxPacketSize -= adjustment
+		if maxPacketSize < 0 {
+			maxPacketSize = 0
+		}
+
+		if maxPacketSize < minInitialPacketSize {
+			minInitialPacketSize = maxPacketSize
+		}
+	}
+
+	if hdr.Type == protocol.PacketTypeInitial && p.Size() < minInitialPacketSize {
 		s.logger.Debugf("Dropping a packet that is too small to be a valid Initial (%d bytes)", p.Size())
 		if s.tracer != nil && s.tracer.DroppedPacket != nil {
 			s.tracer.DroppedPacket(p.remoteAddr, logging.PacketTypeInitial, p.Size(), logging.PacketDropUnexpectedPacket)

+ 1 - 1
vendor/modules.txt

@@ -26,7 +26,7 @@ github.com/Psiphon-Labs/goptlib
 # github.com/Psiphon-Labs/psiphon-tls v0.0.0-20240824224428-ca6969e315a9
 ## explicit; go 1.21
 github.com/Psiphon-Labs/psiphon-tls
-# github.com/Psiphon-Labs/quic-go v0.0.0-20240821052333-b6316b594e39
+# github.com/Psiphon-Labs/quic-go v0.0.0-20250203210204-a4381c68e52f
 ## explicit; go 1.21
 github.com/Psiphon-Labs/quic-go
 github.com/Psiphon-Labs/quic-go/http3