feat: wireguard add remote-dns-resolve and dns settings

This commit is contained in:
wwqgtxx
2023-04-11 10:29:55 +08:00
parent ecdde647b1
commit ab3fce29ab
8 changed files with 121 additions and 56 deletions

View File

@ -8,14 +8,14 @@ import (
"net/netip"
"strings"
tlsC "github.com/Dreamacro/clash/component/tls"
"go.uber.org/atomic"
"github.com/Dreamacro/clash/component/dialer"
"github.com/Dreamacro/clash/component/resolver"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
D "github.com/miekg/dns"
"github.com/zhangyunhao116/fastrand"
"go.uber.org/atomic"
)
type client struct {
@ -24,7 +24,8 @@ type client struct {
port string
host string
iface *atomic.String
proxyAdapter string
proxyAdapter C.ProxyAdapter
proxyName string
addr string
}
@ -81,7 +82,7 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
options = append(options, dialer.WithInterface(c.iface.Load()))
}
conn, err := getDialHandler(c.r, c.proxyAdapter, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port))
conn, err := getDialHandler(c.r, c.proxyAdapter, c.proxyName, options...)(ctx, network, net.JoinHostPort(ip.String(), c.port))
if err != nil {
return nil, err
}

View File

@ -21,6 +21,7 @@ import (
"github.com/metacubex/quic-go"
"github.com/metacubex/quic-go/http3"
D "github.com/miekg/dns"
"golang.org/x/exp/slices"
"golang.org/x/net/http2"
)
@ -63,7 +64,8 @@ type dnsOverHTTPS struct {
url *url.URL
r *Resolver
httpVersions []C.HTTPVersion
proxyAdapter string
proxyAdapter C.ProxyAdapter
proxyName string
addr string
}
@ -71,7 +73,7 @@ type dnsOverHTTPS struct {
var _ dnsClient = (*dnsOverHTTPS)(nil)
// newDoH returns the DNS-over-HTTPS Upstream.
func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter string) dnsClient {
func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) dnsClient {
u, _ := url.Parse(urlString)
httpVersions := DefaultHTTPVersions
if preferH3 {
@ -87,6 +89,7 @@ func newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[strin
addr: u.String(),
r: r,
proxyAdapter: proxyAdapter,
proxyName: proxyName,
quicConfig: &quic.Config{
KeepAlivePeriod: QUICKeepAlivePeriod,
TokenStore: newQUICTokenStore(),
@ -390,14 +393,17 @@ func (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripp
nextProtos = append(nextProtos, string(v))
}
tlsConfig.NextProtos = nextProtos
dialContext := getDialHandler(doh.r, doh.proxyAdapter)
// 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)
if err == nil {
log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String())
return transportH3, nil
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)
if err == nil {
log.Debugln("[%s] using HTTP/3 for this upstream: QUIC was faster", doh.url.String())
return transportH3, nil
}
}
log.Debugln("[%s] using HTTP/2 for this upstream: %v", doh.url.String(), err)
@ -533,7 +539,7 @@ func (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.
IP: net.ParseIP(ip),
Port: portInt,
}
conn, err := listenPacket(ctx, doh.proxyAdapter, "udp", addr, doh.r)
conn, err := listenPacket(ctx, doh.proxyAdapter, doh.proxyName, "udp", addr, doh.r)
if err != nil {
return nil, err
}

View File

@ -13,9 +13,10 @@ import (
"time"
tlsC "github.com/Dreamacro/clash/component/tls"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/metacubex/quic-go"
"github.com/Dreamacro/clash/log"
D "github.com/miekg/dns"
)
@ -60,7 +61,8 @@ type dnsOverQUIC struct {
bytesPoolGuard sync.Mutex
addr string
proxyAdapter string
proxyAdapter C.ProxyAdapter
proxyName string
r *Resolver
}
@ -68,10 +70,11 @@ type dnsOverQUIC struct {
var _ dnsClient = (*dnsOverQUIC)(nil)
// newDoQ returns the DNS-over-QUIC Upstream.
func newDoQ(resolver *Resolver, addr string, adapter string) (dnsClient, error) {
func newDoQ(resolver *Resolver, addr string, proxyAdapter C.ProxyAdapter, proxyName string) (dnsClient, error) {
doq := &dnsOverQUIC{
addr: addr,
proxyAdapter: adapter,
proxyAdapter: proxyAdapter,
proxyName: proxyName,
r: resolver,
quicConfig: &quic.Config{
KeepAlivePeriod: QUICKeepAlivePeriod,
@ -310,7 +313,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)(ctx, "udp", doq.addr)
rawConn, err := getDialHandler(doq.r, doq.proxyAdapter, doq.proxyName)(ctx, "udp", doq.addr)
if err != nil {
return nil, fmt.Errorf("failed to open a QUIC connection: %w", err)
}
@ -325,7 +328,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, "udp", addr, doq.r)
udp, err := listenPacket(ctx, doq.proxyAdapter, doq.proxyName, "udp", addr, doq.r)
if err != nil {
return nil, err
}

View File

@ -100,7 +100,7 @@ func (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr,
ips, err = r.lookupIP(ctx, host, D.TypeA)
var waitIPv6 *time.Timer
if r != nil {
if r != nil && r.ipv6Timeout > 0 {
waitIPv6 = time.NewTimer(r.ipv6Timeout)
} else {
waitIPv6 = time.NewTimer(100 * time.Millisecond)
@ -421,7 +421,8 @@ type NameServer struct {
Net string
Addr string
Interface *atomic.String
ProxyAdapter string
ProxyAdapter C.ProxyAdapter
ProxyName string
Params map[string]string
PreferH3 bool
}
@ -544,3 +545,5 @@ func NewProxyServerHostResolver(old *Resolver) *Resolver {
}
return r
}
var ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go

View File

@ -74,13 +74,13 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
for _, s := range servers {
switch s.Net {
case "https":
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter))
ret = append(ret, newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName))
continue
case "dhcp":
ret = append(ret, newDHCPClient(s.Addr))
continue
case "quic":
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter); err == nil {
if doq, err := newDoQ(resolver, s.Addr, s.ProxyAdapter, s.ProxyName); err == nil {
ret = append(ret, doq)
} else {
log.Fatalln("DoQ format error: %v", err)
@ -103,6 +103,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
iface: s.Interface,
r: resolver,
proxyAdapter: s.ProxyAdapter,
proxyName: s.ProxyName,
})
}
return ret
@ -144,9 +145,9 @@ func msgToDomain(msg *D.Msg) string {
type dialHandler func(ctx context.Context, network, addr string) (net.Conn, error)
func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dialHandler {
func getDialHandler(r *Resolver, proxyAdapter C.ProxyAdapter, proxyName string, opts ...dialer.Option) dialHandler {
return func(ctx context.Context, network, addr string) (net.Conn, error) {
if len(proxyAdapter) == 0 {
if len(proxyName) == 0 && proxyAdapter == nil {
opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...)
} else {
@ -154,10 +155,14 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
if err != nil {
return nil, err
}
adapter, ok := tunnel.Proxies()[proxyAdapter]
if !ok {
opts = append(opts, dialer.WithInterface(proxyAdapter))
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok {
opts = append(opts, dialer.WithInterface(proxyName))
}
}
if strings.Contains(network, "tcp") {
// tcp can resolve host by remote
metadata := &C.Metadata{
@ -165,8 +170,8 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
Host: host,
DstPort: port,
}
if ok {
return adapter.DialContext(ctx, metadata, opts...)
if proxyAdapter != nil {
return proxyAdapter.DialContext(ctx, metadata, opts...)
}
opts = append(opts, dialer.WithResolver(r))
return dialer.DialContext(ctx, network, addr, opts...)
@ -182,15 +187,15 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
DstIP: dstIP,
DstPort: port,
}
if !ok {
if proxyAdapter == nil {
return dialer.DialContext(ctx, network, addr, opts...)
}
if !adapter.SupportUDP() {
if !proxyAdapter.SupportUDP() {
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
}
packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...)
packetConn, err := proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
if err != nil {
return nil, err
}
@ -201,14 +206,17 @@ func getDialHandler(r *Resolver, proxyAdapter string, opts ...dialer.Option) dia
}
}
func listenPacket(ctx context.Context, proxyAdapter string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
func listenPacket(ctx context.Context, proxyAdapter C.ProxyAdapter, proxyName string, network string, addr string, r *Resolver, opts ...dialer.Option) (net.PacketConn, error) {
host, port, err := net.SplitHostPort(addr)
if err != nil {
return nil, err
}
adapter, ok := tunnel.Proxies()[proxyAdapter]
if !ok && len(proxyAdapter) != 0 {
opts = append(opts, dialer.WithInterface(proxyAdapter))
if proxyAdapter == nil {
var ok bool
proxyAdapter, ok = tunnel.Proxies()[proxyName]
if !ok {
opts = append(opts, dialer.WithInterface(proxyName))
}
}
// udp must resolve host first
@ -222,15 +230,15 @@ func listenPacket(ctx context.Context, proxyAdapter string, network string, addr
DstIP: dstIP,
DstPort: port,
}
if !ok {
if proxyAdapter == nil {
return dialer.ListenPacket(ctx, dialer.ParseNetwork(network, dstIP), "", opts...)
}
if !adapter.SupportUDP() {
if !proxyAdapter.SupportUDP() {
return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", proxyAdapter)
}
return adapter.ListenPacketContext(ctx, metadata, opts...)
return proxyAdapter.ListenPacketContext(ctx, metadata, opts...)
}
func batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {