inbound_finalmask_xdns_test.go 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package service
  2. import (
  3. "encoding/json"
  4. "testing"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database"
  6. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  7. )
  8. const legacyXdnsStream = `{"network":"kcp","security":"none","kcpSettings":{"mtu":900},"finalmask":{"udp":[{"type":"xdns","settings":{"domains":["t.example.com"]}}]}}`
  9. func firstXdnsDomain(t *testing.T, stream map[string]any) any {
  10. t.Helper()
  11. finalmask, _ := stream["finalmask"].(map[string]any)
  12. udp, _ := finalmask["udp"].([]any)
  13. if len(udp) != 1 {
  14. t.Fatalf("finalmask.udp = %v, want one mask", finalmask["udp"])
  15. }
  16. mask, _ := udp[0].(map[string]any)
  17. settings, _ := mask["settings"].(map[string]any)
  18. domains, _ := settings["domains"].([]any)
  19. if len(domains) != 1 {
  20. t.Fatalf("xdns domains = %v, want one", settings["domains"])
  21. }
  22. return domains[0]
  23. }
  24. // An API client can still post the pre-26.9.30 string lists; stored as sent they would
  25. // reach the sub links and the form in a shape the core no longer parses.
  26. func TestAddInbound_StoresXdnsMaskInObjectShape(t *testing.T) {
  27. setupConflictDB(t)
  28. in := &model.Inbound{
  29. Tag: "in-45300-kcp", Enable: true, Listen: "0.0.0.0", Port: 45300, Protocol: model.VLESS,
  30. Settings: `{"clients":[],"decryption":"none"}`, StreamSettings: legacyXdnsStream,
  31. }
  32. if _, _, err := (&InboundService{}).AddInbound(in); err != nil {
  33. t.Fatalf("AddInbound: %v", err)
  34. }
  35. var stored model.Inbound
  36. if err := database.GetDB().Where("tag = ?", "in-45300-kcp").First(&stored).Error; err != nil {
  37. t.Fatalf("reload: %v", err)
  38. }
  39. var stream map[string]any
  40. if err := json.Unmarshal([]byte(stored.StreamSettings), &stream); err != nil {
  41. t.Fatalf("stored stream is not JSON: %v", err)
  42. }
  43. domain, ok := firstXdnsDomain(t, stream).(map[string]any)
  44. if !ok || domain["name"] != "t.example.com" {
  45. t.Fatalf("stored xdns domain = %#v, want an object named t.example.com", firstXdnsDomain(t, stream))
  46. }
  47. }
  48. // A row that never went through the save path (restored backup, node sync, direct DB
  49. // edit) must still reach the core in a shape it builds, or it keeps every inbound down.
  50. func TestGetXrayConfig_UpgradesLegacyXdnsMask(t *testing.T) {
  51. setupConflictDB(t)
  52. seedInboundConflict(t, "in-45301-kcp", "127.0.0.1", 45301, model.VLESS,
  53. legacyXdnsStream, `{"clients":[],"decryption":"none"}`)
  54. var legacy map[string]any
  55. if err := json.Unmarshal([]byte(`{"tag":"in-45301-kcp","listen":"127.0.0.1","port":45301,"protocol":"vless",
  56. "settings":{"clients":[],"decryption":"none"},"streamSettings":`+legacyXdnsStream+`}`), &legacy); err != nil {
  57. t.Fatalf("decode legacy inbound: %v", err)
  58. }
  59. if err := buildGoldenInbound(t, legacy); err == nil {
  60. t.Fatal("xray-core accepted the legacy xdns lists; the heal is no longer needed")
  61. }
  62. cfg, err := (&XrayService{}).GetXrayConfig()
  63. if err != nil {
  64. t.Fatalf("GetXrayConfig: %v", err)
  65. }
  66. for i := range cfg.InboundConfigs {
  67. if cfg.InboundConfigs[i].Tag != "in-45301-kcp" {
  68. continue
  69. }
  70. raw, err := json.Marshal(cfg.InboundConfigs[i])
  71. if err != nil {
  72. t.Fatalf("marshal emitted inbound: %v", err)
  73. }
  74. var emitted map[string]any
  75. if err := json.Unmarshal(raw, &emitted); err != nil {
  76. t.Fatalf("decode emitted inbound: %v", err)
  77. }
  78. assertXrayAccepts(t, "the healed xdns inbound", buildGoldenInbound(t, emitted))
  79. return
  80. }
  81. t.Fatal("inbound in-45301-kcp not found in the generated config")
  82. }