diff --git a/docs/configuration/endpoint/tailscale.md b/docs/configuration/endpoint/tailscale.md index 9ecebab9a..6cf10e2ba 100644 --- a/docs/configuration/endpoint/tailscale.md +++ b/docs/configuration/endpoint/tailscale.md @@ -2,11 +2,6 @@ icon: material/new-box --- -!!! quote "Changes in sing-box 1.14.0" - - :material-plus: [control_http_client](#control_http_client) - :material-delete-clock: [Dial Fields](#dial-fields) - !!! quote "Changes in sing-box 1.13.0" :material-plus: [relay_server_port](#relay_server_port) @@ -27,7 +22,6 @@ icon: material/new-box "state_directory": "", "auth_key": "", "control_url": "", - "control_http_client": {}, // or "" "ephemeral": false, "hostname": "", "accept_routes": false, @@ -154,18 +148,10 @@ UDP NAT expiration time. `5m` will be used by default. -#### control_http_client - -!!! question "Since sing-box 1.14.0" - -HTTP Client for connecting to the Tailscale control plane. - -See [HTTP Client Fields](/configuration/shared/http-client/) for details. - ### Dial Fields -!!! failure "Deprecated in sing-box 1.14.0" +!!! note - Dial Fields in Tailscale endpoints are deprecated in sing-box 1.14.0 and will be removed in sing-box 1.16.0, use `control_http_client` instead. + Dial Fields in Tailscale endpoints only control how it connects to the control plane and have nothing to do with actual connections. See [Dial Fields](/configuration/shared/dial/) for details. diff --git a/docs/configuration/endpoint/tailscale.zh.md b/docs/configuration/endpoint/tailscale.zh.md index 58b4708ae..f881dd67f 100644 --- a/docs/configuration/endpoint/tailscale.zh.md +++ b/docs/configuration/endpoint/tailscale.zh.md @@ -2,11 +2,6 @@ icon: material/new-box --- -!!! quote "sing-box 1.14.0 中的更改" - - :material-plus: [control_http_client](#control_http_client) - :material-delete-clock: [拨号字段](#拨号字段) - !!! quote "sing-box 1.13.0 中的更改" :material-plus: [relay_server_port](#relay_server_port) @@ -27,7 +22,6 @@ icon: material/new-box "state_directory": "", "auth_key": "", "control_url": "", - "control_http_client": {}, // 或 "" "ephemeral": false, "hostname": "", "accept_routes": false, @@ -153,18 +147,10 @@ UDP NAT 过期时间。 默认使用 `5m`。 -#### control_http_client - -!!! question "自 sing-box 1.14.0 起" - -用于连接 Tailscale 控制平面的 HTTP 客户端。 - -参阅 [HTTP 客户端字段](/zh/configuration/shared/http-client/) 了解详情。 - ### 拨号字段 -!!! failure "已在 sing-box 1.14.0 废弃" +!!! note - Tailscale 端点中的拨号字段已在 sing-box 1.14.0 废弃且将在 sing-box 1.16.0 中被移除,请使用 `control_http_client` 代替。 + Tailscale 端点中的拨号字段仅控制它如何连接到控制平面,与实际连接无关。 参阅 [拨号字段](/zh/configuration/shared/dial/) 了解详情。 diff --git a/docs/deprecated.md b/docs/deprecated.md index 8a4b05e98..8e8498ae6 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -20,13 +20,6 @@ Configure `http_clients` and `route.default_http_client` explicitly. Old behavior will be removed in sing-box 1.16.0. -#### Legacy dialer options in Tailscale endpoint - -Legacy dialer options in Tailscale endpoints are deprecated, -use `control_http_client` instead. - -Old fields will be removed in sing-box 1.16.0. - #### Inline ACME options in TLS Inline ACME options (`tls.acme`) are deprecated diff --git a/docs/deprecated.zh.md b/docs/deprecated.zh.md index 8888807d2..887139bb6 100644 --- a/docs/deprecated.zh.md +++ b/docs/deprecated.zh.md @@ -20,13 +20,6 @@ icon: material/delete-alert 旧行为将在 sing-box 1.16.0 中被移除。 -#### Tailscale 端点中的旧版拨号选项 - -Tailscale 端点中的旧版拨号选项已废弃, -请使用 `control_http_client` 代替。 - -旧字段将在 sing-box 1.16.0 中被移除。 - #### TLS 中的内联 ACME 选项 TLS 中的内联 ACME 选项(`tls.acme`)已废弃, diff --git a/experimental/deprecated/constants.go b/experimental/deprecated/constants.go index 106514caa..337e5128e 100644 --- a/experimental/deprecated/constants.go +++ b/experimental/deprecated/constants.go @@ -101,14 +101,6 @@ var OptionLegacyRuleSetDownloadDetour = Note{ EnvName: "LEGACY_RULE_SET_DOWNLOAD_DETOUR", } -var OptionLegacyTailscaleEndpointDialer = Note{ - Name: "legacy-tailscale-endpoint-dialer", - Description: "legacy dialer options in Tailscale endpoint", - DeprecatedVersion: "1.14.0", - ScheduledVersion: "1.16.0", - EnvName: "LEGACY_TAILSCALE_ENDPOINT_DIALER", -} - var OptionRuleSetIPCIDRAcceptEmpty = Note{ Name: "dns-rule-rule-set-ip-cidr-accept-empty", Description: "Legacy `rule_set_ip_cidr_accept_empty` DNS rule item", @@ -168,7 +160,6 @@ var Options = []Note{ OptionLegacyDomainStrategyOptions, OptionInlineACME, OptionLegacyRuleSetDownloadDetour, - OptionLegacyTailscaleEndpointDialer, OptionRuleSetIPCIDRAcceptEmpty, OptionLegacyDNSAddressFilter, OptionLegacyDNSRuleStrategy, diff --git a/option/tailscale.go b/option/tailscale.go index a078e9aa8..7d4451199 100644 --- a/option/tailscale.go +++ b/option/tailscale.go @@ -11,12 +11,10 @@ import ( ) type TailscaleEndpointOptions struct { - // Deprecated: use control_http_client instead DialerOptions StateDirectory string `json:"state_directory,omitempty"` AuthKey string `json:"auth_key,omitempty"` ControlURL string `json:"control_url,omitempty"` - ControlHTTPClient *HTTPClientOptions `json:"control_http_client,omitempty"` Ephemeral bool `json:"ephemeral,omitempty"` Hostname string `json:"hostname,omitempty"` AcceptRoutes bool `json:"accept_routes,omitempty"` diff --git a/protocol/tailscale/endpoint.go b/protocol/tailscale/endpoint.go index 6d985c42a..988315577 100644 --- a/protocol/tailscale/endpoint.go +++ b/protocol/tailscale/endpoint.go @@ -4,6 +4,7 @@ package tailscale import ( "context" + "crypto/tls" "fmt" "net" "net/http" @@ -27,7 +28,6 @@ import ( "github.com/sagernet/sing-box/adapter/endpoint" "github.com/sagernet/sing-box/common/dialer" C "github.com/sagernet/sing-box/constant" - "github.com/sagernet/sing-box/experimental/deprecated" "github.com/sagernet/sing-box/log" "github.com/sagernet/sing-box/option" "github.com/sagernet/sing-box/route/rule" @@ -41,6 +41,7 @@ import ( "github.com/sagernet/sing/common/logger" M "github.com/sagernet/sing/common/metadata" N "github.com/sagernet/sing/common/network" + "github.com/sagernet/sing/common/ntp" "github.com/sagernet/sing/service" "github.com/sagernet/sing/service/filemanager" tailscaleroot "github.com/sagernet/tailscale" @@ -118,45 +119,6 @@ type Endpoint struct { fallbackTCPCloser func() } -func (t *Endpoint) registerNetstackHandlers() { - netstack := t.server.ExportNetstack() - if netstack == nil { - return - } - previousTCP := netstack.GetTCPHandlerForFlow - netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) { - if previousTCP != nil { - handler, intercept = previousTCP(src, dst) - if handler != nil || !intercept { - return handler, intercept - } - } - return func(conn net.Conn) { - ctx := log.ContextWithNewID(t.ctx) - source := M.SocksaddrFrom(src.Addr(), src.Port()) - destination := M.SocksaddrFrom(dst.Addr(), dst.Port()) - t.NewConnectionEx(ctx, conn, source, destination, nil) - }, true - } - - previousUDP := netstack.GetUDPHandlerForFlow - netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) { - if previousUDP != nil { - handler, intercept = previousUDP(src, dst) - if handler != nil || !intercept { - return handler, intercept - } - } - return func(conn nettype.ConnPacketConn) { - ctx := log.ContextWithNewID(t.ctx) - source := M.SocksaddrFrom(src.Addr(), src.Port()) - destination := M.SocksaddrFrom(dst.Addr(), dst.Port()) - packetConn := bufio.NewUnbindPacketConnWithAddr(conn, destination) - t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil) - }, true - } -} - func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.TailscaleEndpointOptions) (adapter.Endpoint, error) { stateDirectory := options.StateDirectory if stateDirectory == "" { @@ -198,19 +160,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL // controlplane.tailscale.com remoteIsDomain = true } - hasLegacyDialer := !reflect.DeepEqual(options.DialerOptions, option.DialerOptions{}) - hasControlHTTPClient := options.ControlHTTPClient != nil && !options.ControlHTTPClient.IsEmpty() - if hasLegacyDialer && hasControlHTTPClient { - return nil, E.New("control_http_client is conflict with deprecated dialer options") - } - controlHTTPClientOptions := common.PtrValueOrDefault(options.ControlHTTPClient) - if hasLegacyDialer { - deprecated.Report(ctx, deprecated.OptionLegacyTailscaleEndpointDialer) - controlHTTPClientOptions.DialerOptions = options.DialerOptions - } - if remoteIsDomain { - controlHTTPClientOptions.ResolveOnDetour = true - } outboundDialer, err := dialer.NewWithOptions(dialer.Options{ Context: ctx, Options: options.DialerOptions, @@ -222,12 +171,6 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL return nil, err } dnsRouter := service.FromContext[adapter.DNSRouter](ctx) - httpClientManager := service.FromContext[adapter.HTTPClientManager](ctx) - controlTransport, err := httpClientManager.ResolveTransport(ctx, logger, controlHTTPClientOptions) - if err != nil { - return nil, E.Cause(err, "create control HTTP client") - } - controlHTTPClient := &http.Client{Transport: controlTransport} server := &tsnet.Server{ Dir: stateDirectory, Hostname: hostname, @@ -245,11 +188,22 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL LookupHook: func(ctx context.Context, host string) ([]netip.Addr, error) { return dnsRouter.Lookup(ctx, host, outboundDialer.(dialer.ResolveDialer).QueryOptions()) }, - DNS: &dnsConfigurtor{}, - HTTPClient: controlHTTPClient, + DNS: &dnsConfigurtor{}, + HTTPClient: &http.Client{ + Transport: &http.Transport{ + ForceAttemptHTTP2: true, + DialContext: func(ctx context.Context, network, address string) (net.Conn, error) { + return outboundDialer.DialContext(ctx, network, M.ParseSocksaddr(address)) + }, + TLSClientConfig: &tls.Config{ + RootCAs: adapter.RootPoolFromContext(ctx), + Time: ntp.TimeFuncFromContext(ctx), + }, + }, + }, } return &Endpoint{ - Adapter: endpoint.NewAdapterWithDialerOptions(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, controlHTTPClientOptions.DialerOptions), + Adapter: endpoint.NewAdapter(C.TypeTailscale, tag, []string{N.NetworkTCP, N.NetworkUDP, N.NetworkICMP}, nil), ctx: ctx, router: router, logger: logger, @@ -404,7 +358,41 @@ func (t *Endpoint) postStart() error { ipStack.SetTransportProtocolHandler(icmp.ProtocolNumber6, icmpForwarder.HandlePacket) t.stack = ipStack t.icmpForwarder = icmpForwarder - t.registerNetstackHandlers() + netstack := t.server.ExportNetstack() + if netstack != nil { + previousTCP := netstack.GetTCPHandlerForFlow + netstack.GetTCPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(net.Conn), intercept bool) { + if previousTCP != nil { + handler, intercept = previousTCP(src, dst) + if handler != nil || !intercept { + return handler, intercept + } + } + return func(conn net.Conn) { + ctx := log.ContextWithNewID(t.ctx) + source := M.SocksaddrFrom(src.Addr(), src.Port()) + destination := M.SocksaddrFrom(dst.Addr(), dst.Port()) + t.NewConnectionEx(ctx, conn, source, destination, nil) + }, true + } + + previousUDP := netstack.GetUDPHandlerForFlow + netstack.GetUDPHandlerForFlow = func(src, dst netip.AddrPort) (handler func(nettype.ConnPacketConn), intercept bool) { + if previousUDP != nil { + handler, intercept = previousUDP(src, dst) + if handler != nil || !intercept { + return handler, intercept + } + } + return func(conn nettype.ConnPacketConn) { + ctx := log.ContextWithNewID(t.ctx) + source := M.SocksaddrFrom(src.Addr(), src.Port()) + destination := M.SocksaddrFrom(dst.Addr(), dst.Port()) + packetConn := bufio.NewUnbindPacketConnWithAddr(conn, destination) + t.NewPacketConnectionEx(ctx, packetConn, source, destination, nil) + }, true + } + } localBackend := t.server.ExportLocalBackend() perfs := &ipn.MaskedPrefs{