fix: dns dial to wrong target
Some checks are pending
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run

This commit is contained in:
wwqgtxx 2024-06-15 00:33:03 +08:00
parent 75c16f9b87
commit 9b02d91bf1
6 changed files with 135 additions and 139 deletions

View File

@ -9,9 +9,7 @@ import (
"strings"
"github.com/metacubex/mihomo/component/ca"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/resolver"
C "github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/randv2"
@ -23,9 +21,7 @@ type client struct {
r *Resolver
port string
host string
iface string
proxyAdapter C.ProxyAdapter
proxyName string
dialer *dnsDialer
addr string
}
@ -73,14 +69,8 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
network = "tcp"
}
var options []dialer.Option
if c.iface != "" {
options = append(options, dialer.WithInterface(c.iface))
}
dialHandler := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)
addr := net.JoinHostPort(ip.String(), c.port)
conn, err := dialHandler(ctx, network, addr)
conn, err := c.dialer.DialContext(ctx, network, addr)
if err != nil {
return nil, err
}
@ -115,7 +105,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
tcpClient.Net = "tcp"
network = "tcp"
log.Debugln("[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP", c.host, c.port, m.Question[0].String())
dConn.Conn, err = dialHandler(ctx, network, addr)
dConn.Conn, err = c.dialer.DialContext(ctx, network, addr)
if err != nil {
ch <- result{msg, err}
return

View File

@ -6,7 +6,6 @@ import "github.com/metacubex/mihomo/tunnel"
const RespectRules = tunnel.DnsRespectRules
type dialHandler = tunnel.DnsDialHandler
type dnsDialer = tunnel.DNSDialer
var getDialHandler = tunnel.GetDnsDialHandler
var listenPacket = tunnel.DnsListenPacket
var newDNSDialer = tunnel.NewDNSDialer

View File

@ -62,10 +62,8 @@ type dnsOverHTTPS struct {
quicConfig *quic.Config
quicConfigGuard sync.Mutex
url *url.URL
r *Resolver
httpVersions []C.HTTPVersion
proxyAdapter C.ProxyAdapter
proxyName string
dialer *dnsDialer
addr string
}
@ -87,9 +85,7 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
doh := &dnsOverHTTPS{
url: u,
addr: u.String(),
r: r,
proxyAdapter: proxyAdapter,
proxyName: proxyName,
dialer: newDNSDialer(r, proxyAdapter, proxyName),
quicConfig: &quic.Config{
KeepAlivePeriod: QUICKeepAlivePeriod,
TokenStore: newQUICTokenStore(),
@ -388,13 +384,12 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
nextProtos = append(nextProtos, string(v))
}
tlsConfig.NextProtos = nextProtos
dialContext := getDialHandler(doh.r, doh.proxyAdapter, doh.proxyName)
if slices.Contains(doh.httpVersions, C.HTTPVersion3) {
// First, we attempt to create an HTTP3 transport. If the probe QUIC
// connection is established successfully, we'll be using HTTP3 for this
// upstream.
transportH3, err := doh.createTransportH3(ctx, tlsConfig, dialContext)
transportH3, err := doh.createTransportH3(ctx, tlsConfig)
if err == nil {
log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String())
return transportH3, nil
@ -410,7 +405,7 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
transport := &http.Transport{
TLSClientConfig: tlsConfig,
DisableCompression: true,
DialContext: dialContext,
DialContext: doh.dialer.DialContext,
IdleConnTimeout: transportDefaultIdleConnTimeout,
MaxConnsPerHost: dohMaxConnsPerHost,
MaxIdleConns: dohMaxIdleConns,
@ -490,13 +485,12 @@ func (h *http3Transport) Close() (err error) {
func (doh *dnsOverHTTPS) createTransportH3(
ctx context.Context,
tlsConfig *tls.Config,
dialContext dialHandler,
) (roundTripper http.RoundTripper, err error) {
if !doh.supportsH3() {
return nil, errors.New("HTTP3 support is not enabled")
}
addr, err := doh.probeH3(ctx, tlsConfig, dialContext)
addr, err := doh.probeH3(ctx, tlsConfig)
if err != nil {
return nil, err
}
@ -534,7 +528,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
IP: net.ParseIP(ip),
Port: portInt,
}
conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r)
conn, err := doh.dialer.ListenPacket(ctx, "udp", addr)
if err != nil {
return nil, err
}
@ -557,12 +551,11 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
func (doh *dnsOverHTTPS) probeH3(
ctx context.Context,
tlsConfig *tls.Config,
dialContext dialHandler,
) (addr string, err error) {
// We're using bootstrapped address instead of what's passed to the function
// it does not create an actual connection, but it helps us determine
// what IP is actually reachable (when there are v4/v6 addresses).
rawConn, err := dialContext(ctx, "udp", doh.url.Host)
rawConn, err := doh.dialer.DialContext(ctx, "udp", doh.url.Host)
if err != nil {
return "", fmt.Errorf("failed to dial: %w", err)
}
@ -592,7 +585,7 @@ func (doh *dnsOverHTTPS) probeH3(
chQuic := make(chan error, 1)
chTLS := make(chan error, 1)
go doh.probeQUIC(ctx, addr, probeTLSCfg, chQuic)
go doh.probeTLS(ctx, dialContext, probeTLSCfg, chTLS)
go doh.probeTLS(ctx, probeTLSCfg, chTLS)
select {
case quicErr := <-chQuic:
@ -635,10 +628,10 @@ func (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig *
// probeTLS attempts to establish a TLS connection to the specified address. We
// run probeQUIC and probeTLS in parallel and see which one is faster.
func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, dialContext dialHandler, tlsConfig *tls.Config, ch chan error) {
func (doh *dnsOverHTTPS) probeTLS(ctx context.Context, tlsConfig *tls.Config, ch chan error) {
startTime := time.Now()
conn, err := doh.tlsDial(ctx, dialContext, "tcp", tlsConfig)
conn, err := doh.tlsDial(ctx, "tcp", tlsConfig)
if err != nil {
ch <- fmt.Errorf("opening TLS connection: %w", err)
return
@ -694,10 +687,10 @@ func isHTTP3(client *http.Client) (ok bool) {
// tlsDial is basically the same as tls.DialWithDialer, but we will call our own
// dialContext function to get connection.
func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, dialContext dialHandler, network string, config *tls.Config) (*tls.Conn, error) {
func (doh *dnsOverHTTPS) tlsDial(ctx context.Context, network string, config *tls.Config) (*tls.Conn, error) {
// We're using bootstrapped address instead of what's passed
// to the function.
rawConn, err := dialContext(ctx, network, doh.url.Host)
rawConn, err := doh.dialer.DialContext(ctx, network, doh.url.Host)
if err != nil {
return nil, err
}

View File

@ -61,9 +61,7 @@ type dnsOverQUIC struct {
bytesPoolGuard sync.Mutex
addr string
proxyAdapter C.ProxyAdapter
proxyName string
r *Resolver
dialer *dnsDialer
}
// type check
@ -73,9 +71,7 @@ var _ dnsClient = (*dnsOverQUIC)(nil)
func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) {
doq := &dnsOverQUIC{
addr: addr,
proxyAdapter: proxyAdapter,
proxyName: proxyName,
r: resolver,
dialer: newDNSDialer(resolver, proxyAdapter, proxyName),
quicConfig: &quic.Config{
KeepAlivePeriod: QUICKeepAlivePeriod,
TokenStore: newQUICTokenStore(),
@ -300,7 +296,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
// we're using bootstrapped address instead of what's passed to the function
// it does not create an actual connection, but it helps us determine
// what IP is actually reachable (when there're v4/v6 addresses).
rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr)
rawConn, err := doq.dialer.DialContext(ctx, "udp", doq.addr)
if err != nil {
return nil, fmt.Errorf("failed to open a QUIC connection: %w", err)
}
@ -315,7 +311,7 @@ func (doq *dnsOverQUIC) openConnection(ctx context.Context) (conn quic.Connectio
p, err := strconv.Atoi(port)
udpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p}
udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r)
udp, err := doq.dialer.ListenPacket(ctx, "udp", addr)
if err != nil {
return nil, err
}

View File

@ -12,6 +12,7 @@ import (
"github.com/metacubex/mihomo/common/nnip"
"github.com/metacubex/mihomo/common/picker"
"github.com/metacubex/mihomo/component/dialer"
"github.com/metacubex/mihomo/component/resolver"
"github.com/metacubex/mihomo/log"
@ -115,6 +116,11 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
continue
}
var options []dialer.Option
if s.Interface != "" {
options = append(options, dialer.WithInterface(s.Interface))
}
host, port, _ := net.SplitHostPort(s.Addr)
ret = append(ret, &client{
Client: &D.Client{
@ -127,10 +133,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
},
port: port,
host: host,
iface: s.Interface,
r: resolver,
proxyAdapter: s.ProxyAdapter,
proxyName: s.ProxyName,
dialer: newDNSDialer(resolver, s.ProxyAdapter, s.ProxyName, options...),
})
}
return ret

View File

@ -18,10 +18,22 @@ import (
const DnsRespectRules = "RULES"
type DnsDialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
type DNSDialer struct {
r resolver.Resolver
proxyAdapter C.ProxyAdapter
proxyName string
opts []dialer.Option
}
func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) DnsDialHandler {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
func NewDNSDialer(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) *DNSDialer {
return &DNSDialer{r: r, proxyAdapter: proxyAdapter, proxyName: proxyName, opts: opts}
}
func (d *DNSDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
r := d.r
proxyName := d.proxyName
proxyAdapter := d.proxyAdapter
opts := d.opts
if len(proxyName) == 0 && proxyAdapter == nil {
opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...)
@ -119,9 +131,12 @@ func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyNa
}
}
}
}
func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r resolver.Resolver, opts ...dialer.Option) (net.PacketConn, error) {
func (d *DNSDialer) ListenPacket(ctx context.Context, network, addr string) (net.PacketConn, error) {
r := d.r
proxyAdapter := d.proxyAdapter
proxyName := d.proxyName
opts := d.opts
metadata := &C.Metadata{
NetWork: C.UDP,
Type: C.INNER,