From b4c625543ae37f5954a78fb83a920a11307ef7dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= Date: Tue, 12 May 2026 20:09:53 +0800 Subject: [PATCH] Fix tailscale crash at start --- protocol/tailscale/endpoint.go | 15 +++++++++++++++ protocol/wireguard/endpoint.go | 24 +++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index 30db4b6a..7e5d3542 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -111,6 +111,7 @@ type Endpoint struct { systemInterfaceName string systemInterfaceMTU uint32 serverStarted bool + started atomic.Bool systemTun tun.Tun systemDialer *dialer.DefaultDialer fallbackTCPCloser func() @@ -422,6 +423,7 @@ func (t *Endpoint) postStart() error { } t.filter = localBackend.ExportFilter() go t.watchState() + t.started.Store(true) return nil } @@ -485,6 +487,7 @@ func (t *Endpoint) watchState() { func (t *Endpoint) Close() error { var err error + t.started.Store(false) if t.serverStarted { err = common.Close(common.PtrOrNil(t.server)) t.serverStarted = false @@ -509,6 +512,9 @@ func (t *Endpoint) DialContext(ctx context.Context, network string, destination case N.NetworkUDP: t.logger.InfoContext(ctx, "outbound packet connection to ", destination) } + if !t.started.Load() { + return nil, E.New("Tailscale is not ready yet") + } if destination.IsDomain() { destinationAddresses, err := t.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { @@ -565,6 +571,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.started.Load() { + return nil, E.New("Tailscale is not ready yet") + } if t.systemDialer != nil { return t.systemDialer.ListenPacket(ctx, destination) } @@ -632,6 +641,9 @@ func (t *Endpoint) ListenPacket(ctx context.Context, destination M.Socksaddr) (n } func (t *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) { + if !t.started.Load() { + return nil, E.New("Tailscale is not ready yet") + } tsFilter := t.filter.Load() if tsFilter != nil { var ipProto ipproto.Proto @@ -725,6 +737,9 @@ 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) { + if !t.started.Load() { + return nil, E.New("Tailscale is not ready yet") + } ctx := log.ContextWithNewID(t.ctx) var destination tun.DirectRouteDestination var err error diff --git a/protocol/wireguard/endpoint.go b/protocol/wireguard/endpoint.go index 9fdc4814..2975b05c 100644 --- a/protocol/wireguard/endpoint.go +++ b/protocol/wireguard/endpoint.go @@ -4,6 +4,7 @@ import ( "context" "net" "net/netip" + "sync/atomic" "time" "github.com/sagernet/sing-box/adapter" @@ -41,6 +42,7 @@ type Endpoint struct { logger logger.ContextLogger localAddresses []netip.Prefix endpoint *wireguard.Endpoint + started atomic.Bool } func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.WireGuardEndpointOptions) (adapter.Endpoint, error) { @@ -120,16 +122,24 @@ func (w *Endpoint) Start(stage adapter.StartStage) error { case adapter.StartStateStart: return w.endpoint.Start(false) case adapter.StartStatePostStart: - return w.endpoint.Start(true) + err := w.endpoint.Start(true) + if err != nil { + return err + } + w.started.Store(true) } return nil } func (w *Endpoint) Close() error { + w.started.Store(false) return w.endpoint.Close() } func (w *Endpoint) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) { + if !w.started.Load() { + return nil, E.New("WireGuard is not ready yet") + } var ipVersion uint8 if !destination.IsIPv6() { ipVersion = 4 @@ -210,6 +220,9 @@ func (w *Endpoint) DialContext(ctx context.Context, network string, destination case N.NetworkUDP: w.logger.InfoContext(ctx, "outbound packet connection to ", destination) } + if !w.started.Load() { + return nil, E.New("WireGuard is not ready yet") + } if destination.IsDomain() { destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { @@ -224,6 +237,9 @@ 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 !w.started.Load() { + return nil, netip.Addr{}, E.New("WireGuard is not ready yet") + } if destination.IsDomain() { destinationAddresses, err := w.dnsRouter.Lookup(ctx, destination.Fqdn, adapter.DNSQueryOptions{}) if err != nil { @@ -257,9 +273,15 @@ func (w *Endpoint) PreferredDomain(domain string) bool { } func (w *Endpoint) PreferredAddress(address netip.Addr) bool { + if !w.started.Load() { + return false + } return w.endpoint.Lookup(address) != nil } func (w *Endpoint) NewDirectRouteConnection(metadata adapter.InboundContext, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) { + if !w.started.Load() { + return nil, E.New("WireGuard is not ready yet") + } return w.endpoint.NewDirectRouteConnection(metadata, routeContext, timeout) }