Update sing-box core

This commit is contained in:
Sergei Maklagin
2026-04-06 20:54:24 +03:00
162 changed files with 4889 additions and 1067 deletions

View File

@@ -29,7 +29,10 @@ import (
"golang.org/x/net/http2/h2c"
)
var ConfigureHTTP3ListenerFunc func(ctx context.Context, logger logger.Logger, listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, options option.NaiveInboundOptions) (io.Closer, error)
var (
ConfigureHTTP3ListenerFunc func(ctx context.Context, logger logger.Logger, listener *listener.Listener, handler http.Handler, tlsConfig tls.ServerConfig, options option.NaiveInboundOptions) (io.Closer, error)
WrapError func(error) error
)
func RegisterInbound(registry *inbound.Registry) {
inbound.Register[option.NaiveInboundOptions](registry, C.TypeNaive, NewInbound)

View File

@@ -95,7 +95,7 @@ func (p *paddingConn) writeWithPadding(writer io.Writer, data []byte) (n int, er
binary.BigEndian.PutUint16(header, uint16(len(data)))
header[2] = byte(paddingSize)
common.Must1(buffer.Write(data))
buffer.Extend(paddingSize)
common.Must(buffer.WriteZeroN(paddingSize))
_, err = writer.Write(buffer.Bytes())
if err == nil {
n = len(data)
@@ -117,7 +117,7 @@ func (p *paddingConn) writeBufferWithPadding(writer io.Writer, buffer *buf.Buffe
header := buffer.ExtendHeader(3)
binary.BigEndian.PutUint16(header, uint16(bufferLen))
header[2] = byte(paddingSize)
buffer.Extend(paddingSize)
common.Must(buffer.WriteZeroN(paddingSize))
p.writePadding++
}
return common.Error(writer.Write(buffer.Bytes()))
@@ -179,18 +179,18 @@ type naiveConn struct {
func (c *naiveConn) Read(p []byte) (n int, err error) {
n, err = c.readWithPadding(c.Conn, p)
return n, baderror.WrapH2(err)
return n, wrapError(err)
}
func (c *naiveConn) Write(p []byte) (n int, err error) {
n, err = c.writeChunked(c.Conn, p)
return n, baderror.WrapH2(err)
return n, wrapError(err)
}
func (c *naiveConn) WriteBuffer(buffer *buf.Buffer) error {
defer buffer.Release()
err := c.writeBufferWithPadding(c.Conn, buffer)
return baderror.WrapH2(err)
return wrapError(err)
}
func (c *naiveConn) FrontHeadroom() int { return c.frontHeadroom() }
@@ -210,7 +210,7 @@ type naiveH2Conn struct {
func (c *naiveH2Conn) Read(p []byte) (n int, err error) {
n, err = c.readWithPadding(c.reader, p)
return n, baderror.WrapH2(err)
return n, wrapError(err)
}
func (c *naiveH2Conn) Write(p []byte) (n int, err error) {
@@ -218,7 +218,7 @@ func (c *naiveH2Conn) Write(p []byte) (n int, err error) {
if err == nil {
c.flusher.Flush()
}
return n, baderror.WrapH2(err)
return n, wrapError(err)
}
func (c *naiveH2Conn) WriteBuffer(buffer *buf.Buffer) error {
@@ -227,7 +227,15 @@ func (c *naiveH2Conn) WriteBuffer(buffer *buf.Buffer) error {
if err == nil {
c.flusher.Flush()
}
return baderror.WrapH2(err)
return wrapError(err)
}
func wrapError(err error) error {
err = baderror.WrapH2(err)
if WrapError != nil {
err = WrapError(err)
}
return err
}
func (c *naiveH2Conn) Close() error {

View File

@@ -124,4 +124,5 @@ func init() {
return quicListener, nil
}
naive.WrapError = qtls.WrapError
}

View File

@@ -83,7 +83,7 @@ func (h *Outbound) DialContext(ctx context.Context, network string, destination
default:
return nil, E.Extend(N.ErrUnknownNetwork, network)
}
if h.resolve && destination.IsFqdn() {
if h.resolve && destination.IsDomain() {
destinationAddresses, err := h.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, err
@@ -101,7 +101,7 @@ func (h *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (n
h.logger.InfoContext(ctx, "outbound UoT packet connection to ", destination)
return h.uotClient.ListenPacket(ctx, destination)
}
if h.resolve && destination.IsFqdn() {
if h.resolve && destination.IsDomain() {
destinationAddresses, err := h.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, err

View File

@@ -1,3 +1,5 @@
//go:build with_gvisor
package tailscale
import (
@@ -285,7 +287,7 @@ type DNSDialer struct {
}
func (d *DNSDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
if destination.IsFqdn() {
if destination.IsDomain() {
panic("invalid request here")
}
for _, prefix := range d.transport.routePrefixes {
@@ -297,7 +299,7 @@ func (d *DNSDialer) DialContext(ctx context.Context, network string, destination
}
func (d *DNSDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
if destination.IsFqdn() {
if destination.IsDomain() {
panic("invalid request here")
}
for _, prefix := range d.transport.routePrefixes {

View File

@@ -1,3 +1,5 @@
//go:build with_gvisor
package tailscale
import (
@@ -46,6 +48,7 @@ import (
"github.com/sagernet/tailscale/ipn"
tsDNS "github.com/sagernet/tailscale/net/dns"
"github.com/sagernet/tailscale/net/netmon"
"github.com/sagernet/tailscale/net/netns"
"github.com/sagernet/tailscale/net/tsaddr"
tsTUN "github.com/sagernet/tailscale/net/tstun"
"github.com/sagernet/tailscale/tsnet"
@@ -108,6 +111,7 @@ type Endpoint struct {
systemInterfaceName string
systemInterfaceMTU uint32
systemTun tun.Tun
systemDialer *dialer.DefaultDialer
fallbackTCPCloser func()
}
@@ -144,7 +148,7 @@ func (t *Endpoint) registerNetstackHandlers() {
ctx := log.ContextWithNewID(t.ctx)
source := M.SocksaddrFrom(src.Addr(), src.Port())
destination := M.SocksaddrFrom(dst.Addr(), dst.Port())
packetConn := bufio.NewPacketConn(conn)
packetConn := bufio.NewUnbindPacketConnWithAddr(conn, destination)
t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil)
}, true
}
@@ -186,7 +190,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
if err != nil {
return nil, E.Cause(err, "parse control URL")
}
remoteIsDomain = M.IsDomainName(controlURL.Hostname())
remoteIsDomain = M.ParseSocksaddr(controlURL.Hostname()).IsDomain()
} else {
// controlplane.tailscale.com
remoteIsDomain = true
@@ -285,9 +289,6 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
}
}), nil
})
if runtime.GOOS == "android" {
setAndroidProtectFunc(t.platformInterface)
}
}
if t.systemInterface {
mtu := t.systemInterfaceMTU
@@ -322,9 +323,30 @@ func (t *Endpoint) Start(stage adapter.StartStage) error {
_ = systemTun.Close()
return err
}
systemDialer, err := dialer.NewDefault(t.ctx, option.DialerOptions{
BindInterface: tunName,
})
if err != nil {
_ = systemTun.Close()
return err
}
t.systemTun = systemTun
t.systemDialer = systemDialer
t.server.TunDevice = wgTunDevice
}
if mark := t.network.AutoRedirectOutputMark(); mark > 0 {
controlFunc := t.network.AutoRedirectOutputMarkFunc()
if bindFunc := t.network.AutoDetectInterfaceFunc(); bindFunc != nil {
controlFunc = control.Append(controlFunc, bindFunc)
}
netns.SetControlFunc(controlFunc)
} else if runtime.GOOS == "android" && t.platformInterface != nil {
netns.SetControlFunc(func(network, address string, c syscall.RawConn) error {
return control.Raw(c, func(fd uintptr) error {
return t.platformInterface.AutoDetectInterfaceControl(int(fd))
})
})
}
err := t.server.Start()
if err != nil {
if t.systemTun != nil {
@@ -450,14 +472,17 @@ func (t *Endpoint) watchState() {
func (t *Endpoint) Close() error {
netmon.RegisterInterfaceGetter(nil)
if runtime.GOOS == "android" {
setAndroidProtectFunc(nil)
}
netns.SetControlFunc(nil)
if t.fallbackTCPCloser != nil {
t.fallbackTCPCloser()
t.fallbackTCPCloser = nil
}
return common.Close(common.PtrOrNil(t.server))
err := common.Close(common.PtrOrNil(t.server))
if t.systemTun != nil {
t.systemTun.Close()
t.systemTun = nil
}
return err
}
func (t *Endpoint) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {
@@ -467,13 +492,16 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
case N.NetworkUDP:
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
if destination.IsFqdn() {
if destination.IsDomain() {
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, err
}
return N.DialSerial(ctx, t, network, destination, destinationAddresses)
}
if t.systemDialer != nil {
return t.systemDialer.DialContext(ctx, network, destination)
}
addr4, addr6 := t.server.TailscaleIPs()
remoteAddr := tcpip.FullAddress{
NIC: 1,
@@ -520,6 +548,9 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination
}
func (t *Endpoint) listenPacketWithAddress(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {
if t.systemDialer != nil {
return t.systemDialer.ListenPacket(ctx, destination)
}
addr4, addr6 := t.server.TailscaleIPs()
bind := tcpip.FullAddress{
NIC: 1,
@@ -547,7 +578,7 @@ func (t *Endpoint) listenPacketWithAddress(ctx context.Context, destination M.So
func (t *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
t.logger.InfoContext(ctx, "outbound packet connection to ", destination)
if destination.IsFqdn() {
if destination.IsDomain() {
destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, netip.Addr{}, err
@@ -677,19 +708,29 @@ func (t *Endpoint) NewPacketConnectionEx(ctx context.Context, conn N.PacketConn,
}
func (t *Endpoint) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {
inet4Address, inet6Address := t.server.TailscaleIPs()
if metadata.Destination.Addr.Is4() && !inet4Address.IsValid() || metadata.Destination.Addr.Is6() && !inet6Address.IsValid() {
return nil, E.New("Tailscale is not ready yet")
}
ctx := log.ContextWithNewID(t.ctx)
destination, err := ping.ConnectGVisor(
ctx, t.logger,
metadata.Source.Addr, metadata.Destination.Addr,
routeContext,
t.stack,
inet4Address, inet6Address,
timeout,
)
var destination tun.DirectRouteDestination
var err error
if t.systemDialer != nil {
destination, err = ping.ConnectDestination(
ctx, t.logger,
t.systemDialer.DialerForICMPDestination(metadata.Destination.Addr).Control,
metadata.Destination.Addr, routeContext, timeout,
)
} else {
inet4Address, inet6Address := t.server.TailscaleIPs()
if metadata.Destination.Addr.Is4() && !inet4Address.IsValid() || metadata.Destination.Addr.Is6() && !inet6Address.IsValid() {
return nil, E.New("Tailscale is not ready yet")
}
destination, err = ping.ConnectGVisor(
ctx, t.logger,
metadata.Source.Addr, metadata.Destination.Addr,
routeContext,
t.stack,
inet4Address, inet6Address,
timeout,
)
}
if err != nil {
return nil, err
}

View File

@@ -1,16 +0,0 @@
package tailscale
import (
"github.com/sagernet/sing-box/adapter"
"github.com/sagernet/tailscale/net/netns"
)
func setAndroidProtectFunc(platformInterface adapter.PlatformInterface) {
if platformInterface != nil {
netns.SetAndroidProtectFunc(func(fd int) error {
return platformInterface.AutoDetectInterfaceControl(fd)
})
} else {
netns.SetAndroidProtectFunc(nil)
}
}

View File

@@ -1,8 +0,0 @@
//go:build !android
package tailscale
import "github.com/sagernet/sing-box/adapter"
func setAndroidProtectFunc(platformInterface adapter.PlatformInterface) {
}

View File

@@ -1,4 +1,4 @@
//go:build !windows
//go:build with_gvisor && !windows
package tailscale

View File

@@ -1,4 +1,4 @@
//go:build windows
//go:build with_gvisor && windows
package tailscale

View File

@@ -67,6 +67,10 @@ func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLo
if options.GSO {
return nil, E.New("GSO option in tun is deprecated in sing-box 1.11.0 and removed in sing-box 1.12.0")
}
//nolint:staticcheck
if options.InboundOptions != (option.InboundOptions{}) {
return nil, E.New("legacy inbound fields are deprecated in sing-box 1.11.0 and removed in sing-box 1.13.0, checkout migration: https://sing-box.sagernet.org/migration/#migrate-legacy-inbound-fields-to-rule-actions")
}
address := options.Address
inet4Address := common.Filter(address, func(it netip.Prefix) bool {

View File

@@ -265,7 +265,7 @@ func (h *vlessDialer) DialContext(ctx context.Context, network string, destinati
if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination)
} else if h.packetAddr {
if destination.IsFqdn() {
if destination.IsDomain() {
return nil, E.New("packetaddr: domain destination is not supported")
}
packetConn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})
@@ -310,7 +310,7 @@ func (h *vlessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
if h.xudp {
return h.client.DialEarlyXUDPPacketConn(conn, destination)
} else if h.packetAddr {
if destination.IsFqdn() {
if destination.IsDomain() {
return nil, E.New("packetaddr: domain destination is not supported")
}
conn, err := h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress})

View File

@@ -194,7 +194,7 @@ func (h *vmessDialer) ListenPacket(ctx context.Context, destination M.Socksaddr)
return nil, err
}
if h.packetAddr {
if destination.IsFqdn() {
if destination.IsDomain() {
return nil, E.New("packetaddr: domain destination is not supported")
}
return packetaddr.NewConn(h.client.DialEarlyPacketConn(conn, M.Socksaddr{Fqdn: packetaddr.SeqPacketMagicAddress}), destination), nil

View File

@@ -238,7 +238,7 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination
case N.NetworkUDP:
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
}
if destination.IsFqdn() {
if destination.IsDomain() {
destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, err
@@ -252,7 +252,7 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination
func (w *Endpoint) ListenPacketWithDestination(ctx context.Context, destination M.Socksaddr) (net.PacketConn, netip.Addr, error) {
w.logger.InfoContext(ctx, "outbound packet connection to ", destination)
if destination.IsFqdn() {
if destination.IsDomain() {
destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{})
if err != nil {
return nil, netip.Addr{}, err