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 }