diff --git a/config/config.go b/config/config.go index f532c61a..603523ec 100644 --- a/config/config.go +++ b/config/config.go @@ -62,7 +62,7 @@ type DNS struct { Fallback []dns.NameServer `yaml:"fallback"` FallbackFilter FallbackFilter `yaml:"fallback-filter"` Listen string `yaml:"listen"` - EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"` + EnhancedMode C.DNSMode `yaml:"enhanced-mode"` DefaultNameserver []dns.NameServer `yaml:"default-nameserver"` FakeIPRange *fakeip.Pool Hosts *trie.DomainTrie @@ -107,7 +107,7 @@ type RawDNS struct { Fallback []string `yaml:"fallback"` FallbackFilter RawFallbackFilter `yaml:"fallback-filter"` Listen string `yaml:"listen"` - EnhancedMode dns.EnhancedMode `yaml:"enhanced-mode"` + EnhancedMode C.DNSMode `yaml:"enhanced-mode"` FakeIPRange string `yaml:"fake-ip-range"` FakeIPFilter []string `yaml:"fake-ip-filter"` DefaultNameserver []string `yaml:"default-nameserver"` @@ -584,7 +584,7 @@ func parseDNS(rawCfg *RawConfig, hosts *trie.DomainTrie) (*DNS, error) { } } - if cfg.EnhancedMode == dns.FAKEIP { + if cfg.EnhancedMode == C.DNSFakeIP { _, ipnet, err := net.ParseCIDR(cfg.FakeIPRange) if err != nil { return nil, err diff --git a/constant/dns.go b/constant/dns.go new file mode 100644 index 00000000..d1cd7a61 --- /dev/null +++ b/constant/dns.go @@ -0,0 +1,70 @@ +package constant + +import ( + "encoding/json" + "errors" +) + +// DNSModeMapping is a mapping for EnhancedMode enum +var DNSModeMapping = map[string]DNSMode{ + DNSNormal.String(): DNSNormal, + DNSFakeIP.String(): DNSFakeIP, + DNSMapping.String(): DNSMapping, +} + +const ( + DNSNormal DNSMode = iota + DNSFakeIP + DNSMapping +) + +type DNSMode int + +// UnmarshalYAML unserialize EnhancedMode with yaml +func (e *DNSMode) UnmarshalYAML(unmarshal func(interface{}) error) error { + var tp string + if err := unmarshal(&tp); err != nil { + return err + } + mode, exist := DNSModeMapping[tp] + if !exist { + return errors.New("invalid mode") + } + *e = mode + return nil +} + +// MarshalYAML serialize EnhancedMode with yaml +func (e DNSMode) MarshalYAML() (interface{}, error) { + return e.String(), nil +} + +// UnmarshalJSON unserialize EnhancedMode with json +func (e *DNSMode) UnmarshalJSON(data []byte) error { + var tp string + json.Unmarshal(data, &tp) + mode, exist := DNSModeMapping[tp] + if !exist { + return errors.New("invalid mode") + } + *e = mode + return nil +} + +// MarshalJSON serialize EnhancedMode with json +func (e DNSMode) MarshalJSON() ([]byte, error) { + return json.Marshal(e.String()) +} + +func (e DNSMode) String() string { + switch e { + case DNSNormal: + return "normal" + case DNSFakeIP: + return "fake-ip" + case DNSMapping: + return "redir-host" + default: + return "unknown" + } +} diff --git a/constant/metadata.go b/constant/metadata.go index 9fff773b..822b4714 100644 --- a/constant/metadata.go +++ b/constant/metadata.go @@ -71,6 +71,7 @@ type Metadata struct { DstPort string `json:"destinationPort"` AddrType int `json:"-"` Host string `json:"host"` + DNSMode DNSMode `json:"dnsMode"` } func (m *Metadata) RemoteAddress() string { @@ -85,6 +86,23 @@ func (m *Metadata) Resolved() bool { return m.DstIP != nil } +// Pure is used to solve unexpected behavior +// when dialing proxy connection in DNSMapping mode. +func (m *Metadata) Pure() *Metadata { + if m.DNSMode == DNSMapping && m.DstIP != nil { + copy := *m + copy.Host = "" + if copy.DstIP.To4() != nil { + copy.AddrType = AtypIPv4 + } else { + copy.AddrType = AtypIPv6 + } + return © + } + + return m +} + func (m *Metadata) UDPAddr() *net.UDPAddr { if m.NetWork != UDP || m.DstIP == nil { return nil diff --git a/dns/enhancer.go b/dns/enhancer.go index d2592e6e..76f0f262 100644 --- a/dns/enhancer.go +++ b/dns/enhancer.go @@ -5,20 +5,21 @@ import ( "github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/component/fakeip" + C "github.com/Dreamacro/clash/constant" ) type ResolverEnhancer struct { - mode EnhancedMode + mode C.DNSMode fakePool *fakeip.Pool mapping *cache.LruCache } func (h *ResolverEnhancer) FakeIPEnabled() bool { - return h.mode == FAKEIP + return h.mode == C.DNSFakeIP } func (h *ResolverEnhancer) MappingEnabled() bool { - return h.mode == FAKEIP || h.mode == MAPPING + return h.mode == C.DNSFakeIP || h.mode == C.DNSMapping } func (h *ResolverEnhancer) IsExistFakeIP(ip net.IP) bool { @@ -75,7 +76,7 @@ func NewEnhancer(cfg Config) *ResolverEnhancer { var fakePool *fakeip.Pool var mapping *cache.LruCache - if cfg.EnhancedMode != NORMAL { + if cfg.EnhancedMode != C.DNSNormal { fakePool = cfg.Pool mapping = cache.NewLRUCache(cache.WithSize(4096), cache.WithStale(true)) } diff --git a/dns/middleware.go b/dns/middleware.go index de0b3aff..9d0da596 100644 --- a/dns/middleware.go +++ b/dns/middleware.go @@ -8,6 +8,7 @@ import ( "github.com/Dreamacro/clash/common/cache" "github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/trie" + C "github.com/Dreamacro/clash/constant" "github.com/Dreamacro/clash/context" "github.com/Dreamacro/clash/log" @@ -178,11 +179,11 @@ func newHandler(resolver *Resolver, mapper *ResolverEnhancer) handler { middlewares = append(middlewares, withHosts(resolver.hosts)) } - if mapper.mode == FAKEIP { + if mapper.mode == C.DNSFakeIP { middlewares = append(middlewares, withFakeIP(mapper.fakePool)) } - if mapper.mode != NORMAL { + if mapper.mode != C.DNSNormal { middlewares = append(middlewares, withMapping(mapper.mapping)) } diff --git a/dns/resolver.go b/dns/resolver.go index bf544cb9..91efbb8b 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -14,6 +14,7 @@ import ( "github.com/Dreamacro/clash/component/fakeip" "github.com/Dreamacro/clash/component/resolver" "github.com/Dreamacro/clash/component/trie" + C "github.com/Dreamacro/clash/constant" D "github.com/miekg/dns" "golang.org/x/sync/singleflight" @@ -317,7 +318,7 @@ type Config struct { Main, Fallback []NameServer Default []NameServer IPv6 bool - EnhancedMode EnhancedMode + EnhancedMode C.DNSMode FallbackFilter FallbackFilter Pool *fakeip.Pool Hosts *trie.DomainTrie diff --git a/dns/util.go b/dns/util.go index 0e1badab..d11870f8 100644 --- a/dns/util.go +++ b/dns/util.go @@ -2,8 +2,6 @@ package dns import ( "crypto/tls" - "encoding/json" - "errors" "net" "time" @@ -13,70 +11,6 @@ import ( D "github.com/miekg/dns" ) -// EnhancedModeMapping is a mapping for EnhancedMode enum -var 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 { diff --git a/tunnel/tunnel.go b/tunnel/tunnel.go index ccd6a58b..dd64b015 100644 --- a/tunnel/tunnel.go +++ b/tunnel/tunnel.go @@ -135,9 +135,11 @@ func preHandleMetadata(metadata *C.Metadata) error { metadata.AddrType = C.AtypDomainName if resolver.FakeIPEnabled() { metadata.DstIP = nil + metadata.DNSMode = C.DNSFakeIP } else if node := resolver.DefaultHosts.Search(host); node != nil { // redir-host should lookup the hosts metadata.DstIP = node.Data.(net.IP) + metadata.DNSMode = C.DNSMapping } } else if resolver.IsFakeIP(metadata.DstIP) { return fmt.Errorf("fake DNS record %s missing", metadata.DstIP) @@ -219,7 +221,7 @@ func handleUDPConn(packet *inbound.PacketAdapter) { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout) defer cancel() - rawPc, err := proxy.ListenPacketContext(ctx, metadata) + rawPc, err := proxy.ListenPacketContext(ctx, metadata.Pure()) if err != nil { if rule == nil { log.Warnln("[UDP] dial %s to %s error: %s", proxy.Name(), metadata.RemoteAddress(), err.Error()) @@ -271,7 +273,7 @@ func handleTCPConn(connCtx C.ConnContext) { ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout) defer cancel() - remoteConn, err := proxy.DialContext(ctx, metadata) + remoteConn, err := proxy.DialContext(ctx, metadata.Pure()) if err != nil { if rule == nil { log.Warnln("[TCP] dial %s to %s error: %s", proxy.Name(), metadata.RemoteAddress(), err.Error())