From 8df8c1012f647f1947a4a1d673051825240b8f2f Mon Sep 17 00:00:00 2001 From: Viktor Liu <17948409+lixmal@users.noreply.github.com> Date: Mon, 16 Jun 2025 18:33:51 +0200 Subject: [PATCH] [client] Support wildcard DNS on iOS (#3979) --- client/internal/dns/upstream_android.go | 7 +++++++ client/internal/dns/upstream_general.go | 7 +++++++ client/internal/routemanager/client/client.go | 10 +++++----- .../routemanager/dnsinterceptor/handler.go | 20 +++++++++++++++---- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/client/internal/dns/upstream_android.go b/client/internal/dns/upstream_android.go index 52d2ba58b..e7db581b1 100644 --- a/client/internal/dns/upstream_android.go +++ b/client/internal/dns/upstream_android.go @@ -84,3 +84,10 @@ func (u *upstreamResolver) isLocalResolver(upstream string) bool { } return false } + +func GetClientPrivate(ip netip.Addr, interfaceName string, dialTimeout time.Duration) (*dns.Client, error) { + return &dns.Client{ + Timeout: dialTimeout, + Net: "udp", + }, nil +} diff --git a/client/internal/dns/upstream_general.go b/client/internal/dns/upstream_general.go index 1bc06a7c1..317588a27 100644 --- a/client/internal/dns/upstream_general.go +++ b/client/internal/dns/upstream_general.go @@ -36,3 +36,10 @@ func newUpstreamResolver( func (u *upstreamResolver) exchange(ctx context.Context, upstream string, r *dns.Msg) (rm *dns.Msg, t time.Duration, err error) { return ExchangeWithFallback(ctx, &dns.Client{}, r, upstream) } + +func GetClientPrivate(ip netip.Addr, interfaceName string, dialTimeout time.Duration) (*dns.Client, error) { + return &dns.Client{ + Timeout: dialTimeout, + Net: "udp", + }, nil +} diff --git a/client/internal/routemanager/client/client.go b/client/internal/routemanager/client/client.go index 11c0f5708..46bff96db 100644 --- a/client/internal/routemanager/client/client.go +++ b/client/internal/routemanager/client/client.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "reflect" - "runtime" "time" log "github.com/sirupsen/logrus" @@ -23,7 +22,7 @@ import ( const ( handlerTypeDynamic = iota - handlerTypeDomain + handlerTypeDnsInterceptor handlerTypeStatic ) @@ -566,13 +565,14 @@ func HandlerFromRoute( useNewDNSRoute bool, ) RouteHandler { switch handlerType(rt, useNewDNSRoute) { - case handlerTypeDomain: + case handlerTypeDnsInterceptor: return dnsinterceptor.New( rt, routeRefCounter, allowedIPsRefCounter, statusRecorder, dnsServer, + wgInterface, peerStore, ) case handlerTypeDynamic: @@ -596,8 +596,8 @@ func handlerType(rt *route.Route, useNewDNSRoute bool) int { return handlerTypeStatic } - if useNewDNSRoute && runtime.GOOS != "ios" { - return handlerTypeDomain + if useNewDNSRoute { + return handlerTypeDnsInterceptor } return handlerTypeDynamic } diff --git a/client/internal/routemanager/dnsinterceptor/handler.go b/client/internal/routemanager/dnsinterceptor/handler.go index 78d5e3b30..23478c88c 100644 --- a/client/internal/routemanager/dnsinterceptor/handler.go +++ b/client/internal/routemanager/dnsinterceptor/handler.go @@ -12,6 +12,7 @@ import ( log "github.com/sirupsen/logrus" nberrors "github.com/netbirdio/netbird/client/errors" + "github.com/netbirdio/netbird/client/iface/wgaddr" nbdns "github.com/netbirdio/netbird/client/internal/dns" "github.com/netbirdio/netbird/client/internal/dnsfwd" "github.com/netbirdio/netbird/client/internal/peer" @@ -23,6 +24,11 @@ import ( type domainMap map[domain.Domain][]netip.Prefix +type wgInterface interface { + Name() string + Address() wgaddr.Address +} + type DnsInterceptor struct { mu sync.RWMutex route *route.Route @@ -32,6 +38,7 @@ type DnsInterceptor struct { dnsServer nbdns.Server currentPeerKey string interceptedDomains domainMap + wgInterface wgInterface peerStore *peerstore.Store } @@ -41,6 +48,7 @@ func New( allowedIPsRefCounter *refcounter.AllowedIPsRefCounter, statusRecorder *peer.Status, dnsServer nbdns.Server, + wgInterface wgInterface, peerStore *peerstore.Store, ) *DnsInterceptor { return &DnsInterceptor{ @@ -49,6 +57,7 @@ func New( allowedIPsRefcounter: allowedIPsRefCounter, statusRecorder: statusRecorder, dnsServer: dnsServer, + wgInterface: wgInterface, interceptedDomains: make(domainMap), peerStore: peerStore, } @@ -162,13 +171,16 @@ func (d *DnsInterceptor) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { return } + client, err := nbdns.GetClientPrivate(d.wgInterface.Address().IP, d.wgInterface.Name(), nbdns.UpstreamTimeout) + if err != nil { + d.writeDNSError(w, r, fmt.Sprintf("create DNS client: %v", err)) + return + } + if r.Extra == nil { r.MsgHdr.AuthenticatedData = true } - client := &dns.Client{ - Timeout: nbdns.UpstreamTimeout, - Net: "udp", - } + upstream := fmt.Sprintf("%s:%d", upstreamIP.String(), dnsfwd.ListenPort) reply, _, err := nbdns.ExchangeWithFallback(context.TODO(), client, r, upstream) if err != nil {