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