mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-11-03 03:35:04 +03:00
fix(tuic): bound one client's connections and UDP relays
The UDP association cap applied per QUIC connection, and nothing limited how many connections one credential could authenticate, so a single client could hold enough UDP relays (each a host port) to exhaust the host's ephemeral range. A client now gets at most 32 connections and 512 open UDP relays across all of them; past that a connection is refused at authentication and a relay is not opened.
This commit is contained in:
@@ -252,3 +252,37 @@ func TestDissociateStopsAResponseRelayBlockedOnTheClient(t *testing.T) {
|
||||
t.Fatal("the response relay is still running after its association was dissociated")
|
||||
}
|
||||
}
|
||||
|
||||
// The per-connection association cap means nothing if one credential may open any number
|
||||
// of connections: each UDP relay holds a host port, so the host's whole range ran out.
|
||||
func TestOneClientsSessionsAndUDPRelaysAreBounded(t *testing.T) {
|
||||
socksAddr, cleanup := startMockSocks5Server(t, "udp-bound@x", "lifecycle-socks-password")
|
||||
defer cleanup()
|
||||
server, _, clientID, _ := startLifecycleTestServer(t, socksAddr, "udp-bound@x")
|
||||
_, user := authenticatedServerConnection(t, server, clientID)
|
||||
|
||||
for range maxUserSessions + 5 {
|
||||
server.registerConn(user, nil)
|
||||
}
|
||||
if got := user.sessions.Load(); got > maxUserSessions {
|
||||
t.Fatalf("one client holds %d sessions, want at most %d", got, maxUserSessions)
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
registry := newUdpAssociationRegistry(maxUdpRelayPacketSize)
|
||||
user.udpRelays.Store(maxUserUdpRelays)
|
||||
association := &udpAssociation{responseTransport: packetTransportDatagram, lastActive: time.Now()}
|
||||
registry.associations[1] = association
|
||||
if _, _, err := registry.ensureRelay(ctx, 1, association, user, server.relay); err == nil {
|
||||
t.Fatal("a client past its UDP relay budget opened another relay")
|
||||
}
|
||||
user.udpRelays.Store(0)
|
||||
if _, created, err := registry.ensureRelay(ctx, 1, association, user, server.relay); err != nil || !created {
|
||||
t.Fatalf("ensureRelay under budget = %v, %v", created, err)
|
||||
}
|
||||
registry.dissociate(1)
|
||||
if got := user.udpRelays.Load(); got != 0 {
|
||||
t.Fatalf("closed relay still counted: %d", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user