2018-01-10 06:22:37 -05:00
|
|
|
package dispatcher
|
2017-04-24 18:19:22 -04:00
|
|
|
|
|
|
|
import (
|
2021-02-08 05:18:52 -05:00
|
|
|
"context"
|
|
|
|
|
2021-02-16 15:31:50 -05:00
|
|
|
"github.com/v2fly/v2ray-core/v4/common"
|
2021-11-02 07:36:55 -04:00
|
|
|
"github.com/v2fly/v2ray-core/v4/common/net"
|
2021-02-16 15:31:50 -05:00
|
|
|
"github.com/v2fly/v2ray-core/v4/common/protocol/bittorrent"
|
|
|
|
"github.com/v2fly/v2ray-core/v4/common/protocol/http"
|
2021-11-02 07:36:55 -04:00
|
|
|
"github.com/v2fly/v2ray-core/v4/common/protocol/quic"
|
2021-02-16 15:31:50 -05:00
|
|
|
"github.com/v2fly/v2ray-core/v4/common/protocol/tls"
|
2017-04-24 18:19:22 -04:00
|
|
|
)
|
|
|
|
|
2018-07-16 07:47:00 -04:00
|
|
|
type SniffResult interface {
|
|
|
|
Protocol() string
|
|
|
|
Domain() string
|
2017-04-24 18:19:22 -04:00
|
|
|
}
|
|
|
|
|
2021-02-08 05:18:52 -05:00
|
|
|
type protocolSniffer func(context.Context, []byte) (SniffResult, error)
|
|
|
|
|
|
|
|
type protocolSnifferWithMetadata struct {
|
|
|
|
protocolSniffer protocolSniffer
|
|
|
|
// A Metadata sniffer will be invoked on connection establishment only, with nil body,
|
|
|
|
// for both TCP and UDP connections
|
|
|
|
// It will not be shown as a traffic type for routing unless there is no other successful sniffing.
|
|
|
|
metadataSniffer bool
|
2021-11-02 07:36:55 -04:00
|
|
|
network net.Network
|
2021-02-08 05:18:52 -05:00
|
|
|
}
|
2017-04-24 18:19:22 -04:00
|
|
|
|
2018-07-16 07:47:00 -04:00
|
|
|
type Sniffer struct {
|
2021-02-08 05:18:52 -05:00
|
|
|
sniffer []protocolSnifferWithMetadata
|
2017-04-24 18:19:22 -04:00
|
|
|
}
|
|
|
|
|
2021-02-08 05:18:52 -05:00
|
|
|
func NewSniffer(ctx context.Context) *Sniffer {
|
|
|
|
ret := &Sniffer{
|
|
|
|
sniffer: []protocolSnifferWithMetadata{
|
2021-11-02 07:36:55 -04:00
|
|
|
{func(c context.Context, b []byte) (SniffResult, error) { return http.SniffHTTP(b) }, false, net.Network_TCP},
|
|
|
|
{func(c context.Context, b []byte) (SniffResult, error) { return tls.SniffTLS(b) }, false, net.Network_TCP},
|
|
|
|
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP},
|
2021-11-02 21:25:48 -04:00
|
|
|
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffBittorrent(b) }, false, net.Network_TCP},
|
|
|
|
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP},
|
2018-07-16 07:47:00 -04:00
|
|
|
},
|
2017-04-24 18:19:22 -04:00
|
|
|
}
|
2021-02-08 05:18:52 -05:00
|
|
|
if sniffer, err := newFakeDNSSniffer(ctx); err == nil {
|
2021-04-09 18:59:52 -04:00
|
|
|
others := ret.sniffer
|
2021-02-08 05:18:52 -05:00
|
|
|
ret.sniffer = append(ret.sniffer, sniffer)
|
2021-04-09 18:59:52 -04:00
|
|
|
fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others)
|
2021-04-10 08:40:15 -04:00
|
|
|
if err == nil {
|
|
|
|
ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...)
|
2021-04-09 18:59:52 -04:00
|
|
|
}
|
2021-02-08 05:18:52 -05:00
|
|
|
}
|
|
|
|
return ret
|
2018-07-16 07:47:00 -04:00
|
|
|
}
|
2017-04-24 18:19:22 -04:00
|
|
|
|
2018-07-16 07:47:00 -04:00
|
|
|
var errUnknownContent = newError("unknown content")
|
2017-04-24 18:19:22 -04:00
|
|
|
|
2021-11-02 07:36:55 -04:00
|
|
|
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) {
|
2021-02-08 05:18:52 -05:00
|
|
|
var pendingSniffer []protocolSnifferWithMetadata
|
|
|
|
for _, si := range s.sniffer {
|
|
|
|
s := si.protocolSniffer
|
|
|
|
if si.metadataSniffer {
|
|
|
|
continue
|
|
|
|
}
|
2021-11-02 07:36:55 -04:00
|
|
|
if si.network != network {
|
|
|
|
continue
|
|
|
|
}
|
2021-02-08 05:18:52 -05:00
|
|
|
result, err := s(c, payload)
|
2018-10-11 14:43:37 -04:00
|
|
|
if err == common.ErrNoClue {
|
2021-02-08 05:18:52 -05:00
|
|
|
pendingSniffer = append(pendingSniffer, si)
|
2018-07-16 07:47:00 -04:00
|
|
|
continue
|
2017-04-24 18:19:22 -04:00
|
|
|
}
|
|
|
|
|
2018-07-16 07:47:00 -04:00
|
|
|
if err == nil && result != nil {
|
|
|
|
return result, nil
|
2017-04-24 18:19:22 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-16 07:47:00 -04:00
|
|
|
if len(pendingSniffer) > 0 {
|
|
|
|
s.sniffer = pendingSniffer
|
2018-10-11 14:43:37 -04:00
|
|
|
return nil, common.ErrNoClue
|
2017-04-24 18:19:22 -04:00
|
|
|
}
|
2017-06-05 17:27:50 -04:00
|
|
|
|
2018-07-16 07:47:00 -04:00
|
|
|
return nil, errUnknownContent
|
2017-06-05 17:27:50 -04:00
|
|
|
}
|
2021-02-08 05:18:52 -05:00
|
|
|
|
|
|
|
func (s *Sniffer) SniffMetadata(c context.Context) (SniffResult, error) {
|
|
|
|
var pendingSniffer []protocolSnifferWithMetadata
|
|
|
|
for _, si := range s.sniffer {
|
|
|
|
s := si.protocolSniffer
|
|
|
|
if !si.metadataSniffer {
|
|
|
|
pendingSniffer = append(pendingSniffer, si)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
result, err := s(c, nil)
|
|
|
|
if err == common.ErrNoClue {
|
|
|
|
pendingSniffer = append(pendingSniffer, si)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if err == nil && result != nil {
|
|
|
|
return result, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(pendingSniffer) > 0 {
|
|
|
|
s.sniffer = pendingSniffer
|
|
|
|
return nil, common.ErrNoClue
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, errUnknownContent
|
|
|
|
}
|
|
|
|
|
|
|
|
func CompositeResult(domainResult SniffResult, protocolResult SniffResult) SniffResult {
|
|
|
|
return &compositeResult{domainResult: domainResult, protocolResult: protocolResult}
|
|
|
|
}
|
|
|
|
|
|
|
|
type compositeResult struct {
|
|
|
|
domainResult SniffResult
|
|
|
|
protocolResult SniffResult
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c compositeResult) Protocol() string {
|
|
|
|
return c.protocolResult.Protocol()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c compositeResult) Domain() string {
|
|
|
|
return c.domainResult.Domain()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c compositeResult) ProtocolForDomainResult() string {
|
|
|
|
return c.domainResult.Protocol()
|
|
|
|
}
|
|
|
|
|
|
|
|
type SnifferResultComposite interface {
|
|
|
|
ProtocolForDomainResult() string
|
|
|
|
}
|
2021-04-10 08:40:15 -04:00
|
|
|
|
|
|
|
type SnifferIsProtoSubsetOf interface {
|
|
|
|
IsProtoSubsetOf(protocolName string) bool
|
|
|
|
}
|