Meow
2026-04-14 02:08:51 +08:00
committed by GitHub
parent f17fabfff5
commit d342361c89
7 changed files with 84 additions and 32 deletions

View File

@@ -237,6 +237,9 @@ func (d *DefaultDispatcher) shouldOverride(ctx context.Context, result SniffResu
if request.ExcludeForDomain != nil && request.ExcludeForDomain.MatchAny(strings.ToLower(domain)) {
return false
}
if request.ExcludeForIP != nil && destination.Address.Family().IsIP() && request.ExcludeForIP.Match(destination.Address.IP()) {
return false
}
protocolString := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok {
protocolString = resComp.ProtocolForDomainResult()

View File

@@ -23,5 +23,12 @@ func BuildSniffingRequest(config *SniffingConfig) (session.SniffingRequest, erro
}
request.ExcludeForDomain = excludeForDomain
}
if len(config.IpsExcluded) > 0 {
excludeForIP, err := geodata.IPReg.BuildIPMatcher(config.IpsExcluded)
if err != nil {
return session.SniffingRequest{}, err
}
request.ExcludeForIP = excludeForIP
}
return request, nil
}

View File

@@ -69,6 +69,7 @@ type SniffingConfig struct {
// Supported values are "http", "tls", "fakedns".
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"`
DomainsExcluded []*geodata.DomainRule `protobuf:"bytes,3,rep,name=domains_excluded,json=domainsExcluded,proto3" json:"domains_excluded,omitempty"`
IpsExcluded []*geodata.IPRule `protobuf:"bytes,6,rep,name=ips_excluded,json=ipsExcluded,proto3" json:"ips_excluded,omitempty"`
// Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first
// message.
@@ -129,6 +130,13 @@ func (x *SniffingConfig) GetDomainsExcluded() []*geodata.DomainRule {
return nil
}
func (x *SniffingConfig) GetIpsExcluded() []*geodata.IPRule {
if x != nil {
return x.IpsExcluded
}
return nil
}
func (x *SniffingConfig) GetMetadataOnly() bool {
if x != nil {
return x.MetadataOnly
@@ -479,11 +487,12 @@ var File_app_proxyman_config_proto protoreflect.FileDescriptor
const file_app_proxyman_config_proto_rawDesc = "" +
"\n" +
"\x19app/proxyman/config.proto\x12\x11xray.app.proxyman\x1a\x18common/net/address.proto\x1a\x15common/net/port.proto\x1a\x1ftransport/internet/config.proto\x1a!common/serial/typed_message.proto\x1a\x1bcommon/geodata/geodat.proto\"\x0f\n" +
"\rInboundConfig\"\xed\x01\n" +
"\rInboundConfig\"\xad\x02\n" +
"\x0eSniffingConfig\x12\x18\n" +
"\aenabled\x18\x01 \x01(\bR\aenabled\x121\n" +
"\x14destination_override\x18\x02 \x03(\tR\x13destinationOverride\x12J\n" +
"\x10domains_excluded\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x0fdomainsExcluded\x12#\n" +
"\x10domains_excluded\x18\x03 \x03(\v2\x1f.xray.common.geodata.DomainRuleR\x0fdomainsExcluded\x12>\n" +
"\fips_excluded\x18\x06 \x03(\v2\x1b.xray.common.geodata.IPRuleR\vipsExcluded\x12#\n" +
"\rmetadata_only\x18\x04 \x01(\bR\fmetadataOnly\x12\x1d\n" +
"\n" +
"route_only\x18\x05 \x01(\bR\trouteOnly\"\xe5\x02\n" +
@@ -534,31 +543,33 @@ var file_app_proxyman_config_proto_goTypes = []any{
(*SenderConfig)(nil), // 5: xray.app.proxyman.SenderConfig
(*MultiplexingConfig)(nil), // 6: xray.app.proxyman.MultiplexingConfig
(*geodata.DomainRule)(nil), // 7: xray.common.geodata.DomainRule
(*net.PortList)(nil), // 8: xray.common.net.PortList
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain
(*internet.StreamConfig)(nil), // 10: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 11: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 12: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 13: xray.transport.internet.DomainStrategy
(*geodata.IPRule)(nil), // 8: xray.common.geodata.IPRule
(*net.PortList)(nil), // 9: xray.common.net.PortList
(*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
(*internet.StreamConfig)(nil), // 11: xray.transport.internet.StreamConfig
(*serial.TypedMessage)(nil), // 12: xray.common.serial.TypedMessage
(*internet.ProxyConfig)(nil), // 13: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 14: xray.transport.internet.DomainStrategy
}
var file_app_proxyman_config_proto_depIdxs = []int32{
7, // 0: xray.app.proxyman.SniffingConfig.domains_excluded:type_name -> xray.common.geodata.DomainRule
8, // 1: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
9, // 2: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
10, // 3: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
1, // 4: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
11, // 5: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
11, // 6: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
9, // 7: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
10, // 8: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
12, // 9: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
6, // 10: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
13, // 11: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
12, // [12:12] is the sub-list for method output_type
12, // [12:12] is the sub-list for method input_type
12, // [12:12] is the sub-list for extension type_name
12, // [12:12] is the sub-list for extension extendee
0, // [0:12] is the sub-list for field type_name
8, // 1: xray.app.proxyman.SniffingConfig.ips_excluded:type_name -> xray.common.geodata.IPRule
9, // 2: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
10, // 3: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
11, // 4: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
1, // 5: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
12, // 6: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
12, // 7: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
10, // 8: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
11, // 9: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
13, // 10: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
6, // 11: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
14, // 12: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
13, // [13:13] is the sub-list for method output_type
13, // [13:13] is the sub-list for method input_type
13, // [13:13] is the sub-list for extension type_name
13, // [13:13] is the sub-list for extension extendee
0, // [0:13] is the sub-list for field type_name
}
func init() { file_app_proxyman_config_proto_init() }

View File

@@ -24,6 +24,8 @@ message SniffingConfig {
repeated xray.common.geodata.DomainRule domains_excluded = 3;
repeated xray.common.geodata.IPRule ips_excluded = 6;
// Whether should only try to sniff metadata without waiting for client input.
// Can be used to support SMTP like protocol where server send the first
// message.

View File

@@ -80,6 +80,7 @@ type Outbound struct {
// SniffingRequest controls the behavior of content sniffing. They are from inbound config. Read-only
type SniffingRequest struct {
ExcludeForDomain geodata.DomainMatcher
ExcludeForIP geodata.IPMatcher
OverrideDestinationForProtocol []string
Enabled bool
MetadataOnly bool

View File

@@ -55,37 +55,44 @@ type SniffingConfig struct {
Enabled bool `json:"enabled"`
DestOverride StringList `json:"destOverride"`
DomainsExcluded StringList `json:"domainsExcluded"`
IPsExcluded StringList `json:"ipsExcluded"`
MetadataOnly bool `json:"metadataOnly"`
RouteOnly bool `json:"routeOnly"`
}
// Build implements Buildable.
func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
var p []string
var protocols []string
for _, protocol := range c.DestOverride {
switch strings.ToLower(protocol) {
case "http":
p = append(p, "http")
protocols = append(protocols, "http")
case "tls", "https", "ssl":
p = append(p, "tls")
protocols = append(protocols, "tls")
case "quic":
p = append(p, "quic")
protocols = append(protocols, "quic")
case "fakedns", "fakedns+others":
p = append(p, "fakedns")
protocols = append(protocols, "fakedns")
default:
return nil, errors.New("unknown protocol: ", protocol)
}
}
d, err := geodata.ParseDomainRules(c.DomainsExcluded, geodata.Domain_Substr)
domains, err := geodata.ParseDomainRules(c.DomainsExcluded, geodata.Domain_Substr)
if err != nil {
return nil, err
}
ips, err := geodata.ParseIPRules(c.IPsExcluded)
if err != nil {
return nil, err
}
return &proxyman.SniffingConfig{
Enabled: c.Enabled,
DestinationOverride: p,
DomainsExcluded: d,
DestinationOverride: protocols,
DomainsExcluded: domains,
IpsExcluded: ips,
MetadataOnly: c.MetadataOnly,
RouteOnly: c.RouteOnly,
}, nil

View File

@@ -163,6 +163,7 @@ func TestSniffingConfig_Build(t *testing.T) {
Enabled: true,
DestOverride: StringList{"http", "tls"},
DomainsExcluded: StringList{"full:api.example.com", "domain:blocked.example", "regexp:^test[0-9]+\\.internal$"},
IPsExcluded: StringList{"192.168.1.1", "2001:db8::/32"},
MetadataOnly: true,
RouteOnly: true,
}
@@ -181,6 +182,9 @@ func TestSniffingConfig_Build(t *testing.T) {
if len(built.DomainsExcluded) != 3 {
t.Fatalf("SniffingConfig.Build() produced %d domain rules", len(built.DomainsExcluded))
}
if len(built.IpsExcluded) != 2 {
t.Fatalf("SniffingConfig.Build() produced %d ip rules", len(built.IpsExcluded))
}
want := []struct {
ruleType geodata.Domain_Type
@@ -199,6 +203,23 @@ func TestSniffingConfig_Build(t *testing.T) {
t.Fatalf("SniffingConfig.Build() produced wrong rule at index %d: got (%v, %q), want (%v, %q)", i, rule.Type, rule.Value, tc.ruleType, tc.value)
}
}
wantIPs := []struct {
ip []byte
prefix uint32
}{
{ip: []byte(net.ParseAddress("192.168.1.1").IP()), prefix: 32},
{ip: []byte(net.ParseAddress("2001:db8::").IP()), prefix: 32},
}
for i, tc := range wantIPs {
rule := built.IpsExcluded[i].GetCustom()
if rule == nil {
t.Fatalf("SniffingConfig.Build() produced a non-custom ip rule at index %d", i)
}
if !reflect.DeepEqual(rule.Ip, tc.ip) || rule.Prefix != tc.prefix {
t.Fatalf("SniffingConfig.Build() produced wrong ip rule at index %d: got (%v, %d), want (%v, %d)", i, rule.Ip, rule.Prefix, tc.ip, tc.prefix)
}
}
}
func TestMuxConfig_Build(t *testing.T) {