Browse Source

Enhance DNS and Dialer (#341)

Jim Han 4 years ago
parent
commit
db32ce6fd9

+ 60 - 35
app/dns/dohdns.go

@@ -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
 		}
 

+ 2 - 0
app/dns/server.go

@@ -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()
 

+ 12 - 0
app/dns/udpns.go

@@ -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
 		}
 

+ 20 - 10
app/log/config.pb.go

@@ -88,6 +88,7 @@ type Config struct {
 	ErrorLogPath  string       `protobuf:"bytes,3,opt,name=error_log_path,json=errorLogPath,proto3" json:"error_log_path,omitempty"`
 	AccessLogType LogType      `protobuf:"varint,4,opt,name=access_log_type,json=accessLogType,proto3,enum=xray.app.log.LogType" json:"access_log_type,omitempty"`
 	AccessLogPath string       `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"`
+	EnableDnsLog  bool         `protobuf:"varint,6,opt,name=enable_dns_log,json=enableDnsLog,proto3" json:"enable_dns_log,omitempty"`
 }
 
 func (x *Config) Reset() {
@@ -157,13 +158,20 @@ func (x *Config) GetAccessLogPath() string {
 	return ""
 }
 
+func (x *Config) GetEnableDnsLog() bool {
+	if x != nil {
+		return x.EnableDnsLog
+	}
+	return false
+}
+
 var File_app_log_config_proto protoreflect.FileDescriptor
 
 var file_app_log_config_proto_rawDesc = []byte{
 	0x0a, 0x14, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
 	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70,
 	0x2e, 0x6c, 0x6f, 0x67, 0x1a, 0x14, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6c, 0x6f, 0x67,
-	0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x95, 0x02, 0x0a, 0x06, 0x43,
+	0x2f, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbb, 0x02, 0x0a, 0x06, 0x43,
 	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6c,
 	0x6f, 0x67, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x15, 0x2e,
 	0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x2e, 0x4c, 0x6f, 0x67,
@@ -181,15 +189,17 @@ var file_app_log_config_proto_rawDesc = []byte{
 	0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x26, 0x0a, 0x0f, 0x61, 0x63,
 	0x63, 0x65, 0x73, 0x73, 0x5f, 0x6c, 0x6f, 0x67, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x05, 0x20,
 	0x01, 0x28, 0x09, 0x52, 0x0d, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4c, 0x6f, 0x67, 0x50, 0x61,
-	0x74, 0x68, 0x2a, 0x35, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x54, 0x79, 0x70, 0x65, 0x12, 0x08, 0x0a,
-	0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f,
-	0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x69, 0x6c, 0x65, 0x10, 0x02, 0x12, 0x09,
-	0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x10, 0x03, 0x42, 0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d,
-	0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x67, 0x50, 0x01, 0x5a,
-	0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73,
-	0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6c,
-	0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f,
-	0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x74, 0x68, 0x12, 0x24, 0x0a, 0x0e, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x64, 0x6e, 0x73,
+	0x5f, 0x6c, 0x6f, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62,
+	0x6c, 0x65, 0x44, 0x6e, 0x73, 0x4c, 0x6f, 0x67, 0x2a, 0x35, 0x0a, 0x07, 0x4c, 0x6f, 0x67, 0x54,
+	0x79, 0x70, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12, 0x0b, 0x0a,
+	0x07, 0x43, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x69,
+	0x6c, 0x65, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x10, 0x03, 0x42,
+	0x46, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x61, 0x70, 0x70, 0x2e,
+	0x6c, 0x6f, 0x67, 0x50, 0x01, 0x5a, 0x21, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,
+	0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65,
+	0x2f, 0x61, 0x70, 0x70, 0x2f, 0x6c, 0x6f, 0x67, 0xaa, 0x02, 0x0c, 0x58, 0x72, 0x61, 0x79, 0x2e,
+	0x41, 0x70, 0x70, 0x2e, 0x4c, 0x6f, 0x67, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (

+ 1 - 0
app/log/config.proto

@@ -22,4 +22,5 @@ message Config {
 
   LogType access_log_type = 4;
   string access_log_path = 5;
+  bool enable_dns_log = 6;
 }

+ 6 - 0
app/log/log.go

@@ -17,6 +17,7 @@ type Instance struct {
 	accessLogger log.Handler
 	errorLogger  log.Handler
 	active       bool
+	dns          bool
 }
 
 // New creates a new log.Instance based on the given config.
@@ -24,6 +25,7 @@ func New(ctx context.Context, config *Config) (*Instance, error) {
 	g := &Instance{
 		config: config,
 		active: false,
+		dns:    config.EnableDnsLog,
 	}
 	log.RegisterHandler(g)
 
@@ -103,6 +105,10 @@ func (g *Instance) Handle(msg log.Message) {
 		if g.accessLogger != nil {
 			g.accessLogger.Handle(msg)
 		}
+	case *log.DNSLog:
+		if g.dns && g.accessLogger != nil {
+			g.accessLogger.Handle(msg)
+		}
 	case *log.GeneralMessage:
 		if g.errorLogger != nil && msg.Severity <= g.config.ErrorLogLevel {
 			g.errorLogger.Handle(msg)

+ 59 - 0
common/log/dns.go

@@ -0,0 +1,59 @@
+package log
+
+import (
+	"net"
+	"strings"
+	"time"
+)
+
+type DNSLog struct {
+	Server  string
+	Domain  string
+	Result  []net.IP
+	Status  dnsStatus
+	Elapsed time.Duration
+	Error   error
+}
+
+func (l *DNSLog) String() string {
+	builder := &strings.Builder{}
+
+	// Server got answer: domain -> [ip1, ip2] 23ms
+	builder.WriteString(l.Server)
+	builder.WriteString(" ")
+	builder.WriteString(string(l.Status))
+	builder.WriteString(" ")
+	builder.WriteString(l.Domain)
+	builder.WriteString(" -> [")
+	builder.WriteString(joinNetIP(l.Result))
+	builder.WriteString("]")
+
+	if l.Elapsed > 0 {
+		builder.WriteString(" ")
+		builder.WriteString(l.Elapsed.String())
+	}
+	if l.Error != nil {
+		builder.WriteString(" <")
+		builder.WriteString(l.Error.Error())
+		builder.WriteString(">")
+	}
+	return builder.String()
+}
+
+type dnsStatus string
+
+var (
+	DNSQueried  = dnsStatus("got answer:")
+	DNSCacheHit = dnsStatus("cache HIT:")
+)
+
+func joinNetIP(ips []net.IP) string {
+	if len(ips) == 0 {
+		return ""
+	}
+	sips := make([]string, 0, len(ips))
+	for _, ip := range ips {
+		sips = append(sips, ip.String())
+	}
+	return strings.Join(sips, ", ")
+}

+ 4 - 4
core/config.pb.go

@@ -9,7 +9,7 @@ package core
 import (
 	proto "github.com/golang/protobuf/proto"
 	serial "github.com/xtls/xray-core/common/serial"
-	transport "github.com/xtls/xray-core/transport"
+	"github.com/xtls/xray-core/transport/global"
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
@@ -48,7 +48,7 @@ type Config struct {
 	// config. Date to remove: 2020-01-13
 	//
 	// Deprecated: Do not use.
-	Transport *transport.Config `protobuf:"bytes,5,opt,name=transport,proto3" json:"transport,omitempty"`
+	Transport *global.Config `protobuf:"bytes,5,opt,name=transport,proto3" json:"transport,omitempty"`
 	// Configuration for extensions. The config may not work if corresponding
 	// extension is not loaded into Xray. Xray will ignore such config during
 	// initialization.
@@ -109,7 +109,7 @@ func (x *Config) GetApp() []*serial.TypedMessage {
 }
 
 // Deprecated: Do not use.
-func (x *Config) GetTransport() *transport.Config {
+func (x *Config) GetTransport() *global.Config {
 	if x != nil {
 		return x.Transport
 	}
@@ -356,7 +356,7 @@ var file_core_config_proto_goTypes = []interface{}{
 	(*InboundHandlerConfig)(nil),  // 1: xray.core.InboundHandlerConfig
 	(*OutboundHandlerConfig)(nil), // 2: xray.core.OutboundHandlerConfig
 	(*serial.TypedMessage)(nil),   // 3: xray.common.serial.TypedMessage
-	(*transport.Config)(nil),      // 4: xray.transport.Config
+	(*global.Config)(nil),         // 4: xray.transport.Config
 }
 var file_core_config_proto_depIdxs = []int32{
 	1, // 0: xray.core.Config.inbound:type_name -> xray.core.InboundHandlerConfig

+ 9 - 0
core/xray.go

@@ -16,6 +16,7 @@ import (
 	"github.com/xtls/xray-core/features/policy"
 	"github.com/xtls/xray-core/features/routing"
 	"github.com/xtls/xray-core/features/stats"
+	"github.com/xtls/xray-core/transport/internet"
 )
 
 // Server is an instance of Xray. At any time, there must be at most one Server instance running.
@@ -223,6 +224,14 @@ func initInstanceWithConfig(config *Config, server *Instance) (bool, error) {
 		}
 	}
 
+	internet.InitSystemDialer(
+		server.GetFeature(dns.ClientType()).(dns.Client),
+		func() outbound.Manager {
+			obm, _ := server.GetFeature(outbound.ManagerType()).(outbound.Manager)
+			return obm
+		}(),
+	)
+
 	if server.featureResolutions != nil {
 		return true, newError("not all dependency are resolved.")
 	}

+ 2 - 0
infra/conf/log.go

@@ -19,6 +19,7 @@ type LogConfig struct {
 	AccessLog string `json:"access"`
 	ErrorLog  string `json:"error"`
 	LogLevel  string `json:"loglevel"`
+	DNSLog    bool   `json:"dnsLog"`
 }
 
 func (v *LogConfig) Build() *log.Config {
@@ -28,6 +29,7 @@ func (v *LogConfig) Build() *log.Config {
 	config := &log.Config{
 		ErrorLogType:  log.LogType_Console,
 		AccessLogType: log.LogType_Console,
+		EnableDnsLog:  v.DNSLog,
 	}
 
 	if v.AccessLog == "none" {

+ 3 - 3
infra/conf/transport.go

@@ -2,7 +2,7 @@ package conf
 
 import (
 	"github.com/xtls/xray-core/common/serial"
-	"github.com/xtls/xray-core/transport"
+	"github.com/xtls/xray-core/transport/global"
 	"github.com/xtls/xray-core/transport/internet"
 )
 
@@ -16,8 +16,8 @@ type TransportConfig struct {
 }
 
 // Build implements Buildable.
-func (c *TransportConfig) Build() (*transport.Config, error) {
-	config := new(transport.Config)
+func (c *TransportConfig) Build() (*global.Config, error) {
+	config := new(global.Config)
 
 	if c.TCPConfig != nil {
 		ts, err := c.TCPConfig.Build()

+ 19 - 1
infra/conf/transport_internet.go

@@ -455,6 +455,8 @@ type SocketConfig struct {
 	TFO                 interface{} `json:"tcpFastOpen"`
 	TProxy              string      `json:"tproxy"`
 	AcceptProxyProtocol bool        `json:"acceptProxyProtocol"`
+	DomainStrategy      string      `json:"domainStrategy"`
+	DialerProxy         string      `json:"dialerProxy"`
 }
 
 // Build implements Buildable.
@@ -487,11 +489,23 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
 		tproxy = internet.SocketConfig_Off
 	}
 
+	var dStrategy = internet.DomainStrategy_AS_IS
+	switch strings.ToLower(c.DomainStrategy) {
+	case "useip", "use_ip":
+		dStrategy = internet.DomainStrategy_USE_IP
+	case "useip4", "useipv4", "use_ipv4", "use_ip_v4", "use_ip4":
+		dStrategy = internet.DomainStrategy_USE_IP4
+	case "useip6", "useipv6", "use_ipv6", "use_ip_v6", "use_ip6":
+		dStrategy = internet.DomainStrategy_USE_IP6
+	}
+
 	return &internet.SocketConfig{
 		Mark:                c.Mark,
 		Tfo:                 tfo,
 		Tproxy:              tproxy,
+		DomainStrategy:      dStrategy,
 		AcceptProxyProtocol: c.AcceptProxyProtocol,
+		DialerProxy:         c.DialerProxy,
 	}, nil
 }
 
@@ -628,6 +642,9 @@ func (c *StreamConfig) Build() (*internet.StreamConfig, error) {
 
 type ProxyConfig struct {
 	Tag string `json:"tag"`
+
+	// TransportLayerProxy: For compatibility.
+	TransportLayerProxy bool `json:"transportLayer"`
 }
 
 // Build implements Buildable.
@@ -636,6 +653,7 @@ func (v *ProxyConfig) Build() (*internet.ProxyConfig, error) {
 		return nil, newError("Proxy tag is not set.")
 	}
 	return &internet.ProxyConfig{
-		Tag: v.Tag,
+		Tag:                 v.Tag,
+		TransportLayerProxy: v.TransportLayerProxy,
 	}, nil
 }

+ 9 - 5
infra/conf/transport_test.go

@@ -8,7 +8,7 @@ import (
 	"github.com/xtls/xray-core/common/protocol"
 	"github.com/xtls/xray-core/common/serial"
 	. "github.com/xtls/xray-core/infra/conf"
-	"github.com/xtls/xray-core/transport"
+	"github.com/xtls/xray-core/transport/global"
 	"github.com/xtls/xray-core/transport/internet"
 	"github.com/xtls/xray-core/transport/internet/headers/http"
 	"github.com/xtls/xray-core/transport/internet/headers/noop"
@@ -34,12 +34,16 @@ func TestSocketConfig(t *testing.T) {
 		{
 			Input: `{
 				"mark": 1,
-				"tcpFastOpen": true
+				"tcpFastOpen": true,
+				"domainStrategy": "UseIP",
+				"dialerProxy": "tag"
 			}`,
 			Parser: createParser(),
 			Output: &internet.SocketConfig{
-				Mark: 1,
-				Tfo:  256,
+				Mark:           1,
+				Tfo:            256,
+				DomainStrategy: internet.DomainStrategy_USE_IP,
+				DialerProxy:    "tag",
 			},
 		},
 	})
@@ -119,7 +123,7 @@ func TestTransportConfig(t *testing.T) {
 				}
 			}`,
 			Parser: createParser(),
-			Output: &transport.Config{
+			Output: &global.Config{
 				TransportSettings: []*internet.TransportConfig{
 					{
 						ProtocolName: "tcp",

+ 26 - 0
infra/conf/xray.go

@@ -2,6 +2,7 @@ package conf
 
 import (
 	"encoding/json"
+	"github.com/xtls/xray-core/transport/internet"
 	"log"
 	"os"
 	"strings"
@@ -267,9 +268,22 @@ type OutboundDetourConfig struct {
 	MuxSettings   *MuxConfig       `json:"mux"`
 }
 
+func (c *OutboundDetourConfig) checkChainProxyConfig() error {
+	if c.StreamSetting == nil || c.ProxySettings == nil || c.StreamSetting.SocketSettings == nil {
+		return nil
+	}
+	if len(c.ProxySettings.Tag) > 0 && len(c.StreamSetting.SocketSettings.DialerProxy) > 0 {
+		return newError("proxySettings.tag is conflicted with sockopt.dialerProxy").AtWarning()
+	}
+	return nil
+}
+
 // Build implements Buildable.
 func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
 	senderSettings := &proxyman.SenderConfig{}
+	if err := c.checkChainProxyConfig(); err != nil {
+		return nil, err
+	}
 
 	if c.SendThrough != nil {
 		address := c.SendThrough
@@ -295,6 +309,18 @@ func (c *OutboundDetourConfig) Build() (*core.OutboundHandlerConfig, error) {
 		if err != nil {
 			return nil, newError("invalid outbound detour proxy settings.").Base(err)
 		}
+		if ps.TransportLayerProxy {
+			if senderSettings.StreamSettings != nil {
+				if senderSettings.StreamSettings.SocketSettings != nil {
+					senderSettings.StreamSettings.SocketSettings.DialerProxy = ps.Tag
+				} else {
+					senderSettings.StreamSettings.SocketSettings = &internet.SocketConfig{DialerProxy: ps.Tag}
+				}
+			} else {
+				senderSettings.StreamSettings = &internet.StreamConfig{SocketSettings: &internet.SocketConfig{DialerProxy: ps.Tag}}
+			}
+			ps = nil
+		}
 		senderSettings.ProxySettings = ps
 	}
 

+ 0 - 165
transport/config.pb.go

@@ -1,165 +0,0 @@
-// Code generated by protoc-gen-go. DO NOT EDIT.
-// versions:
-// 	protoc-gen-go v1.25.0
-// 	protoc        v3.14.0
-// source: transport/config.proto
-
-package transport
-
-import (
-	proto "github.com/golang/protobuf/proto"
-	internet "github.com/xtls/xray-core/transport/internet"
-	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
-	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
-	reflect "reflect"
-	sync "sync"
-)
-
-const (
-	// Verify that this generated code is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
-	// Verify that runtime/protoimpl is sufficiently up-to-date.
-	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
-)
-
-// This is a compile-time assertion that a sufficiently up-to-date version
-// of the legacy proto package is being used.
-const _ = proto.ProtoPackageIsVersion4
-
-// Global transport settings. This affects all type of connections that go
-// through Xray. Deprecated. Use each settings in StreamConfig.
-//
-// Deprecated: Do not use.
-type Config struct {
-	state         protoimpl.MessageState
-	sizeCache     protoimpl.SizeCache
-	unknownFields protoimpl.UnknownFields
-
-	TransportSettings []*internet.TransportConfig `protobuf:"bytes,1,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"`
-}
-
-func (x *Config) Reset() {
-	*x = Config{}
-	if protoimpl.UnsafeEnabled {
-		mi := &file_transport_config_proto_msgTypes[0]
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		ms.StoreMessageInfo(mi)
-	}
-}
-
-func (x *Config) String() string {
-	return protoimpl.X.MessageStringOf(x)
-}
-
-func (*Config) ProtoMessage() {}
-
-func (x *Config) ProtoReflect() protoreflect.Message {
-	mi := &file_transport_config_proto_msgTypes[0]
-	if protoimpl.UnsafeEnabled && x != nil {
-		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
-		if ms.LoadMessageInfo() == nil {
-			ms.StoreMessageInfo(mi)
-		}
-		return ms
-	}
-	return mi.MessageOf(x)
-}
-
-// Deprecated: Use Config.ProtoReflect.Descriptor instead.
-func (*Config) Descriptor() ([]byte, []int) {
-	return file_transport_config_proto_rawDescGZIP(), []int{0}
-}
-
-func (x *Config) GetTransportSettings() []*internet.TransportConfig {
-	if x != nil {
-		return x.TransportSettings
-	}
-	return nil
-}
-
-var File_transport_config_proto protoreflect.FileDescriptor
-
-var file_transport_config_proto_rawDesc = []byte{
-	0x0a, 0x16, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
-	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x1a, 0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
-	0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x65, 0x0a, 0x06, 0x43, 0x6f, 0x6e,
-	0x66, 0x69, 0x67, 0x12, 0x57, 0x0a, 0x12, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
-	0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
-	0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
-	0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
-	0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73,
-	0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x3a, 0x02, 0x18, 0x01,
-	0x42, 0x4c, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61,
-	0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x01, 0x5a, 0x23, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
-	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
-	0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0xaa, 0x02, 0x0e,
-	0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
-}
-
-var (
-	file_transport_config_proto_rawDescOnce sync.Once
-	file_transport_config_proto_rawDescData = file_transport_config_proto_rawDesc
-)
-
-func file_transport_config_proto_rawDescGZIP() []byte {
-	file_transport_config_proto_rawDescOnce.Do(func() {
-		file_transport_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_config_proto_rawDescData)
-	})
-	return file_transport_config_proto_rawDescData
-}
-
-var file_transport_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
-var file_transport_config_proto_goTypes = []interface{}{
-	(*Config)(nil),                   // 0: xray.transport.Config
-	(*internet.TransportConfig)(nil), // 1: xray.transport.internet.TransportConfig
-}
-var file_transport_config_proto_depIdxs = []int32{
-	1, // 0: xray.transport.Config.transport_settings:type_name -> xray.transport.internet.TransportConfig
-	1, // [1:1] is the sub-list for method output_type
-	1, // [1:1] is the sub-list for method input_type
-	1, // [1:1] is the sub-list for extension type_name
-	1, // [1:1] is the sub-list for extension extendee
-	0, // [0:1] is the sub-list for field type_name
-}
-
-func init() { file_transport_config_proto_init() }
-func file_transport_config_proto_init() {
-	if File_transport_config_proto != nil {
-		return
-	}
-	if !protoimpl.UnsafeEnabled {
-		file_transport_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*Config); i {
-			case 0:
-				return &v.state
-			case 1:
-				return &v.sizeCache
-			case 2:
-				return &v.unknownFields
-			default:
-				return nil
-			}
-		}
-	}
-	type x struct{}
-	out := protoimpl.TypeBuilder{
-		File: protoimpl.DescBuilder{
-			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
-			RawDescriptor: file_transport_config_proto_rawDesc,
-			NumEnums:      0,
-			NumMessages:   1,
-			NumExtensions: 0,
-			NumServices:   0,
-		},
-		GoTypes:           file_transport_config_proto_goTypes,
-		DependencyIndexes: file_transport_config_proto_depIdxs,
-		MessageInfos:      file_transport_config_proto_msgTypes,
-	}.Build()
-	File_transport_config_proto = out.File
-	file_transport_config_proto_rawDesc = nil
-	file_transport_config_proto_goTypes = nil
-	file_transport_config_proto_depIdxs = nil
-}

+ 1 - 1
transport/config.go → transport/global/config.go

@@ -1,4 +1,4 @@
-package transport
+package global
 
 import (
 	"github.com/xtls/xray-core/transport/internet"

+ 167 - 0
transport/global/config.pb.go

@@ -0,0 +1,167 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.25.0
+// 	protoc        v3.14.0
+// source: transport/global/config.proto
+
+package global
+
+import (
+	proto "github.com/golang/protobuf/proto"
+	internet "github.com/xtls/xray-core/transport/internet"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// This is a compile-time assertion that a sufficiently up-to-date version
+// of the legacy proto package is being used.
+const _ = proto.ProtoPackageIsVersion4
+
+// Global transport settings. This affects all type of connections that go
+// through Xray. Deprecated. Use each settings in StreamConfig.
+//
+// Deprecated: Do not use.
+type Config struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	TransportSettings []*internet.TransportConfig `protobuf:"bytes,1,rep,name=transport_settings,json=transportSettings,proto3" json:"transport_settings,omitempty"`
+}
+
+func (x *Config) Reset() {
+	*x = Config{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_transport_global_config_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Config) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Config) ProtoMessage() {}
+
+func (x *Config) ProtoReflect() protoreflect.Message {
+	mi := &file_transport_global_config_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Config.ProtoReflect.Descriptor instead.
+func (*Config) Descriptor() ([]byte, []int) {
+	return file_transport_global_config_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *Config) GetTransportSettings() []*internet.TransportConfig {
+	if x != nil {
+		return x.TransportSettings
+	}
+	return nil
+}
+
+var File_transport_global_config_proto protoreflect.FileDescriptor
+
+var file_transport_global_config_proto_rawDesc = []byte{
+	0x0a, 0x1d, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x6c, 0x6f, 0x62,
+	0x61, 0x6c, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+	0x0e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x1a,
+	0x1f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72,
+	0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x22, 0x65, 0x0a, 0x06, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x57, 0x0a, 0x12, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+	0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x52, 0x11, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69,
+	0x6e, 0x67, 0x73, 0x3a, 0x02, 0x18, 0x01, 0x42, 0x61, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
+	0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x67, 0x6c,
+	0x6f, 0x62, 0x61, 0x6c, 0x50, 0x01, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
+	0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
+	0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x67, 0x6c, 0x6f, 0x62,
+	0x61, 0x6c, 0xaa, 0x02, 0x15, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
+	0x6f, 0x72, 0x74, 0x2e, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
+}
+
+var (
+	file_transport_global_config_proto_rawDescOnce sync.Once
+	file_transport_global_config_proto_rawDescData = file_transport_global_config_proto_rawDesc
+)
+
+func file_transport_global_config_proto_rawDescGZIP() []byte {
+	file_transport_global_config_proto_rawDescOnce.Do(func() {
+		file_transport_global_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_global_config_proto_rawDescData)
+	})
+	return file_transport_global_config_proto_rawDescData
+}
+
+var file_transport_global_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_transport_global_config_proto_goTypes = []interface{}{
+	(*Config)(nil),                   // 0: xray.transport.Config
+	(*internet.TransportConfig)(nil), // 1: xray.transport.internet.TransportConfig
+}
+var file_transport_global_config_proto_depIdxs = []int32{
+	1, // 0: xray.transport.Config.transport_settings:type_name -> xray.transport.internet.TransportConfig
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_transport_global_config_proto_init() }
+func file_transport_global_config_proto_init() {
+	if File_transport_global_config_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_transport_global_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Config); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_transport_global_config_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_transport_global_config_proto_goTypes,
+		DependencyIndexes: file_transport_global_config_proto_depIdxs,
+		MessageInfos:      file_transport_global_config_proto_msgTypes,
+	}.Build()
+	File_transport_global_config_proto = out.File
+	file_transport_global_config_proto_rawDesc = nil
+	file_transport_global_config_proto_goTypes = nil
+	file_transport_global_config_proto_depIdxs = nil
+}

+ 3 - 3
transport/config.proto → transport/global/config.proto

@@ -1,9 +1,9 @@
 syntax = "proto3";
 
 package xray.transport;
-option csharp_namespace = "Xray.Transport";
-option go_package = "github.com/xtls/xray-core/transport";
-option java_package = "com.xray.transport";
+option csharp_namespace = "Xray.Transport.Global";
+option go_package = "github.com/xtls/xray-core/transport/global";
+option java_package = "com.xray.transport.global";
 option java_multiple_files = true;
 
 import "transport/internet/config.proto";

+ 154 - 62
transport/internet/config.pb.go

@@ -84,6 +84,58 @@ func (TransportProtocol) EnumDescriptor() ([]byte, []int) {
 	return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
 }
 
+type DomainStrategy int32
+
+const (
+	DomainStrategy_AS_IS   DomainStrategy = 0
+	DomainStrategy_USE_IP  DomainStrategy = 1
+	DomainStrategy_USE_IP4 DomainStrategy = 2
+	DomainStrategy_USE_IP6 DomainStrategy = 3
+)
+
+// Enum value maps for DomainStrategy.
+var (
+	DomainStrategy_name = map[int32]string{
+		0: "AS_IS",
+		1: "USE_IP",
+		2: "USE_IP4",
+		3: "USE_IP6",
+	}
+	DomainStrategy_value = map[string]int32{
+		"AS_IS":   0,
+		"USE_IP":  1,
+		"USE_IP4": 2,
+		"USE_IP6": 3,
+	}
+)
+
+func (x DomainStrategy) Enum() *DomainStrategy {
+	p := new(DomainStrategy)
+	*p = x
+	return p
+}
+
+func (x DomainStrategy) String() string {
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
+}
+
+func (DomainStrategy) Descriptor() protoreflect.EnumDescriptor {
+	return file_transport_internet_config_proto_enumTypes[1].Descriptor()
+}
+
+func (DomainStrategy) Type() protoreflect.EnumType {
+	return &file_transport_internet_config_proto_enumTypes[1]
+}
+
+func (x DomainStrategy) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use DomainStrategy.Descriptor instead.
+func (DomainStrategy) EnumDescriptor() ([]byte, []int) {
+	return file_transport_internet_config_proto_rawDescGZIP(), []int{1}
+}
+
 type SocketConfig_TProxyMode int32
 
 const (
@@ -120,11 +172,11 @@ func (x SocketConfig_TProxyMode) String() string {
 }
 
 func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
-	return file_transport_internet_config_proto_enumTypes[1].Descriptor()
+	return file_transport_internet_config_proto_enumTypes[2].Descriptor()
 }
 
 func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
-	return &file_transport_internet_config_proto_enumTypes[1]
+	return &file_transport_internet_config_proto_enumTypes[2]
 }
 
 func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
@@ -305,7 +357,8 @@ type ProxyConfig struct {
 	sizeCache     protoimpl.SizeCache
 	unknownFields protoimpl.UnknownFields
 
-	Tag string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
+	Tag                 string `protobuf:"bytes,1,opt,name=tag,proto3" json:"tag,omitempty"`
+	TransportLayerProxy bool   `protobuf:"varint,2,opt,name=transportLayerProxy,proto3" json:"transportLayerProxy,omitempty"`
 }
 
 func (x *ProxyConfig) Reset() {
@@ -347,6 +400,13 @@ func (x *ProxyConfig) GetTag() string {
 	return ""
 }
 
+func (x *ProxyConfig) GetTransportLayerProxy() bool {
+	if x != nil {
+		return x.TransportLayerProxy
+	}
+	return false
+}
+
 // SocketConfig is options to be applied on network sockets.
 type SocketConfig struct {
 	state         protoimpl.MessageState
@@ -361,10 +421,12 @@ type SocketConfig struct {
 	Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=xray.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"`
 	// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
 	// option. This option is for UDP only.
-	ReceiveOriginalDestAddress bool   `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
-	BindAddress                []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
-	BindPort                   uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
-	AcceptProxyProtocol        bool   `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
+	ReceiveOriginalDestAddress bool           `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
+	BindAddress                []byte         `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
+	BindPort                   uint32         `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
+	AcceptProxyProtocol        bool           `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
+	DomainStrategy             DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
+	DialerProxy                string         `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"`
 }
 
 func (x *SocketConfig) Reset() {
@@ -448,6 +510,20 @@ func (x *SocketConfig) GetAcceptProxyProtocol() bool {
 	return false
 }
 
+func (x *SocketConfig) GetDomainStrategy() DomainStrategy {
+	if x != nil {
+		return x.DomainStrategy
+	}
+	return DomainStrategy_AS_IS
+}
+
+func (x *SocketConfig) GetDialerProxy() string {
+	if x != nil {
+		return x.DialerProxy
+	}
+	return ""
+}
+
 var File_transport_internet_config_proto protoreflect.FileDescriptor
 
 var file_transport_internet_config_proto_rawDesc = []byte{
@@ -495,44 +571,58 @@ var file_transport_internet_config_proto_rawDesc = []byte{
 	0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e,
 	0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52,
 	0x0e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x73, 0x22,
-	0x1f, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10,
+	0x51, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x10,
 	0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67,
-	0x22, 0xe6, 0x02, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52,
-	0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f, 0x78,
-	0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
-	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
-	0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54,
-	0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x70, 0x72, 0x6f, 0x78,
-	0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f, 0x72, 0x69,
-	0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65,
-	0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76,
-	0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x41, 0x64, 0x64,
-	0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61, 0x64, 0x64,
-	0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69, 0x6e, 0x64,
-	0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e, 0x64, 0x5f,
-	0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64,
-	0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x70,
-	0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x07, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79,
-	0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f,
-	0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12,
-	0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52,
-	0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61,
-	0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07,
-	0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01,
-	0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65,
-	0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54,
-	0x50, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63,
-	0x6b, 0x65, 0x74, 0x10, 0x05, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61,
-	0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
-	0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
-	0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72,
-	0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65,
-	0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e,
-	0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06,
-	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x12, 0x30, 0x0a, 0x13, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79,
+	0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x74,
+	0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x4c, 0x61, 0x79, 0x65, 0x72, 0x50, 0x72, 0x6f,
+	0x78, 0x79, 0x22, 0xdb, 0x03, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72,
+	0x6f, 0x78, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x30, 0x2e, 0x78, 0x72, 0x61, 0x79,
+	0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72,
+	0x6e, 0x65, 0x74, 0x2e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x2e, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x52, 0x06, 0x74, 0x70, 0x72,
+	0x6f, 0x78, 0x79, 0x12, 0x41, 0x0a, 0x1d, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x5f, 0x6f,
+	0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x5f, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x61, 0x64, 0x64,
+	0x72, 0x65, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x1a, 0x72, 0x65, 0x63, 0x65,
+	0x69, 0x76, 0x65, 0x4f, 0x72, 0x69, 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x44, 0x65, 0x73, 0x74, 0x41,
+	0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61,
+	0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69,
+	0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e,
+	0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x69,
+	0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32, 0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74,
+	0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f,
+	0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x50, 0x0a, 0x0f, 0x64, 0x6f,
+	0x6d, 0x61, 0x69, 0x6e, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x08, 0x20,
+	0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
+	0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f,
+	0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0e, 0x64, 0x6f,
+	0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x21, 0x0a, 0x0c,
+	0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x18, 0x09, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x0b, 0x64, 0x69, 0x61, 0x6c, 0x65, 0x72, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x22,
+	0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a,
+	0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79,
+	0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02,
+	0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f,
+	0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07,
+	0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10,
+	0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03,
+	0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f,
+	0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x05, 0x2a, 0x41, 0x0a, 0x0e,
+	0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x09,
+	0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45,
+	0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
+	0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x03, 0x42,
+	0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e,
+	0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01,
+	0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c,
+	0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e,
+	0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02,
+	0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,
+	0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
@@ -547,30 +637,32 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
 	return file_transport_internet_config_proto_rawDescData
 }
 
-var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
 var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
 var file_transport_internet_config_proto_goTypes = []interface{}{
 	(TransportProtocol)(0),       // 0: xray.transport.internet.TransportProtocol
-	(SocketConfig_TProxyMode)(0), // 1: xray.transport.internet.SocketConfig.TProxyMode
-	(*TransportConfig)(nil),      // 2: xray.transport.internet.TransportConfig
-	(*StreamConfig)(nil),         // 3: xray.transport.internet.StreamConfig
-	(*ProxyConfig)(nil),          // 4: xray.transport.internet.ProxyConfig
-	(*SocketConfig)(nil),         // 5: xray.transport.internet.SocketConfig
-	(*serial.TypedMessage)(nil),  // 6: xray.common.serial.TypedMessage
+	(DomainStrategy)(0),          // 1: xray.transport.internet.DomainStrategy
+	(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
+	(*TransportConfig)(nil),      // 3: xray.transport.internet.TransportConfig
+	(*StreamConfig)(nil),         // 4: xray.transport.internet.StreamConfig
+	(*ProxyConfig)(nil),          // 5: xray.transport.internet.ProxyConfig
+	(*SocketConfig)(nil),         // 6: xray.transport.internet.SocketConfig
+	(*serial.TypedMessage)(nil),  // 7: xray.common.serial.TypedMessage
 }
 var file_transport_internet_config_proto_depIdxs = []int32{
 	0, // 0: xray.transport.internet.TransportConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
-	6, // 1: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
+	7, // 1: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
 	0, // 2: xray.transport.internet.StreamConfig.protocol:type_name -> xray.transport.internet.TransportProtocol
-	2, // 3: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
-	6, // 4: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
-	5, // 5: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
-	1, // 6: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
-	7, // [7:7] is the sub-list for method output_type
-	7, // [7:7] is the sub-list for method input_type
-	7, // [7:7] is the sub-list for extension type_name
-	7, // [7:7] is the sub-list for extension extendee
-	0, // [0:7] is the sub-list for field type_name
+	3, // 3: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
+	7, // 4: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
+	6, // 5: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
+	2, // 6: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
+	1, // 7: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
+	8, // [8:8] is the sub-list for method output_type
+	8, // [8:8] is the sub-list for method input_type
+	8, // [8:8] is the sub-list for extension type_name
+	8, // [8:8] is the sub-list for extension extendee
+	0, // [0:8] is the sub-list for field type_name
 }
 
 func init() { file_transport_internet_config_proto_init() }
@@ -633,7 +725,7 @@ func file_transport_internet_config_proto_init() {
 		File: protoimpl.DescBuilder{
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_transport_internet_config_proto_rawDesc,
-			NumEnums:      2,
+			NumEnums:      3,
 			NumMessages:   4,
 			NumExtensions: 0,
 			NumServices:   0,

+ 14 - 1
transport/internet/config.proto

@@ -17,6 +17,13 @@ enum TransportProtocol {
   DomainSocket = 5;
 }
 
+enum DomainStrategy {
+  AS_IS = 0;
+  USE_IP = 1;
+  USE_IP4 = 2;
+  USE_IP6 = 3;
+}
+
 message TransportConfig {
   // Type of network that this settings supports.
   // Deprecated. Use the string form below.
@@ -47,7 +54,10 @@ message StreamConfig {
   SocketConfig socket_settings = 6;
 }
 
-message ProxyConfig { string tag = 1; }
+message ProxyConfig {
+  string tag = 1;
+  bool transportLayerProxy = 2;
+}
 
 // SocketConfig is options to be applied on network sockets.
 message SocketConfig {
@@ -78,4 +88,7 @@ message SocketConfig {
   uint32 bind_port = 6;
 
   bool accept_proxy_protocol = 7;
+
+  DomainStrategy domain_strategy = 8;
+  string dialer_proxy = 9;
 }

+ 93 - 0
transport/internet/system_dialer.go

@@ -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())
 }

+ 18 - 0
transport/internet/system_dialer_context.go

@@ -0,0 +1,18 @@
+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 ""
+}