inbound_protocol_test.go 4.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. package service
  2. import (
  3. "testing"
  4. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  5. )
  6. // A representative vlessenc/ML-KEM encryption value as produced by `xray
  7. // vlessenc` — a dotted string, never the literal "vlessenc".
  8. const vlessEncValue = "mlkem768x25519plus.native.0rtt.G3cdPSd1-NnlpTbWNSM5vHsT5VNzWfFzYSKwbUMnV1Y"
  9. func TestInboundCanEnableTlsFlow(t *testing.T) {
  10. cases := []struct {
  11. name string
  12. protocol string
  13. streamSettings string
  14. settings string
  15. want bool
  16. }{
  17. {"vless tcp tls", string(model.VLESS), `{"network":"tcp","security":"tls"}`, "", true},
  18. {"vless tcp reality", string(model.VLESS), `{"network":"tcp","security":"reality"}`, "", true},
  19. {"vless tcp none no enc", string(model.VLESS), `{"network":"tcp","security":"none"}`, "", false},
  20. {"vless ws tls", string(model.VLESS), `{"network":"ws","security":"tls"}`, "", false},
  21. {"vless grpc reality", string(model.VLESS), `{"network":"grpc","security":"reality"}`, "", false},
  22. {"vmess tcp tls", string(model.VMESS), `{"network":"tcp","security":"tls"}`, "", false},
  23. {"empty stream", string(model.VLESS), "", "", false},
  24. // xray-core runs Vision inside VLESS encryption on any transport (encryption.CommonConn).
  25. {"vless raw vlessenc", string(model.VLESS), `{"network":"tcp","security":"none"}`, `{"decryption":"mlkem768x25519plus.native.600s.mMFxPe7lz5xoq2qBk22cQYefu5fpc_2dGR8lMOKem0E","encryption":"mlkem768x25519plus.native.0rtt.hT4AY_tPWY9NVuKR3BIXxXq6zx9DqN2X86QPYW09XEM"}`, true},
  26. {"vless ws vlessenc", string(model.VLESS), `{"network":"ws","security":"none"}`, `{"encryption":"` + vlessEncValue + `"}`, true},
  27. {"vless xdrive vlessenc", string(model.VLESS), `{"network":"xdrive","security":"none"}`, `{"decryption":"` + vlessEncValue + `"}`, true},
  28. {"vless xdrive no encryption", string(model.VLESS), `{"network":"xdrive","security":"none"}`, `{"decryption":"none"}`, false},
  29. // XHTTP + VLESS encryption (the #5157 case).
  30. {"vless xhttp vlessenc", string(model.VLESS), `{"network":"xhttp","security":"none"}`, `{"encryption":"` + vlessEncValue + `"}`, true},
  31. {"vless xhttp encryption none", string(model.VLESS), `{"network":"xhttp","security":"none"}`, `{"encryption":"none"}`, false},
  32. {"vless xhttp no settings", string(model.VLESS), `{"network":"xhttp","security":"none"}`, "", false},
  33. // Regression for PR #5185: the gate is "any non-none encryption", NOT an
  34. // equality check against the literal "vlessenc" (which the buggy PR used
  35. // and which never matches a real, generated encryption value). An x25519
  36. // auth value must enable it just like the ML-KEM value above.
  37. {"vless xhttp x25519 enc", string(model.VLESS), `{"network":"xhttp","security":"none"}`, `{"encryption":"native.0rtt.121s-180s.xRMUYYjQctqYO1pSyffM-w"}`, true},
  38. // Server-side configs (API/JSON) may carry only decryption; that alone
  39. // must also enable the flow gate.
  40. {"vless xhttp decryption only", string(model.VLESS), `{"network":"xhttp","security":"none"}`, `{"decryption":"` + vlessEncValue + `","encryption":"none"}`, true},
  41. // XHTTP without encryption stays off even with tls (Vision over XHTTP is
  42. // gated on vlessenc, not transport security).
  43. {"vless xhttp tls no encryption", string(model.VLESS), `{"network":"xhttp","security":"tls"}`, `{"encryption":"none"}`, false},
  44. }
  45. for _, tc := range cases {
  46. t.Run(tc.name, func(t *testing.T) {
  47. got := inboundCanEnableTlsFlow(tc.protocol, tc.streamSettings, tc.settings)
  48. if got != tc.want {
  49. t.Errorf("inboundCanEnableTlsFlow(%q, %q, %q) = %v, want %v",
  50. tc.protocol, tc.streamSettings, tc.settings, got, tc.want)
  51. }
  52. })
  53. }
  54. }
  55. // Fallbacks must remain raw-TCP-only and must NOT follow the broadened flow gate
  56. // onto XHTTP+vlessenc.
  57. func TestInboundCanHostFallbacks_StaysTcpOnly(t *testing.T) {
  58. cases := []struct {
  59. name string
  60. protocol model.Protocol
  61. streamSettings string
  62. settings string
  63. want bool
  64. }{
  65. {"vless tcp tls", model.VLESS, `{"network":"tcp","security":"tls"}`, "", true},
  66. {"trojan tcp reality", model.Trojan, `{"network":"tcp","security":"reality"}`, "", true},
  67. {"vless xhttp vlessenc not fallback-capable", model.VLESS, `{"network":"xhttp","security":"none"}`, `{"encryption":"` + vlessEncValue + `"}`, false},
  68. {"vmess tcp tls not fallback-capable", model.VMESS, `{"network":"tcp","security":"tls"}`, "", false},
  69. {"nil-ish empty stream", model.VLESS, "", "", false},
  70. }
  71. for _, tc := range cases {
  72. t.Run(tc.name, func(t *testing.T) {
  73. ib := &model.Inbound{Protocol: tc.protocol, StreamSettings: tc.streamSettings, Settings: tc.settings}
  74. if got := inboundCanHostFallbacks(ib); got != tc.want {
  75. t.Errorf("inboundCanHostFallbacks = %v, want %v", got, tc.want)
  76. }
  77. })
  78. }
  79. if inboundCanHostFallbacks(nil) {
  80. t.Errorf("inboundCanHostFallbacks(nil) = true, want false")
  81. }
  82. }