clash_external_shadowsocks_test.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. package sub
  2. import (
  3. "reflect"
  4. "testing"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. // Clash has no shadowsocks tcp/http header, so the inbound path drops the node
  8. // (applyTransport returns false) and the external-link path must drop it too,
  9. // instead of handing mihomo a proxy that connects with the wrong obfuscation.
  10. func TestClashExternalShadowsocksMatchesInboundPath(t *testing.T) {
  11. const settings = `{"method":"aes-256-gcm","password":"inboundpw","clients":[{"password":"clientpw","email":"user"}]}`
  12. // base64("aes-256-gcm:clientpw") — the SIP002 userinfo of the links below.
  13. const userinfo = "YWVzLTI1Ni1nY206Y2xpZW50cHc"
  14. tests := []struct {
  15. name string
  16. stream string
  17. link string
  18. wantDropped bool
  19. }{
  20. {
  21. name: "plain tcp",
  22. stream: `{"network":"tcp","security":"none"}`,
  23. link: "ss://" + userinfo + "@203.0.113.1:8443?type=tcp#ss",
  24. },
  25. {
  26. name: "tcp http header",
  27. stream: `{"network":"tcp","security":"none","tcpSettings":{"header":{"type":"http","request":{"path":["/"],"headers":{"Host":["test"]}}}}}`,
  28. link: "ss://" + userinfo + "@203.0.113.1:8443?type=tcp&headerType=http&host=test#ss",
  29. wantDropped: true,
  30. },
  31. {
  32. name: "tls",
  33. stream: `{"network":"tcp","security":"tls","tlsSettings":{"serverName":"ss.sni"}}`,
  34. link: "ss://" + userinfo + "@203.0.113.1:8443?type=tcp&security=tls&sni=ss.sni#ss",
  35. },
  36. }
  37. svc := NewSubClashService(false, "", &SubService{})
  38. client := model.Client{Password: "clientpw", Email: "user"}
  39. for _, tc := range tests {
  40. t.Run(tc.name, func(t *testing.T) {
  41. inbound := &model.Inbound{
  42. Listen: "203.0.113.1",
  43. Port: 8443,
  44. Protocol: model.Shadowsocks,
  45. Remark: "ss",
  46. Settings: settings,
  47. StreamSettings: tc.stream,
  48. }
  49. fromInbound := svc.buildProxy(svc.SubService, inbound, client, svc.streamData(tc.stream), nil)
  50. fromLink := svc.clashProxyFromExternal(tc.link, "external")
  51. if tc.wantDropped {
  52. if fromInbound != nil || fromLink != nil {
  53. t.Fatalf("not representable in clash, want both dropped: inbound %#v, link %#v", fromInbound, fromLink)
  54. }
  55. return
  56. }
  57. if fromInbound == nil || fromLink == nil {
  58. t.Fatalf("want a proxy from both paths: inbound %#v, link %#v", fromInbound, fromLink)
  59. }
  60. delete(fromInbound, "name")
  61. delete(fromLink, "name")
  62. if !reflect.DeepEqual(fromInbound, fromLink) {
  63. t.Fatalf("inbound %#v != link %#v", fromInbound, fromLink)
  64. }
  65. })
  66. }
  67. }