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

@ -5,20 +5,35 @@ import (
"syscall"
"time"
"github.com/xtls/xray-core/common"
"github.com/xtls/xray-core/common/dice"
"github.com/xtls/xray-core/common/net"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/pipe"
)
var (
effectiveSystemDialer SystemDialer = &DefaultSystemDialer{}
)
// InitSystemDialer: It's private method and you are NOT supposed to use this function.
func InitSystemDialer(dc dns.Client, om outbound.Manager) {
effectiveSystemDialer.init(dc, om)
}
type SystemDialer interface {
Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *SocketConfig) (net.Conn, error)
init(dc dns.Client, om outbound.Manager)
}
type DefaultSystemDialer struct {
controllers []controller
dns dns.Client
obm outbound.Manager
}
func resolveSrcAddr(network net.Network, src net.Address) net.Addr {
@ -43,7 +58,78 @@ func hasBindAddr(sockopt *SocketConfig) bool {
return sockopt != nil && len(sockopt.BindAddress) > 0 && sockopt.BindPort > 0
}
func (d *DefaultSystemDialer) lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) {
if d.dns == nil {
return nil, nil
}
var lookup = d.dns.LookupIP
switch {
case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()):
if lookupIPv4, ok := d.dns.(dns.IPv4Lookup); ok {
lookup = lookupIPv4.LookupIPv4
}
case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()):
if lookupIPv4, ok := d.dns.(dns.IPv4Lookup); ok {
lookup = lookupIPv4.LookupIPv4
}
case strategy == DomainStrategy_AS_IS:
return nil, nil
}
return lookup(domain)
}
func (d *DefaultSystemDialer) canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {
if sockopt == nil || dst.Address.Family().IsIP() || d.dns == nil {
return false
}
if dst.Address.Domain() == LookupDomainFromContext(ctx) {
newError("infinite loop detected").AtError().WriteToLog(session.ExportIDToError(ctx))
return false
}
return sockopt.DomainStrategy != DomainStrategy_AS_IS
}
func (d *DefaultSystemDialer) redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx))
h := d.obm.GetHandler(obt)
ctx = session.ContextWithOutbound(ctx, &session.Outbound{dst, nil})
if h != nil {
ur, uw := pipe.New(pipe.OptionsFromContext(ctx)...)
dr, dw := pipe.New(pipe.OptionsFromContext(ctx)...)
go h.Dispatch(ctx, &transport.Link{ur, dw})
nc := cnc.NewConnection(
cnc.ConnectionInputMulti(uw),
cnc.ConnectionOutputMulti(dr),
cnc.ConnectionOnClose(common.ChainedClosable{uw, dw}),
)
return nc
}
return nil
}
func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
newError("dialing to " + dest.String()).AtDebug().WriteToLog()
if d.obm != nil && sockopt != nil && len(sockopt.DialerProxy) > 0 {
nc := d.redirect(ctx, dest, sockopt.DialerProxy)
if nc != nil {
return nc, nil
}
}
if d.canLookupIP(ctx, dest, sockopt) {
ips, err := d.lookupIP(dest.Address.String(), sockopt.DomainStrategy, src)
if err == nil && len(ips) > 0 {
dest.Address = net.IPAddress(ips[dice.Roll(len(ips))])
newError("replace destination with " + dest.String()).AtInfo().WriteToLog()
} else if err != nil {
newError("failed to resolve ip").Base(err).AtWarning().WriteToLog()
}
}
if dest.Network == net.Network_UDP && !hasBindAddr(sockopt) {
srcAddr := resolveSrcAddr(net.Network_UDP, src)
if srcAddr == nil {
@ -98,6 +184,11 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
return dialer.DialContext(ctx, dest.Network.SystemString(), dest.NetAddr())
}
func (d *DefaultSystemDialer) init(dc dns.Client, om outbound.Manager) {
d.dns = dc
d.obm = om
}
type PacketConnWrapper struct {
conn net.PacketConn
dest net.Addr
@ -158,6 +249,8 @@ func WithAdapter(dialer SystemDialerAdapter) SystemDialer {
}
}
func (v *SimpleSystemDialer) init(_ dns.Client, _ outbound.Manager) {}
func (v *SimpleSystemDialer) Dial(ctx context.Context, src net.Address, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
return v.adapter.Dial(dest.Network.SystemString(), dest.NetAddr())
}