github.com/v2fly/v2ray-core/v4@v4.45.2/app/dispatcher/sniffer.go (about)

     1  //go:build !confonly
     2  // +build !confonly
     3  
     4  package dispatcher
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/v2fly/v2ray-core/v4/common"
    10  	"github.com/v2fly/v2ray-core/v4/common/protocol/bittorrent"
    11  	"github.com/v2fly/v2ray-core/v4/common/protocol/http"
    12  	"github.com/v2fly/v2ray-core/v4/common/protocol/tls"
    13  )
    14  
    15  type SniffResult interface {
    16  	Protocol() string
    17  	Domain() string
    18  }
    19  
    20  type protocolSniffer func(context.Context, []byte) (SniffResult, error)
    21  
    22  type protocolSnifferWithMetadata struct {
    23  	protocolSniffer protocolSniffer
    24  	// A Metadata sniffer will be invoked on connection establishment only, with nil body,
    25  	// for both TCP and UDP connections
    26  	// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
    27  	metadataSniffer bool
    28  }
    29  
    30  type Sniffer struct {
    31  	sniffer []protocolSnifferWithMetadata
    32  }
    33  
    34  func NewSniffer(ctx context.Context) *Sniffer {
    35  	ret := &Sniffer{
    36  		sniffer: []protocolSnifferWithMetadata{
    37  			{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false},
    38  			{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false},
    39  			{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false},
    40  		},
    41  	}
    42  	if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
    43  		others := ret.sniffer
    44  		ret.sniffer = append(ret.sniffer, sniffer)
    45  		fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others)
    46  		if err == nil {
    47  			ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...)
    48  		}
    49  	}
    50  	return ret
    51  }
    52  
    53  var errUnknownContent = newError("unknown content")
    54  
    55  func (s *Sniffer) Sniff(c context.Context, payload []byte) (SniffResult, error) {
    56  	var pendingSniffer []protocolSnifferWithMetadata
    57  	for _, si := range s.sniffer {
    58  		s := si.protocolSniffer
    59  		if si.metadataSniffer {
    60  			continue
    61  		}
    62  		result, err := s(c, payload)
    63  		if err == common.ErrNoClue {
    64  			pendingSniffer = append(pendingSniffer, si)
    65  			continue
    66  		}
    67  
    68  		if err == nil && result != nil {
    69  			return result, nil
    70  		}
    71  	}
    72  
    73  	if len(pendingSniffer) > 0 {
    74  		s.sniffer = pendingSniffer
    75  		return nil, common.ErrNoClue
    76  	}
    77  
    78  	return nil, errUnknownContent
    79  }
    80  
    81  func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) {
    82  	var pendingSniffer []protocolSnifferWithMetadata
    83  	for _, si := range s.sniffer {
    84  		s := si.protocolSniffer
    85  		if !si.metadataSniffer {
    86  			pendingSniffer = append(pendingSniffer, si)
    87  			continue
    88  		}
    89  		result, err := s(c, nil)
    90  		if err == common.ErrNoClue {
    91  			pendingSniffer = append(pendingSniffer, si)
    92  			continue
    93  		}
    94  
    95  		if err == nil && result != nil {
    96  			return result, nil
    97  		}
    98  	}
    99  
   100  	if len(pendingSniffer) > 0 {
   101  		s.sniffer = pendingSniffer
   102  		return nil, common.ErrNoClue
   103  	}
   104  
   105  	return nil, errUnknownContent
   106  }
   107  
   108  func CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult {
   109  	return &compositeResult{domainResult: domainResult, protocolResult: protocolResult}
   110  }
   111  
   112  type compositeResult struct {
   113  	domainResult   SniffResult
   114  	protocolResult SniffResult
   115  }
   116  
   117  func (c compositeResult) Protocol() string {
   118  	return c.protocolResult.Protocol()
   119  }
   120  
   121  func (c compositeResult) Domain() string {
   122  	return c.domainResult.Domain()
   123  }
   124  
   125  func (c compositeResult) ProtocolForDomainResult() string {
   126  	return c.domainResult.Protocol()
   127  }
   128  
   129  type SnifferResultComposite interface {
   130  	ProtocolForDomainResult() string
   131  }
   132  
   133  type SnifferIsProtoSubsetOf interface {
   134  	IsProtoSubsetOf(protocolName string) bool
   135  }