github.com/eagleql/xray-core@v1.4.4/app/dispatcher/sniffer.go (about)

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