mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-14 04:38:28 +10:00
Fix cloudflared registration parameter inconsistencies
- Set QUIC InitialPacketSize per IP family (IPv4: 1252, IPv6: 1232) - Set MaxIncomingStreams/MaxIncomingUniStreams to 1<<60 - Populate OriginLocalIP from local socket address in both QUIC and HTTP/2 - Pass NumPreviousAttempts from retry counter to registration - Include version number in client version string - Use OS_GOARCH format for Arch field
This commit is contained in:
@@ -40,8 +40,9 @@ type HTTP2Connection struct {
|
||||
gracePeriod time.Duration
|
||||
inbound *Inbound
|
||||
|
||||
registrationClient *RegistrationClient
|
||||
registrationResult *RegistrationResult
|
||||
numPreviousAttempts uint8
|
||||
registrationClient *RegistrationClient
|
||||
registrationResult *RegistrationResult
|
||||
|
||||
activeRequests sync.WaitGroup
|
||||
closeOnce sync.Once
|
||||
@@ -55,6 +56,7 @@ func NewHTTP2Connection(
|
||||
credentials Credentials,
|
||||
connectorID uuid.UUID,
|
||||
features []string,
|
||||
numPreviousAttempts uint8,
|
||||
gracePeriod time.Duration,
|
||||
inbound *Inbound,
|
||||
logger log.ContextLogger,
|
||||
@@ -92,10 +94,11 @@ func NewHTTP2Connection(
|
||||
edgeAddr: edgeAddr,
|
||||
connIndex: connIndex,
|
||||
credentials: credentials,
|
||||
connectorID: connectorID,
|
||||
features: features,
|
||||
gracePeriod: gracePeriod,
|
||||
inbound: inbound,
|
||||
connectorID: connectorID,
|
||||
features: features,
|
||||
numPreviousAttempts: numPreviousAttempts,
|
||||
gracePeriod: gracePeriod,
|
||||
inbound: inbound,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -149,7 +152,9 @@ func (c *HTTP2Connection) handleControlStream(ctx context.Context, r *http.Reque
|
||||
|
||||
c.registrationClient = NewRegistrationClient(ctx, stream)
|
||||
|
||||
options := BuildConnectionOptions(c.connectorID, c.features, 0)
|
||||
host, _, _ := net.SplitHostPort(c.conn.LocalAddr().String())
|
||||
originLocalIP := net.ParseIP(host)
|
||||
options := BuildConnectionOptions(c.connectorID, c.features, c.numPreviousAttempts, originLocalIP)
|
||||
result, err := c.registrationClient.RegisterConnection(
|
||||
ctx, c.credentials.Auth(), c.credentials.TunnelID, c.connIndex, options,
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -25,18 +26,27 @@ const (
|
||||
quicKeepAlivePeriod = 1 * time.Second
|
||||
)
|
||||
|
||||
func quicInitialPacketSize(ipVersion int) uint16 {
|
||||
initialPacketSize := uint16(1252)
|
||||
if ipVersion == 4 {
|
||||
initialPacketSize = 1232
|
||||
}
|
||||
return initialPacketSize
|
||||
}
|
||||
|
||||
// QUICConnection manages a single QUIC connection to the Cloudflare edge.
|
||||
type QUICConnection struct {
|
||||
conn *quic.Conn
|
||||
logger log.ContextLogger
|
||||
edgeAddr *EdgeAddr
|
||||
connIndex uint8
|
||||
credentials Credentials
|
||||
connectorID uuid.UUID
|
||||
features []string
|
||||
gracePeriod time.Duration
|
||||
registrationClient *RegistrationClient
|
||||
registrationResult *RegistrationResult
|
||||
conn *quic.Conn
|
||||
logger log.ContextLogger
|
||||
edgeAddr *EdgeAddr
|
||||
connIndex uint8
|
||||
credentials Credentials
|
||||
connectorID uuid.UUID
|
||||
features []string
|
||||
numPreviousAttempts uint8
|
||||
gracePeriod time.Duration
|
||||
registrationClient *RegistrationClient
|
||||
registrationResult *RegistrationResult
|
||||
|
||||
closeOnce sync.Once
|
||||
}
|
||||
@@ -49,6 +59,7 @@ func NewQUICConnection(
|
||||
credentials Credentials,
|
||||
connectorID uuid.UUID,
|
||||
features []string,
|
||||
numPreviousAttempts uint8,
|
||||
gracePeriod time.Duration,
|
||||
logger log.ContextLogger,
|
||||
) (*QUICConnection, error) {
|
||||
@@ -65,10 +76,13 @@ func NewQUICConnection(
|
||||
}
|
||||
|
||||
quicConfig := &quic.Config{
|
||||
HandshakeIdleTimeout: quicHandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicMaxIdleTimeout,
|
||||
KeepAlivePeriod: quicKeepAlivePeriod,
|
||||
EnableDatagrams: true,
|
||||
HandshakeIdleTimeout: quicHandshakeIdleTimeout,
|
||||
MaxIdleTimeout: quicMaxIdleTimeout,
|
||||
KeepAlivePeriod: quicKeepAlivePeriod,
|
||||
MaxIncomingStreams: 1 << 60,
|
||||
MaxIncomingUniStreams: 1 << 60,
|
||||
EnableDatagrams: true,
|
||||
InitialPacketSize: quicInitialPacketSize(edgeAddr.IPVersion),
|
||||
}
|
||||
|
||||
conn, err := quic.DialAddr(ctx, edgeAddr.UDP.String(), tlsConfig, quicConfig)
|
||||
@@ -77,14 +91,15 @@ func NewQUICConnection(
|
||||
}
|
||||
|
||||
return &QUICConnection{
|
||||
conn: conn,
|
||||
logger: logger,
|
||||
edgeAddr: edgeAddr,
|
||||
connIndex: connIndex,
|
||||
credentials: credentials,
|
||||
connectorID: connectorID,
|
||||
features: features,
|
||||
gracePeriod: gracePeriod,
|
||||
conn: conn,
|
||||
logger: logger,
|
||||
edgeAddr: edgeAddr,
|
||||
connIndex: connIndex,
|
||||
credentials: credentials,
|
||||
connectorID: connectorID,
|
||||
features: features,
|
||||
numPreviousAttempts: numPreviousAttempts,
|
||||
gracePeriod: gracePeriod,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -128,7 +143,9 @@ func (q *QUICConnection) Serve(ctx context.Context, handler StreamHandler) error
|
||||
func (q *QUICConnection) register(ctx context.Context, stream *quic.Stream) error {
|
||||
q.registrationClient = NewRegistrationClient(ctx, newStreamReadWriteCloser(stream))
|
||||
|
||||
options := BuildConnectionOptions(q.connectorID, q.features, 0)
|
||||
host, _, _ := net.SplitHostPort(q.conn.LocalAddr().String())
|
||||
originLocalIP := net.ParseIP(host)
|
||||
options := BuildConnectionOptions(q.connectorID, q.features, q.numPreviousAttempts, originLocalIP)
|
||||
result, err := q.registrationClient.RegisterConnection(
|
||||
ctx, q.credentials.Auth(), q.credentials.TunnelID, q.connIndex, options,
|
||||
)
|
||||
|
||||
25
protocol/cloudflare/connection_quic_test.go
Normal file
25
protocol/cloudflare/connection_quic_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
//go:build with_cloudflare_tunnel
|
||||
|
||||
package cloudflare
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestQUICInitialPacketSize(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
ipVersion int
|
||||
expected uint16
|
||||
}{
|
||||
{name: "ipv4", ipVersion: 4, expected: 1232},
|
||||
{name: "ipv6", ipVersion: 6, expected: 1252},
|
||||
{name: "default", ipVersion: 0, expected: 1252},
|
||||
}
|
||||
|
||||
for _, testCase := range testCases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
if actual := quicInitialPacketSize(testCase.ipVersion); actual != testCase.expected {
|
||||
t.Fatalf("quicInitialPacketSize(%d) = %d, want %d", testCase.ipVersion, actual, testCase.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,11 @@ package cloudflare
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
C "github.com/sagernet/sing-box/constant"
|
||||
"github.com/sagernet/sing-box/protocol/cloudflare/tunnelrpc"
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
@@ -18,9 +20,10 @@ import (
|
||||
|
||||
const (
|
||||
registrationTimeout = 10 * time.Second
|
||||
clientVersion = "sing-box"
|
||||
)
|
||||
|
||||
var clientVersion = "sing-box " + C.Version
|
||||
|
||||
// RegistrationClient handles the Cap'n Proto RPC for tunnel registration.
|
||||
type RegistrationClient struct {
|
||||
client tunnelrpc.TunnelServer
|
||||
@@ -148,14 +151,15 @@ func (c *RegistrationClient) Close() error {
|
||||
}
|
||||
|
||||
// BuildConnectionOptions creates the ConnectionOptions to send during registration.
|
||||
func BuildConnectionOptions(connectorID uuid.UUID, features []string, numPreviousAttempts uint8) *RegistrationConnectionOptions {
|
||||
func BuildConnectionOptions(connectorID uuid.UUID, features []string, numPreviousAttempts uint8, originLocalIP net.IP) *RegistrationConnectionOptions {
|
||||
return &RegistrationConnectionOptions{
|
||||
Client: RegistrationClientInfo{
|
||||
ClientID: connectorID[:],
|
||||
Features: features,
|
||||
Version: clientVersion,
|
||||
Arch: runtime.GOARCH,
|
||||
Arch: runtime.GOOS + "_" + runtime.GOARCH,
|
||||
},
|
||||
OriginLocalIP: originLocalIP,
|
||||
NumPreviousAttempts: numPreviousAttempts,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +267,7 @@ func (i *Inbound) superviseConnection(connIndex uint8, edgeAddrs []*EdgeAddr, fe
|
||||
}
|
||||
|
||||
edgeAddr := edgeAddrs[rand.Intn(len(edgeAddrs))]
|
||||
err := i.serveConnection(connIndex, edgeAddr, features)
|
||||
err := i.serveConnection(connIndex, edgeAddr, features, uint8(retries))
|
||||
if err == nil || i.ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
@@ -284,7 +284,7 @@ func (i *Inbound) superviseConnection(connIndex uint8, edgeAddrs []*EdgeAddr, fe
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Inbound) serveConnection(connIndex uint8, edgeAddr *EdgeAddr, features []string) error {
|
||||
func (i *Inbound) serveConnection(connIndex uint8, edgeAddr *EdgeAddr, features []string, numPreviousAttempts uint8) error {
|
||||
protocol := i.protocol
|
||||
if protocol == "" {
|
||||
protocol = "quic"
|
||||
@@ -292,21 +292,21 @@ func (i *Inbound) serveConnection(connIndex uint8, edgeAddr *EdgeAddr, features
|
||||
|
||||
switch protocol {
|
||||
case "quic":
|
||||
return i.serveQUIC(connIndex, edgeAddr, features)
|
||||
return i.serveQUIC(connIndex, edgeAddr, features, numPreviousAttempts)
|
||||
case "http2":
|
||||
return i.serveHTTP2(connIndex, edgeAddr, features)
|
||||
return i.serveHTTP2(connIndex, edgeAddr, features, numPreviousAttempts)
|
||||
default:
|
||||
return E.New("unsupported protocol: ", protocol)
|
||||
}
|
||||
}
|
||||
|
||||
func (i *Inbound) serveQUIC(connIndex uint8, edgeAddr *EdgeAddr, features []string) error {
|
||||
func (i *Inbound) serveQUIC(connIndex uint8, edgeAddr *EdgeAddr, features []string, numPreviousAttempts uint8) error {
|
||||
i.logger.Info("connecting to edge via QUIC (connection ", connIndex, ")")
|
||||
|
||||
connection, err := NewQUICConnection(
|
||||
i.ctx, edgeAddr, connIndex,
|
||||
i.credentials, i.connectorID,
|
||||
features, i.gracePeriod, i.logger,
|
||||
features, numPreviousAttempts, i.gracePeriod, i.logger,
|
||||
)
|
||||
if err != nil {
|
||||
return E.Cause(err, "create QUIC connection")
|
||||
@@ -321,13 +321,13 @@ func (i *Inbound) serveQUIC(connIndex uint8, edgeAddr *EdgeAddr, features []stri
|
||||
return connection.Serve(i.ctx, i)
|
||||
}
|
||||
|
||||
func (i *Inbound) serveHTTP2(connIndex uint8, edgeAddr *EdgeAddr, features []string) error {
|
||||
func (i *Inbound) serveHTTP2(connIndex uint8, edgeAddr *EdgeAddr, features []string, numPreviousAttempts uint8) error {
|
||||
i.logger.Info("connecting to edge via HTTP/2 (connection ", connIndex, ")")
|
||||
|
||||
connection, err := NewHTTP2Connection(
|
||||
i.ctx, edgeAddr, connIndex,
|
||||
i.credentials, i.connectorID,
|
||||
features, i.gracePeriod, i, i.logger,
|
||||
features, numPreviousAttempts, i.gracePeriod, i, i.logger,
|
||||
)
|
||||
if err != nil {
|
||||
return E.Cause(err, "create HTTP/2 connection")
|
||||
|
||||
Reference in New Issue
Block a user