github.com/imannamdari/v2ray-core/v5@v5.0.5/app/dispatcher/sniffer.go (about) 1 package dispatcher 2 3 import ( 4 "context" 5 6 "github.com/imannamdari/v2ray-core/v5/common" 7 "github.com/imannamdari/v2ray-core/v5/common/net" 8 "github.com/imannamdari/v2ray-core/v5/common/protocol/bittorrent" 9 "github.com/imannamdari/v2ray-core/v5/common/protocol/http" 10 "github.com/imannamdari/v2ray-core/v5/common/protocol/quic" 11 "github.com/imannamdari/v2ray-core/v5/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 quic.SniffQUIC(b) }, false, net.Network_UDP}, 40 {func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP}, 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 { 62 continue 63 } 64 if si.network != network { 65 continue 66 } 67 result, err := s(c, payload) 68 if err == common.ErrNoClue { 69 pendingSniffer = append(pendingSniffer, si) 70 continue 71 } 72 73 if err == nil && result != nil { 74 return result, nil 75 } 76 } 77 78 if len(pendingSniffer) > 0 { 79 s.sniffer = pendingSniffer 80 return nil, common.ErrNoClue 81 } 82 83 return nil, errUnknownContent 84 } 85 86 func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) { 87 var pendingSniffer []protocolSnifferWithMetadata 88 for _, si := range s.sniffer { 89 s := si.protocolSniffer 90 if !si.metadataSniffer { 91 pendingSniffer = append(pendingSniffer, si) 92 continue 93 } 94 result, err := s(c, nil) 95 if err == common.ErrNoClue { 96 pendingSniffer = append(pendingSniffer, si) 97 continue 98 } 99 100 if err == nil && result != nil { 101 return result, nil 102 } 103 } 104 105 if len(pendingSniffer) > 0 { 106 s.sniffer = pendingSniffer 107 return nil, common.ErrNoClue 108 } 109 110 return nil, errUnknownContent 111 } 112 113 func CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult { 114 return &compositeResult{domainResult: domainResult, protocolResult: protocolResult} 115 } 116 117 type compositeResult struct { 118 domainResult SniffResult 119 protocolResult SniffResult 120 } 121 122 func (c compositeResult) Protocol() string { 123 return c.protocolResult.Protocol() 124 } 125 126 func (c compositeResult) Domain() string { 127 return c.domainResult.Domain() 128 } 129 130 func (c compositeResult) ProtocolForDomainResult() string { 131 return c.domainResult.Protocol() 132 } 133 134 type SnifferResultComposite interface { 135 ProtocolForDomainResult() string 136 } 137 138 type SnifferIsProtoSubsetOf interface { 139 IsProtoSubsetOf(protocolName string) bool 140 }