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)) { if request.ExcludeForDomain != nil && request.ExcludeForDomain.MatchAny(strings.ToLower(domain)) {
return false return false
} }
if request.ExcludeForIP != nil && destination.Address.Family().IsIP() && request.ExcludeForIP.Match(destination.Address.IP()) {
return false
}
protocolString := result.Protocol() protocolString := result.Protocol()
if resComp, ok := result.(SnifferResultComposite); ok { if resComp, ok := result.(SnifferResultComposite); ok {
protocolString = resComp.ProtocolForDomainResult() protocolString = resComp.ProtocolForDomainResult()

View File

@@ -23,5 +23,12 @@ func BuildSniffingRequest(config *SniffingConfig) (session.SniffingRequest, erro
} }
request.ExcludeForDomain = excludeForDomain 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 return request, nil
} }

View File

@@ -69,6 +69,7 @@ type SniffingConfig struct {
// Supported values are "http", "tls", "fakedns". // Supported values are "http", "tls", "fakedns".
DestinationOverride []string `protobuf:"bytes,2,rep,name=destination_override,json=destinationOverride,proto3" json:"destination_override,omitempty"` 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"` 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. // 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 // Can be used to support SMTP like protocol where server send the first
// message. // message.
@@ -129,6 +130,13 @@ func (x *SniffingConfig) GetDomainsExcluded() []*geodata.DomainRule {
return nil return nil
} }
func (x *SniffingConfig) GetIpsExcluded() []*geodata.IPRule {
if x != nil {
return x.IpsExcluded
}
return nil
}
func (x *SniffingConfig) GetMetadataOnly() bool { func (x *SniffingConfig) GetMetadataOnly() bool {
if x != nil { if x != nil {
return x.MetadataOnly return x.MetadataOnly
@@ -479,11 +487,12 @@ var File_app_proxyman_config_proto protoreflect.FileDescriptor
const file_app_proxyman_config_proto_rawDesc = "" + const file_app_proxyman_config_proto_rawDesc = "" +
"\n" + "\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" + "\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" + "\x0eSniffingConfig\x12\x18\n" +
"\aenabled\x18\x01 \x01(\bR\aenabled\x121\n" + "\aenabled\x18\x01 \x01(\bR\aenabled\x121\n" +
"\x14destination_override\x18\x02 \x03(\tR\x13destinationOverride\x12J\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" + "\rmetadata_only\x18\x04 \x01(\bR\fmetadataOnly\x12\x1d\n" +
"\n" + "\n" +
"route_only\x18\x05 \x01(\bR\trouteOnly\"\xe5\x02\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 (*SenderConfig)(nil), // 5: xray.app.proxyman.SenderConfig
(*MultiplexingConfig)(nil), // 6: xray.app.proxyman.MultiplexingConfig (*MultiplexingConfig)(nil), // 6: xray.app.proxyman.MultiplexingConfig
(*geodata.DomainRule)(nil), // 7: xray.common.geodata.DomainRule (*geodata.DomainRule)(nil), // 7: xray.common.geodata.DomainRule
(*net.PortList)(nil), // 8: xray.common.net.PortList (*geodata.IPRule)(nil), // 8: xray.common.geodata.IPRule
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain (*net.PortList)(nil), // 9: xray.common.net.PortList
(*internet.StreamConfig)(nil), // 10: xray.transport.internet.StreamConfig (*net.IPOrDomain)(nil), // 10: xray.common.net.IPOrDomain
(*serial.TypedMessage)(nil), // 11: xray.common.serial.TypedMessage (*internet.StreamConfig)(nil), // 11: xray.transport.internet.StreamConfig
(*internet.ProxyConfig)(nil), // 12: xray.transport.internet.ProxyConfig (*serial.TypedMessage)(nil), // 12: xray.common.serial.TypedMessage
(internet.DomainStrategy)(0), // 13: xray.transport.internet.DomainStrategy (*internet.ProxyConfig)(nil), // 13: xray.transport.internet.ProxyConfig
(internet.DomainStrategy)(0), // 14: xray.transport.internet.DomainStrategy
} }
var file_app_proxyman_config_proto_depIdxs = []int32{ var file_app_proxyman_config_proto_depIdxs = []int32{
7, // 0: xray.app.proxyman.SniffingConfig.domains_excluded:type_name -> xray.common.geodata.DomainRule 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 8, // 1: xray.app.proxyman.SniffingConfig.ips_excluded:type_name -> xray.common.geodata.IPRule
9, // 2: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain 9, // 2: xray.app.proxyman.ReceiverConfig.port_list:type_name -> xray.common.net.PortList
10, // 3: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 10, // 3: xray.app.proxyman.ReceiverConfig.listen:type_name -> xray.common.net.IPOrDomain
1, // 4: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig 11, // 4: xray.app.proxyman.ReceiverConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
11, // 5: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage 1, // 5: xray.app.proxyman.ReceiverConfig.sniffing_settings:type_name -> xray.app.proxyman.SniffingConfig
11, // 6: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage 12, // 6: xray.app.proxyman.InboundHandlerConfig.receiver_settings:type_name -> xray.common.serial.TypedMessage
9, // 7: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain 12, // 7: xray.app.proxyman.InboundHandlerConfig.proxy_settings:type_name -> xray.common.serial.TypedMessage
10, // 8: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig 10, // 8: xray.app.proxyman.SenderConfig.via:type_name -> xray.common.net.IPOrDomain
12, // 9: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig 11, // 9: xray.app.proxyman.SenderConfig.stream_settings:type_name -> xray.transport.internet.StreamConfig
6, // 10: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig 13, // 10: xray.app.proxyman.SenderConfig.proxy_settings:type_name -> xray.transport.internet.ProxyConfig
13, // 11: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy 6, // 11: xray.app.proxyman.SenderConfig.multiplex_settings:type_name -> xray.app.proxyman.MultiplexingConfig
12, // [12:12] is the sub-list for method output_type 14, // 12: xray.app.proxyman.SenderConfig.target_strategy:type_name -> xray.transport.internet.DomainStrategy
12, // [12:12] is the sub-list for method input_type 13, // [13:13] is the sub-list for method output_type
12, // [12:12] is the sub-list for extension type_name 13, // [13:13] is the sub-list for method input_type
12, // [12:12] is the sub-list for extension extendee 13, // [13:13] is the sub-list for extension type_name
0, // [0:12] is the sub-list for field 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() } 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.DomainRule domains_excluded = 3;
repeated xray.common.geodata.IPRule ips_excluded = 6;
// Whether should only try to sniff metadata without waiting for client input. // 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 // Can be used to support SMTP like protocol where server send the first
// message. // message.

View File

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

View File

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

View File

@@ -163,6 +163,7 @@ func TestSniffingConfig_Build(t *testing.T) {
Enabled: true, Enabled: true,
DestOverride: StringList{"http", "tls"}, DestOverride: StringList{"http", "tls"},
DomainsExcluded: StringList{"full:api.example.com", "domain:blocked.example", "regexp:^test[0-9]+\\.internal$"}, 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, MetadataOnly: true,
RouteOnly: true, RouteOnly: true,
} }
@@ -181,6 +182,9 @@ func TestSniffingConfig_Build(t *testing.T) {
if len(built.DomainsExcluded) != 3 { if len(built.DomainsExcluded) != 3 {
t.Fatalf("SniffingConfig.Build() produced %d domain rules", len(built.DomainsExcluded)) 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 { want := []struct {
ruleType geodata.Domain_Type 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) 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) { func TestMuxConfig_Build(t *testing.T) {