client_device_test.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package amneziawgnet
  2. import (
  3. "strings"
  4. "testing"
  5. "github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
  6. wgutil "github.com/mhsanaei/3x-ui/v3/internal/util/wireguard"
  7. )
  8. // clientDeviceTestInstance builds a minimal valid client instance with one
  9. // peer and a non-zero keepalive -- the exact shape the outbound form seeds.
  10. func clientDeviceTestInstance(t *testing.T) amneziawg.OutboundInstance {
  11. t.Helper()
  12. priv, pub, err := wgutil.GenerateWireguardKeypair()
  13. if err != nil {
  14. t.Fatal(err)
  15. }
  16. return amneziawg.OutboundInstance{
  17. Tag: "awg-out-test",
  18. Address: []string{"10.8.0.2/32"},
  19. MTU: 1420,
  20. PrivateKey: priv,
  21. Peers: []amneziawg.OutboundPeer{{
  22. PublicKey: pub,
  23. AllowedIPs: []string{"0.0.0.0/0", "::/0"},
  24. Endpoint: "203.0.113.7:51820",
  25. KeepAlive: 25,
  26. }},
  27. }
  28. }
  29. func TestBuildClientUAPIConfig_KeepAliveKeyIsValidUAPIPeerKey(t *testing.T) {
  30. inst := clientDeviceTestInstance(t)
  31. conf, err := buildClientUAPIConfig(inst, DeviceOptions{})
  32. if err != nil {
  33. t.Fatal(err)
  34. }
  35. want := "persistent_keepalive_interval=25\n"
  36. if !strings.Contains(conf, want) {
  37. t.Fatalf("UAPI config missing %q:\n%s", want, conf)
  38. }
  39. if strings.Contains(conf, "persistent_keepalive_seconds") {
  40. t.Fatalf("UAPI config contains invalid peer key persistent_keepalive_seconds:\n%s", conf)
  41. }
  42. }
  43. func TestBuildClientUAPIConfig_ZeroKeepAliveOmitsLine(t *testing.T) {
  44. inst := clientDeviceTestInstance(t)
  45. inst.Peers[0].KeepAlive = 0
  46. conf, err := buildClientUAPIConfig(inst, DeviceOptions{})
  47. if err != nil {
  48. t.Fatal(err)
  49. }
  50. if strings.Contains(conf, "persistent_keepalive") {
  51. t.Fatalf("zero KeepAlive must not emit a keepalive line:\n%s", conf)
  52. }
  53. }
  54. // amneziawg-go reads an absent UAPI line as "keep the current value", and
  55. // ensureLocked reconfigures in place, so a cleared key must be sent as zero.
  56. func TestBuildClientUAPIConfig_ClearedHeaderProtectionKeyIsSentAsZero(t *testing.T) {
  57. inst := clientDeviceTestInstance(t)
  58. inst.Obfuscation = amneziawg.Obfuscation31{S1: 20, S2: 20, S3: 20, S4: 20}
  59. key, err := wgutil.GenerateWireguardPSK()
  60. if err != nil {
  61. t.Fatal(err)
  62. }
  63. withKey, err := buildClientUAPIConfig(inst, DeviceOptions{HeaderProtectionKey: key})
  64. if err != nil {
  65. t.Fatal(err)
  66. }
  67. keyHex, err := wgutil.KeyToHex(key)
  68. if err != nil {
  69. t.Fatal(err)
  70. }
  71. if !strings.Contains(withKey, "header_protection_key="+keyHex+"\n") {
  72. t.Fatalf("a set key must be emitted verbatim, got:\n%s", withKey)
  73. }
  74. cleared, err := buildClientUAPIConfig(inst, DeviceOptions{})
  75. if err != nil {
  76. t.Fatal(err)
  77. }
  78. zero := "header_protection_key=" + strings.Repeat("0", 64) + "\n"
  79. if !strings.Contains(cleared, zero) {
  80. t.Fatalf("an unset key must be emitted as the all-zero key, got:\n%s", cleared)
  81. }
  82. }
  83. // With no explicit MTU the netstack is built from S4, so an S4-only edit must
  84. // move the fingerprint or ensureLocked reconfigures in place and keeps the old.
  85. func TestOutboundFingerprintTracksTheS4DerivedMTU(t *testing.T) {
  86. tests := []struct {
  87. name string
  88. mtu int
  89. wantChange bool
  90. }{
  91. {"derived MTU", 0, true},
  92. {"explicit MTU", 1420, false},
  93. }
  94. for _, tt := range tests {
  95. t.Run(tt.name, func(t *testing.T) {
  96. inst := clientDeviceTestInstance(t)
  97. inst.MTU = tt.mtu
  98. inst.Obfuscation.S4 = 12
  99. before := outboundFingerprint(inst)
  100. inst.Obfuscation.S4 = 28
  101. after := outboundFingerprint(inst)
  102. if changed := before != after; changed != tt.wantChange {
  103. t.Fatalf("fingerprint changed = %v, want %v (%q -> %q)", changed, tt.wantChange, before, after)
  104. }
  105. })
  106. }
  107. }