mirror of
https://github.com/SagerNet/sing-box.git
synced 2025-06-13 21:54:13 +08:00
Add outbound sniff option
This commit is contained in:
parent
ea18d75a28
commit
1abe1a8ca8
@ -19,6 +19,11 @@ type Outbound interface {
|
|||||||
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
NewPacketConnection(ctx context.Context, conn N.PacketConn, metadata InboundContext) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SniffOutbound interface {
|
||||||
|
Outbound
|
||||||
|
UseSniffedDestination() bool
|
||||||
|
}
|
||||||
|
|
||||||
type IPOutbound interface {
|
type IPOutbound interface {
|
||||||
Outbound
|
Outbound
|
||||||
NewIPConnection(ctx context.Context, conn tun.RouteContext, metadata InboundContext) (tun.DirectDestination, error)
|
NewIPConnection(ctx context.Context, conn tun.RouteContext, metadata InboundContext) (tun.DirectDestination, error)
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"password": "admin",
|
"password": "admin",
|
||||||
"network": "udp",
|
"network": "udp",
|
||||||
"udp_over_tcp": false | {},
|
"udp_over_tcp": false | {},
|
||||||
|
"use_sniffed_destination": false
|
||||||
|
|
||||||
... // Dial Fields
|
... // Dial Fields
|
||||||
}
|
}
|
||||||
@ -61,6 +62,10 @@ UDP over TCP protocol settings.
|
|||||||
|
|
||||||
See [UDP Over TCP](/configuration/shared/udp-over-tcp) for details.
|
See [UDP Over TCP](/configuration/shared/udp-over-tcp) for details.
|
||||||
|
|
||||||
|
#### use_sniffed_destination
|
||||||
|
|
||||||
|
When an inbound request is routed to the outbound, the detected domain name will be used to override the connection target address before establishing a connection. This option is only effective when `sniff` is set to `true` and `sniff_override_destination` is `false` in the inbound.
|
||||||
|
|
||||||
### Dial Fields
|
### Dial Fields
|
||||||
|
|
||||||
See [Dial Fields](/configuration/shared/dial) for details.
|
See [Dial Fields](/configuration/shared/dial) for details.
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
"password": "admin",
|
"password": "admin",
|
||||||
"network": "udp",
|
"network": "udp",
|
||||||
"udp_over_tcp": false | {},
|
"udp_over_tcp": false | {},
|
||||||
|
"use_sniffed_destination": false
|
||||||
|
|
||||||
... // 拨号字段
|
... // 拨号字段
|
||||||
}
|
}
|
||||||
@ -61,6 +62,10 @@ UDP over TCP 配置。
|
|||||||
|
|
||||||
参阅 [UDP Over TCP](/zh/configuration/shared/udp-over-tcp)。
|
参阅 [UDP Over TCP](/zh/configuration/shared/udp-over-tcp)。
|
||||||
|
|
||||||
|
#### use_sniffed_destination
|
||||||
|
|
||||||
|
当入站请求路由到该出站时,在建立连接前会用探测出的域名覆盖连接目标地址,仅当入站设置了`sniff`为`true`同时`sniff_override_destination`为`false`时该选项有效。
|
||||||
|
|
||||||
### 拨号字段
|
### 拨号字段
|
||||||
|
|
||||||
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
参阅 [拨号字段](/zh/configuration/shared/dial/)。
|
||||||
|
@ -144,6 +144,10 @@ func (o ServerOptions) Build() M.Socksaddr {
|
|||||||
return M.ParseSocksaddrHostPort(o.Server, o.ServerPort)
|
return M.ParseSocksaddrHostPort(o.Server, o.ServerPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SniffOptions struct {
|
||||||
|
UseSniffedDestination bool `json:"use_sniffed_destination,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type MultiplexOptions struct {
|
type MultiplexOptions struct {
|
||||||
Enabled bool `json:"enabled,omitempty"`
|
Enabled bool `json:"enabled,omitempty"`
|
||||||
Protocol string `json:"protocol,omitempty"`
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
@ -17,6 +17,7 @@ type HTTPMixedInboundOptions struct {
|
|||||||
type SocksOutboundOptions struct {
|
type SocksOutboundOptions struct {
|
||||||
DialerOptions
|
DialerOptions
|
||||||
ServerOptions
|
ServerOptions
|
||||||
|
SniffOptions
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
Username string `json:"username,omitempty"`
|
Username string `json:"username,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
|
@ -17,13 +17,18 @@ import (
|
|||||||
"github.com/sagernet/sing/protocol/socks"
|
"github.com/sagernet/sing/protocol/socks"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ adapter.Outbound = (*Socks)(nil)
|
var _ adapter.SniffOutbound = (*Socks)(nil)
|
||||||
|
|
||||||
type Socks struct {
|
type Socks struct {
|
||||||
myOutboundAdapter
|
myOutboundAdapter
|
||||||
client *socks.Client
|
client *socks.Client
|
||||||
resolve bool
|
resolve bool
|
||||||
uotClient *uot.Client
|
uotClient *uot.Client
|
||||||
|
useSniffedDestination bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Socks) UseSniffedDestination() bool {
|
||||||
|
return h.useSniffedDestination
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) {
|
func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, options option.SocksOutboundOptions) (*Socks, error) {
|
||||||
@ -45,8 +50,9 @@ func NewSocks(router adapter.Router, logger log.ContextLogger, tag string, optio
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
tag: tag,
|
tag: tag,
|
||||||
},
|
},
|
||||||
client: socks.NewClient(dialer.New(router, options.DialerOptions), options.ServerOptions.Build(), version, options.Username, options.Password),
|
client: socks.NewClient(dialer.New(router, options.DialerOptions), options.ServerOptions.Build(), version, options.Username, options.Password),
|
||||||
resolve: version == socks.Version4,
|
resolve: version == socks.Version4,
|
||||||
|
useSniffedDestination: options.UseSniffedDestination,
|
||||||
}
|
}
|
||||||
uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions)
|
uotOptions := common.PtrValueOrDefault(options.UDPOverTCPOptions)
|
||||||
if uotOptions.Enabled {
|
if uotOptions.Enabled {
|
||||||
|
@ -657,6 +657,7 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
|
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sniffedDestination M.Socksaddr
|
||||||
if metadata.InboundOptions.SniffEnabled {
|
if metadata.InboundOptions.SniffEnabled {
|
||||||
buffer := buf.NewPacket()
|
buffer := buf.NewPacket()
|
||||||
buffer.FullReset()
|
buffer.FullReset()
|
||||||
@ -664,11 +665,14 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
if sniffMetadata != nil {
|
if sniffMetadata != nil {
|
||||||
metadata.Protocol = sniffMetadata.Protocol
|
metadata.Protocol = sniffMetadata.Protocol
|
||||||
metadata.Domain = sniffMetadata.Domain
|
metadata.Domain = sniffMetadata.Domain
|
||||||
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
|
if M.IsDomainName(metadata.Domain) {
|
||||||
metadata.Destination = M.Socksaddr{
|
sniffedDestination = M.Socksaddr{
|
||||||
Fqdn: metadata.Domain,
|
Fqdn: metadata.Domain,
|
||||||
Port: metadata.Destination.Port,
|
Port: metadata.Destination.Port,
|
||||||
}
|
}
|
||||||
|
if metadata.InboundOptions.SniffOverrideDestination {
|
||||||
|
metadata.Destination = sniffedDestination
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if metadata.Domain != "" {
|
if metadata.Domain != "" {
|
||||||
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
r.logger.DebugContext(ctx, "sniffed protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
||||||
@ -706,6 +710,13 @@ func (r *Router) RouteConnection(ctx context.Context, conn net.Conn, metadata ad
|
|||||||
if !common.Contains(detour.Network(), N.NetworkTCP) {
|
if !common.Contains(detour.Network(), N.NetworkTCP) {
|
||||||
return E.New("missing supported outbound, closing connection")
|
return E.New("missing supported outbound, closing connection")
|
||||||
}
|
}
|
||||||
|
if metadata.InboundOptions.SniffEnabled && !metadata.InboundOptions.SniffOverrideDestination && sniffedDestination.IsValid() {
|
||||||
|
sniffOutbound, loaded := detour.(adapter.SniffOutbound)
|
||||||
|
if loaded && sniffOutbound.UseSniffedDestination() {
|
||||||
|
r.logger.DebugContext(ctx, "use sniffed destination, detor: ", detour.Tag(), ", domain: ", metadata.Domain)
|
||||||
|
metadata.Destination = sniffedDestination
|
||||||
|
}
|
||||||
|
}
|
||||||
if r.clashServer != nil {
|
if r.clashServer != nil {
|
||||||
trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, matchedRule)
|
trackerConn, tracker := r.clashServer.RoutedConnection(ctx, conn, metadata, matchedRule)
|
||||||
defer tracker.Leave()
|
defer tracker.Leave()
|
||||||
@ -760,6 +771,7 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||||||
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
|
r.logger.DebugContext(ctx, "found fakeip domain: ", domain)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var sniffedDestination M.Socksaddr
|
||||||
if metadata.InboundOptions.SniffEnabled {
|
if metadata.InboundOptions.SniffEnabled {
|
||||||
buffer := buf.NewPacket()
|
buffer := buf.NewPacket()
|
||||||
buffer.FullReset()
|
buffer.FullReset()
|
||||||
@ -772,11 +784,14 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||||||
if sniffMetadata != nil {
|
if sniffMetadata != nil {
|
||||||
metadata.Protocol = sniffMetadata.Protocol
|
metadata.Protocol = sniffMetadata.Protocol
|
||||||
metadata.Domain = sniffMetadata.Domain
|
metadata.Domain = sniffMetadata.Domain
|
||||||
if metadata.InboundOptions.SniffOverrideDestination && M.IsDomainName(metadata.Domain) {
|
if M.IsDomainName(metadata.Domain) {
|
||||||
metadata.Destination = M.Socksaddr{
|
sniffedDestination = M.Socksaddr{
|
||||||
Fqdn: metadata.Domain,
|
Fqdn: metadata.Domain,
|
||||||
Port: metadata.Destination.Port,
|
Port: metadata.Destination.Port,
|
||||||
}
|
}
|
||||||
|
if metadata.InboundOptions.SniffOverrideDestination {
|
||||||
|
metadata.Destination = sniffedDestination
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if metadata.Domain != "" {
|
if metadata.Domain != "" {
|
||||||
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
r.logger.DebugContext(ctx, "sniffed packet protocol: ", metadata.Protocol, ", domain: ", metadata.Domain)
|
||||||
@ -808,6 +823,13 @@ func (r *Router) RoutePacketConnection(ctx context.Context, conn N.PacketConn, m
|
|||||||
if !common.Contains(detour.Network(), N.NetworkUDP) {
|
if !common.Contains(detour.Network(), N.NetworkUDP) {
|
||||||
return E.New("missing supported outbound, closing packet connection")
|
return E.New("missing supported outbound, closing packet connection")
|
||||||
}
|
}
|
||||||
|
if metadata.InboundOptions.SniffEnabled && !metadata.InboundOptions.SniffOverrideDestination && sniffedDestination.IsValid() {
|
||||||
|
sniffOutbound, loaded := detour.(adapter.SniffOutbound)
|
||||||
|
if loaded && sniffOutbound.UseSniffedDestination() {
|
||||||
|
r.logger.DebugContext(ctx, "use sniffed destination, detor: ", detour.Tag(), ", domain: ", metadata.Domain)
|
||||||
|
metadata.Destination = sniffedDestination
|
||||||
|
}
|
||||||
|
}
|
||||||
if r.clashServer != nil {
|
if r.clashServer != nil {
|
||||||
trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
|
trackerConn, tracker := r.clashServer.RoutedPacketConnection(ctx, conn, metadata, matchedRule)
|
||||||
defer tracker.Leave()
|
defer tracker.Leave()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user