mirror of https://github.com/XTLS/Xray-core
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
3.8 KiB
138 lines
3.8 KiB
package dispatcher |
|
|
|
import ( |
|
"context" |
|
|
|
"github.com/xtls/xray-core/common" |
|
"github.com/xtls/xray-core/common/errors" |
|
"github.com/xtls/xray-core/common/net" |
|
"github.com/xtls/xray-core/common/protocol/bittorrent" |
|
"github.com/xtls/xray-core/common/protocol/http" |
|
"github.com/xtls/xray-core/common/protocol/quic" |
|
"github.com/xtls/xray-core/common/protocol/tls" |
|
) |
|
|
|
type SniffResult interface { |
|
Protocol() string |
|
Domain() string |
|
} |
|
|
|
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 |
|
network net.Network |
|
} |
|
|
|
type Sniffer struct { |
|
sniffer []protocolSnifferWithMetadata |
|
} |
|
|
|
func NewSniffer(ctx context.Context) *Sniffer { |
|
ret := &Sniffer{ |
|
sniffer: []protocolSnifferWithMetadata{ |
|
{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 bittorrent.SniffBittorrent(b) }, false, net.Network_TCP}, |
|
{func(c context.Context, b []byte) (SniffResult, error) { return quic.SniffQUIC(b) }, false, net.Network_UDP}, |
|
{func(c context.Context, b []byte) (SniffResult, error) { return bittorrent.SniffUTP(b) }, false, net.Network_UDP}, |
|
}, |
|
} |
|
if sniffer, err := newFakeDNSSniffer(ctx); err == nil { |
|
others := ret.sniffer |
|
ret.sniffer = append(ret.sniffer, sniffer) |
|
fakeDNSThenOthers, err := newFakeDNSThenOthers(ctx, sniffer, others) |
|
if err == nil { |
|
ret.sniffer = append([]protocolSnifferWithMetadata{fakeDNSThenOthers}, ret.sniffer...) |
|
} |
|
} |
|
return ret |
|
} |
|
|
|
var errUnknownContent = errors.New("unknown content") |
|
|
|
func (s *Sniffer) Sniff(c context.Context, payload []byte, network net.Network) (SniffResult, error) { |
|
var pendingSniffer []protocolSnifferWithMetadata |
|
for _, si := range s.sniffer { |
|
s := si.protocolSniffer |
|
if si.metadataSniffer || si.network != network { |
|
continue |
|
} |
|
result, err := s(c, payload) |
|
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 (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 |
|
} |
|
|
|
type SnifferIsProtoSubsetOf interface { |
|
IsProtoSubsetOf(protocolName string) bool |
|
}
|
|
|