mirror of
https://github.com/XTLS/Xray-core.git
synced 2026-09-23 01:27:04 +00:00
WireGuard outbound: Add remoteDNS & honor TTL (#6620)
Closes https://github.com/XTLS/Xray-core/pull/6569#issuecomment-5263789755 Fixes https://github.com/XTLS/Xray-core/issues/6567#issuecomment-5150957597 --------- Co-authored-by: LagPixelLOL <2282688304@qq.com>
This commit is contained in:
@@ -66,6 +66,7 @@ type WireGuardConfig struct {
|
|||||||
MTU int32 `json:"mtu"`
|
MTU int32 `json:"mtu"`
|
||||||
Reserved []byte `json:"reserved"`
|
Reserved []byte `json:"reserved"`
|
||||||
DomainStrategy string `json:"domainStrategy"`
|
DomainStrategy string `json:"domainStrategy"`
|
||||||
|
DNS []string `json:"remoteDNS"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *WireGuardConfig) Build() (proto.Message, error) {
|
func (c *WireGuardConfig) Build() (proto.Message, error) {
|
||||||
@@ -141,6 +142,7 @@ func (c *WireGuardConfig) Build() (proto.Message, error) {
|
|||||||
|
|
||||||
config.IsClient = c.IsClient
|
config.IsClient = c.IsClient
|
||||||
config.NoKernelTun = c.NoKernelTun
|
config.NoKernelTun = c.NoKernelTun
|
||||||
|
config.DNS = c.DNS
|
||||||
|
|
||||||
return config, nil
|
return config, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-17
@@ -5,9 +5,10 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
gonet "net"
|
gonet "net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
reflect "reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"golang.zx2c4.com/wireguard/tun"
|
"golang.zx2c4.com/wireguard/tun"
|
||||||
|
|
||||||
@@ -30,6 +31,11 @@ import (
|
|||||||
"golang.zx2c4.com/wireguard/device"
|
"golang.zx2c4.com/wireguard/device"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type entry struct {
|
||||||
|
got []net.IP
|
||||||
|
time time.Time
|
||||||
|
}
|
||||||
|
|
||||||
type Handler struct {
|
type Handler struct {
|
||||||
conf *DeviceConfig
|
conf *DeviceConfig
|
||||||
policyManager policy.Manager
|
policyManager policy.Manager
|
||||||
@@ -43,6 +49,11 @@ type Handler struct {
|
|||||||
tnet *Net
|
tnet *Net
|
||||||
dev *device.Device
|
dev *device.Device
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
|
// TODO: cache cleanup loop
|
||||||
|
local bool
|
||||||
|
cache map[string]entry
|
||||||
|
cacheMu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
||||||
@@ -98,6 +109,20 @@ func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
local := false
|
||||||
|
dns := conf.DNS
|
||||||
|
if len(dns) == 0 {
|
||||||
|
dns = []string{"1.1.1.1", "1.0.0.1", "2606:4700:4700::1111", "2606:4700:4700::1001"}
|
||||||
|
}
|
||||||
|
if len(dns) == 1 && dns[0] == "local" {
|
||||||
|
local = true
|
||||||
|
dns = nil
|
||||||
|
}
|
||||||
|
dnses := make([]netip.Addr, 0, len(dns))
|
||||||
|
for _, dns := range dns {
|
||||||
|
dnses = append(dnses, netip.MustParseAddr(dns))
|
||||||
|
}
|
||||||
|
|
||||||
kernelTunSupported, err := KernelTunSupported()
|
kernelTunSupported, err := KernelTunSupported()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.LogWarningInner(context.Background(), err, "Failed to check kernel TUN support")
|
errors.LogWarningInner(context.Background(), err, "Failed to check kernel TUN support")
|
||||||
@@ -106,10 +131,10 @@ func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
|||||||
var tnet *Net
|
var tnet *Net
|
||||||
if !conf.NoKernelTun && kernelTunSupported {
|
if !conf.NoKernelTun && kernelTunSupported {
|
||||||
errors.LogWarning(context.Background(), "Using kernel TUN")
|
errors.LogWarning(context.Background(), "Using kernel TUN")
|
||||||
tun, tnet, err = createKernelTun(localAddresses, []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("1.0.0.1"), netip.MustParseAddr("2606:4700:4700::1111"), netip.MustParseAddr("2606:4700:4700::1001")}, int(conf.Mtu))
|
tun, tnet, err = createKernelTun(localAddresses, dnses, int(conf.Mtu))
|
||||||
} else {
|
} else {
|
||||||
errors.LogWarning(context.Background(), "Using gVisor TUN")
|
errors.LogWarning(context.Background(), "Using gVisor TUN")
|
||||||
tun, tnet, _, err = CreateNetTUN(localAddresses, []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("1.0.0.1"), netip.MustParseAddr("2606:4700:4700::1111"), netip.MustParseAddr("2606:4700:4700::1001")}, int(conf.Mtu), true)
|
tun, tnet, _, err = CreateNetTUN(localAddresses, dnses, int(conf.Mtu), true)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -126,6 +151,9 @@ func NewClient(ctx context.Context, conf *DeviceConfig) (*Handler, error) {
|
|||||||
|
|
||||||
tun: tun,
|
tun: tun,
|
||||||
tnet: tnet,
|
tnet: tnet,
|
||||||
|
|
||||||
|
local: local,
|
||||||
|
cache: make(map[string]entry),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -343,31 +371,34 @@ func (h *Handler) init(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) resolveLocal(host string) (net.IP, error) {
|
func (h *Handler) resolveLocal(host string) (net.IP, error) {
|
||||||
return resolveDomain(host, h.conf.DomainStrategy, func(host string) ([]net.IP, error) {
|
return h.resolveDomain(host, h.conf.DomainStrategy, func(host string) ([]net.IP, uint32, error) {
|
||||||
ips, _, err := h.dns.LookupIP(host, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
|
return h.dns.LookupIP(host, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
|
||||||
return ips, err
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) resolveRemote(host string) (net.IP, error) {
|
func (h *Handler) resolveRemote(host string) (net.IP, error) {
|
||||||
return resolveDomain(host, h.conf.DomainStrategy, func(host string) ([]net.IP, error) {
|
return h.resolveDomain(host, h.conf.DomainStrategy, func(host string) ([]net.IP, uint32, error) {
|
||||||
addrs, err := h.tnet.LookupHost(host)
|
if h.local {
|
||||||
if err != nil {
|
return h.dns.LookupIP(host, dns.IPOption{IPv4Enable: true, IPv6Enable: true})
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
ips := make([]net.IP, 0, len(addrs))
|
return h.tnet.LookupHost(host)
|
||||||
for _, addr := range addrs {
|
|
||||||
ips = append(ips, net.ParseIP(addr))
|
|
||||||
}
|
|
||||||
return ips, nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveDomain(host string, strategy DeviceConfig_DomainStrategy, lookupIP func(host string) ([]net.IP, error)) (net.IP, error) {
|
func (h *Handler) resolveDomain(host string, strategy DeviceConfig_DomainStrategy, lookupIP func(host string) ([]net.IP, uint32, error)) (net.IP, error) {
|
||||||
if ip := net.ParseIP(host); ip != nil {
|
if ip := net.ParseIP(host); ip != nil {
|
||||||
return ip, nil
|
return ip, nil
|
||||||
}
|
}
|
||||||
ips, err := lookupIP(host)
|
h.cacheMu.Lock()
|
||||||
|
if entry, ok := h.cache[host]; ok {
|
||||||
|
if time.Now().Before(entry.time) {
|
||||||
|
h.cacheMu.Unlock()
|
||||||
|
return entry.got[dice.Roll(len(entry.got))], nil
|
||||||
|
}
|
||||||
|
delete(h.cache, host)
|
||||||
|
}
|
||||||
|
h.cacheMu.Unlock()
|
||||||
|
ips, ttl, err := lookupIP(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -407,6 +438,13 @@ func resolveDomain(host string, strategy DeviceConfig_DomainStrategy, lookupIP f
|
|||||||
if len(got) == 0 {
|
if len(got) == 0 {
|
||||||
return nil, dns.ErrEmptyResponse
|
return nil, dns.ErrEmptyResponse
|
||||||
}
|
}
|
||||||
|
entry := entry{
|
||||||
|
got: got,
|
||||||
|
time: time.Now().Add(time.Duration(ttl) * time.Second),
|
||||||
|
}
|
||||||
|
h.cacheMu.Lock()
|
||||||
|
h.cache[host] = entry
|
||||||
|
h.cacheMu.Unlock()
|
||||||
return got[dice.Roll(len(got))], nil
|
return got[dice.Roll(len(got))], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ type DeviceConfig struct {
|
|||||||
DomainStrategy DeviceConfig_DomainStrategy `protobuf:"varint,7,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.wireguard.DeviceConfig_DomainStrategy" json:"domain_strategy,omitempty"`
|
DomainStrategy DeviceConfig_DomainStrategy `protobuf:"varint,7,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.proxy.wireguard.DeviceConfig_DomainStrategy" json:"domain_strategy,omitempty"`
|
||||||
IsClient bool `protobuf:"varint,8,opt,name=is_client,json=isClient,proto3" json:"is_client,omitempty"`
|
IsClient bool `protobuf:"varint,8,opt,name=is_client,json=isClient,proto3" json:"is_client,omitempty"`
|
||||||
NoKernelTun bool `protobuf:"varint,9,opt,name=no_kernel_tun,json=noKernelTun,proto3" json:"no_kernel_tun,omitempty"`
|
NoKernelTun bool `protobuf:"varint,9,opt,name=no_kernel_tun,json=noKernelTun,proto3" json:"no_kernel_tun,omitempty"`
|
||||||
|
DNS []string `protobuf:"bytes,10,rep,name=DNS,proto3" json:"DNS,omitempty"`
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
}
|
}
|
||||||
@@ -261,6 +262,13 @@ func (x *DeviceConfig) GetNoKernelTun() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (x *DeviceConfig) GetDNS() []string {
|
||||||
|
if x != nil {
|
||||||
|
return x.DNS
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var File_proxy_wireguard_config_proto protoreflect.FileDescriptor
|
var File_proxy_wireguard_config_proto protoreflect.FileDescriptor
|
||||||
|
|
||||||
const file_proxy_wireguard_config_proto_rawDesc = "" +
|
const file_proxy_wireguard_config_proto_rawDesc = "" +
|
||||||
@@ -275,7 +283,7 @@ const file_proxy_wireguard_config_proto_rawDesc = "" +
|
|||||||
"\n" +
|
"\n" +
|
||||||
"keep_alive\x18\x04 \x01(\tR\tkeepAlive\x12\x1f\n" +
|
"keep_alive\x18\x04 \x01(\tR\tkeepAlive\x12\x1f\n" +
|
||||||
"\vallowed_ips\x18\x05 \x03(\tR\n" +
|
"\vallowed_ips\x18\x05 \x03(\tR\n" +
|
||||||
"allowedIps\"\xdc\x03\n" +
|
"allowedIps\"\xee\x03\n" +
|
||||||
"\fDeviceConfig\x12\x1d\n" +
|
"\fDeviceConfig\x12\x1d\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"secret_key\x18\x01 \x01(\tR\tsecretKey\x12\x1a\n" +
|
"secret_key\x18\x01 \x01(\tR\tsecretKey\x12\x1a\n" +
|
||||||
@@ -286,7 +294,9 @@ const file_proxy_wireguard_config_proto_rawDesc = "" +
|
|||||||
"\breserved\x18\x06 \x01(\fR\breserved\x12Z\n" +
|
"\breserved\x18\x06 \x01(\fR\breserved\x12Z\n" +
|
||||||
"\x0fdomain_strategy\x18\a \x01(\x0e21.xray.proxy.wireguard.DeviceConfig.DomainStrategyR\x0edomainStrategy\x12\x1b\n" +
|
"\x0fdomain_strategy\x18\a \x01(\x0e21.xray.proxy.wireguard.DeviceConfig.DomainStrategyR\x0edomainStrategy\x12\x1b\n" +
|
||||||
"\tis_client\x18\b \x01(\bR\bisClient\x12\"\n" +
|
"\tis_client\x18\b \x01(\bR\bisClient\x12\"\n" +
|
||||||
"\rno_kernel_tun\x18\t \x01(\bR\vnoKernelTun\"\\\n" +
|
"\rno_kernel_tun\x18\t \x01(\bR\vnoKernelTun\x12\x10\n" +
|
||||||
|
"\x03DNS\x18\n" +
|
||||||
|
" \x03(\tR\x03DNS\"\\\n" +
|
||||||
"\x0eDomainStrategy\x12\f\n" +
|
"\x0eDomainStrategy\x12\f\n" +
|
||||||
"\bFORCE_IP\x10\x00\x12\r\n" +
|
"\bFORCE_IP\x10\x00\x12\r\n" +
|
||||||
"\tFORCE_IP4\x10\x01\x12\r\n" +
|
"\tFORCE_IP4\x10\x01\x12\r\n" +
|
||||||
|
|||||||
@@ -34,4 +34,5 @@ message DeviceConfig {
|
|||||||
DomainStrategy domain_strategy = 7;
|
DomainStrategy domain_strategy = 7;
|
||||||
bool is_client = 8;
|
bool is_client = 8;
|
||||||
bool no_kernel_tun = 9;
|
bool no_kernel_tun = 9;
|
||||||
|
repeated string DNS = 10;
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ var (
|
|||||||
errTimeout = errors.New("i/o timeout")
|
errTimeout = errors.New("i/o timeout")
|
||||||
)
|
)
|
||||||
|
|
||||||
func (net *Net) LookupHost(host string) (addrs []string, err error) {
|
func (net *Net) LookupHost(host string) (addrs []net.IP, ttl uint32, err error) {
|
||||||
return net.LookupContextHost(context.Background(), host)
|
return net.LookupContextHost(context.Background(), host)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,9 +567,9 @@ func (tnet *Net) tryOneName(ctx context.Context, name string, qtype dnsmessage.T
|
|||||||
return dnsmessage.Parser{}, "", lastErr
|
return dnsmessage.Parser{}, "", lastErr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string, error) {
|
func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]net.IP, uint32, error) {
|
||||||
if host == "" || (!tnet.hasV6 && !tnet.hasV4) {
|
if host == "" || (!tnet.hasV6 && !tnet.hasV4) {
|
||||||
return nil, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
return nil, 0, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
||||||
}
|
}
|
||||||
zlen := len(host)
|
zlen := len(host)
|
||||||
if strings.IndexByte(host, ':') != -1 {
|
if strings.IndexByte(host, ':') != -1 {
|
||||||
@@ -578,11 +578,11 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ip, err := netip.ParseAddr(host[:zlen]); err == nil {
|
if ip, err := netip.ParseAddr(host[:zlen]); err == nil {
|
||||||
return []string{ip.String()}, nil
|
return []net.IP{ip.AsSlice()}, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !isDomainName(host) {
|
if !isDomainName(host) {
|
||||||
return nil, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
return nil, 0, &net.DNSError{Err: errNoSuchHost.Error(), Name: host, IsNotFound: true}
|
||||||
}
|
}
|
||||||
type result struct {
|
type result struct {
|
||||||
p dnsmessage.Parser
|
p dnsmessage.Parser
|
||||||
@@ -611,6 +611,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
|||||||
lane <- result{p, server, err}
|
lane <- result{p, server, err}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
ttl := uint32(300)
|
||||||
for l := 0; l < lanes; l++ {
|
for l := 0; l < lanes; l++ {
|
||||||
result := <-lane
|
result := <-lane
|
||||||
if result.error != nil {
|
if result.error != nil {
|
||||||
@@ -644,6 +645,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
|||||||
}
|
}
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
ttl = min(ttl, h.TTL)
|
||||||
addrsV4 = append(addrsV4, netip.AddrFrom4(a.A))
|
addrsV4 = append(addrsV4, netip.AddrFrom4(a.A))
|
||||||
|
|
||||||
case dnsmessage.TypeAAAA:
|
case dnsmessage.TypeAAAA:
|
||||||
@@ -656,6 +658,7 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
|||||||
}
|
}
|
||||||
break loop
|
break loop
|
||||||
}
|
}
|
||||||
|
ttl = min(ttl, h.TTL)
|
||||||
addrsV6 = append(addrsV6, netip.AddrFrom16(aaaa.AAAA))
|
addrsV6 = append(addrsV6, netip.AddrFrom16(aaaa.AAAA))
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@@ -680,11 +683,11 @@ func (tnet *Net) LookupContextHost(ctx context.Context, host string) ([]string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(addrs) == 0 && lastErr != nil {
|
if len(addrs) == 0 && lastErr != nil {
|
||||||
return nil, lastErr
|
return nil, 0, lastErr
|
||||||
}
|
}
|
||||||
saddrs := make([]string, 0, len(addrs))
|
ips := make([]net.IP, 0, len(addrs))
|
||||||
for _, ip := range addrs {
|
for _, ip := range addrs {
|
||||||
saddrs = append(saddrs, ip.String())
|
ips = append(ips, ip.AsSlice())
|
||||||
}
|
}
|
||||||
return saddrs, nil
|
return ips, ttl, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user