This commit is contained in:
Mazeorz
2021-11-17 16:03:47 +08:00
parent 1f3968bd50
commit 900e852525
115 changed files with 9990 additions and 585 deletions

View File

@ -15,10 +15,11 @@ import (
type client struct {
*D.Client
r *Resolver
port string
host string
iface string
r *Resolver
port string
host string
iface string
proxyAdapter string
}
func (c *client) Exchange(m *D.Msg) (*D.Msg, error) {
@ -30,14 +31,15 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
ip net.IP
err error
)
if c.r == nil {
// a default ip dns
if ip = net.ParseIP(c.host); ip == nil {
if ip = net.ParseIP(c.host); ip == nil {
if c.r == nil {
return nil, fmt.Errorf("dns %s not a valid ip", c.host)
}
} else {
if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil {
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
} else {
if ip, err = resolver.ResolveIPWithResolver(c.host, c.r); err != nil {
return nil, fmt.Errorf("use default dns resolve failed: %w", err)
}
c.host = ip.String()
}
}
@ -50,7 +52,14 @@ func (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error)
if c.iface != "" {
options = append(options, dialer.WithInterface(c.iface))
}
conn, err := dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), c.port), options...)
var conn net.Conn
if c.proxyAdapter == "" {
conn, err = dialer.DialContext(ctx, network, net.JoinHostPort(ip.String(), c.port), options...)
} else {
conn, err = dialContextWithProxyAdapter(ctx, c.proxyAdapter, network, ip, c.port, options...)
}
if err != nil {
return nil, err
}

View File

@ -19,8 +19,9 @@ const (
)
type dohClient struct {
url string
transport *http.Transport
url string
proxyAdapter string
transport *http.Transport
}
func (dc *dohClient) Exchange(m *D.Msg) (msg *D.Msg, err error) {
@ -62,7 +63,7 @@ func (dc *dohClient) newRequest(m *D.Msg) (*http.Request, error) {
return req, nil
}
func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) {
func (dc *dohClient) doRequest(req *http.Request) (*D.Msg, error) {
client := &http.Client{Transport: dc.transport}
resp, err := client.Do(req)
if err != nil {
@ -74,14 +75,15 @@ func (dc *dohClient) doRequest(req *http.Request) (msg *D.Msg, err error) {
if err != nil {
return nil, err
}
msg = &D.Msg{}
msg := &D.Msg{}
err = msg.Unpack(buf)
return msg, err
}
func newDoHClient(url string, r *Resolver) *dohClient {
func newDoHClient(url string, r *Resolver, proxyAdapter string) *dohClient {
return &dohClient{
url: url,
url: url,
proxyAdapter: proxyAdapter,
transport: &http.Transport{
ForceAttemptHTTP2: true,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
@ -95,7 +97,11 @@ func newDoHClient(url string, r *Resolver) *dohClient {
return nil, err
}
return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port))
if proxyAdapter == "" {
return dialer.DialContext(ctx, "tcp", net.JoinHostPort(ip.String(), port))
} else {
return dialContextWithProxyAdapter(ctx, proxyAdapter, "tcp", ip, port)
}
},
},
}

View File

@ -4,8 +4,10 @@ import (
"net"
"strings"
"github.com/Dreamacro/clash/component/geodata/router"
"github.com/Dreamacro/clash/component/mmdb"
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
)
type fallbackIPFilter interface {
@ -18,7 +20,7 @@ type geoipFilter struct {
func (gf *geoipFilter) Match(ip net.IP) bool {
record, _ := mmdb.Instance().Country(ip)
return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate()
return !strings.EqualFold(record.Country.IsoCode, gf.code) && !ip.IsPrivate() && !ip.Equal(C.TunBroadcastAddr)
}
type ipnetFilter struct {
@ -48,3 +50,16 @@ func NewDomainFilter(domains []string) *domainFilter {
func (df *domainFilter) Match(domain string) bool {
return df.tree.Search(domain) != nil
}
type geoSiteFilter struct {
matchers []*router.DomainMatcher
}
func (gsf *geoSiteFilter) Match(domain string) bool {
for _, matcher := range gsf.matchers {
if matcher.ApplyDomain(domain) {
return true
}
}
return false
}

View File

@ -172,7 +172,7 @@ func compose(middlewares []middleware, endpoint handler) handler {
return h
}
func newHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {
func NewHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {
middlewares := []middleware{}
if resolver.hosts != nil {

16
dns/patch.go Normal file
View File

@ -0,0 +1,16 @@
package dns
import D "github.com/miekg/dns"
type LocalServer struct {
handler handler
}
// ServeMsg implement resolver.LocalServer ResolveMsg
func (s *LocalServer) ServeMsg(msg *D.Msg) (*D.Msg, error) {
return handlerWithContext(s.handler, msg)
}
func NewLocalServer(resolver *Resolver, mapper *ResolverEnhancer) *LocalServer {
return &LocalServer{handler: NewHandler(resolver, mapper)}
}

View File

@ -12,6 +12,7 @@ import (
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/common/picker"
"github.com/Dreamacro/clash/component/fakeip"
"github.com/Dreamacro/clash/component/geodata/router"
"github.com/Dreamacro/clash/component/resolver"
"github.com/Dreamacro/clash/component/trie"
C "github.com/Dreamacro/clash/constant"
@ -149,7 +150,7 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
return
}
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, err error) {
func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (*D.Msg, error) {
fast, ctx := picker.WithTimeout(ctx, resolver.DefaultDNSTimeout)
for _, client := range clients {
r := client
@ -173,8 +174,8 @@ func (r *Resolver) batchExchange(ctx context.Context, clients []dnsClient, m *D.
return nil, err
}
msg = elm.(*D.Msg)
return
msg := elm.(*D.Msg)
return msg, nil
}
func (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {
@ -215,7 +216,7 @@ func (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {
return false
}
func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {
func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (*D.Msg, error) {
if matched := r.matchPolicy(m); len(matched) != 0 {
res := <-r.asyncExchange(ctx, matched, m)
return res.Msg, res.Error
@ -230,27 +231,22 @@ func (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err er
msgCh := r.asyncExchange(ctx, r.main, m)
if r.fallback == nil { // directly return if no fallback servers are available
if r.fallback == nil || len(r.fallback) == 0 { // directly return if no fallback servers are available
res := <-msgCh
msg, err = res.Msg, res.Error
return
return res.Msg, res.Error
}
fallbackMsg := r.asyncExchange(ctx, r.fallback, m)
res := <-msgCh
if res.Error == nil {
if ips := msgToIP(res.Msg); len(ips) != 0 {
if !r.shouldIPFallback(ips[0]) {
msg = res.Msg // no need to wait for fallback result
err = res.Error
return msg, err
return res.Msg, res.Error // no need to wait for fallback result
}
}
}
res = <-fallbackMsg
msg, err = res.Msg, res.Error
return
res = <-r.asyncExchange(ctx, r.fallback, m)
return res.Msg, res.Error
}
func (r *Resolver) resolveIP(host string, dnsType uint16) (ip net.IP, err error) {
@ -302,9 +298,10 @@ func (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D
}
type NameServer struct {
Net string
Addr string
Interface string
Net string
Addr string
Interface string
ProxyAdapter string
}
type FallbackFilter struct {
@ -312,6 +309,7 @@ type FallbackFilter struct {
GeoIPCode string
IPCIDR []*net.IPNet
Domain []string
GeoSite []*router.DomainMatcher
}
type Config struct {
@ -360,10 +358,28 @@ func NewResolver(config Config) *Resolver {
}
r.fallbackIPFilters = fallbackIPFilters
fallbackDomainFilters := []fallbackDomainFilter{}
if len(config.FallbackFilter.Domain) != 0 {
fallbackDomainFilters := []fallbackDomainFilter{NewDomainFilter(config.FallbackFilter.Domain)}
r.fallbackDomainFilters = fallbackDomainFilters
fallbackDomainFilters = append(fallbackDomainFilters, NewDomainFilter(config.FallbackFilter.Domain))
}
if len(config.FallbackFilter.GeoSite) != 0 {
fallbackDomainFilters = append(fallbackDomainFilters, &geoSiteFilter{
matchers: config.FallbackFilter.GeoSite,
})
}
r.fallbackDomainFilters = fallbackDomainFilters
return r
}
func NewMainResolver(old *Resolver) *Resolver {
r := &Resolver{
ipv6: old.ipv6,
main: old.main,
lruCache: old.lruCache,
hosts: old.hosts,
policy: old.policy,
}
return r
}

View File

@ -43,14 +43,14 @@ func handlerWithContext(handler handler, msg *D.Msg) (*D.Msg, error) {
return handler(ctx, msg)
}
func (s *Server) setHandler(handler handler) {
func (s *Server) SetHandler(handler handler) {
s.handler = handler
}
func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) error {
if addr == address && resolver != nil {
handler := newHandler(resolver, mapper)
server.setHandler(handler)
handler := NewHandler(resolver, mapper)
server.SetHandler(handler)
return nil
}
@ -81,7 +81,7 @@ func ReCreateServer(addr string, resolver *Resolver, mapper *ResolverEnhancer) e
}
address = addr
handler := newHandler(resolver, mapper)
handler := NewHandler(resolver, mapper)
server = &Server{handler: handler}
server.Server = &D.Server{Addr: addr, PacketConn: p, Handler: server}

View File

@ -1,12 +1,17 @@
package dns
import (
"context"
"crypto/tls"
"fmt"
"net"
"time"
"github.com/Dreamacro/clash/common/cache"
"github.com/Dreamacro/clash/component/dialer"
C "github.com/Dreamacro/clash/constant"
"github.com/Dreamacro/clash/log"
"github.com/Dreamacro/clash/tunnel"
D "github.com/miekg/dns"
)
@ -51,7 +56,7 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
for _, s := range servers {
switch s.Net {
case "https":
ret = append(ret, newDoHClient(s.Addr, resolver))
ret = append(ret, newDoHClient(s.Addr, resolver, s.ProxyAdapter))
continue
case "dhcp":
ret = append(ret, newDHCPClient(s.Addr))
@ -70,10 +75,11 @@ func transform(servers []NameServer, resolver *Resolver) []dnsClient {
UDPSize: 4096,
Timeout: 5 * time.Second,
},
port: port,
host: host,
iface: s.Interface,
r: resolver,
port: port,
host: host,
iface: s.Interface,
r: resolver,
proxyAdapter: s.ProxyAdapter,
})
}
return ret
@ -104,3 +110,63 @@ func msgToIP(msg *D.Msg) []net.IP {
return ips
}
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, opts ...dialer.Option) (net.Conn, error) {
adapter, ok := tunnel.Proxies()[adapterName]
if !ok {
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: networkType,
AddrType: addrType,
Host: "",
DstIP: dstIP,
DstPort: port,
}
if networkType == C.UDP {
packetConn, err := adapter.ListenPacketContext(ctx, metadata, opts...)
if err != nil {
return nil, err
}
return &wrapPacketConn{
PacketConn: packetConn,
rAddr: metadata.UDPAddr(),
}, nil
}
return adapter.DialContext(ctx, metadata, opts...)
}