1
0

instance_test.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package amneziawg
  2. import (
  3. "encoding/json"
  4. "testing"
  5. "github.com/mhsanaei/3x-ui/v3/internal/database/model"
  6. )
  7. func mkInboundSettings(t *testing.T, server *ServerSettings, clients []model.Client) string {
  8. t.Helper()
  9. bs, err := json.Marshal(InboundSettings{Server: server, Clients: clients})
  10. if err != nil {
  11. t.Fatalf("marshal settings: %v", err)
  12. }
  13. return string(bs)
  14. }
  15. func validServer() *ServerSettings {
  16. return &ServerSettings{
  17. PrivateKey: "serverPriv",
  18. PublicKey: "serverPub",
  19. SubnetIP: "10.8.1.0",
  20. SubnetCIDR: 24,
  21. }
  22. }
  23. func TestInstanceFromInboundParsesEnabledPeers(t *testing.T) {
  24. settings := mkInboundSettings(t, validServer(), []model.Client{
  25. {Email: "a@x", Enable: true, PublicKey: "pubA", PreSharedKey: "pskA", AllowedIPs: []string{"10.8.1.2/32"}},
  26. {Email: "b@x", Enable: false, PublicKey: "pubB", AllowedIPs: []string{"10.8.1.3/32"}},
  27. {Email: "c@x", Enable: true, PublicKey: "", AllowedIPs: []string{"10.8.1.4/32"}}, // no key: skipped
  28. {Email: "d@x", Enable: true, PublicKey: "pubD", AllowedIPs: nil}, // no address: skipped
  29. })
  30. ib := &model.Inbound{Id: 7, Tag: "awg-tag", Protocol: model.AmneziaWG, Port: 51820, Listen: "203.0.113.10", Settings: settings}
  31. inst, ok := InstanceFromInbound(ib)
  32. if !ok {
  33. t.Fatal("expected a usable instance")
  34. }
  35. if inst.Id != 7 || inst.Tag != "awg-tag" || inst.ListenPort != 51820 {
  36. t.Fatalf("instance identity not carried over: %+v", inst)
  37. }
  38. if inst.Listen != "203.0.113.10" {
  39. t.Fatalf("Listen = %q, want inbound listen carried through", inst.Listen)
  40. }
  41. if inst.InterfaceName != "awg7" {
  42. t.Fatalf("InterfaceName = %q, want awg7", inst.InterfaceName)
  43. }
  44. if len(inst.Address) != 1 || inst.Address[0] != "10.8.1.1/24" {
  45. t.Fatalf("Address = %v, want [10.8.1.1/24]", inst.Address)
  46. }
  47. if len(inst.Peers) != 1 {
  48. t.Fatalf("Peers = %+v, want exactly 1 (only a@x qualifies)", inst.Peers)
  49. }
  50. p := inst.Peers[0]
  51. if p.Email != "a@x" || p.PublicKey != "pubA" || p.PresharedKey != "pskA" || len(p.AllowedIPs) != 1 || p.AllowedIPs[0] != "10.8.1.2/32" {
  52. t.Fatalf("peer mismatch: %+v", p)
  53. }
  54. }
  55. func TestInstanceFromInboundCopiesAWG30Fields(t *testing.T) {
  56. server := validServer()
  57. server.S1, server.S2, server.S3, server.S4 = 20, 20, 20, 20
  58. server.HeaderProtectionKey = "some-header-protection-key"
  59. server.ContentPaddingAddition = "50-100"
  60. settings := mkInboundSettings(t, server, []model.Client{
  61. {Email: "a@x", Enable: true, PublicKey: "pubA", AllowedIPs: []string{"10.8.1.2/32"}},
  62. })
  63. ib := &model.Inbound{Id: 7, Protocol: model.AmneziaWG, Port: 51820, Settings: settings}
  64. inst, ok := InstanceFromInbound(ib)
  65. if !ok {
  66. t.Fatal("expected a usable instance")
  67. }
  68. if inst.Obfuscation.HeaderProtectionKey != "some-header-protection-key" {
  69. t.Fatalf("HeaderProtectionKey = %q, want it copied from ServerSettings", inst.Obfuscation.HeaderProtectionKey)
  70. }
  71. if inst.Obfuscation.ContentPaddingAddition != "50-100" {
  72. t.Fatalf("ContentPaddingAddition = %q, want it copied from ServerSettings", inst.Obfuscation.ContentPaddingAddition)
  73. }
  74. }
  75. func TestInstanceFromInboundRejectsWrongProtocol(t *testing.T) {
  76. settings := mkInboundSettings(t, validServer(), []model.Client{
  77. {Email: "a@x", Enable: true, PublicKey: "pubA", AllowedIPs: []string{"10.8.1.2/32"}},
  78. })
  79. ib := &model.Inbound{Id: 1, Protocol: model.VLESS, Settings: settings}
  80. if _, ok := InstanceFromInbound(ib); ok {
  81. t.Fatal("non-AmneziaWG inbound must be rejected")
  82. }
  83. }
  84. func TestInstanceFromInboundRejectsNil(t *testing.T) {
  85. if _, ok := InstanceFromInbound(nil); ok {
  86. t.Fatal("nil inbound must be rejected")
  87. }
  88. }
  89. func TestInstanceFromInboundRejectsMissingServer(t *testing.T) {
  90. ib := &model.Inbound{Id: 1, Protocol: model.AmneziaWG, Settings: `{"clients":[]}`}
  91. if _, ok := InstanceFromInbound(ib); ok {
  92. t.Fatal("settings with no server block must be rejected")
  93. }
  94. }
  95. func TestInstanceFromInboundRejectsUnparseableSettings(t *testing.T) {
  96. ib := &model.Inbound{Id: 1, Protocol: model.AmneziaWG, Settings: `not json`}
  97. if _, ok := InstanceFromInbound(ib); ok {
  98. t.Fatal("unparseable settings must be rejected")
  99. }
  100. }
  101. func TestInstanceFromInboundEmptyWhenNoEnabledPeers(t *testing.T) {
  102. settings := mkInboundSettings(t, validServer(), []model.Client{
  103. {Email: "a@x", Enable: false, PublicKey: "pubA", AllowedIPs: []string{"10.8.1.2/32"}},
  104. })
  105. ib := &model.Inbound{Id: 1, Protocol: model.AmneziaWG, Settings: settings}
  106. if _, ok := InstanceFromInbound(ib); ok {
  107. t.Fatal("an inbound with zero enabled peers must be skipped, like mtproto.InstanceFromInbound")
  108. }
  109. }
  110. func TestServerAddress(t *testing.T) {
  111. cases := []struct {
  112. subnet string
  113. cidr int
  114. want string
  115. }{
  116. {"10.8.1.0", 24, "10.8.1.1/24"},
  117. {"10.8.1.0", 0, "10.8.1.1/24"}, // cidr <= 0 defaults to /24
  118. {"10.8.1.5", 24, "10.8.1.1/24"}, // non-network base: must not collide with peer allocation starting at .2
  119. {"10.8.1.254", 24, "10.8.1.1/24"},
  120. {"192.168.5.10", 32, "192.168.5.10/32"}, // /32 has no host bits: used as-is
  121. }
  122. for _, c := range cases {
  123. if got := serverAddress(c.subnet, c.cidr); got != c.want {
  124. t.Errorf("serverAddress(%q, %d) = %q, want %q", c.subnet, c.cidr, got, c.want)
  125. }
  126. }
  127. }
  128. func TestInterfaceNameForID(t *testing.T) {
  129. if got := interfaceNameForID(42); got != "awg42" {
  130. t.Errorf("interfaceNameForID(42) = %q, want awg42", got)
  131. }
  132. }
  133. func TestFirstIPv4(t *testing.T) {
  134. cases := []struct {
  135. name string
  136. ips []string
  137. want string
  138. }{
  139. {"single v4 CIDR", []string{"10.8.1.2/32"}, "10.8.1.2"},
  140. {"bare v4 address, no mask", []string{"10.8.1.2"}, "10.8.1.2"},
  141. {"v6 first, v4 second", []string{"fd86:ea04:1115::2/128", "10.8.1.2/32"}, "10.8.1.2"},
  142. {"v4-only among several", []string{"10.8.1.2/32", "10.8.1.3/32"}, "10.8.1.2"},
  143. {"v6 only", []string{"fd86:ea04:1115::2/128"}, ""},
  144. {"empty input", nil, ""},
  145. {"unparseable entries skipped", []string{"not-an-ip", "10.8.1.2/32"}, "10.8.1.2"},
  146. }
  147. for _, c := range cases {
  148. if got := FirstIPv4(c.ips); got != c.want {
  149. t.Errorf("%s: FirstIPv4(%v) = %q, want %q", c.name, c.ips, got, c.want)
  150. }
  151. }
  152. }
  153. func TestFirstIPv6(t *testing.T) {
  154. cases := []struct {
  155. name string
  156. ips []string
  157. want string
  158. }{
  159. {"single v6 CIDR", []string{"fd86:ea04:1115::2/128"}, "fd86:ea04:1115::2"},
  160. {"bare v6 address, no mask", []string{"fd86:ea04:1115::2"}, "fd86:ea04:1115::2"},
  161. {"v4 first, v6 second", []string{"10.8.1.2/32", "fd86:ea04:1115::2/128"}, "fd86:ea04:1115::2"},
  162. {"only first of two v6 entries returned", []string{"fd86:ea04:1115::2/128", "fd86:ea04:1115::3/128"}, "fd86:ea04:1115::2"},
  163. {"v4 only", []string{"10.8.1.2/32"}, ""},
  164. {"empty input", nil, ""},
  165. {"unparseable entries skipped", []string{"not-an-ip", "fd86:ea04:1115::2/128"}, "fd86:ea04:1115::2"},
  166. {"v4-mapped v6 is not a real v6 identity", []string{"::ffff:10.8.1.2/128"}, ""},
  167. }
  168. for _, c := range cases {
  169. if got := FirstIPv6(c.ips); got != c.want {
  170. t.Errorf("%s: FirstIPv6(%v) = %q, want %q", c.name, c.ips, got, c.want)
  171. }
  172. }
  173. }