github.com/metacubex/mihomo@v1.18.5/adapter/outbound/dns.go (about)

     1  package outbound
     2  
     3  import (
     4  	"context"
     5  	"net"
     6  	"time"
     7  
     8  	N "github.com/metacubex/mihomo/common/net"
     9  	"github.com/metacubex/mihomo/common/pool"
    10  	"github.com/metacubex/mihomo/component/dialer"
    11  	"github.com/metacubex/mihomo/component/resolver"
    12  	C "github.com/metacubex/mihomo/constant"
    13  	"github.com/metacubex/mihomo/log"
    14  )
    15  
    16  type Dns struct {
    17  	*Base
    18  }
    19  
    20  type DnsOption struct {
    21  	BasicOption
    22  	Name string `proxy:"name"`
    23  }
    24  
    25  // DialContext implements C.ProxyAdapter
    26  func (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.Conn, error) {
    27  	left, right := N.Pipe()
    28  	go resolver.RelayDnsConn(context.Background(), right, 0)
    29  	return NewConn(left, d), nil
    30  }
    31  
    32  // ListenPacketContext implements C.ProxyAdapter
    33  func (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata, opts ...dialer.Option) (C.PacketConn, error) {
    34  	log.Debugln("[DNS] hijack udp:%s from %s", metadata.RemoteAddress(), metadata.SourceAddrPort())
    35  
    36  	ctx, cancel := context.WithCancel(context.Background())
    37  
    38  	return newPacketConn(&dnsPacketConn{
    39  		response: make(chan dnsPacket, 1),
    40  		ctx:      ctx,
    41  		cancel:   cancel,
    42  	}, d), nil
    43  }
    44  
    45  type dnsPacket struct {
    46  	data []byte
    47  	put  func()
    48  	addr net.Addr
    49  }
    50  
    51  // dnsPacketConn implements net.PacketConn
    52  type dnsPacketConn struct {
    53  	response chan dnsPacket
    54  	ctx      context.Context
    55  	cancel   context.CancelFunc
    56  }
    57  
    58  func (d *dnsPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
    59  	select {
    60  	case packet := <-d.response:
    61  		return packet.data, packet.put, packet.addr, nil
    62  	case <-d.ctx.Done():
    63  		return nil, nil, nil, net.ErrClosed
    64  	}
    65  }
    66  
    67  func (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
    68  	select {
    69  	case packet := <-d.response:
    70  		n = copy(p, packet.data)
    71  		if packet.put != nil {
    72  			packet.put()
    73  		}
    74  		return n, packet.addr, nil
    75  	case <-d.ctx.Done():
    76  		return 0, nil, net.ErrClosed
    77  	}
    78  }
    79  
    80  func (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
    81  	select {
    82  	case <-d.ctx.Done():
    83  		return 0, net.ErrClosed
    84  	default:
    85  	}
    86  
    87  	if len(p) > resolver.SafeDnsPacketSize {
    88  		// wtf???
    89  		return len(p), nil
    90  	}
    91  
    92  	buf := pool.Get(resolver.SafeDnsPacketSize)
    93  	put := func() { _ = pool.Put(buf) }
    94  	copy(buf, p) // avoid p be changed after WriteTo returned
    95  
    96  	go func() { // don't block the WriteTo function
    97  		ctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout)
    98  		defer cancel()
    99  
   100  		buf, err = resolver.RelayDnsPacket(ctx, buf[:len(p)], buf)
   101  		if err != nil {
   102  			put()
   103  			return
   104  		}
   105  
   106  		packet := dnsPacket{
   107  			data: buf,
   108  			put:  put,
   109  			addr: addr,
   110  		}
   111  		select {
   112  		case d.response <- packet:
   113  			break
   114  		case <-d.ctx.Done():
   115  			put()
   116  		}
   117  	}()
   118  	return len(p), nil
   119  }
   120  
   121  func (d *dnsPacketConn) Close() error {
   122  	d.cancel()
   123  	return nil
   124  }
   125  
   126  func (*dnsPacketConn) LocalAddr() net.Addr {
   127  	return &net.UDPAddr{
   128  		IP:   net.IPv4(127, 0, 0, 1),
   129  		Port: 53,
   130  		Zone: "",
   131  	}
   132  }
   133  
   134  func (*dnsPacketConn) SetDeadline(t time.Time) error {
   135  	return nil
   136  }
   137  
   138  func (*dnsPacketConn) SetReadDeadline(t time.Time) error {
   139  	return nil
   140  }
   141  
   142  func (*dnsPacketConn) SetWriteDeadline(t time.Time) error {
   143  	return nil
   144  }
   145  
   146  func NewDnsWithOption(option DnsOption) *Dns {
   147  	return &Dns{
   148  		Base: &Base{
   149  			name:   option.Name,
   150  			tp:     C.Dns,
   151  			udp:    true,
   152  			tfo:    option.TFO,
   153  			mpTcp:  option.MPTCP,
   154  			iface:  option.Interface,
   155  			rmark:  option.RoutingMark,
   156  			prefer: C.NewDNSPrefer(option.IPVersion),
   157  		},
   158  	}
   159  }