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 }