github.com/kelleygo/clashcore@v1.0.2/component/resolver/relay.go (about) 1 package resolver 2 3 import ( 4 "context" 5 "encoding/binary" 6 "io" 7 "net" 8 "time" 9 10 "github.com/kelleygo/clashcore/common/pool" 11 12 D "github.com/miekg/dns" 13 ) 14 15 const DefaultDnsReadTimeout = time.Second * 10 16 const DefaultDnsRelayTimeout = time.Second * 5 17 18 const SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough 19 20 func RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration) error { 21 buff := pool.Get(pool.UDPBufferSize) 22 defer func() { 23 _ = pool.Put(buff) 24 _ = conn.Close() 25 }() 26 for { 27 if readTimeout > 0 { 28 _ = conn.SetReadDeadline(time.Now().Add(readTimeout)) 29 } 30 31 length := uint16(0) 32 if err := binary.Read(conn, binary.BigEndian, &length); err != nil { 33 break 34 } 35 36 if int(length) > len(buff) { 37 break 38 } 39 40 n, err := io.ReadFull(conn, buff[:length]) 41 if err != nil { 42 break 43 } 44 45 err = func() error { 46 ctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout) 47 defer cancel() 48 inData := buff[:n] 49 msg, err := relayDnsPacket(ctx, inData, buff, 0) 50 if err != nil { 51 return err 52 } 53 54 err = binary.Write(conn, binary.BigEndian, uint16(len(msg))) 55 if err != nil { 56 return err 57 } 58 59 _, err = conn.Write(msg) 60 if err != nil { 61 return err 62 } 63 return nil 64 }() 65 if err != nil { 66 return err 67 } 68 } 69 return nil 70 } 71 72 func relayDnsPacket(ctx context.Context, payload []byte, target []byte, maxSize int) ([]byte, error) { 73 msg := &D.Msg{} 74 if err := msg.Unpack(payload); err != nil { 75 return nil, err 76 } 77 78 r, err := ServeMsg(ctx, msg) 79 if err != nil { 80 m := new(D.Msg) 81 m.SetRcode(msg, D.RcodeServerFailure) 82 return m.PackBuffer(target) 83 } 84 85 r.SetRcode(msg, r.Rcode) 86 if maxSize > 0 { 87 r.Truncate(maxSize) 88 } 89 r.Compress = true 90 return r.PackBuffer(target) 91 } 92 93 // RelayDnsPacket will truncate udp message up to SafeDnsPacketSize 94 func RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) { 95 return relayDnsPacket(ctx, payload, target, SafeDnsPacketSize) 96 }