clash/dns/util.go

293 lines
7.3 KiB
Go
Raw Normal View History

2018-12-05 13:13:29 +00:00
package dns
import (
2021-11-17 08:03:47 +00:00
"context"
2019-06-28 04:29:08 +00:00
"crypto/tls"
2022-08-24 13:36:19 +00:00
"errors"
2021-11-17 08:03:47 +00:00
"fmt"
2020-02-17 14:13:15 +00:00
"net"
2022-04-19 17:52:51 +00:00
"net/netip"
"strings"
2018-12-05 13:13:29 +00:00
"time"
"github.com/Dreamacro/clash/common/cache"
2022-12-19 13:34:07 +00:00
N "github.com/Dreamacro/clash/common/net"
2022-04-19 17:52:51 +00:00
"github.com/Dreamacro/clash/common/nnip"
2022-08-24 13:36:19 +00:00
"github.com/Dreamacro/clash/common/picker"
2021-11-17 08:03:47 +00:00
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/resolver"
2021-11-17 08:03:47 +00:00
C "github.com/Dreamacro/clash/constant"
2018-12-05 13:13:29 +00:00
"github.com/Dreamacro/clash/log"
2021-11-17 08:03:47 +00:00
"github.com/Dreamacro/clash/tunnel"
2018-12-05 13:13:29 +00:00
D "github.com/miekg/dns"
)
const (
MaxMsgSize = 65535
)
2022-04-05 12:23:16 +00:00
func putMsgToCache(c *cache.LruCache[string, *D.Msg], key string, msg *D.Msg) {
// skip dns cache for acme challenge
2023-01-11 01:53:56 +00:00
if len(msg.Question) != 0 {
if q := msg.Question[0]; 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
}
}
var ttl uint32
2019-10-14 09:13:23 +00:00
switch {
case len(msg.Answer) != 0:
ttl = msg.Answer[0].Header().Ttl
2019-10-14 09:13:23 +00:00
case len(msg.Ns) != 0:
ttl = msg.Ns[0].Header().Ttl
2019-10-14 09:13:23 +00:00
case len(msg.Extra) != 0:
ttl = msg.Extra[0].Header().Ttl
2019-10-14 09:13:23 +00:00
default:
log.Debugln("[DNS] response msg empty: %#v", msg)
2018-12-05 13:13:29 +00:00
return
}
c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Second*time.Duration(ttl)))
2019-02-23 12:31:59 +00: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 13:13:29 +00:00
}
2019-06-28 04:29:08 +00: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 04:29:08 +00:00
}
func transform(servers []NameServer, resolver *Resolver) []dnsClient {
ret := []dnsClient{}
2019-06-28 04:29:08 +00: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 04:29:08 +00:00
continue
case "dhcp":
ret = append(ret, newDHCPClient(s.Addr))
continue
2023-04-26 07:57:55 +00:00
case "system":
clients, err := loadSystemResolver()
if err != nil {
log.Errorln("[DNS:system] load system resolver failed: %s", err.Error())
continue
}
if len(clients) == 0 {
log.Errorln("[DNS:system] no nameserver found in system")
2023-04-26 07:57:55 +00:00
continue
}
ret = append(ret, clients...)
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 04:29:08 +00:00
}
2020-02-17 14:13:15 +00:00
host, port, _ := net.SplitHostPort(s.Addr)
2019-06-28 04:29:08 +00:00
ret = append(ret, &client{
Client: &D.Client{
Net: s.Net,
TLSConfig: &tls.Config{
ServerName: host,
2019-06-28 04:29:08 +00:00
},
UDPSize: 4096,
Timeout: 5 * time.Second,
2019-06-28 04:29:08 +00:00
},
2021-11-17 08:03:47 +00:00
port: port,
host: host,
iface: s.Interface,
r: resolver,
proxyAdapter: s.ProxyAdapter,
proxyName: s.ProxyName,
2019-06-28 04:29:08 +00: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-19 17:52:51 +00: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-19 17:52:51 +00:00
ips = append(ips, nnip.IpToAddr(ans.AAAA))
case *D.A:
2022-04-19 17:52:51 +00:00
ips = append(ips, nnip.IpToAddr(ans.A))
}
}
return ips
}
2021-11-17 08:03:47 +00:00
func msgToDomain(msg *D.Msg) string {
if len(msg.Question) > 0 {
return strings.TrimRight(msg.Question[0].Name, ".")
}
return ""
}
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
2022-06-21 14:59:35 +00:00
func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
if len(proxyName) == 0 && proxyAdapter == nil {
opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...)
} else {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok {
opts = append(opts, dialer.WithInterface(proxyName))
}
}
if strings.Contains(network, "tcp") {
// tcp can resolve host by remote
metadata := &C.Metadata{
NetWork: C.TCP,
Host: host,
DstPort: port,
}
if proxyAdapter != nil {
if proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
if err != nil {
return nil, err
}
metadata.Host = ""
metadata.DstIP = dstIP
}
return proxyAdapter.DialContext(ctx, metadata, opts...)
}
opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...)
} else {
// udp must resolve host first
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
if err != nil {
return nil, err
}
metadata := &C.Metadata{
NetWork: C.UDP,
Host: "",
DstIP: dstIP,
DstPort: port,
}
if proxyAdapter == nil {
return dialer.DialContext(ctx, network, addr, opts...)
}
if !proxyAdapter.SupportUDP() {
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
}
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
if err != nil {
return nil, err
}
2022-12-19 13:34:07 +00:00
return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil
}
}
2021-11-17 08:03:47 +00:00
}
}
2021-11-17 08:03:47 +00:00
func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
2021-11-17 08:03:47 +00:00
}
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok {
opts = append(opts, dialer.WithInterface(proxyName))
}
}
2022-06-21 14:59:35 +00:00
// udp must resolve host first
dstIP, err := resolver.ResolveIPWithResolver(ctx, host, r)
if err != nil {
return nil, err
}
metadata := &C.Metadata{
NetWork: C.UDP,
Host: "",
DstIP: dstIP,
DstPort: port,
}
if proxyAdapter == nil {
return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...)
}
2021-11-17 08:03:47 +00:00
if !proxyAdapter.SupportUDP() {
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
2021-11-17 08:03:47 +00:00
}
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
2021-11-17 08:03:47 +00:00
}
2022-08-24 13:36:19 +00:00
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
fast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)
2023-01-28 14:33:03 +00:00
domain := msgToDomain(m)
2022-08-24 13:36:19 +00:00
for _, client := range clients {
r := client
2022-11-19 02:35:45 +00:00
fast.Go(func() (*D.Msg, error) {
2023-01-28 14:33:03 +00:00
log.Debugln("[DNS] resolve %s from %s", domain, r.Address())
2022-08-24 13:36:19 +00:00
m, err := r.ExchangeContext(ctx, m)
if err != nil {
return nil, err
} else if m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused {
return nil, errors.New("server failure")
}
2023-01-28 14:33:03 +00:00
log.Debugln("[DNS] %s --> %s, from %s", domain, msgToIP(m), r.Address())
2022-08-24 13:36:19 +00:00
return m, nil
})
}
elm := fast.Wait()
if elm == nil {
err := errors.New("all DNS requests failed")
if fErr := fast.Error(); fErr != nil {
err = fmt.Errorf("%w, first error: %s", err, fErr.Error())
}
return nil, err
}
msg = elm
2022-08-24 13:36:19 +00:00
return
}