package dns import ( "crypto/tls" "encoding/json" "errors" "net" "time" "github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/log" D "github.com/miekg/dns" ) var ( // EnhancedModeMapping is a mapping for EnhancedMode enum EnhancedModeMapping = map[string]EnhancedMode{ NORMAL.String(): NORMAL, FAKEIP.String(): FAKEIP, MAPPING.String(): MAPPING, } ) const ( NORMAL EnhancedMode = iota FAKEIP MAPPING ) type EnhancedMode int // UnmarshalYAML unserialize EnhancedMode with yaml func (e *EnhancedMode) UnmarshalYAML(unmarshal func(interface{}) error) error { var tp string if err := unmarshal(&tp); err != nil { return err } mode, exist := EnhancedModeMapping[tp] if !exist { return errors.New("invalid mode") } *e = mode return nil } // MarshalYAML serialize EnhancedMode with yaml func (e EnhancedMode) MarshalYAML() (interface{}, error) { return e.String(), nil } // UnmarshalJSON unserialize EnhancedMode with json func (e *EnhancedMode) UnmarshalJSON(data []byte) error { var tp string json.Unmarshal(data, &tp) mode, exist := EnhancedModeMapping[tp] if !exist { return errors.New("invalid mode") } *e = mode return nil } // MarshalJSON serialize EnhancedMode with json func (e EnhancedMode) MarshalJSON() ([]byte, error) { return json.Marshal(e.String()) } func (e EnhancedMode) String() string { switch e { case NORMAL: return "normal" case FAKEIP: return "fake-ip" case MAPPING: return "redir-host" default: return "unknown" } } func putMsgToCache(c *cache.LruCache, key string, msg *D.Msg) { var ttl uint32 switch { case len(msg.Answer) != 0: ttl = msg.Answer[0].Header().Ttl case len(msg.Ns) != 0: ttl = msg.Ns[0].Header().Ttl case len(msg.Extra) != 0: ttl = msg.Extra[0].Header().Ttl default: log.Debugln("[DNS] response msg empty: %#v", msg) return } c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Second*time.Duration(ttl))) } 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 } } func isIPRequest(q D.Question) bool { return q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA) } func transform(servers []NameServer, resolver *Resolver) []dnsClient { ret := []dnsClient{} for _, s := range servers { switch s.Net { case "https": ret = append(ret, newDoHClient(s.Addr, resolver)) continue case "dhcp": ret = append(ret, newDHCPClient(s.Addr)) continue } host, port, _ := net.SplitHostPort(s.Addr) ret = append(ret, &client{ Client: &D.Client{ Net: s.Net, TLSConfig: &tls.Config{ // alpn identifier, see https://tools.ietf.org/html/draft-hoffman-dprive-dns-tls-alpn-00#page-6 NextProtos: []string{"dns"}, ServerName: host, }, UDPSize: 4096, Timeout: 5 * time.Second, }, port: port, host: host, iface: s.Interface, r: resolver, }) } 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 } func msgToIP(msg *D.Msg) []net.IP { ips := []net.IP{} for _, answer := range msg.Answer { switch ans := answer.(type) { case *D.AAAA: ips = append(ips, ans.AAAA) case *D.A: ips = append(ips, ans.A) } } return ips }