dns.go 3.3 KB

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