mirror of
https://github.com/MetaCubeX/mihomo.git
synced 2024-11-16 11:42:43 +08:00
fix: dns dial to wrong target
Some checks are pending
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run
Some checks are pending
Trigger CMFA Update / trigger-CMFA-update (push) Waiting to run
This commit is contained in:
parent
ad5bc51c77
commit
40f40f6d24
|
@ -3,7 +3,6 @@ package outbound
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
|
@ -58,7 +57,7 @@ func (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata,
|
|||
}
|
||||
metadata.DstIP = ip
|
||||
}
|
||||
pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
||||
pc, err := dialer.NewDialer(d.Base.DialOptions(opts...)...).ListenPacket(ctx, "udp", "", metadata.AddrPort())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -5,27 +5,19 @@ import (
|
|||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"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"
|
||||
D "github.com/miekg/dns"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
*D.Client
|
||||
r *Resolver
|
||||
port string
|
||||
host string
|
||||
iface string
|
||||
proxyAdapter C.ProxyAdapter
|
||||
proxyName string
|
||||
dialer *dnsDialer
|
||||
addr string
|
||||
}
|
||||
|
||||
|
@ -49,38 +41,13 @@ func (c *client) Address() string {
|
|||
}
|
||||
|
||||
func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {
|
||||
var (
|
||||
ip netip.Addr
|
||||
err error
|
||||
)
|
||||
if c.r == nil {
|
||||
// a default ip dns
|
||||
if ip, err = netip.ParseAddr(c.host); err != nil {
|
||||
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
|
||||
}
|
||||
} else {
|
||||
ips, err := resolver.LookupIPWithResolver(ctx, c.host, c.r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
|
||||
} else if len(ips) == 0 {
|
||||
return nil, fmt.Errorf("%w: %s", resolver.ErrIPNotFound, c.host)
|
||||
}
|
||||
ip = ips[randv2.IntN(len(ips))]
|
||||
}
|
||||
|
||||
network := "udp"
|
||||
if strings.HasPrefix(c.Client.Net, "tcp") {
|
||||
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)
|
||||
addr := net.JoinHostPort(c.host, c.port)
|
||||
conn, err := c.dialer.DialContext(ctx, network, addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -115,7 +82,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
|
||||
|
|
|
@ -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
|
||||
|
|
31
dns/doh.go
31
dns/doh.go
|
@ -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
|
||||
}
|
||||
|
|
12
dns/doq.go
12
dns/doq.go
|
@ -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
|
||||
}
|
||||
|
|
11
dns/util.go
11
dns/util.go
|
@ -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
|
||||
|
|
|
@ -6,7 +6,6 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
N "github.com/metacubex/mihomo/common/net"
|
||||
|
@ -18,14 +17,23 @@ 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) {
|
||||
if len(proxyName) == 0 && proxyAdapter == nil {
|
||||
opts = append(opts, dialer.WithResolver(r))
|
||||
return dialer.DialContext(ctx, network, addr, opts...)
|
||||
} else {
|
||||
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
|
||||
var rule C.Rule
|
||||
metadata := &C.Metadata{
|
||||
NetWork: C.TCP,
|
||||
Type: C.INNER,
|
||||
|
@ -46,8 +54,7 @@ func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyNa
|
|||
}
|
||||
}
|
||||
|
||||
var rule C.Rule
|
||||
if proxyAdapter == nil {
|
||||
if proxyAdapter == nil && len(proxyName) != 0 {
|
||||
if proxyName == DnsRespectRules {
|
||||
if !metadata.Resolved() {
|
||||
// resolve here before resolveMetadata to avoid its inner resolver.ResolveIP
|
||||
|
@ -70,7 +77,7 @@ func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyNa
|
|||
}
|
||||
}
|
||||
|
||||
if strings.Contains(network, "tcp") {
|
||||
if metadata.NetWork == C.TCP {
|
||||
if proxyAdapter == nil {
|
||||
opts = append(opts, dialer.WithResolver(r))
|
||||
return dialer.DialContext(ctx, network, addr, opts...)
|
||||
|
@ -99,7 +106,7 @@ func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyNa
|
|||
return conn, nil
|
||||
} else {
|
||||
if proxyAdapter == nil {
|
||||
return dialer.DialContext(ctx, network, addr, opts...)
|
||||
return dialer.DialContext(ctx, network, metadata.AddrPort().String(), opts...)
|
||||
}
|
||||
|
||||
if !proxyAdapter.SupportUDP() {
|
||||
|
@ -117,11 +124,14 @@ func GetDnsDialHandler(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyNa
|
|||
|
||||
return N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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,
|
||||
|
@ -156,7 +166,7 @@ func DnsListenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName
|
|||
}
|
||||
|
||||
if proxyAdapter == nil {
|
||||
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", netip.AddrPortFrom(metadata.DstIP, metadata.DstPort))
|
||||
return dialer.NewDialer(opts...).ListenPacket(ctx, network, "", metadata.AddrPort())
|
||||
}
|
||||
|
||||
if !proxyAdapter.SupportUDP() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user