|
@@ -0,0 +1,160 @@
|
|
|
|
|
+package amneziawgnet
|
|
|
|
|
+
|
|
|
|
|
+import (
|
|
|
|
|
+ "net"
|
|
|
|
|
+ "net/netip"
|
|
|
|
|
+ "strconv"
|
|
|
|
|
+ "testing"
|
|
|
|
|
+ "time"
|
|
|
|
|
+
|
|
|
|
|
+ awgconn "github.com/amnezia-vpn/amneziawg-go/v3/conn"
|
|
|
|
|
+
|
|
|
|
|
+ "github.com/mhsanaei/3x-ui/v3/internal/amneziawg"
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+func TestParseListenAddr(t *testing.T) {
|
|
|
|
|
+ cases := []struct {
|
|
|
|
|
+ in string
|
|
|
|
|
+ pinned bool
|
|
|
|
|
+ want string
|
|
|
|
|
+ }{
|
|
|
|
|
+ {in: "", pinned: false},
|
|
|
|
|
+ {in: " ", pinned: false},
|
|
|
|
|
+ {in: "0.0.0.0", pinned: false},
|
|
|
|
|
+ {in: "::", pinned: false},
|
|
|
|
|
+ {in: "::0", pinned: false},
|
|
|
|
|
+ {in: "[::]", pinned: false},
|
|
|
|
|
+ {in: "[::0]", pinned: false},
|
|
|
|
|
+ {in: "127.0.0.1", pinned: true, want: "127.0.0.1"},
|
|
|
|
|
+ {in: "::1", pinned: true, want: "::1"},
|
|
|
|
|
+ {in: "[::1]", pinned: true, want: "::1"},
|
|
|
|
|
+ {in: "not-an-ip", pinned: false},
|
|
|
|
|
+ {in: "/var/run/awg.sock", pinned: false},
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, tc := range cases {
|
|
|
|
|
+ addr, ok := parseListenAddr(tc.in)
|
|
|
|
|
+ if ok != tc.pinned {
|
|
|
|
|
+ t.Fatalf("parseListenAddr(%q) pinned=%v, want %v", tc.in, ok, tc.pinned)
|
|
|
|
|
+ }
|
|
|
|
|
+ if tc.pinned && addr.String() != tc.want {
|
|
|
|
|
+ t.Fatalf("parseListenAddr(%q) = %s, want %s", tc.in, addr, tc.want)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func TestNewListenBindPinsSpecificAddress(t *testing.T) {
|
|
|
|
|
+ bind := newListenBind("127.0.0.1")
|
|
|
|
|
+ pb, ok := bind.(*pinnedBind)
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ t.Fatalf("bind type = %T, want *pinnedBind", bind)
|
|
|
|
|
+ }
|
|
|
|
|
+ fns, port, err := pb.Open(0)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("Open: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer pb.Close()
|
|
|
|
|
+ if len(fns) != 1 {
|
|
|
|
|
+ t.Fatalf("ReceiveFuncs = %d, want 1", len(fns))
|
|
|
|
|
+ }
|
|
|
|
|
+ if port == 0 {
|
|
|
|
|
+ t.Fatal("expected a concrete ephemeral port")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ laddr := pb.conn.LocalAddr().(*net.UDPAddr)
|
|
|
|
|
+ got := laddr.AddrPort().Addr().Unmap()
|
|
|
|
|
+ if got.String() != "127.0.0.1" {
|
|
|
|
|
+ t.Fatalf("LocalAddr = %v, want 127.0.0.1", got)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ clash := newListenBind("127.0.0.1")
|
|
|
|
|
+ if _, _, err := clash.Open(port); err == nil {
|
|
|
|
|
+ clash.Close()
|
|
|
|
|
+ t.Fatalf("Open(%d) unexpectedly succeeded on an already-bound address", port)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func TestNewListenBindWildcardUsesDefault(t *testing.T) {
|
|
|
|
|
+ for _, listen := range []string{"", "0.0.0.0", "::", "::0", "[::]", "hostname.example", "203.0.113.10", "not-an-ip"} {
|
|
|
|
|
+ bind := newListenBind(listen)
|
|
|
|
|
+ if _, ok := bind.(*pinnedBind); ok {
|
|
|
|
|
+ t.Fatalf("newListenBind(%q) returned pinnedBind, want default StdNetBind", listen)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func TestPinnedBindRoundTrip(t *testing.T) {
|
|
|
|
|
+ server := newListenBind("127.0.0.1")
|
|
|
|
|
+ recvFns, port, err := server.Open(0)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("server Open: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer server.Close()
|
|
|
|
|
+
|
|
|
|
|
+ client, err := net.ListenUDP("udp4", &net.UDPAddr{IP: net.IPv4(127, 0, 0, 1)})
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("client listen: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ defer client.Close()
|
|
|
|
|
+
|
|
|
|
|
+ payload := []byte("hello-awg-listen")
|
|
|
|
|
+ dst := net.JoinHostPort("127.0.0.1", strconv.Itoa(int(port)))
|
|
|
|
|
+ ap, err := netip.ParseAddrPort(dst)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("ParseAddrPort: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if _, err := client.WriteToUDPAddrPort(payload, ap); err != nil {
|
|
|
|
|
+ t.Fatalf("client write: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bufs := [][]byte{make([]byte, 1500)}
|
|
|
|
|
+ sizes := make([]int, 1)
|
|
|
|
|
+ eps := make([]awgconn.Endpoint, 1)
|
|
|
|
|
+ n, err := recvFns[0](bufs, sizes, eps)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("receive: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if n != 1 || sizes[0] != len(payload) {
|
|
|
|
|
+ t.Fatalf("receive n=%d size=%d, want 1/%d", n, sizes[0], len(payload))
|
|
|
|
|
+ }
|
|
|
|
|
+ if string(bufs[0][:sizes[0]]) != string(payload) {
|
|
|
|
|
+ t.Fatalf("payload = %q, want %q", bufs[0][:sizes[0]], payload)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ reply := []byte("pong")
|
|
|
|
|
+ if err := server.Send([][]byte{reply}, eps[0]); err != nil {
|
|
|
|
|
+ t.Fatalf("Send: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ _ = client.SetReadDeadline(time.Now().Add(2 * time.Second))
|
|
|
|
|
+ buf := make([]byte, 1500)
|
|
|
|
|
+ rn, _, err := client.ReadFromUDP(buf)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ t.Fatalf("client read: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ if string(buf[:rn]) != string(reply) {
|
|
|
|
|
+ t.Fatalf("reply = %q, want %q", buf[:rn], reply)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func TestAddressFingerprintIncludesListen(t *testing.T) {
|
|
|
|
|
+ base := amneziawg.Instance{
|
|
|
|
|
+ MTU: 1420,
|
|
|
|
|
+ Address: []string{"10.8.1.1/24"},
|
|
|
|
|
+ Obfuscation: amneziawg.Obfuscation31{},
|
|
|
|
|
+ }
|
|
|
|
|
+ a := addressFingerprint(base)
|
|
|
|
|
+ base.Listen = "127.0.0.1"
|
|
|
|
|
+ b := addressFingerprint(base)
|
|
|
|
|
+ if a == b {
|
|
|
|
|
+ t.Fatalf("listen edit did not change addressFingerprint: %q", a)
|
|
|
|
|
+ }
|
|
|
|
|
+ base.Listen = "0.0.0.0"
|
|
|
|
|
+ if addressFingerprint(base) != a {
|
|
|
|
|
+ t.Fatal("wildcard spellings must share the empty-listen fingerprint")
|
|
|
|
|
+ }
|
|
|
|
|
+ base.Listen = "hostname.example"
|
|
|
|
|
+ if addressFingerprint(base) != a {
|
|
|
|
|
+ t.Fatal("unusable listen must fingerprint like wildcard fallback")
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+var _ awgconn.Bind = (*pinnedBind)(nil)
|