github.com/inazumav/sing-box@v0.0.0-20230926072359-ab51429a14f1/experimental/libbox/dns.go (about)

     1  package libbox
     2  
     3  import (
     4  	"context"
     5  	"net/netip"
     6  	"strings"
     7  	"syscall"
     8  
     9  	"github.com/sagernet/sing-dns"
    10  	"github.com/sagernet/sing/common"
    11  	E "github.com/sagernet/sing/common/exceptions"
    12  	"github.com/sagernet/sing/common/logger"
    13  	M "github.com/sagernet/sing/common/metadata"
    14  	N "github.com/sagernet/sing/common/network"
    15  	"github.com/sagernet/sing/common/task"
    16  
    17  	mDNS "github.com/miekg/dns"
    18  )
    19  
    20  type LocalDNSTransport interface {
    21  	Raw() bool
    22  	Lookup(ctx *ExchangeContext, network string, domain string) error
    23  	Exchange(ctx *ExchangeContext, message []byte) error
    24  }
    25  
    26  func RegisterLocalDNSTransport(transport LocalDNSTransport) {
    27  	if transport == nil {
    28  		dns.RegisterTransport([]string{"local"}, dns.CreateLocalTransport)
    29  	} else {
    30  		dns.RegisterTransport([]string{"local"}, func(name string, ctx context.Context, logger logger.ContextLogger, dialer N.Dialer, link string) (dns.Transport, error) {
    31  			return &platformLocalDNSTransport{
    32  				iif: transport,
    33  			}, nil
    34  		})
    35  	}
    36  }
    37  
    38  var _ dns.Transport = (*platformLocalDNSTransport)(nil)
    39  
    40  type platformLocalDNSTransport struct {
    41  	iif LocalDNSTransport
    42  }
    43  
    44  func (p *platformLocalDNSTransport) Name() string {
    45  	return "local"
    46  }
    47  
    48  func (p *platformLocalDNSTransport) Start() error {
    49  	return nil
    50  }
    51  
    52  func (p *platformLocalDNSTransport) Reset() {
    53  }
    54  
    55  func (p *platformLocalDNSTransport) Close() error {
    56  	return nil
    57  }
    58  
    59  func (p *platformLocalDNSTransport) Raw() bool {
    60  	return p.iif.Raw()
    61  }
    62  
    63  func (p *platformLocalDNSTransport) Exchange(ctx context.Context, message *mDNS.Msg) (*mDNS.Msg, error) {
    64  	messageBytes, err := message.Pack()
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	response := &ExchangeContext{
    69  		context: ctx,
    70  	}
    71  	var responseMessage *mDNS.Msg
    72  	return responseMessage, task.Run(ctx, func() error {
    73  		err = p.iif.Exchange(response, messageBytes)
    74  		if err != nil {
    75  			return err
    76  		}
    77  		if response.error != nil {
    78  			return response.error
    79  		}
    80  		responseMessage = &response.message
    81  		return nil
    82  	})
    83  }
    84  
    85  func (p *platformLocalDNSTransport) Lookup(ctx context.Context, domain string, strategy dns.DomainStrategy) ([]netip.Addr, error) {
    86  	var network string
    87  	switch strategy {
    88  	case dns.DomainStrategyUseIPv4:
    89  		network = "ip4"
    90  	case dns.DomainStrategyPreferIPv6:
    91  		network = "ip6"
    92  	default:
    93  		network = "ip"
    94  	}
    95  	response := &ExchangeContext{
    96  		context: ctx,
    97  	}
    98  	var responseAddr []netip.Addr
    99  	return responseAddr, task.Run(ctx, func() error {
   100  		err := p.iif.Lookup(response, network, domain)
   101  		if err != nil {
   102  			return err
   103  		}
   104  		if response.error != nil {
   105  			return response.error
   106  		}
   107  		switch strategy {
   108  		case dns.DomainStrategyUseIPv4:
   109  			responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
   110  				return it.Is4()
   111  			})
   112  		case dns.DomainStrategyPreferIPv6:
   113  			responseAddr = common.Filter(response.addresses, func(it netip.Addr) bool {
   114  				return it.Is6()
   115  			})
   116  		default:
   117  			responseAddr = response.addresses
   118  		}
   119  		/*if len(responseAddr) == 0 {
   120  			response.error = dns.RCodeSuccess
   121  		}*/
   122  		return nil
   123  	})
   124  }
   125  
   126  type Func interface {
   127  	Invoke() error
   128  }
   129  
   130  type ExchangeContext struct {
   131  	context   context.Context
   132  	message   mDNS.Msg
   133  	addresses []netip.Addr
   134  	error     error
   135  }
   136  
   137  func (c *ExchangeContext) OnCancel(callback Func) {
   138  	go func() {
   139  		<-c.context.Done()
   140  		callback.Invoke()
   141  	}()
   142  }
   143  
   144  func (c *ExchangeContext) Success(result string) {
   145  	c.addresses = common.Map(common.Filter(strings.Split(result, "\n"), func(it string) bool {
   146  		return !common.IsEmpty(it)
   147  	}), func(it string) netip.Addr {
   148  		return M.ParseSocksaddrHostPort(it, 0).Unwrap().Addr
   149  	})
   150  }
   151  
   152  func (c *ExchangeContext) RawSuccess(result []byte) {
   153  	err := c.message.Unpack(result)
   154  	if err != nil {
   155  		c.error = E.Cause(err, "parse response")
   156  	}
   157  }
   158  
   159  func (c *ExchangeContext) ErrorCode(code int32) {
   160  	c.error = dns.RCodeError(code)
   161  }
   162  
   163  func (c *ExchangeContext) ErrnoCode(code int32) {
   164  	c.error = syscall.Errno(code)
   165  }