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 }