mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-16 15:17:14 +00:00
d9c7c76fb0
* fix(limit-ip): leave a reverse client out of the temporary disconnect The LIMIT_IP cycle removes the client and adds it back 100 ms later. For a vless client carrying a reverse config that is not reversible: RemoveUser calls RemoveReverse and deletes the client's outbound handler, while the account added back is built without the reverse field, so the tunnel stays down until Xray restarts and the core's forward-proxy guard for that client no longer fires (proxy/vless/inbound/inbound.go:245 and :542-544 at the pinned core). The cycle now skips such a client and says so, instead of trading a limit violation for a tunnel that needs a restart to come back. TestDisconnectClientTemporarilySkipsReverseClient fails without this -- watched red, the client is removed and re-added -- and asserts the skip is logged rather than silent. * style(limit-ip): keep the reverse-client comment within the 2-line cap The block explaining why a reverse client is skipped was three lines, against the rule this repo sets for committed Go comments; the same why fits in two.
76 lines
2.2 KiB
Go
76 lines
2.2 KiB
Go
package job
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
|
"github.com/mhsanaei/3x-ui/v3/internal/logger"
|
|
)
|
|
|
|
// A reverse client has to be left alone: the cycle drops its reverse outbound
|
|
// handler and re-adds an account without a reverse, which no retry restores.
|
|
func TestDisconnectClientTemporarilySkipsReverseClient(t *testing.T) {
|
|
setupIntegrationDB(t)
|
|
|
|
const email = "rev-limit-probe"
|
|
inbound := &model.Inbound{
|
|
Id: 1,
|
|
Protocol: model.VLESS,
|
|
Tag: "rev-limit-probe-tag",
|
|
Settings: `{"clients":[]}`,
|
|
}
|
|
clients := []model.Client{{Email: email, ID: "11111111-1111-1111-1111-111111111111", Reverse: &model.ClientReverse{Tag: "rev-out"}}}
|
|
|
|
(&CheckClientIpJob{}).disconnectClientTemporarily(inbound, email, clients)
|
|
|
|
var skipped, attempted bool
|
|
for _, line := range logger.GetLogs(500, "warning") {
|
|
if strings.Contains(line, "Not disconnecting "+email) {
|
|
skipped = true
|
|
}
|
|
if strings.Contains(line, "Failed to remove user "+email) {
|
|
attempted = true
|
|
}
|
|
}
|
|
if attempted {
|
|
t.Fatal("a reverse client must not be removed and re-added")
|
|
}
|
|
if !skipped {
|
|
t.Fatal("the skip must be reported, not silent")
|
|
}
|
|
}
|
|
|
|
// The protocol gate must let hysteria through: XrayAPI supports it, and the
|
|
// skip left over-limit Hysteria2 sessions alive until the fail2ban ban caught up.
|
|
func TestDisconnectClientTemporarilyAllowsHysteria(t *testing.T) {
|
|
setupIntegrationDB(t)
|
|
|
|
const email = "hy2-limit-probe"
|
|
inbound := &model.Inbound{
|
|
Id: 1,
|
|
Protocol: model.Hysteria,
|
|
Tag: "hy2-limit-probe-tag",
|
|
Settings: `{"clients":[]}`,
|
|
}
|
|
clients := []model.Client{{Email: email, Auth: "secret"}}
|
|
|
|
(&CheckClientIpJob{}).disconnectClientTemporarily(inbound, email, clients)
|
|
|
|
var unsupported, attempted bool
|
|
for _, line := range logger.GetLogs(500, "warning") {
|
|
if strings.Contains(line, "Temporary disconnect is not supported for protocol hysteria") {
|
|
unsupported = true
|
|
}
|
|
if strings.Contains(line, "Failed to remove user "+email) {
|
|
attempted = true
|
|
}
|
|
}
|
|
if unsupported {
|
|
t.Fatal("hysteria was rejected by the protocol gate")
|
|
}
|
|
if !attempted {
|
|
t.Fatal("expected a remove attempt against the Xray API for hysteria")
|
|
}
|
|
}
|