package awg import ( "encoding/base64" "net" "testing" ) func TestParseINI_AWG20(t *testing.T) { raw := `[Interface] PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= Address = 10.8.0.2/32 DNS = 1.1.1.1 Jc = 4 Jmin = 40 Jmax = 70 H1 = 1 H2 = 2 H3 = 3 H4 = 4 I1 = [Peer] PublicKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE= Endpoint = 203.0.113.10:51820 AllowedIPs = 0.0.0.0/0 PersistentKeepalive = 25 ` c, err := Parse(raw) if err != nil { t.Fatal(err) } if c.Jc != 4 || c.I1 != "" || c.Endpoint != "203.0.113.10:51820" { t.Fatalf("unexpected conf: %+v", c) } if !c.IsAWG20() { t.Fatal("expected AWG2 markers") } ipc, err := c.ToIPC() if err != nil { t.Fatal(err) } if !containsAll(ipc, "jc=4", "i1=", "endpoint=203.0.113.10:51820") { t.Fatalf("bad ipc: %s", ipc) } } func TestParseURI_UserInfoPrivateKey(t *testing.T) { priv := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" pub := "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=" raw := "awg://" + priv + "@203.0.113.10:51820/?public_key=" + pub + "&address=10.8.0.2/32&jc=4" c, err := Parse(raw) if err != nil { t.Fatal(err) } if c.PrivateKey != priv { t.Fatalf("private key: %q", c.PrivateKey) } if c.PublicKey != pub { t.Fatalf("public key: %q", c.PublicKey) } if c.Endpoint != "203.0.113.10:51820" { t.Fatalf("endpoint: %q", c.Endpoint) } } func TestParseINI_SpacedKeysAndQuotes(t *testing.T) { raw := `Private Key = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" Address = 10.8.0.2/32 [Peer] Public Key = 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=' Endpoint = 1.2.3.4:51820 ` c, err := Parse(raw) if err != nil { t.Fatal(err) } if c.PrivateKey == "" || c.PublicKey == "" { t.Fatalf("keys missing: %+v", c) } } func TestParseAmneziaJSON(t *testing.T) { raw := `{ "client_priv_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", "server_pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=", "client_ip": "10.8.0.2/32", "hostName": "203.0.113.10", "port": "51820", "Jc": 4, "Jmin": 10, "Jmax": 50, "H1": "1", "H2": "2", "H3": "3", "H4": "4" }` c, err := Parse(raw) if err != nil { t.Fatal(err) } if c.Endpoint != "203.0.113.10:51820" { t.Fatalf("endpoint %q", c.Endpoint) } if c.Jc != 4 { t.Fatalf("jc %d", c.Jc) } } func TestParseVPNScheme(t *testing.T) { inner := `{"client_priv_key":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=","server_pub_key":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=","client_ip":"10.8.0.2/32","hostName":"9.9.9.9","port":"51820"}` raw := "vpn://" + base64.StdEncoding.EncodeToString([]byte(inner)) c, err := Parse(raw) if err != nil { t.Fatal(err) } if c.Endpoint != "9.9.9.9:51820" { t.Fatalf("endpoint %q", c.Endpoint) } } func TestDetect(t *testing.T) { if !Detect("[Interface]\nPrivateKey = x") { t.Fatal("expected detect") } if !Detect("awg://1.2.3.4:51820/?private_key=a&public_key=b&address=10.0.0.2/32") { t.Fatal("expected awg uri detect") } if !Detect(`{"client_priv_key":"x","server_pub_key":"y"}`) { t.Fatal("expected json detect") } } func TestDecodeKeyURLSafe(t *testing.T) { // 32 zero bytes, URL-safe without padding raw := base64.RawURLEncoding.EncodeToString(make([]byte, 32)) b, err := decodeKey(raw) if err != nil || len(b) != 32 { t.Fatalf("url-safe: %v %d", err, len(b)) } } func TestResolveEndpointPrefersIPv4(t *testing.T) { got, err := resolveEndpointWith("peer.example:41421", func(host string) ([]net.IP, error) { if host != "peer.example" { t.Fatalf("host %q", host) } return []net.IP{ net.ParseIP("2001:db8::1"), net.ParseIP("203.0.113.44"), }, nil }) if err != nil { t.Fatal(err) } if got != "203.0.113.44:41421" { t.Fatalf("got %q", got) } } func TestResolveEndpointAlreadyIP(t *testing.T) { got, err := resolveEndpointWith("203.0.113.10:51820", func(string) ([]net.IP, error) { t.Fatal("lookup should not run for IP") return nil, nil }) if err != nil || got != "203.0.113.10:51820" { t.Fatalf("%q %v", got, err) } } func TestResolveEndpointDNSError(t *testing.T) { _, err := resolveEndpointWith("missing.invalid:41421", func(string) ([]net.IP, error) { return nil, &net.DNSError{Err: "no such host", Name: "missing.invalid", IsNotFound: true} }) if err == nil || !stringsContains(err.Error(), "не удалось разрешить DNS") { t.Fatalf("expected Russian DNS error, got %v", err) } } func TestToIPCResolvesEndpoint(t *testing.T) { old := lookupIP lookupIP = func(host string) ([]net.IP, error) { return []net.IP{net.ParseIP("198.51.100.9")}, nil } defer func() { lookupIP = old }() c := Conf{ PrivateKey: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", PublicKey: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=", Address: []string{"10.8.0.2/32"}, Endpoint: "peer.example:41421", } ipc, err := c.ToIPC() if err != nil { t.Fatal(err) } if !stringsContains(ipc, "endpoint=198.51.100.9:41421") { t.Fatalf("ipc missing resolved endpoint: %s", ipc) } } func TestConfResolveEndpointMutates(t *testing.T) { old := lookupIP lookupIP = func(string) ([]net.IP, error) { return []net.IP{net.ParseIP("198.51.100.1")}, nil } defer func() { lookupIP = old }() c := Conf{Endpoint: "host.example:41421"} if err := c.ResolveEndpoint(); err != nil { t.Fatal(err) } if c.Endpoint != "198.51.100.1:41421" { t.Fatalf("endpoint %q", c.Endpoint) } } func containsAll(s string, parts ...string) bool { for _, p := range parts { if !stringsContains(s, p) { return false } } return true } func stringsContains(s, sub string) bool { return len(sub) == 0 || (len(s) >= len(sub) && indexOf(s, sub) >= 0) } func indexOf(s, sub string) int { for i := 0; i+len(sub) <= len(s); i++ { if s[i:i+len(sub)] == sub { return i } } return -1 } func TestHostPortLightINI(t *testing.T) { raw := `[Interface] PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= Address = 10.8.0.2/32 [Peer] PublicKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE= Endpoint = 203.0.113.10:51820 AllowedIPs = 0.0.0.0/0 ` h, p, err := HostPort(raw) if err != nil { t.Fatal(err) } if h != "203.0.113.10" || p != "51820" { t.Fatalf("got %s:%s", h, p) } h2, p2, err := HostPort("awg://AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=@9.9.9.9:41421") if err != nil { t.Fatal(err) } if h2 != "9.9.9.9" || p2 != "41421" { t.Fatalf("uri got %s:%s", h2, p2) } }