Fix tailscale dns

This commit is contained in:
世界
2026-05-21 21:38:29 +08:00
parent ee808c46be
commit 4056231e5c
4 changed files with 53 additions and 34 deletions

View File

@@ -120,13 +120,16 @@ func NewHTTPSRaw(
serverAddr M.Socksaddr, serverAddr M.Socksaddr,
tlsConfig tls.Config, tlsConfig tls.Config,
) *HTTPSTransport { ) *HTTPSTransport {
if tlsConfig != nil {
dialer = tls.NewDialer(dialer, tlsConfig)
}
return &HTTPSTransport{ return &HTTPSTransport{
TransportAdapter: adapter, TransportAdapter: adapter,
logger: logger, logger: logger,
dialer: dialer, dialer: dialer,
destination: destination, destination: destination,
headers: headers, headers: headers,
transport: NewHTTPSTransportWrapper(tls.NewDialer(dialer, tlsConfig), serverAddr), transport: NewHTTPSTransportWrapper(dialer, serverAddr, destination),
} }
} }

View File

@@ -5,11 +5,13 @@ import (
"errors" "errors"
"net" "net"
"net/http" "net/http"
"net/url"
"sync/atomic" "sync/atomic"
"github.com/sagernet/sing-box/common/tls" "github.com/sagernet/sing-box/common/tls"
E "github.com/sagernet/sing/common/exceptions" E "github.com/sagernet/sing/common/exceptions"
M "github.com/sagernet/sing/common/metadata" M "github.com/sagernet/sing/common/metadata"
N "github.com/sagernet/sing/common/network"
"golang.org/x/net/http2" "golang.org/x/net/http2"
) )
@@ -22,27 +24,36 @@ type HTTPSTransportWrapper struct {
fallback *atomic.Bool fallback *atomic.Bool
} }
func NewHTTPSTransportWrapper(dialer tls.Dialer, serverAddr M.Socksaddr) *HTTPSTransportWrapper { func NewHTTPSTransportWrapper(dialer N.Dialer, serverAddr M.Socksaddr, destination *url.URL) *HTTPSTransportWrapper {
var fallback atomic.Bool var fallback atomic.Bool
if destination.Scheme == "http" {
// plain HTTP DoH used by Tailscale
fallback.Store(true)
}
return &HTTPSTransportWrapper{ return &HTTPSTransportWrapper{
http2Transport: &http2.Transport{ http2Transport: &http2.Transport{
DialTLSContext: func(ctx context.Context, _, _ string, _ *tls.STDConfig) (net.Conn, error) { DialTLSContext: func(ctx context.Context, _, _ string, _ *tls.STDConfig) (net.Conn, error) {
tlsConn, err := dialer.DialTLSContext(ctx, serverAddr) resultConn, err := dialer.DialContext(ctx, N.NetworkTCP, serverAddr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
state := tlsConn.ConnectionState() if tlsConn, isTLSConn := resultConn.(tls.Conn); isTLSConn {
if state.NegotiatedProtocol == http2.NextProtoTLS { state := tlsConn.ConnectionState()
return tlsConn, nil if state.NegotiatedProtocol != http2.NextProtoTLS {
tlsConn.Close()
fallback.Store(true)
return nil, errFallback
}
} }
tlsConn.Close() return resultConn, nil
fallback.Store(true)
return nil, errFallback
}, },
}, },
httpTransport: &http.Transport{ httpTransport: &http.Transport{
DialContext: func(ctx context.Context, _, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, N.NetworkTCP, serverAddr)
},
DialTLSContext: func(ctx context.Context, _, _ string) (net.Conn, error) { DialTLSContext: func(ctx context.Context, _, _ string) (net.Conn, error) {
return dialer.DialTLSContext(ctx, serverAddr) return dialer.DialContext(ctx, N.NetworkTCP, serverAddr)
}, },
}, },
fallback: &fallback, fallback: &fallback,
@@ -52,16 +63,15 @@ func NewHTTPSTransportWrapper(dialer tls.Dialer, serverAddr M.Socksaddr) *HTTPST
func (h *HTTPSTransportWrapper) RoundTrip(request *http.Request) (*http.Response, error) { func (h *HTTPSTransportWrapper) RoundTrip(request *http.Request) (*http.Response, error) {
if h.fallback.Load() { if h.fallback.Load() {
return h.httpTransport.RoundTrip(request) return h.httpTransport.RoundTrip(request)
} else {
response, err := h.http2Transport.RoundTrip(request)
if err != nil {
if errors.Is(err, errFallback) {
return h.httpTransport.RoundTrip(request)
}
return nil, err
}
return response, nil
} }
response, err := h.http2Transport.RoundTrip(request)
if err != nil {
if errors.Is(err, errFallback) {
return h.httpTransport.RoundTrip(request)
}
return nil, err
}
return response, nil
} }
func (h *HTTPSTransportWrapper) CloseIdleConnections() { func (h *HTTPSTransportWrapper) CloseIdleConnections() {

View File

@@ -161,8 +161,9 @@ func (t *DNSTransport) updateDNSServers(routeConfig *router.Config, dnsConfig *n
func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dnstype.Resolver) (adapter.DNSTransport, error) { func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dnstype.Resolver) (adapter.DNSTransport, error) {
serverURL, parseURLErr := url.Parse(resolver.Addr) serverURL, parseURLErr := url.Parse(resolver.Addr)
isHTTPScheme := parseURLErr == nil && (serverURL.Scheme == "http" || serverURL.Scheme == "https")
var myDialer N.Dialer var myDialer N.Dialer
if parseURLErr == nil && serverURL.Scheme == "http" { if isHTTPScheme && serverURL.Scheme == "http" {
myDialer = t.endpoint myDialer = t.endpoint
} else { } else {
myDialer = directDialer() myDialer = directDialer()
@@ -170,36 +171,39 @@ func (t *DNSTransport) createResolver(directDialer func() N.Dialer, resolver *dn
if len(resolver.BootstrapResolution) > 0 { if len(resolver.BootstrapResolution) > 0 {
bootstrapTransport := transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, M.SocksaddrFrom(resolver.BootstrapResolution[0], 53)) bootstrapTransport := transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, M.SocksaddrFrom(resolver.BootstrapResolution[0], 53))
myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", adapter.DNSQueryOptions{Transport: bootstrapTransport}, 0) myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", adapter.DNSQueryOptions{Transport: bootstrapTransport}, 0)
}
if serverAddr := M.ParseSocksaddr(resolver.Addr); serverAddr.IsValid() {
if serverAddr.Port == 0 {
serverAddr.Port = 53
}
return transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, serverAddr), nil
} else if parseURLErr != nil {
return nil, E.Cause(parseURLErr, "parse resolver address")
} else { } else {
myDialer = dialer.NewResolveDialer(t.ctx, myDialer, false, "", t.endpoint.queryOptions, 0)
}
if isHTTPScheme {
serverAddr := M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
switch serverURL.Scheme { switch serverURL.Scheme {
case "https": case "https":
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
if serverAddr.Port == 0 { if serverAddr.Port == 0 {
serverAddr.Port = 443 serverAddr.Port = 443
} }
tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.AddrString(), option.OutboundTLSOptions{ tlsConfig := common.Must1(tls.NewClient(t.ctx, t.logger, serverAddr.AddrString(), option.OutboundTLSOptions{
ALPN: []string{http2.NextProtoTLS, "http/1.1"}, Enabled: true,
ALPN: []string{http2.NextProtoTLS, "http/1.1"},
})) }))
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, tlsConfig), nil
case "http": case "http":
serverAddr = M.ParseSocksaddrHostPortStr(serverURL.Hostname(), serverURL.Port())
if serverAddr.Port == 0 { if serverAddr.Port == 0 {
serverAddr.Port = 80 serverAddr.Port = 80
} }
return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, nil), nil return transport.NewHTTPSRaw(t.TransportAdapter, t.logger, myDialer, serverURL, http.Header{}, serverAddr, nil), nil
// case "tls":
default:
return nil, E.New("unknown resolver scheme: ", serverURL.Scheme)
} }
} }
serverAddr := M.ParseSocksaddr(resolver.Addr)
if !serverAddr.IsValid() {
if parseURLErr != nil {
return nil, E.Cause(parseURLErr, "parse resolver address")
}
return nil, E.New("invalid resolver address: ", resolver.Addr)
}
if serverAddr.Port == 0 {
serverAddr.Port = 53
}
return transport.NewUDPRaw(t.logger, t.TransportAdapter, myDialer, serverAddr), nil
} }
func buildRoutePrefixes(routeConfig *router.Config) []netip.Prefix { func buildRoutePrefixes(routeConfig *router.Config) []netip.Prefix {

View File

@@ -83,6 +83,7 @@ type Endpoint struct {
ctx context.Context ctx context.Context
router adapter.Router router adapter.Router
logger logger.ContextLogger logger logger.ContextLogger
queryOptions adapter.DNSQueryOptions
dnsRouter adapter.DNSRouter dnsRouter adapter.DNSRouter
network adapter.NetworkManager network adapter.NetworkManager
platformInterface adapter.PlatformInterface platformInterface adapter.PlatformInterface
@@ -246,6 +247,7 @@ func NewEndpoint(ctx context.Context, router adapter.Router, logger log.ContextL
ctx: ctx, ctx: ctx,
router: router, router: router,
logger: logger, logger: logger,
queryOptions: outboundDialer.(dialer.ResolveDialer).QueryOptions(),
dnsRouter: dnsRouter, dnsRouter: dnsRouter,
network: service.FromContext[adapter.NetworkManager](ctx), network: service.FromContext[adapter.NetworkManager](ctx),
platformInterface: service.FromContext[adapter.PlatformInterface](ctx), platformInterface: service.FromContext[adapter.PlatformInterface](ctx),