Merge pull request #609 from XTLS/fix/dialer

Move `DomainStrategy` & `DialerProxy` to `DialSystem`
This commit is contained in:
Arthur Morgan 2021-09-08 00:53:37 +08:00 committed by GitHub
commit 64892fb2c3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 97 additions and 117 deletions

View file

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

View file

@ -2,7 +2,6 @@ package dns
import ( import (
"context" "context"
"github.com/xtls/xray-core/transport/internet"
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
@ -192,7 +191,7 @@ func (s *ClassicNameServer) sendQuery(ctx context.Context, domain string, option
if inbound := session.InboundFromContext(ctx); inbound != nil { if inbound := session.InboundFromContext(ctx); inbound != nil {
udpCtx = session.ContextWithInbound(udpCtx, inbound) udpCtx = session.ContextWithInbound(udpCtx, inbound)
} }
udpCtx = internet.ContextWithLookupDomain(udpCtx, internet.LookupDomainFromContext(ctx))
udpCtx = session.ContextWithContent(udpCtx, &session.Content{ udpCtx = session.ContextWithContent(udpCtx, &session.Content{
Protocol: "dns", Protocol: "dns",
}) })

View file

@ -3,8 +3,15 @@ package internet
import ( import (
"context" "context"
"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"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/session" "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"
) )
// Dialer is the interface for dialing outbound connections. // Dialer is the interface for dialing outbound connections.
@ -62,11 +69,99 @@ func Dial(ctx context.Context, dest net.Destination, streamSettings *MemoryStrea
return nil, newError("unknown network ", dest.Network) return nil, newError("unknown network ", dest.Network)
} }
var (
dnsClient dns.Client
obm outbound.Manager
)
func lookupIP(domain string, strategy DomainStrategy, localAddr net.Address) ([]net.IP, error) {
if dnsClient == nil {
return nil, nil
}
var option = dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}
switch {
case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()):
option = dns.IPOption{
IPv4Enable: true,
IPv6Enable: false,
FakeEnable: false,
}
case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()):
option = dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
}
case strategy == DomainStrategy_AS_IS:
return nil, nil
}
return dnsClient.LookupIP(domain, option)
}
func canLookupIP(ctx context.Context, dst net.Destination, sockopt *SocketConfig) bool {
if dst.Address.Family().IsIP() || dnsClient == nil {
return false
}
return sockopt.DomainStrategy != DomainStrategy_AS_IS
}
func redirect(ctx context.Context, dst net.Destination, obt string) net.Conn {
newError("redirecting request " + dst.String() + " to " + obt).WriteToLog(session.ExportIDToError(ctx))
h := 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
}
// DialSystem calls system dialer to create a network connection. // DialSystem calls system dialer to create a network connection.
func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) { func DialSystem(ctx context.Context, dest net.Destination, sockopt *SocketConfig) (net.Conn, error) {
var src net.Address var src net.Address
if outbound := session.OutboundFromContext(ctx); outbound != nil { if outbound := session.OutboundFromContext(ctx); outbound != nil {
src = outbound.Gateway src = outbound.Gateway
} }
if sockopt == nil {
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
}
if canLookupIP(ctx, dest, sockopt) {
ips, err := 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 obm != nil && len(sockopt.DialerProxy) > 0 {
nc := redirect(ctx, dest, sockopt.DialerProxy)
if nc != nil {
return nc, nil
}
}
return effectiveSystemDialer.Dial(ctx, src, dest, sockopt) return effectiveSystemDialer.Dial(ctx, src, dest, sockopt)
} }
func InitSystemDialer(dc dns.Client, om outbound.Manager) {
dnsClient = dc
obm = om
}

View file

@ -5,29 +5,18 @@ import (
"syscall" "syscall"
"time" "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"
"github.com/xtls/xray-core/common/net/cnc"
"github.com/xtls/xray-core/common/session" "github.com/xtls/xray-core/common/session"
"github.com/xtls/xray-core/features/dns" "github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/outbound" "github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/transport"
"github.com/xtls/xray-core/transport/pipe"
) )
var ( var (
effectiveSystemDialer SystemDialer = &DefaultSystemDialer{} 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 { type SystemDialer interface {
Dial(ctx context.Context, source net.Address, destination net.Destination, sockopt *SocketConfig) (net.Conn, error) 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 { type DefaultSystemDialer struct {
@ -58,85 +47,8 @@ func hasBindAddr(sockopt *SocketConfig) bool {
return sockopt != nil && len(sockopt.BindAddress) > 0 && sockopt.BindPort > 0 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 option = dns.IPOption{
IPv4Enable: true,
IPv6Enable: true,
FakeEnable: false,
}
switch {
case strategy == DomainStrategy_USE_IP4 || (localAddr != nil && localAddr.Family().IsIPv4()):
option = dns.IPOption{
IPv4Enable: true,
IPv6Enable: false,
FakeEnable: false,
}
case strategy == DomainStrategy_USE_IP6 || (localAddr != nil && localAddr.Family().IsIPv6()):
option = dns.IPOption{
IPv4Enable: false,
IPv6Enable: true,
FakeEnable: false,
}
case strategy == DomainStrategy_AS_IS:
return nil, nil
}
return d.dns.LookupIP(domain, option)
}
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) { 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() 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) { if dest.Network == net.Network_UDP && !hasBindAddr(sockopt) {
srcAddr := resolveSrcAddr(net.Network_UDP, src) srcAddr := resolveSrcAddr(net.Network_UDP, src)
@ -192,11 +104,6 @@ func (d *DefaultSystemDialer) Dial(ctx context.Context, src net.Address, dest ne
return dialer.DialContext(ctx, dest.Network.SystemString(), dest.NetAddr()) 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 { type PacketConnWrapper struct {
conn net.PacketConn conn net.PacketConn
dest net.Addr dest net.Addr
@ -257,8 +164,6 @@ 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) { 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()) return v.adapter.Dial(dest.Network.SystemString(), dest.NetAddr())
} }

View file

@ -1,18 +0,0 @@
package internet
import "context"
type systemDialer int
const systemDialerKey systemDialer = 0
func ContextWithLookupDomain(ctx context.Context, domain string) context.Context {
return context.WithValue(ctx, systemDialerKey, domain)
}
func LookupDomainFromContext(ctx context.Context) string {
if domain, ok := ctx.Value(systemDialerKey).(string); ok {
return domain
}
return ""
}