sub_panic_test.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. package sub
  2. import (
  3. "fmt"
  4. "strings"
  5. "testing"
  6. "github.com/gin-gonic/gin"
  7. "github.com/mhsanaei/3x-ui/v3/internal/database"
  8. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  9. )
  10. func TestGetSubsToleratesUnusualStreamSettings(t *testing.T) {
  11. gin.SetMode(gin.TestMode)
  12. cases := []struct {
  13. name string
  14. stream string
  15. }{
  16. {"reality empty arrays", `{"network":"tcp","security":"reality","realitySettings":{"serverNames":[],"shortIds":[],"settings":{"publicKey":"pk"}}}`},
  17. {"tcp http missing request", `{"network":"tcp","security":"none","tcpSettings":{"header":{"type":"http","response":{"headers":{}}}}}`},
  18. {"tcp http empty path", `{"network":"tcp","security":"none","tcpSettings":{"header":{"type":"http","request":{"path":[]}}}}`},
  19. {"grpc missing keys", `{"network":"grpc","security":"none","grpcSettings":{}}`},
  20. {"empty stream settings", `{}`},
  21. {"ws missing wsSettings", `{"network":"ws","security":"none"}`},
  22. {"httpupgrade missing settings", `{"network":"httpupgrade","security":"none"}`},
  23. {"tls alpn non-string element", `{"network":"tcp","security":"tls","tlsSettings":{"alpn":[123]}}`},
  24. }
  25. for i, tc := range cases {
  26. t.Run(tc.name, func(t *testing.T) {
  27. seedSubDB(t)
  28. subId := fmt.Sprintf("s%d", i)
  29. seedSubInbound(t, subId, fmt.Sprintf("t%d", i), 46000+i, 1, tc.stream)
  30. links, _, _, _, err := NewSubService("").GetSubs(subId, "req.example.com")
  31. if err != nil {
  32. t.Fatalf("GetSubs errored: %v", err)
  33. }
  34. if len(links) != 1 {
  35. t.Fatalf("expected 1 share link, got %d", len(links))
  36. }
  37. })
  38. }
  39. }
  40. func TestGetJsonToleratesHysteriaWithoutHysteriaSettings(t *testing.T) {
  41. seedSubDB(t)
  42. db := database.GetDB()
  43. const subId = "hy1"
  44. const email = "hy@e"
  45. ib := &model.Inbound{
  46. UserId: 1, Tag: "hy", Enable: true, Listen: "203.0.113.5", Port: 46200,
  47. Protocol: model.Hysteria,
  48. Remark: "hy",
  49. Settings: fmt.Sprintf(`{"version":2,"clients":[{"auth":"hyauth","email":%q,"subId":%q,"enable":true}]}`, email, subId),
  50. StreamSettings: `{"security":"tls","tlsSettings":{"serverName":"hy.sni"}}`,
  51. }
  52. if err := db.Create(ib).Error; err != nil {
  53. t.Fatalf("seed inbound: %v", err)
  54. }
  55. client := &model.ClientRecord{Email: email, SubID: subId, Enable: true}
  56. if err := db.Create(client).Error; err != nil {
  57. t.Fatalf("seed client: %v", err)
  58. }
  59. if err := db.Create(&model.ClientInbound{ClientId: client.Id, InboundId: ib.Id}).Error; err != nil {
  60. t.Fatalf("seed client_inbound: %v", err)
  61. }
  62. jsonService := NewSubJsonService("", "", "", NewSubService(""))
  63. out, _, err := jsonService.GetJson(subId, "sub.example.com", true)
  64. if err != nil {
  65. t.Fatalf("GetJson: %v", err)
  66. }
  67. if out == "" {
  68. t.Fatal("GetJson returned empty for a hysteria inbound without hysteriaSettings")
  69. }
  70. }
  71. func TestGetJsonToleratesNonStringRealityShortId(t *testing.T) {
  72. seedSubDB(t)
  73. stream := `{"network":"tcp","security":"reality","realitySettings":{"serverNames":["sni.example.com"],"shortIds":[42],"settings":{"publicKey":"pk"}}}`
  74. seedSubInbound(t, "rlty1", "rlty", 46400, 1, stream)
  75. jsonService := NewSubJsonService("", "", "", NewSubService(""))
  76. out, _, err := jsonService.GetJson("rlty1", "sub.example.com", true)
  77. if err != nil {
  78. t.Fatalf("GetJson: %v", err)
  79. }
  80. if out == "" {
  81. t.Fatal("GetJson returned empty for a reality inbound with a non-string shortId element")
  82. }
  83. }
  84. func TestGetClashEmitsPinnedCertSha256(t *testing.T) {
  85. seedSubDB(t)
  86. const pin = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
  87. stream := `{"network":"tcp","security":"tls","tlsSettings":{"serverName":"pin.sni","settings":{"pinnedPeerCertSha256":["` + pin + `"]}}}`
  88. seedSubInbound(t, "pin1", "pin", 46300, 1, stream)
  89. out, _, err := NewSubClashService(false, "", NewSubService("")).GetClash("pin1", "sub.example.com")
  90. if err != nil {
  91. t.Fatalf("GetClash: %v", err)
  92. }
  93. if !strings.Contains(out, "pin-sha256") {
  94. t.Fatalf("Clash proxy dropped the pinned cert sha256:\n%s", out)
  95. }
  96. }
  97. func TestJsonAndClashTolerateExternalProxyMissingPort(t *testing.T) {
  98. seedSubDB(t)
  99. stream := `{"network":"tcp","security":"none","externalProxy":[{"forceTls":"same","dest":"cdn.example.com"}]}`
  100. seedSubInbound(t, "extp1", "extp", 46500, 1, stream)
  101. jsonService := NewSubJsonService("", "", "", NewSubService(""))
  102. jsonOut, _, err := jsonService.GetJson("extp1", "sub.example.com", true)
  103. if err != nil {
  104. t.Fatalf("GetJson: %v", err)
  105. }
  106. if jsonOut == "" {
  107. t.Fatal("GetJson returned empty for an externalProxy entry missing port")
  108. }
  109. clashOut, _, err := NewSubClashService(false, "", NewSubService("")).GetClash("extp1", "sub.example.com")
  110. if err != nil {
  111. t.Fatalf("GetClash: %v", err)
  112. }
  113. if clashOut == "" {
  114. t.Fatal("GetClash returned empty for an externalProxy entry missing port")
  115. }
  116. }