mirror of
https://github.com/SagerNet/sing-box.git
synced 2026-04-11 17:47:20 +10:00
Fix darwin local DNS transport
This commit is contained in:
@@ -5,10 +5,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
RcodeSuccess RcodeError = mDNS.RcodeSuccess
|
RcodeSuccess RcodeError = mDNS.RcodeSuccess
|
||||||
RcodeFormatError RcodeError = mDNS.RcodeFormatError
|
RcodeServerFailure RcodeError = mDNS.RcodeServerFailure
|
||||||
RcodeNameError RcodeError = mDNS.RcodeNameError
|
RcodeFormatError RcodeError = mDNS.RcodeFormatError
|
||||||
RcodeRefused RcodeError = mDNS.RcodeRefused
|
RcodeNameError RcodeError = mDNS.RcodeNameError
|
||||||
|
RcodeRefused RcodeError = mDNS.RcodeRefused
|
||||||
)
|
)
|
||||||
|
|
||||||
type RcodeError int
|
type RcodeError int
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ package local
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/sagernet/sing-box/adapter"
|
"github.com/sagernet/sing-box/adapter"
|
||||||
C "github.com/sagernet/sing-box/constant"
|
C "github.com/sagernet/sing-box/constant"
|
||||||
@@ -14,7 +12,6 @@ import (
|
|||||||
"github.com/sagernet/sing-box/log"
|
"github.com/sagernet/sing-box/log"
|
||||||
"github.com/sagernet/sing-box/option"
|
"github.com/sagernet/sing-box/option"
|
||||||
"github.com/sagernet/sing/common"
|
"github.com/sagernet/sing/common"
|
||||||
E "github.com/sagernet/sing/common/exceptions"
|
|
||||||
"github.com/sagernet/sing/common/logger"
|
"github.com/sagernet/sing/common/logger"
|
||||||
M "github.com/sagernet/sing/common/metadata"
|
M "github.com/sagernet/sing/common/metadata"
|
||||||
N "github.com/sagernet/sing/common/network"
|
N "github.com/sagernet/sing/common/network"
|
||||||
@@ -35,10 +32,8 @@ type Transport struct {
|
|||||||
logger logger.ContextLogger
|
logger logger.ContextLogger
|
||||||
hosts *hosts.File
|
hosts *hosts.File
|
||||||
dialer N.Dialer
|
dialer N.Dialer
|
||||||
preferGo bool
|
|
||||||
fallback bool
|
fallback bool
|
||||||
dhcpTransport dhcpTransport
|
dhcpTransport dhcpTransport
|
||||||
resolver net.Resolver
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type dhcpTransport interface {
|
type dhcpTransport interface {
|
||||||
@@ -52,14 +47,12 @@ func NewTransport(ctx context.Context, logger log.ContextLogger, tag string, opt
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
transportAdapter := dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options)
|
|
||||||
return &Transport{
|
return &Transport{
|
||||||
TransportAdapter: transportAdapter,
|
TransportAdapter: dns.NewTransportAdapterWithLocalOptions(C.DNSTypeLocal, tag, options),
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
hosts: hosts.NewFile(hosts.DefaultPath),
|
hosts: hosts.NewFile(hosts.DefaultPath),
|
||||||
dialer: transportDialer,
|
dialer: transportDialer,
|
||||||
preferGo: options.PreferGo,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,44 +90,3 @@ func (t *Transport) Reset() {
|
|||||||
t.dhcpTransport.Reset()
|
t.dhcpTransport.Reset()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
|
||||||
question := message.Question[0]
|
|
||||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
|
||||||
addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name))
|
|
||||||
if len(addresses) > 0 {
|
|
||||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !t.fallback {
|
|
||||||
return t.exchange(ctx, message, question.Name)
|
|
||||||
}
|
|
||||||
if t.dhcpTransport != nil {
|
|
||||||
dhcpTransports := t.dhcpTransport.Fetch()
|
|
||||||
if len(dhcpTransports) > 0 {
|
|
||||||
return t.dhcpTransport.Exchange0(ctx, message, dhcpTransports)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t.preferGo {
|
|
||||||
// Assuming the user knows what they are doing, we still execute the query which will fail.
|
|
||||||
return t.exchange(ctx, message, question.Name)
|
|
||||||
}
|
|
||||||
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
|
||||||
var network string
|
|
||||||
if question.Qtype == mDNS.TypeA {
|
|
||||||
network = "ip4"
|
|
||||||
} else {
|
|
||||||
network = "ip6"
|
|
||||||
}
|
|
||||||
addresses, err := t.resolver.LookupNetIP(ctx, network, question.Name)
|
|
||||||
if err != nil {
|
|
||||||
var dnsError *net.DNSError
|
|
||||||
if errors.As(err, &dnsError) && dnsError.IsNotFound {
|
|
||||||
return nil, dns.RcodeRefused
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return dns.FixedResponse(message.Id, question, addresses, C.DefaultDNSTTL), nil
|
|
||||||
}
|
|
||||||
return nil, E.New("only A and AAAA queries are supported on Apple platforms when using TUN and DHCP unavailable.")
|
|
||||||
}
|
|
||||||
|
|||||||
162
dns/transport/local/local_darwin_cgo.go
Normal file
162
dns/transport/local/local_darwin_cgo.go
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
//go:build darwin
|
||||||
|
|
||||||
|
package local
|
||||||
|
|
||||||
|
/*
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <resolv.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
static void *cgo_res_init() {
|
||||||
|
res_state state = calloc(1, sizeof(struct __res_state));
|
||||||
|
if (state == NULL) return NULL;
|
||||||
|
if (res_ninit(state) != 0) {
|
||||||
|
free(state);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cgo_res_destroy(void *opaque) {
|
||||||
|
res_state state = (res_state)opaque;
|
||||||
|
res_ndestroy(state);
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cgo_res_nsearch(void *opaque, const char *dname, int class, int type,
|
||||||
|
unsigned char *answer, int anslen,
|
||||||
|
int timeout_seconds,
|
||||||
|
int *out_h_errno) {
|
||||||
|
res_state state = (res_state)opaque;
|
||||||
|
state->retrans = timeout_seconds;
|
||||||
|
state->retry = 1;
|
||||||
|
int n = res_nsearch(state, dname, class, type, answer, anslen);
|
||||||
|
if (n < 0) {
|
||||||
|
*out_h_errno = state->res_h_errno;
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
import "C"
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
boxC "github.com/sagernet/sing-box/constant"
|
||||||
|
"github.com/sagernet/sing-box/dns"
|
||||||
|
E "github.com/sagernet/sing/common/exceptions"
|
||||||
|
|
||||||
|
mDNS "github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resolvSearch(name string, class, qtype int, timeoutSeconds int) (*mDNS.Msg, error) {
|
||||||
|
state := C.cgo_res_init()
|
||||||
|
if state == nil {
|
||||||
|
return nil, E.New("res_ninit failed")
|
||||||
|
}
|
||||||
|
defer C.cgo_res_destroy(state)
|
||||||
|
|
||||||
|
cName := C.CString(name)
|
||||||
|
defer C.free(unsafe.Pointer(cName))
|
||||||
|
bufSize := 1232
|
||||||
|
for {
|
||||||
|
answer := make([]byte, bufSize)
|
||||||
|
var hErrno C.int
|
||||||
|
n := C.cgo_res_nsearch(state, cName, C.int(class), C.int(qtype),
|
||||||
|
(*C.uchar)(unsafe.Pointer(&answer[0])), C.int(len(answer)),
|
||||||
|
C.int(timeoutSeconds),
|
||||||
|
&hErrno)
|
||||||
|
if n >= 0 {
|
||||||
|
if int(n) > bufSize {
|
||||||
|
bufSize = int(n)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var response mDNS.Msg
|
||||||
|
err := response.Unpack(answer[:int(n)])
|
||||||
|
if err != nil {
|
||||||
|
return nil, E.Cause(err, "unpack res_nsearch response")
|
||||||
|
}
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
var response mDNS.Msg
|
||||||
|
_ = response.Unpack(answer[:bufSize])
|
||||||
|
if response.Response {
|
||||||
|
if response.Truncated && bufSize < 65535 {
|
||||||
|
bufSize *= 2
|
||||||
|
if bufSize > 65535 {
|
||||||
|
bufSize = 65535
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return &response, nil
|
||||||
|
}
|
||||||
|
switch hErrno {
|
||||||
|
case C.HOST_NOT_FOUND:
|
||||||
|
return nil, dns.RcodeNameError
|
||||||
|
case C.TRY_AGAIN:
|
||||||
|
return nil, dns.RcodeNameError
|
||||||
|
case C.NO_RECOVERY:
|
||||||
|
return nil, dns.RcodeServerFailure
|
||||||
|
case C.NO_DATA:
|
||||||
|
return nil, dns.RcodeSuccess
|
||||||
|
default:
|
||||||
|
return nil, E.New("res_nsearch: unknown error ", int(hErrno), " for ", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Transport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
|
||||||
|
question := message.Question[0]
|
||||||
|
if question.Qtype == mDNS.TypeA || question.Qtype == mDNS.TypeAAAA {
|
||||||
|
addresses := t.hosts.Lookup(dns.FqdnToDomain(question.Name))
|
||||||
|
if len(addresses) > 0 {
|
||||||
|
return dns.FixedResponse(message.Id, question, addresses, boxC.DefaultDNSTTL), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t.fallback && t.dhcpTransport != nil {
|
||||||
|
dhcpServers := t.dhcpTransport.Fetch()
|
||||||
|
if len(dhcpServers) > 0 {
|
||||||
|
return t.dhcpTransport.Exchange0(ctx, message, dhcpServers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
name := question.Name
|
||||||
|
timeoutSeconds := int(boxC.DNSTimeout / time.Second)
|
||||||
|
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
||||||
|
remaining := time.Until(deadline)
|
||||||
|
if remaining <= 0 {
|
||||||
|
return nil, context.DeadlineExceeded
|
||||||
|
}
|
||||||
|
seconds := int(remaining.Seconds())
|
||||||
|
if seconds < 1 {
|
||||||
|
seconds = 1
|
||||||
|
}
|
||||||
|
timeoutSeconds = seconds
|
||||||
|
}
|
||||||
|
type resolvResult struct {
|
||||||
|
response *mDNS.Msg
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
resultCh := make(chan resolvResult, 1)
|
||||||
|
go func() {
|
||||||
|
response, err := resolvSearch(name, int(question.Qclass), int(question.Qtype), timeoutSeconds)
|
||||||
|
resultCh <- resolvResult{response, err}
|
||||||
|
}()
|
||||||
|
var result resolvResult
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, ctx.Err()
|
||||||
|
case result = <-resultCh:
|
||||||
|
}
|
||||||
|
if result.err != nil {
|
||||||
|
var rcodeError dns.RcodeError
|
||||||
|
if errors.As(result.err, &rcodeError) {
|
||||||
|
return dns.FixedResponseStatus(message, int(rcodeError)), nil
|
||||||
|
}
|
||||||
|
return nil, result.err
|
||||||
|
}
|
||||||
|
result.response.Id = message.Id
|
||||||
|
return result.response, nil
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !darwin
|
||||||
|
|
||||||
package local
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
Reference in New Issue
Block a user