Files
3x-ui/internal/amneziawgnet/socks_bridge_test.go
T
Rouzbeh† d5ab84e8d5 feat(amneziawg): add AmneziaWG as an outbound protocol (#6320)
* feat(amneziawg): add AmneziaWG as an outbound protocol

- AmneziaWG outbound protocol end-to-end: config schema, socks bridge, netstack, panel UI
- Route amneziawg outbounds to HTTP probe in TCP mode (backend + frontend classifiers) with pinning test
- Add 2-minute idle read deadline to pumpUDPEgress to reap idle egress sessions
- Require SOCKS5 username/password auth on the egress server (reject NO-AUTH with 0xFF) with test
- Bound the egress TCP tunnel dial with portForwardDialTimeout (10s), matching portfwd.go
- Resolve UDP domain targets off the association's reader loop via deliverUDPDatagram; race-safe getOrDial starts the reply pump at session creation; client passed by value into resolver goroutines (pinned by TestEgressUDPDatagramDomainInterleavedClients)
- Reconcile early-returns on an empty desired set and closes the egress listener; EgressBasePort (64900) is reserved against local inbound port conflicts like the internal API port, with pinning tests for both the port reservation (TestCheckPortConflict_EgressPortBlockedLocal) and the Reconcile empty-desired Close/Listen lifecycle (TestOutboundManagerReconcileEmptyDesiredClosesEgress)
- Eliminate acceptLoop shutdown race by validating listener != nil and registering to tracked under s.mu before wg.Add; bound pre-auth handshake with deadline (pinned by TestEgressServerCloseDuringConcurrentAccepts)
- Support AAAA and dual-stack domain resolution in tunnel DNS resolver with v6 default fallback (DefaultTunnelDNSServerV6); add DNS field to frontend protocol form; avoid unneeded cache flushes on unchanged SetStack ticks

* fix(amneziawg): resolve IPv6-only DNS default fallback and validate required keys

- Default to IPv6 tunnel DNS on IPv6-only outbounds with blank dns
- Require non-empty secretKey and peer publicKey in ValidateAmneziaWGOutbound
- Add end-to-end IPv6 tunnel domain resolution test and test empty key rejection
- Trim comment blocks exceeding 2 lines across modified files
- Fix Storybook test execution on environments with POSIX locale

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: rqzbeh <rqzbeh@users.noreply.github.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Sanaei <ho3ein.sanaei@gmail.com>
2026-09-10 14:50:48 +02:00

53 lines
1.5 KiB
Go

package amneziawgnet
import (
"encoding/json"
"testing"
)
func TestBuildSocksBridge_BridgesAndPreservesSiblings(t *testing.T) {
raw := []byte(`{
"protocol": "amneziawg",
"tag": "awg-hop",
"sendThrough": "0.0.0.0",
"targetStrategy": {"strategy": "UseIPv4"},
"mux": {"enabled": false},
"streamSettings": {"sockopt": {"tcpFastOpen": true}},
"settings": {"secretKey": "x"}
}`)
out, ok := BuildSocksBridge(raw)
if !ok {
t.Fatal("valid entry rejected")
}
var got map[string]any
if err := json.Unmarshal(out, &got); err != nil {
t.Fatal(err)
}
if got["protocol"] != "socks" {
t.Fatalf("protocol = %v", got["protocol"])
}
if got["tag"] != "awg-hop" || got["sendThrough"] != "0.0.0.0" {
t.Fatalf("siblings dropped: %v", got)
}
if _, ok := got["targetStrategy"].(map[string]any); !ok {
t.Fatalf("targetStrategy dropped: %v", got["targetStrategy"])
}
settings, _ := got["settings"].(map[string]any)
if settings == nil || settings["user"] != "awg-hop" || settings["address"] != "127.0.0.1" {
t.Fatalf("bridge settings wrong: %v", settings)
}
}
func TestBuildSocksBridge_RejectsUnusableTags(t *testing.T) {
for name, raw := range map[string][]byte{
"missing tag": []byte(`{"protocol":"amneziawg","settings":{}}`),
"empty tag": []byte(`{"protocol":"amneziawg","tag":"","settings":{}}`),
"non-string tag": []byte(`{"protocol":"amneziawg","tag":123,"settings":{}}`),
"not an object": []byte(`[1,2,3]`),
} {
if _, ok := BuildSocksBridge(raw); ok {
t.Fatalf("%s: expected rejection", name)
}
}
}