github.com/kelleygo/clashcore@v1.0.2/component/sniffer/dispatcher.go (about)

     1  package sniffer
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"net"
     7  	"net/netip"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/kelleygo/clashcore/common/lru"
    12  	N "github.com/kelleygo/clashcore/common/net"
    13  	"github.com/kelleygo/clashcore/component/trie"
    14  	C "github.com/kelleygo/clashcore/constant"
    15  	"github.com/kelleygo/clashcore/constant/sniffer"
    16  	"github.com/kelleygo/clashcore/log"
    17  )
    18  
    19  var (
    20  	ErrorUnsupportedSniffer = errors.New("unsupported sniffer")
    21  	ErrorSniffFailed        = errors.New("all sniffer failed")
    22  	ErrNoClue               = errors.New("not enough information for making a decision")
    23  )
    24  
    25  var Dispatcher *SnifferDispatcher
    26  
    27  type SnifferDispatcher struct {
    28  	enable          bool
    29  	sniffers        map[sniffer.Sniffer]SnifferConfig
    30  	forceDomain     *trie.DomainSet
    31  	skipSNI         *trie.DomainSet
    32  	skipList        *lru.LruCache[string, uint8]
    33  	rwMux           sync.RWMutex
    34  	forceDnsMapping bool
    35  	parsePureIp     bool
    36  }
    37  
    38  func (sd *SnifferDispatcher) shouldOverride(metadata *C.Metadata) bool {
    39  	return (metadata.Host == "" && sd.parsePureIp) ||
    40  		sd.forceDomain.Has(metadata.Host) ||
    41  		(metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping)
    42  }
    43  
    44  func (sd *SnifferDispatcher) UDPSniff(packet C.PacketAdapter) bool {
    45  	metadata := packet.Metadata()
    46  
    47  	if sd.shouldOverride(packet.Metadata()) {
    48  		for sniffer, config := range sd.sniffers {
    49  			if sniffer.SupportNetwork() == C.UDP || sniffer.SupportNetwork() == C.ALLNet {
    50  				inWhitelist := sniffer.SupportPort(metadata.DstPort)
    51  				overrideDest := config.OverrideDest
    52  
    53  				if inWhitelist {
    54  					host, err := sniffer.SniffData(packet.Data())
    55  					if err != nil {
    56  						continue
    57  					}
    58  
    59  					sd.replaceDomain(metadata, host, overrideDest)
    60  					return true
    61  				}
    62  			}
    63  		}
    64  	}
    65  
    66  	return false
    67  }
    68  
    69  // TCPSniff returns true if the connection is sniffed to have a domain
    70  func (sd *SnifferDispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool {
    71  	if sd.shouldOverride(metadata) {
    72  		inWhitelist := false
    73  		overrideDest := false
    74  		for sniffer, config := range sd.sniffers {
    75  			if sniffer.SupportNetwork() == C.TCP || sniffer.SupportNetwork() == C.ALLNet {
    76  				inWhitelist = sniffer.SupportPort(metadata.DstPort)
    77  				if inWhitelist {
    78  					overrideDest = config.OverrideDest
    79  					break
    80  				}
    81  			}
    82  		}
    83  
    84  		if !inWhitelist {
    85  			return false
    86  		}
    87  
    88  		sd.rwMux.RLock()
    89  		dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort)
    90  		if count, ok := sd.skipList.Get(dst); ok && count > 5 {
    91  			log.Debugln("[Sniffer] Skip sniffing[%s] due to multiple failures", dst)
    92  			defer sd.rwMux.RUnlock()
    93  			return false
    94  		}
    95  		sd.rwMux.RUnlock()
    96  
    97  		if host, err := sd.sniffDomain(conn, metadata); err != nil {
    98  			sd.cacheSniffFailed(metadata)
    99  			log.Debugln("[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)
   100  			return false
   101  		} else {
   102  			if sd.skipSNI.Has(host) {
   103  				log.Debugln("[Sniffer] Skip sni[%s]", host)
   104  				return false
   105  			}
   106  
   107  			sd.rwMux.RLock()
   108  			sd.skipList.Delete(dst)
   109  			sd.rwMux.RUnlock()
   110  
   111  			sd.replaceDomain(metadata, host, overrideDest)
   112  			return true
   113  		}
   114  	}
   115  	return false
   116  }
   117  
   118  func (sd *SnifferDispatcher) replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {
   119  	// show log early, since the following code may mutate `metadata.Host`
   120  	log.Debugln("[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]",
   121  		metadata.NetWork,
   122  		metadata.SourceDetail(),
   123  		metadata.RemoteAddress(),
   124  		metadata.Host, host)
   125  	metadata.SniffHost = host
   126  	if overrideDest {
   127  		metadata.Host = host
   128  	}
   129  	metadata.DNSMode = C.DNSNormal
   130  }
   131  
   132  func (sd *SnifferDispatcher) Enable() bool {
   133  	return sd.enable
   134  }
   135  
   136  func (sd *SnifferDispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) {
   137  	for s := range sd.sniffers {
   138  		if s.SupportNetwork() == C.TCP {
   139  			_ = conn.SetReadDeadline(time.Now().Add(1 * time.Second))
   140  			_, err := conn.Peek(1)
   141  			_ = conn.SetReadDeadline(time.Time{})
   142  			if err != nil {
   143  				_, ok := err.(*net.OpError)
   144  				if ok {
   145  					sd.cacheSniffFailed(metadata)
   146  					log.Errorln("[Sniffer] [%s] may not have any sent data, Consider adding skip", metadata.DstIP.String())
   147  					_ = conn.Close()
   148  				}
   149  
   150  				return "", err
   151  			}
   152  
   153  			bufferedLen := conn.Buffered()
   154  			bytes, err := conn.Peek(bufferedLen)
   155  			if err != nil {
   156  				log.Debugln("[Sniffer] the data length not enough")
   157  				continue
   158  			}
   159  
   160  			host, err := s.SniffData(bytes)
   161  			if err != nil {
   162  				//log.Debugln("[Sniffer] [%s] Sniff data failed %s", s.Protocol(), metadata.DstIP)
   163  				continue
   164  			}
   165  
   166  			_, err = netip.ParseAddr(host)
   167  			if err == nil {
   168  				//log.Debugln("[Sniffer] [%s] Sniff data failed %s", s.Protocol(), metadata.DstIP)
   169  				continue
   170  			}
   171  
   172  			return host, nil
   173  		}
   174  	}
   175  
   176  	return "", ErrorSniffFailed
   177  }
   178  
   179  func (sd *SnifferDispatcher) cacheSniffFailed(metadata *C.Metadata) {
   180  	sd.rwMux.Lock()
   181  	dst := fmt.Sprintf("%s:%d", metadata.DstIP, metadata.DstPort)
   182  	count, _ := sd.skipList.Get(dst)
   183  	if count <= 5 {
   184  		count++
   185  	}
   186  	sd.skipList.Set(dst, count)
   187  	sd.rwMux.Unlock()
   188  }
   189  
   190  func NewCloseSnifferDispatcher() (*SnifferDispatcher, error) {
   191  	dispatcher := SnifferDispatcher{
   192  		enable: false,
   193  	}
   194  
   195  	return &dispatcher, nil
   196  }
   197  
   198  func NewSnifferDispatcher(snifferConfig map[sniffer.Type]SnifferConfig,
   199  	forceDomain *trie.DomainSet, skipSNI *trie.DomainSet,
   200  	forceDnsMapping bool, parsePureIp bool) (*SnifferDispatcher, error) {
   201  	dispatcher := SnifferDispatcher{
   202  		enable:          true,
   203  		forceDomain:     forceDomain,
   204  		skipSNI:         skipSNI,
   205  		skipList:        lru.New(lru.WithSize[string, uint8](128), lru.WithAge[string, uint8](600)),
   206  		forceDnsMapping: forceDnsMapping,
   207  		parsePureIp:     parsePureIp,
   208  		sniffers:        make(map[sniffer.Sniffer]SnifferConfig, 0),
   209  	}
   210  
   211  	for snifferName, config := range snifferConfig {
   212  		s, err := NewSniffer(snifferName, config)
   213  		if err != nil {
   214  			log.Errorln("Sniffer name[%s] is error", snifferName)
   215  			return &SnifferDispatcher{enable: false}, err
   216  		}
   217  		dispatcher.sniffers[s] = config
   218  	}
   219  
   220  	return &dispatcher, nil
   221  }
   222  
   223  func NewSniffer(name sniffer.Type, snifferConfig SnifferConfig) (sniffer.Sniffer, error) {
   224  	switch name {
   225  	case sniffer.TLS:
   226  		return NewTLSSniffer(snifferConfig)
   227  	case sniffer.HTTP:
   228  		return NewHTTPSniffer(snifferConfig)
   229  	case sniffer.QUIC:
   230  		return NewQuicSniffer(snifferConfig)
   231  	default:
   232  		return nil, ErrorUnsupportedSniffer
   233  	}
   234  }