diff --git a/common/dialer/default.go b/common/dialer/default.go index d461883c..d46bd2e5 100644 --- a/common/dialer/default.go +++ b/common/dialer/default.go @@ -3,7 +3,6 @@ package dialer import ( "context" "net" - "net/netip" "time" "github.com/sagernet/sing-box/adapter" @@ -54,10 +53,13 @@ var warnTFOOnUnsupportedPlatform = warning.New( ) type DefaultDialer struct { - dialer tfo.Dialer - udpDialer net.Dialer + dialer4 tfo.Dialer + dialer6 tfo.Dialer + udpDialer4 net.Dialer + udpDialer6 net.Dialer udpListener net.ListenConfig - bindUDPAddr string + udpAddr4 string + udpAddr6 string } func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDialer { @@ -120,22 +122,37 @@ func NewDefault(router adapter.Router, options option.DialerOptions) *DefaultDia dialer.Control = control.Append(dialer.Control, control.DisableUDPFragment()) listener.Control = control.Append(listener.Control, control.DisableUDPFragment()) } - var bindUDPAddr string - udpDialer := dialer - var bindAddress netip.Addr - if options.BindAddress != nil { - bindAddress = options.BindAddress.Build() + var ( + dialer4 = dialer + udpDialer4 = dialer + udpAddr4 string + ) + if options.Inet4BindAddress != nil { + bindAddr := options.Inet4BindAddress.Build() + dialer4.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()} + udpDialer4.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()} + udpAddr4 = M.SocksaddrFrom(bindAddr, 0).String() } - if bindAddress.IsValid() { - dialer.LocalAddr = &net.TCPAddr{ - IP: bindAddress.AsSlice(), - } - udpDialer.LocalAddr = &net.UDPAddr{ - IP: bindAddress.AsSlice(), - } - bindUDPAddr = M.SocksaddrFrom(bindAddress, 0).String() + var ( + dialer6 = dialer + udpDialer6 = dialer + udpAddr6 string + ) + if options.Inet6BindAddress != nil { + bindAddr := options.Inet6BindAddress.Build() + dialer6.LocalAddr = &net.TCPAddr{IP: bindAddr.AsSlice()} + udpDialer6.LocalAddr = &net.UDPAddr{IP: bindAddr.AsSlice()} + udpAddr6 = M.SocksaddrFrom(bindAddr, 0).String() + } + return &DefaultDialer{ + tfo.Dialer{Dialer: dialer4, DisableTFO: !options.TCPFastOpen}, + tfo.Dialer{Dialer: dialer6, DisableTFO: !options.TCPFastOpen}, + udpDialer4, + udpDialer6, + listener, + udpAddr4, + udpAddr6, } - return &DefaultDialer{tfo.Dialer{Dialer: dialer, DisableTFO: !options.TCPFastOpen}, udpDialer, listener, bindUDPAddr} } func (d *DefaultDialer) DialContext(ctx context.Context, network string, address M.Socksaddr) (net.Conn, error) { @@ -144,11 +161,23 @@ func (d *DefaultDialer) DialContext(ctx context.Context, network string, address } switch N.NetworkName(network) { case N.NetworkUDP: - return d.udpDialer.DialContext(ctx, network, address.String()) + if !address.IsIPv6() { + return d.udpDialer4.DialContext(ctx, network, address.String()) + } else { + return d.udpDialer6.DialContext(ctx, network, address.String()) + } + } + if !address.IsIPv6() { + return DialSlowContext(&d.dialer4, ctx, network, address) + } else { + return DialSlowContext(&d.dialer6, ctx, network, address) } - return DialSlowContext(&d.dialer, ctx, network, address) } func (d *DefaultDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) { - return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.bindUDPAddr) + if !destination.IsIPv6() { + return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr4) + } else { + return d.udpListener.ListenPacket(ctx, N.NetworkUDP, d.udpAddr6) + } } diff --git a/option/outbound.go b/option/outbound.go index ca4d938c..abea81a0 100644 --- a/option/outbound.go +++ b/option/outbound.go @@ -122,7 +122,8 @@ func (h *Outbound) UnmarshalJSON(bytes []byte) error { type DialerOptions struct { Detour string `json:"detour,omitempty"` BindInterface string `json:"bind_interface,omitempty"` - BindAddress *ListenAddress `json:"bind_address,omitempty"` + Inet4BindAddress *ListenAddress `json:"inet4_bind_address,omitempty"` + Inet6BindAddress *ListenAddress `json:"inet6_bind_address,omitempty"` ProtectPath string `json:"protect_path,omitempty"` RoutingMark int `json:"routing_mark,omitempty"` ReuseAddr bool `json:"reuse_addr,omitempty"`