@@ -4,52 +4,23 @@ package local
44
55/*
66#include <stdlib.h>
7+ #include <netdb.h>
78#include <dns.h>
8- #include <resolv.h>
99
10- static void *cgo_dns_open_super() {
11- return (void *)dns_open(NULL);
12- }
13-
14- static void cgo_dns_close(void *opaque) {
15- if (opaque != NULL) dns_free((dns_handle_t)opaque);
16- }
17-
18- static int cgo_dns_search(void *opaque, const char *name, int class, int type,
19- unsigned char *answer, int anslen) {
20- dns_handle_t handle = (dns_handle_t)opaque;
10+ static int cgo_dns_search(const char *name, int class, int type,
11+ 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+ }
2117 struct sockaddr_storage from;
2218 uint32_t fromlen = sizeof(from);
23- return dns_search(handle, name, class, type, (char *)answer, anslen, (struct sockaddr *)&from, &fromlen);
24- }
25-
26- static void *cgo_res_init() {
27- res_state state = calloc(1, sizeof(struct __res_state));
28- if (state == NULL) return NULL;
29- if (res_ninit(state) != 0) {
30- free(state);
31- return NULL;
32- }
33- return state;
34- }
35-
36- static void cgo_res_destroy(void *opaque) {
37- res_state state = (res_state)opaque;
38- res_ndestroy(state);
39- free(state);
40- }
41-
42- static int cgo_res_nsearch(void *opaque, const char *dname, int class, int type,
43- unsigned char *answer, int anslen,
44- int timeout_seconds,
45- int *out_h_errno) {
46- res_state state = (res_state)opaque;
47- state->retrans = timeout_seconds;
48- state->retry = 1;
49- int n = res_nsearch(state, dname, class, type, answer, anslen);
50- if (n < 0) {
51- *out_h_errno = state->res_h_errno;
52- }
19+ h_errno = 0;
20+ int n = dns_search(handle, name, class, type, (char *)answer, anslen,
21+ (struct sockaddr *)&from, &fromlen);
22+ *out_h_errno = h_errno;
23+ dns_free(handle);
5324 return n;
5425}
5526*/
@@ -58,7 +29,6 @@ import "C"
5829import (
5930 "context"
6031 "errors"
61- "time"
6232 "unsafe"
6333
6434 boxC "github.com/sagernet/sing-box/constant"
@@ -73,125 +43,38 @@ const (
7343 darwinResolverTryAgain = 2
7444 darwinResolverNoRecovery = 3
7545 darwinResolverNoData = 4
76-
77- darwinResolverMaxPacketSize = 65535
7846)
7947
80- var errDarwinNeedLargerBuffer = errors .New ("darwin resolver response truncated" )
81-
82- func darwinLookupSystemDNS (name string , class , qtype , timeoutSeconds int ) (* mDNS.Msg , error ) {
83- response , err := darwinSearchWithSystemRouting (name , class , qtype )
84- if err == nil {
85- return response , nil
86- }
87- fallbackResponse , fallbackErr := darwinSearchWithResolv (name , class , qtype , timeoutSeconds )
88- if fallbackErr == nil || fallbackResponse != nil {
89- return fallbackResponse , fallbackErr
90- }
91- return nil , E .Errors (
92- E .Cause (err , "dns_search" ),
93- E .Cause (fallbackErr , "res_nsearch" ),
94- )
95- }
96-
97- func darwinSearchWithSystemRouting (name string , class , qtype int ) (* mDNS.Msg , error ) {
98- handle := C .cgo_dns_open_super ()
99- if handle == nil {
100- return nil , E .New ("dns_open failed" )
101- }
102- defer C .cgo_dns_close (handle )
103-
104- cName := C .CString (name )
105- defer C .free (unsafe .Pointer (cName ))
106-
107- bufSize := 1232
108- for {
109- answer := make ([]byte , bufSize )
110- n := C .cgo_dns_search (handle , cName , C .int (class ), C .int (qtype ),
111- (* C .uchar )(unsafe .Pointer (& answer [0 ])), C .int (len (answer )))
112- if n <= 0 {
113- return nil , E .New ("dns_search failed for " , name )
114- }
115- if int (n ) > bufSize {
116- bufSize = int (n )
117- continue
118- }
119- return unpackDarwinResolverMessage (answer [:int (n )], "dns_search" )
120- }
121- }
122-
123- func darwinSearchWithResolv (name string , class , qtype int , timeoutSeconds int ) (* mDNS.Msg , error ) {
124- state := C .cgo_res_init ()
125- if state == nil {
126- return nil , E .New ("res_ninit failed" )
127- }
128- defer C .cgo_res_destroy (state )
129-
48+ func darwinLookupSystemDNS (name string , class , qtype int ) (* mDNS.Msg , error ) {
13049 cName := C .CString (name )
13150 defer C .free (unsafe .Pointer (cName ))
13251
133- bufSize := 1232
134- for {
135- answer := make ([]byte , bufSize )
136- var hErrno C.int
137- n := C .cgo_res_nsearch (state , cName , C .int (class ), C .int (qtype ),
138- (* C .uchar )(unsafe .Pointer (& answer [0 ])), C .int (len (answer )),
139- C .int (timeoutSeconds ),
140- & hErrno )
141- if n >= 0 {
142- if int (n ) > bufSize {
143- bufSize = int (n )
144- continue
145- }
146- return unpackDarwinResolverMessage (answer [:int (n )], "res_nsearch" )
147- }
148- response , err := handleDarwinResolvFailure (name , answer , int (hErrno ))
149- if err == nil {
150- return response , nil
151- }
152- if errors .Is (err , errDarwinNeedLargerBuffer ) && bufSize < darwinResolverMaxPacketSize {
153- bufSize *= 2
154- if bufSize > darwinResolverMaxPacketSize {
155- bufSize = darwinResolverMaxPacketSize
156- }
157- continue
158- }
159- return nil , err
52+ answer := make ([]byte , 4096 )
53+ var hErrno C.int
54+ n := C .cgo_dns_search (cName , C .int (class ), C .int (qtype ),
55+ (* C .uchar )(unsafe .Pointer (& answer [0 ])), C .int (len (answer )),
56+ & hErrno )
57+ if n <= 0 {
58+ return nil , darwinResolverHErrno (name , int (hErrno ))
16059 }
161- }
162-
163- func unpackDarwinResolverMessage (packet []byte , source string ) (* mDNS.Msg , error ) {
16460 var response mDNS.Msg
165- err := response .Unpack (packet )
61+ err := response .Unpack (answer [: int ( n )] )
16662 if err != nil {
167- return nil , E .Cause (err , "unpack " , source , " response" )
63+ return nil , E .Cause (err , "unpack dns_search response" )
16864 }
16965 return & response , nil
17066}
17167
172- func handleDarwinResolvFailure (name string , answer []byte , hErrno int ) (* mDNS.Msg , error ) {
173- response , err := unpackDarwinResolverMessage (answer , "res_nsearch failure" )
174- if err == nil && response .Response {
175- if response .Truncated && len (answer ) < darwinResolverMaxPacketSize {
176- return nil , errDarwinNeedLargerBuffer
177- }
178- return response , nil
179- }
180- return nil , darwinResolverHErrno (name , hErrno )
181- }
182-
18368func darwinResolverHErrno (name string , hErrno int ) error {
18469 switch hErrno {
18570 case darwinResolverHostNotFound :
18671 return dns .RcodeNameError
187- case darwinResolverTryAgain :
188- return dns .RcodeServerFailure
189- case darwinResolverNoRecovery :
190- return dns .RcodeServerFailure
19172 case darwinResolverNoData :
19273 return dns .RcodeSuccess
74+ case darwinResolverTryAgain , darwinResolverNoRecovery :
75+ return dns .RcodeServerFailure
19376 default :
194- return E .New ("res_nsearch : unknown error " , hErrno , " for " , name )
77+ return E .New ("dns_search : unknown h_errno " , hErrno , " for " , name )
19578 }
19679}
19780
@@ -209,26 +92,13 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
20992 return t .dhcpTransport .Exchange0 (ctx , message , dhcpServers )
21093 }
21194 }
212- name := question .Name
213- timeoutSeconds := int (boxC .DNSTimeout / time .Second )
214- if deadline , hasDeadline := ctx .Deadline (); hasDeadline {
215- remaining := time .Until (deadline )
216- if remaining <= 0 {
217- return nil , context .DeadlineExceeded
218- }
219- seconds := int (remaining .Seconds ())
220- if seconds < 1 {
221- seconds = 1
222- }
223- timeoutSeconds = seconds
224- }
22595 type resolvResult struct {
22696 response * mDNS.Msg
22797 err error
22898 }
22999 resultCh := make (chan resolvResult , 1 )
230100 go func () {
231- response , err := darwinLookupSystemDNS (name , int (question .Qclass ), int (question .Qtype ), timeoutSeconds )
101+ response , err := darwinLookupSystemDNS (question . Name , int (question .Qclass ), int (question .Qtype ))
232102 resultCh <- resolvResult {response , err }
233103 }()
234104 var result resolvResult
@@ -245,5 +115,17 @@ func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg,
245115 return nil , result .err
246116 }
247117 result .response .Id = message .Id
118+ // Workaround for a bug in Apple libresolv: res_query_mDNSResponder
119+ // (libresolv/res_query.c), used when the resolver has
120+ // DNS_FLAG_FORWARD_TO_MDNSRESPONDER set (typical inside a Network
121+ // Extension), writes:
122+ //
123+ // ans->qr = 1;
124+ // ans->qr = htons(ans->qr);
125+ //
126+ // HEADER.qr is a 1-bit bitfield (<arpa/nameser_compat.h>), so
127+ // htons(1) == 0x0100 gets truncated back to 0, clearing the QR bit.
128+ // Force it on so downstream clients see a valid response.
129+ result .response .Response = true
248130 return result .response , nil
249131}
0 commit comments