@@ -7,20 +7,14 @@ package local
77#include <netdb.h>
88#include <dns.h>
99
10- static int cgo_dns_search(const char *name, int class, int type,
10+ static int cgo_dns_search(dns_handle_t handle, const char *name, int class, int type,
1111 unsigned char *answer, int anslen, int *out_h_errno) {
12- dns_handle_t handle = (dns_handle_t)dns_open(NULL);
13- if (handle == NULL) {
14- *out_h_errno = NO_RECOVERY;
15- return -1;
16- }
1712 struct sockaddr_storage from;
1813 uint32_t fromlen = sizeof(from);
1914 h_errno = 0;
2015 int n = dns_search(handle, name, class, type, (char *)answer, anslen,
2116 (struct sockaddr *)&from, &fromlen);
2217 *out_h_errno = h_errno;
23- dns_free(handle);
2418 return n;
2519}
2620*/
@@ -29,6 +23,8 @@ import "C"
2923import (
3024 "context"
3125 "errors"
26+ "net"
27+ "sync"
3228 "unsafe"
3329
3430 "github.com/sagernet/sing-box/dns"
@@ -44,48 +40,115 @@ const (
4440 darwinResolverNoData = 4
4541)
4642
47- func darwinLookupSystemDNS (name string , class , qtype int ) (* mDNS.Msg , error ) {
48- cName := C .CString (name )
49- defer C .free (unsafe .Pointer (cName ))
43+ type darwinDNSHandle struct {
44+ handle C.dns_handle_t
45+ generation uint64
46+ }
5047
51- answer := make ([]byte , 4096 )
52- var hErrno C.int
53- n := C .cgo_dns_search (cName , C .int (class ), C .int (qtype ),
54- (* C .uchar )(unsafe .Pointer (& answer [0 ])), C .int (len (answer )),
55- & hErrno )
56- if n <= 0 {
57- return nil , darwinResolverHErrno (name , int (hErrno ))
48+ // darwinSystemResolver pools libresolv handles. iOS 26.5.1 NULL-derefs in
49+ // libresolv when resolver state is rebuilt by concurrent dns_open(NULL) calls,
50+ // and dns_search corrupts state when two queries share one handle. Handles are
51+ // therefore reused across queries and leased exclusively: each in-flight query
52+ // owns one handle, dns_search runs concurrently on independent handles, while
53+ // dns_open and dns_free are serialized by lifecycleAccess. A leased handle is
54+ // removed from idle and owned by its caller, so Reset/Close only ever free idle
55+ // handles and the caller frees its own handle on release — dns_free never races
56+ // an in-flight dns_search and is never called twice for the same handle.
57+ type darwinSystemResolver struct {
58+ access sync.Mutex
59+ lifecycleAccess sync.Mutex
60+ idle []* darwinDNSHandle
61+ generation uint64
62+ closed bool
63+ }
64+
65+ func newSystemResolver () systemResolver {
66+ return & darwinSystemResolver {}
67+ }
68+
69+ func (r * darwinSystemResolver ) acquire () (* darwinDNSHandle , error ) {
70+ r .access .Lock ()
71+ if r .closed {
72+ r .access .Unlock ()
73+ return nil , net .ErrClosed
5874 }
59- var response mDNS.Msg
60- err := response .Unpack (answer [:int (n )])
61- if err != nil {
62- return nil , E .Cause (err , "unpack dns_search response" )
75+ if count := len (r .idle ); count > 0 {
76+ handle := r .idle [count - 1 ]
77+ r .idle [count - 1 ] = nil
78+ r .idle = r .idle [:count - 1 ]
79+ r .access .Unlock ()
80+ return handle , nil
6381 }
64- return & response , nil
82+ generation := r .generation
83+ r .access .Unlock ()
84+
85+ r .lifecycleAccess .Lock ()
86+ cHandle := C .dns_open (nil )
87+ r .lifecycleAccess .Unlock ()
88+ if cHandle == nil {
89+ return nil , dns .RcodeServerFailure
90+ }
91+ return & darwinDNSHandle {handle : cHandle , generation : generation }, nil
6592}
6693
67- func darwinResolverHErrno (name string , hErrno int ) error {
68- switch hErrno {
69- case darwinResolverHostNotFound :
70- return dns .RcodeNameError
71- case darwinResolverNoData :
72- return dns .RcodeSuccess
73- case darwinResolverTryAgain , darwinResolverNoRecovery :
74- return dns .RcodeServerFailure
75- default :
76- return E .New ("dns_search: unknown h_errno " , hErrno , " for " , name )
94+ func (r * darwinSystemResolver ) release (handle * darwinDNSHandle ) {
95+ r .access .Lock ()
96+ reuse := ! r .closed && handle .generation == r .generation
97+ if reuse {
98+ r .idle = append (r .idle , handle )
99+ }
100+ r .access .Unlock ()
101+ if ! reuse {
102+ r .free (handle )
77103 }
78104}
79105
80- func (t * Transport ) systemExchange (ctx context.Context , message * mDNS.Msg ) (* mDNS.Msg , error ) {
106+ func (r * darwinSystemResolver ) free (handle * darwinDNSHandle ) {
107+ r .lifecycleAccess .Lock ()
108+ C .dns_free (handle .handle )
109+ r .lifecycleAccess .Unlock ()
110+ }
111+
112+ func (r * darwinSystemResolver ) Reset () {
113+ r .access .Lock ()
114+ if r .closed {
115+ r .access .Unlock ()
116+ return
117+ }
118+ r .generation ++
119+ idle := r .idle
120+ r .idle = nil
121+ r .access .Unlock ()
122+ for _ , handle := range idle {
123+ r .free (handle )
124+ }
125+ }
126+
127+ func (r * darwinSystemResolver ) Close () error {
128+ r .access .Lock ()
129+ if r .closed {
130+ r .access .Unlock ()
131+ return nil
132+ }
133+ r .closed = true
134+ idle := r .idle
135+ r .idle = nil
136+ r .access .Unlock ()
137+ for _ , handle := range idle {
138+ r .free (handle )
139+ }
140+ return nil
141+ }
142+
143+ func (r * darwinSystemResolver ) Exchange (ctx context.Context , message * mDNS.Msg ) (* mDNS.Msg , error ) {
81144 question := message .Question [0 ]
82145 type resolvResult struct {
83146 response * mDNS.Msg
84147 err error
85148 }
86149 resultCh := make (chan resolvResult , 1 )
87150 go func () {
88- response , err := darwinLookupSystemDNS (question .Name , int (question .Qclass ), int (question .Qtype ))
151+ response , err := r . lookup (question .Name , int (question .Qclass ), int (question .Qtype ))
89152 resultCh <- resolvResult {response , err }
90153 }()
91154 var result resolvResult
@@ -116,3 +179,46 @@ func (t *Transport) systemExchange(ctx context.Context, message *mDNS.Msg) (*mDN
116179 result .response .Response = true
117180 return result .response , nil
118181}
182+
183+ func (r * darwinSystemResolver ) lookup (name string , class , qtype int ) (* mDNS.Msg , error ) {
184+ handle , err := r .acquire ()
185+ if err != nil {
186+ return nil , err
187+ }
188+ response , err := darwinSearch (handle .handle , name , class , qtype )
189+ r .release (handle )
190+ return response , err
191+ }
192+
193+ func darwinSearch (handle C.dns_handle_t , name string , class , qtype int ) (* mDNS.Msg , error ) {
194+ cName := C .CString (name )
195+ defer C .free (unsafe .Pointer (cName ))
196+
197+ answer := make ([]byte , 4096 )
198+ var hErrno C.int
199+ n := C .cgo_dns_search (handle , cName , C .int (class ), C .int (qtype ),
200+ (* C .uchar )(unsafe .Pointer (& answer [0 ])), C .int (len (answer )),
201+ & hErrno )
202+ if n <= 0 {
203+ return nil , darwinResolverHErrno (name , int (hErrno ))
204+ }
205+ var response mDNS.Msg
206+ err := response .Unpack (answer [:int (n )])
207+ if err != nil {
208+ return nil , E .Cause (err , "unpack dns_search response" )
209+ }
210+ return & response , nil
211+ }
212+
213+ func darwinResolverHErrno (name string , hErrno int ) error {
214+ switch hErrno {
215+ case darwinResolverHostNotFound :
216+ return dns .RcodeNameError
217+ case darwinResolverNoData :
218+ return dns .RcodeSuccess
219+ case darwinResolverTryAgain , darwinResolverNoRecovery :
220+ return dns .RcodeServerFailure
221+ default :
222+ return E .New ("dns_search: unknown h_errno " , hErrno , " for " , name )
223+ }
224+ }
0 commit comments