Skip to content

Commit

Permalink
Fix: unexpected proxy dial behavior on mapping mode
Browse files Browse the repository at this point in the history
  • Loading branch information
Dreamacro committed Oct 18, 2021
1 parent fea9d1c commit 81d5da5
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 78 deletions.
6 changes: 3 additions & 3 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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
Expand Down
70 changes: 70 additions & 0 deletions constant/dns.go
Original file line number Diff line number Diff line change
@@ -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"
}
}
18 changes: 18 additions & 0 deletions constant/metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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 &copy
}

return m
}

func (m *Metadata) UDPAddr() *net.UDPAddr {
if m.NetWork != UDP || m.DstIP == nil {
return nil
Expand Down
9 changes: 5 additions & 4 deletions dns/enhancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
}
Expand Down
5 changes: 3 additions & 2 deletions dns/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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))
}

Expand Down
3 changes: 2 additions & 1 deletion dns/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
66 changes: 0 additions & 66 deletions dns/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package dns

import (
"crypto/tls"
"encoding/json"
"errors"
"net"
"time"

Expand All @@ -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 {
Expand Down
6 changes: 4 additions & 2 deletions tunnel/tunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand Down

0 comments on commit 81d5da5

Please sign in to comment.