fix(masque): address review findings for PR #76

- Return errors from MasqueConn.Read/Write instead of silently
  succeeding (prevents infinite busy-loops and silent data loss
  if these methods are accidentally called).
- Set recorder Network field in protocol dispatch switch rather
  than hardcoding "udp", so early errors don't misreport type.
- Clarify that DatagramConn context-ID handling only supports
  context ID 0 (sufficient for CONNECT-UDP per RFC 9298).
This commit is contained in:
ginuerzh
2026-05-21 22:36:30 +08:00
parent 66b53580af
commit 01a2bbaa11
3 changed files with 15 additions and 11 deletions
+7 -6
View File
@@ -3,6 +3,7 @@ package masque
import ( import (
"context" "context"
"crypto/tls" "crypto/tls"
"errors"
"net" "net"
"time" "time"
@@ -11,6 +12,8 @@ import (
"github.com/quic-go/quic-go/http3" "github.com/quic-go/quic-go/http3"
) )
var errReadWriteNotSupported = errors.New("masque: direct Read/Write not supported on MasqueConn, use GetRequestStream")
// Client manages HTTP/3 connections for MASQUE proxying. // Client manages HTTP/3 connections for MASQUE proxying.
// It wraps a QUIC connection and HTTP/3 client, following the session pattern // It wraps a QUIC connection and HTTP/3 client, following the session pattern
// used by other gost dialers (ssh, quic, mws, etc.). // used by other gost dialers (ssh, quic, mws, etc.).
@@ -105,17 +108,15 @@ func (c *MasqueConn) GetHost() string {
} }
// Read implements net.Conn but is not used for MASQUE. // Read implements net.Conn but is not used for MASQUE.
// The actual data transfer happens via datagrams. // The actual data transfer happens via datagrams or the request stream.
func (c *MasqueConn) Read(b []byte) (n int, err error) { func (c *MasqueConn) Read(b []byte) (n int, err error) {
// This should not be called - datagrams are used for data transfer return 0, errReadWriteNotSupported
return 0, nil
} }
// Write implements net.Conn but is not used for MASQUE. // Write implements net.Conn but is not used for MASQUE.
// The actual data transfer happens via datagrams. // The actual data transfer happens via datagrams or the request stream.
func (c *MasqueConn) Write(b []byte) (n int, err error) { func (c *MasqueConn) Write(b []byte) (n int, err error) {
// This should not be called - datagrams are used for data transfer return 0, errReadWriteNotSupported
return len(b), nil
} }
// Close closes the connection. // Close closes the connection.
+2 -1
View File
@@ -107,7 +107,7 @@ func (h *masqueHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl
start := time.Now() start := time.Now()
ro := &xrecorder.HandlerRecorderObject{ ro := &xrecorder.HandlerRecorderObject{
Network: "udp", Network: "",
Service: h.options.Service, Service: h.options.Service,
RemoteAddr: conn.RemoteAddr().String(), RemoteAddr: conn.RemoteAddr().String(),
LocalAddr: conn.LocalAddr().String(), LocalAddr: conn.LocalAddr().String(),
@@ -183,6 +183,7 @@ func (h *masqueHandler) Handle(ctx context.Context, conn net.Conn, opts ...handl
switch r.Proto { switch r.Proto {
case "connect-udp": case "connect-udp":
// Extended CONNECT for UDP (RFC 9298) // Extended CONNECT for UDP (RFC 9298)
ro.Network = "udp"
return h.handleConnectUDP(ctx, w, r, conn.LocalAddr(), ro, log) return h.handleConnectUDP(ctx, w, r, conn.LocalAddr(), ro, log)
case "HTTP/3.0", "": case "HTTP/3.0", "":
// Standard CONNECT for TCP (RFC 9114) // Standard CONNECT for TCP (RFC 9114)
+6 -4
View File
@@ -83,9 +83,11 @@ func (c *DatagramConn) ReadFrom(b []byte) (n int, addr net.Addr, err error) {
return 0, c.remoteAddr, nil return 0, c.remoteAddr, nil
} }
// Per RFC 9297: datagram format is context-id (varint) + payload // Per RFC 9297: datagram format is context-id (varint) + payload.
// For CONNECT-UDP with context ID 0, the first byte is 0x00 // For CONNECT-UDP (RFC 9298), the context ID is always 0.
// We strip the context ID prefix // QUIC varints encode 0 as a single byte 0x00.
// NOTE: Only context ID 0 is supported. Non-zero context IDs would
// require full QUIC varint decoding.
if data[0] == 0x00 { if data[0] == 0x00 {
data = data[1:] data = data[1:]
} }
@@ -103,7 +105,7 @@ func (c *DatagramConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {
default: default:
} }
// Prepend context ID (0x00 for context ID 0) // Prepend context ID (0x00 for CONNECT-UDP context ID 0 per RFC 9298)
datagram := make([]byte, 1+len(b)) datagram := make([]byte, 1+len(b))
datagram[0] = 0x00 // Context ID = 0 datagram[0] = 0x00 // Context ID = 0
copy(datagram[1:], b) copy(datagram[1:], b)