Clash.Meta/dns/util.go

237 lines
5.6 KiB
Go
Raw Normal View History

2018-12-05 21:13:29 +08:00
package dns
import (
2021-11-17 16:03:47 +08:00
"context"
2019-06-28 12:29:08 +08:00
"crypto/tls"
2022-08-24 21:36:19 +08:00
"errors"
2021-11-17 16:03:47 +08:00
"fmt"
2020-02-17 22:13:15 +08:00
"net"
2022-04-20 01:52:51 +08:00
"net/netip"
"strings"
2018-12-05 21:13:29 +08:00
"time"
2023-11-03 21:01:45 +08:00
"github.com/metacubex/mihomo/common/nnip"
"github.com/metacubex/mihomo/common/picker"
2024-06-15 00:33:03 +08:00
"github.com/metacubex/mihomo/component/dialer"
2023-11-03 21:01:45 +08:00
"github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/log"
2018-12-05 21:13:29 +08:00
D "github.com/miekg/dns"
"github.com/samber/lo"
2018-12-05 21:13:29 +08:00
)
const (
MaxMsgSize = 65535
)
const serverFailureCacheTTL uint32 = 5
func minimalTTL(records []D.RR) uint32 {
rr := lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool {
return r1.Header().Ttl < r2.Header().Ttl
})
if rr == nil {
return 0
}
return rr.Header().Ttl
}
func updateTTL(records []D.RR, ttl uint32) {
if len(records) == 0 {
return
}
delta := minimalTTL(records) - ttl
for i := range records {
records[i].Header().Ttl = lo.Clamp(records[i].Header().Ttl-delta, 1, records[i].Header().Ttl)
}
}
2023-12-02 17:07:36 +08:00
func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) {
// skip dns cache for acme challenge
if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") {
log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name)
return
2018-12-05 21:13:29 +08:00
}
var ttl uint32
if msg.Rcode == D.RcodeServerFailure {
// [...] a resolver MAY cache a server failure response.
// If it does so it MUST NOT cache it for longer than five (5) minutes [...]
ttl = serverFailureCacheTTL
} else {
ttl = minimalTTL(append(append(msg.Answer, msg.Ns...), msg.Extra...))
}
if ttl == 0 {
return
}
c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(ttl)*time.Second))
2019-02-23 20:31:59 +08:00
}
func setMsgTTL(msg *D.Msg, ttl uint32) {
for _, answer := range msg.Answer {
answer.Header().Ttl = ttl
}
for _, ns := range msg.Ns {
ns.Header().Ttl = ttl
}
for _, extra := range msg.Extra {
extra.Header().Ttl = ttl
}
2018-12-05 21:13:29 +08:00
}
2019-06-28 12:29:08 +08:00
func updateMsgTTL(msg *D.Msg, ttl uint32) {
updateTTL(msg.Answer, ttl)
updateTTL(msg.Ns, ttl)
updateTTL(msg.Extra, ttl)
}
2019-06-28 12:29:08 +08:00
func isIPRequest(q D.Question) bool {
return q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA || q.Qtype == D.TypeCNAME)
2019-06-28 12:29:08 +08:00
}
func transform(servers []NameServer, resolver *Resolver) []dnsClient {
2023-06-11 23:01:45 +08:00
ret := make([]dnsClient, 0, len(servers))
2019-06-28 12:29:08 +08:00
for _, s := range servers {
switch s.Net {
case "https":
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName))
2019-06-28 12:29:08 +08:00
continue
case "dhcp":
ret = append(ret, newDHCPClient(s.Addr))
continue
2023-04-26 15:57:55 +08:00
case "system":
2023-10-25 19:20:44 +08:00
ret = append(ret, newSystemClient())
2023-04-26 15:57:55 +08:00
continue
2023-06-11 23:01:45 +08:00
case "rcode":
ret = append(ret, newRCodeClient(s.Addr))
continue
case "quic":
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
ret = append(ret, doq)
} else {
log.Fatalln("DoQ format error: %v", err)
}
continue
2019-06-28 12:29:08 +08:00
}
2024-06-15 00:33:03 +08:00
var options []dialer.Option
if s.Interface != "" {
options = append(options, dialer.WithInterface(s.Interface))
}
2020-02-17 22:13:15 +08:00
host, port, _ := net.SplitHostPort(s.Addr)
2019-06-28 12:29:08 +08:00
ret = append(ret, &client{
Client: &D.Client{
Net: s.Net,
TLSConfig: &tls.Config{
ServerName: host,
2019-06-28 12:29:08 +08:00
},
UDPSize: 4096,
Timeout: 5 * time.Second,
2019-06-28 12:29:08 +08:00
},
2024-06-15 00:33:03 +08:00
port: port,
host: host,
dialer: newDNSDialer(resolver, s.ProxyAdapter, s.ProxyName, options...),
2019-06-28 12:29:08 +08:00
})
}
return ret
}
func handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {
msg := &D.Msg{}
msg.Answer = []D.RR{}
msg.SetRcode(r, D.RcodeSuccess)
msg.Authoritative = true
msg.RecursionAvailable = true
return msg
}
2022-04-20 01:52:51 +08:00
func msgToIP(msg *D.Msg) []netip.Addr {
ips := []netip.Addr{}
for _, answer := range msg.Answer {
switch ans := answer.(type) {
case *D.AAAA:
2022-04-20 01:52:51 +08:00
ips = append(ips, nnip.IpToAddr(ans.AAAA))
case *D.A:
2022-04-20 01:52:51 +08:00
ips = append(ips, nnip.IpToAddr(ans.A))
}
}
return ips
}
2021-11-17 16:03:47 +08:00
func msgToDomain(msg *D.Msg) string {
if len(msg.Question) > 0 {
return strings.TrimRight(msg.Question[0].Name, ".")
}
return ""
}
2024-07-19 19:27:29 +08:00
func msgToQtype(msg *D.Msg) (uint16, string) {
if len(msg.Question) > 0 {
qType := msg.Question[0].Qtype
return qType, D.Type(qType).String()
}
return 0, ""
}
2023-06-11 20:58:51 +08:00
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {
2023-07-14 09:55:43 +08:00
cache = true
fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)
2023-07-14 09:55:43 +08:00
defer fast.Close()
2023-01-28 22:33:03 +08:00
domain := msgToDomain(m)
2024-07-19 19:27:29 +08:00
qType, qTypeStr := msgToQtype(m)
2023-10-25 20:16:44 +08:00
var noIpMsg *D.Msg
2022-08-24 21:36:19 +08:00
for _, client := range clients {
2023-07-14 09:55:43 +08:00
if _, isRCodeClient := client.(rcodeClient); isRCodeClient {
2023-10-25 18:07:45 +08:00
msg, err = client.ExchangeContext(ctx, m)
2023-07-14 09:55:43 +08:00
return msg, false, err
}
2023-06-12 18:01:13 +08:00
client := client // shadow define client to ensure the value captured by the closure will not be changed in the next loop
2022-11-19 10:35:45 +08:00
fast.Go(func() (*D.Msg, error) {
2024-07-19 19:27:29 +08:00
log.Debugln("[DNS] resolve %s %s from %s", domain, qTypeStr, client.Address())
2023-06-11 23:01:45 +08:00
m, err := client.ExchangeContext(ctx, m)
2022-08-24 21:36:19 +08:00
if err != nil {
return nil, err
2023-06-11 20:58:51 +08:00
} else if cache && (m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused) {
// currently, cache indicates whether this msg was from a RCode client,
// so we would ignore RCode errors from RCode clients.
2023-07-14 09:55:43 +08:00
return nil, errors.New("server failure: " + D.RcodeToString[m.Rcode])
2022-08-24 21:36:19 +08:00
}
2024-07-19 19:27:29 +08:00
ips := msgToIP(m)
log.Debugln("[DNS] %s --> %s %s from %s", domain, ips, qTypeStr, client.Address())
switch qType {
case D.TypeAAAA:
if len(ips) == 0 {
noIpMsg = m
return nil, resolver.ErrIPNotFound
}
case D.TypeA:
if len(ips) == 0 {
noIpMsg = m
return nil, resolver.ErrIPNotFound
2023-10-25 20:16:44 +08:00
}
}
2022-08-24 21:36:19 +08:00
return m, nil
})
}
2023-07-14 09:55:43 +08:00
msg = fast.Wait()
if msg == nil {
2023-10-25 20:16:44 +08:00
if noIpMsg != nil {
return noIpMsg, false, nil
}
2023-07-14 09:55:43 +08:00
err = errors.New("all DNS requests failed")
2022-08-24 21:36:19 +08:00
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %w", err, fErr)
2022-08-24 21:36:19 +08:00
}
}
return
}