mirror of https://github.com/XTLS/Xray-core
Sniff: Add SniffingObject.ignoreClientIp (bool) option to override client-requested IP when routeOnly is enabled
- Added `ignoreClientIp` option in the sniffing module, which forces internal DNS resolution to override the client-requested IP when `routeOnly` is enabled. - Resolves Issue #4335 by addressing routing failures caused by DoH (DNS over HTTPS) interfering with DNS resolution in transparent proxy scenarios. - Eliminates the need to block common DoH providers when using geoip-based routing, ensuring accurate routing without additional configurations. Use case: When Xray acts as a transparent proxy gateway with geoip-based routing, `routeOnly` must be set to `true`. However, if the client enables DoH (e.g., Chrome automatically enabling DoH when detecting OS-level support), DNS resolution may be polluted, causing critical routing failures. Previously, users had to block common DoH providers to prevent such issues—an impractical and cumbersome solution. With `ignoreClientIp` enabled, Xray no longer trusts the client-requested IP and instead performs internal DNS resolution, ensuring correct routing behavior without the need for DoH blocking.pull/4423/head
parent
be43f66b63
commit
685e29bd76
|
@ -9,6 +9,7 @@ import (
|
|||
|
||||
"github.com/xtls/xray-core/common"
|
||||
"github.com/xtls/xray-core/common/buf"
|
||||
"github.com/xtls/xray-core/common/dice"
|
||||
"github.com/xtls/xray-core/common/errors"
|
||||
"github.com/xtls/xray-core/common/log"
|
||||
"github.com/xtls/xray-core/common/net"
|
||||
|
@ -290,6 +291,19 @@ func (d *DefaultDispatcher) Dispatch(ctx context.Context, destination net.Destin
|
|||
}
|
||||
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
|
||||
ob.RouteTarget = destination
|
||||
if sniffingRequest.IgnoreClientIp {
|
||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{
|
||||
IPv4Enable: ob.OriginalTarget.Address.Family().IsIPv4(),
|
||||
IPv6Enable: !ob.OriginalTarget.Address.Family().IsIPv4(),
|
||||
FakeEnable: false,
|
||||
})
|
||||
if len(ips) == 0 || err != nil {
|
||||
errors.LogWarning(ctx, "failed to resolve domain:", domain, ", Falling back to client-requested IP:", destination.Address.String())
|
||||
} else {
|
||||
destination.Address = net.IPAddress(ips[dice.Roll(len(ips))])
|
||||
ob.Target = destination
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ob.Target = destination
|
||||
}
|
||||
|
@ -344,6 +358,19 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
|
|||
}
|
||||
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
|
||||
ob.RouteTarget = destination
|
||||
if sniffingRequest.IgnoreClientIp {
|
||||
ips, err := d.dns.LookupIP(domain, dns.IPOption{
|
||||
IPv4Enable: ob.OriginalTarget.Address.Family().IsIPv4(),
|
||||
IPv6Enable: !ob.OriginalTarget.Address.Family().IsIPv4(),
|
||||
FakeEnable: false,
|
||||
})
|
||||
if len(ips) == 0 || err != nil {
|
||||
errors.LogWarning(ctx, "failed to resolve domain:", domain, ", Falling back to client-requested IP:", destination.Address.String())
|
||||
} else {
|
||||
destination.Address = net.IPAddress(ips[dice.Roll(len(ips))])
|
||||
ob.Target = destination
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ob.Target = destination
|
||||
}
|
||||
|
|
|
@ -192,6 +192,7 @@ type SniffingConfig struct {
|
|||
// message.
|
||||
MetadataOnly bool `protobuf:"varint,4,opt,name=metadata_only,json=metadataOnly,proto3" json:"metadata_only,omitempty"`
|
||||
RouteOnly bool `protobuf:"varint,5,opt,name=route_only,json=routeOnly,proto3" json:"route_only,omitempty"`
|
||||
IgnoreClientIp bool `protobuf:"varint,6,opt,name=ignore_client_ip,json=ignoreClientIp,proto3" json:"ignoreClientIp,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SniffingConfig) Reset() {
|
||||
|
@ -259,6 +260,13 @@ func (x *SniffingConfig) GetRouteOnly() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (x *SniffingConfig) GetIgnoreClientIp() bool {
|
||||
if x != nil {
|
||||
return x.IgnoreClientIp
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ReceiverConfig struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
|
|
|
@ -103,6 +103,7 @@ func (w *tcpWorker) callback(conn stat.Connection) {
|
|||
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
|
||||
content.SniffingRequest.IgnoreClientIp = w.sniffingConfig.IgnoreClientIp
|
||||
}
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
|
||||
|
@ -326,6 +327,7 @@ func (w *udpWorker) callback(b *buf.Buffer, source net.Destination, originalDest
|
|||
content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
|
||||
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
|
||||
content.SniffingRequest.IgnoreClientIp = w.sniffingConfig.IgnoreClientIp
|
||||
}
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
if err := w.proxy.Process(ctx, net.Network_UDP, conn, w.dispatcher); err != nil {
|
||||
|
@ -477,6 +479,7 @@ func (w *dsWorker) callback(conn stat.Connection) {
|
|||
content.SniffingRequest.ExcludeForDomain = w.sniffingConfig.DomainsExcluded
|
||||
content.SniffingRequest.MetadataOnly = w.sniffingConfig.MetadataOnly
|
||||
content.SniffingRequest.RouteOnly = w.sniffingConfig.RouteOnly
|
||||
content.SniffingRequest.IgnoreClientIp = w.sniffingConfig.IgnoreClientIp
|
||||
}
|
||||
ctx = session.ContextWithContent(ctx, content)
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ type SniffingRequest struct {
|
|||
Enabled bool
|
||||
MetadataOnly bool
|
||||
RouteOnly bool
|
||||
IgnoreClientIp bool
|
||||
}
|
||||
|
||||
// Content is the metadata of the connection content.
|
||||
|
|
|
@ -55,6 +55,7 @@ type SniffingConfig struct {
|
|||
DomainsExcluded *StringList `json:"domainsExcluded"`
|
||||
MetadataOnly bool `json:"metadataOnly"`
|
||||
RouteOnly bool `json:"routeOnly"`
|
||||
IgnoreClientIp bool `json:"ignoreClientIp"`
|
||||
}
|
||||
|
||||
// Build implements Buildable.
|
||||
|
@ -92,6 +93,7 @@ func (c *SniffingConfig) Build() (*proxyman.SniffingConfig, error) {
|
|||
DomainsExcluded: d,
|
||||
MetadataOnly: c.MetadataOnly,
|
||||
RouteOnly: c.RouteOnly,
|
||||
IgnoreClientIp: c.IgnoreClientIp,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue