Enhance DNS and Dialer (#341)

This commit is contained in:
Jim Han 2021-03-07 00:29:17 +08:00 committed by GitHub
parent ad1807dd99
commit db32ce6fd9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 682 additions and 290 deletions

View file

@ -13,6 +13,7 @@ import (
"time"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/protocol/dns"
@ -47,6 +48,56 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.
s := baseDOHNameServer(url, "DOH", clientIP)
s.dispatcher = dispatcher
tr := &http.Transport{
MaxIdleConns: 30,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 30 * time.Second,
ForceAttemptHTTP2: true,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dispatcherCtx := context.Background()
if inbound := session.InboundFromContext(ctx); inbound != nil {
dispatcherCtx = session.ContextWithInbound(dispatcherCtx, inbound)
}
if content := session.ContentFromContext(ctx); content != nil {
dispatcherCtx = session.ContextWithContent(dispatcherCtx, content)
}
dispatcherCtx = internet.ContextWithLookupDomain(dispatcherCtx, internet.LookupDomainFromContext(ctx))
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil {
return nil, err
}
dispatcherCtx = log.ContextWithAccessMessage(dispatcherCtx, &log.AccessMessage{
From: "DoH",
To: s.dohURL,
Status: log.AccessAccepted,
Reason: "",
})
link, err := s.dispatcher.Dispatch(dispatcherCtx, dest)
if err != nil {
return nil, err
}
cc := common.ChainedClosable{}
if cw, ok := link.Writer.(common.Closable); ok {
cc = append(cc, cw)
}
if cr, ok := link.Reader.(common.Closable); ok {
cc = append(cc, cr)
}
return cnc.NewConnection(
cnc.ConnectionInputMulti(link.Writer),
cnc.ConnectionOutputMulti(link.Reader),
cnc.ConnectionOnClose(cc),
), nil
},
}
s.httpClient = &http.Client{
Timeout: time.Second * 180,
Transport: tr,
}
return s, nil
}
@ -64,6 +115,12 @@ func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer {
return nil, err
}
conn, err := internet.DialSystem(ctx, dest, nil)
log.Record(&log.AccessMessage{
From: "DoH",
To: s.dohURL,
Status: log.AccessAccepted,
Detour: "local",
})
if err != nil {
return nil, err
}
@ -249,41 +306,6 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
hc := s.httpClient
// Dispatched connection will be closed (interrupted) after each request
// This makes DOH inefficient without a keep-alived connection
// See: core/app/proxyman/outbound/handler.go:113
// Using mux (https request wrapped in a stream layer) improves the situation.
// Recommend to use NewDoHLocalNameServer (DOHL:) if xray instance is running on
// a normal network eg. the server side of xray
if s.dispatcher != nil {
tr := &http.Transport{
MaxIdleConns: 30,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 30 * time.Second,
ForceAttemptHTTP2: true,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
dest, err := net.ParseDestination(network + ":" + addr)
if err != nil {
return nil, err
}
link, err := s.dispatcher.Dispatch(ctx, dest)
if err != nil {
return nil, err
}
return cnc.NewConnection(
cnc.ConnectionInputMulti(link.Writer),
cnc.ConnectionOutputMulti(link.Reader),
), nil
},
}
hc = &http.Client{
Timeout: time.Second * 180,
Transport: tr,
}
}
resp, err := hc.Do(req.WithContext(ctx))
if err != nil {
return nil, err
@ -347,6 +369,7 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option IPOpt
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
return ips, err
}
@ -377,10 +400,12 @@ func (s *DoHNameServer) QueryIP(ctx context.Context, domain string, option IPOpt
close(done)
}()
s.sendQuery(ctx, fqdn, option)
start := time.Now()
for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
return ips, err
}

View file

@ -22,6 +22,7 @@ import (
"github.com/xtls/xray-core/features"
"github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/routing"
"github.com/xtls/xray-core/transport/internet"
)
// Server is a DNS rely server.
@ -301,6 +302,7 @@ func (s *Server) queryIPTimeout(idx int, client Client, domain string, option IP
Tag: s.tag,
})
}
ctx = internet.ContextWithLookupDomain(ctx, Fqdn(domain))
ips, err := client.QueryIP(ctx, domain, option)
cancel()

View file

@ -2,12 +2,14 @@ package dns
import (
"context"
"github.com/xtls/xray-core/transport/internet"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/log"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/protocol/dns"
udp_proto "github.com/xtls/xray-core/common/protocol/udp"
@ -190,9 +192,16 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option
if inbound := session.InboundFromContext(ctx); inbound != nil {
udpCtx = session.ContextWithInbound(udpCtx, inbound)
}
udpCtx = internet.ContextWithLookupDomain(udpCtx, internet.LookupDomainFromContext(ctx))
udpCtx = session.ContextWithContent(udpCtx, &session.Content{
Protocol: "dns",
})
udpCtx = log.ContextWithAccessMessage(udpCtx, &log.AccessMessage{
From: "DNS",
To: s.address,
Status: log.AccessAccepted,
Reason: "",
})
s.udpServer.Dispatch(udpCtx, s.address, b)
}
}
@ -241,6 +250,7 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option I
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
newError(s.name, " cache HIT ", domain, " -> ", ips).Base(err).AtDebug().WriteToLog()
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSCacheHit, 0, err})
return ips, err
}
@ -271,10 +281,12 @@ func (s *ClassicNameServer) QueryIP(ctx context.Context, domain string, option I
close(done)
}()
s.sendQuery(ctx, fqdn, option)
start := time.Now()
for {
ips, err := s.findIPsForDomain(fqdn, option)
if err != errRecordNotFound {
log.Record(&log.DNSLog{s.name, domain, ips, log.DNSQueried, time.Since(start), err})
return ips, err
}