diff --git a/README.md b/README.md index 4552ab5c..7090f752 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,37 @@ Documentations are now moved to [GitHub Wiki](https://github.com/Dreamacro/clash/wiki). ## Advanced usage for this branch +### DNS configuration +Support resolve ip with a proxy tunnel. + +Support `geosite` with `fallback-filter`. +```yaml +dns: + enable: true + use-hosts: true + ipv6: false + enhanced-mode: fake-ip + fake-ip-range: 198.18.0.1/16 + listen: 127.0.0.1:6868 + default-nameserver: + - 119.29.29.29 + - 114.114.114.114 + nameserver: + - https://doh.pub/dns-query + - tls://223.5.5.5:853 + fallback: + - 'https://1.0.0.1/dns-query#Proxy' # append the proxy adapter name to the end of DNS URL with '#' prefix. + - 'tls://8.8.4.4:853#Proxy' + fallback-filter: + geoip: false + geosite: + - gfw # `geosite` filter only use fallback server to resolve ip, prevent DNS leaks to unsafe DNS providers. + domain: + - +.example.com + ipcidr: + - 0.0.0.0/32 +``` + ### TUN configuration Supports macOS, Linux and Windows. @@ -90,7 +121,7 @@ rules: - GEOSITE,geolocation-!cn,PROXY # source IPCIDR condition for all rules in gateway proxy - #- GEOSITE,apple,PROXY,192.168.1.88/32,192.168.1.99/32 + #- GEOSITE,geolocation-!cn,REJECT,192.168.1.88/32,192.168.1.99/32 - GEOIP,telegram,PROXY,no-resolve - GEOIP,private,DIRECT,no-resolve diff --git a/dns/client.go b/dns/client.go index 04ee7d11..31a09d85 100644 --- a/dns/client.go +++ b/dns/client.go @@ -49,14 +49,14 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) } var conn net.Conn - if c.proxyAdapter != "" && network == "tcp" { - conn, err = dialContextWithProxyAdapter(ctx, c.proxyAdapter, ip, c.port) - } else { + if c.proxyAdapter == "" { options := []dialer.Option{} if c.iface != "" { options = append(options, dialer.WithInterface(c.iface)) } - conn, err = dialer.DialContext(ctx, network, net.JoinHostPort(c.host, c.port), options...) + conn, err = dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), c.port), options...) + } else { + conn, err = dialContextWithProxyAdapter(ctx, c.proxyAdapter, network, ip, c.port) } if err != nil { diff --git a/dns/doh.go b/dns/doh.go index 5d36f706..36502265 100644 --- a/dns/doh.go +++ b/dns/doh.go @@ -100,7 +100,7 @@ func newDoHClient(url string, r *Resolver, proxyAdapter string) *dohClient { if proxyAdapter == "" { return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port)) } else { - return dialContextWithProxyAdapter(ctx, proxyAdapter, ip, port) + return dialContextWithProxyAdapter(ctx, proxyAdapter, "tcp", ip, port) } }, }, diff --git a/dns/resolver.go b/dns/resolver.go index 31929a54..8a10f317 100644 --- a/dns/resolver.go +++ b/dns/resolver.go @@ -379,6 +379,7 @@ func NewMainResolver(old *Resolver) *Resolver { main: old.main, lruCache: old.lruCache, hosts: old.hosts, + policy: old.policy, } return r } diff --git a/dns/util.go b/dns/util.go index a0e38933..fd32b5c6 100644 --- a/dns/util.go +++ b/dns/util.go @@ -110,25 +110,62 @@ func msgToIP(msg *D.Msg) []net.IP { return ips } -func dialContextWithProxyAdapter(ctx context.Context, adapterName string, dstIP net.IP, port string) (net.Conn, error) { +type wrapPacketConn struct { + net.PacketConn + rAddr net.Addr +} + +func (wpc *wrapPacketConn) Read(b []byte) (n int, err error) { + n, _, err = wpc.PacketConn.ReadFrom(b) + return n, err +} + +func (wpc *wrapPacketConn) Write(b []byte) (n int, err error) { + return wpc.PacketConn.WriteTo(b, wpc.rAddr) +} + +func (wpc *wrapPacketConn) RemoteAddr() net.Addr { + return wpc.rAddr +} + +func dialContextWithProxyAdapter(ctx context.Context, adapterName string, network string, dstIP net.IP, port string) (net.Conn, error) { adapter, ok := tunnel.Proxies()[adapterName] if !ok { - return nil, fmt.Errorf("proxy dapter [%s] not found", adapterName) + return nil, fmt.Errorf("proxy adapter [%s] not found", adapterName) + } + + networkType := C.TCP + if network == "udp" { + if !adapter.SupportUDP() { + return nil, fmt.Errorf("proxy adapter [%s] UDP is not supported", adapterName) + } + networkType = C.UDP } addrType := C.AtypIPv4 - if dstIP.To4() == nil { addrType = C.AtypIPv6 } metadata := &C.Metadata{ - NetWork: C.TCP, + NetWork: networkType, AddrType: addrType, Host: "", DstIP: dstIP, DstPort: port, } + if networkType == C.UDP { + packetConn, err := adapter.ListenPacketContext(ctx, metadata) + if err != nil { + return nil, err + } + + return &wrapPacketConn{ + PacketConn: packetConn, + rAddr: metadata.UDPAddr(), + }, nil + } + return adapter.DialContext(ctx, metadata) }