Browse Source

doh route strategy optimized

vcptr 5 years ago
parent
commit
04a0c04934
3 changed files with 47 additions and 59 deletions
  1. 7 1
      app/dispatcher/default.go
  2. 38 58
      app/dns/dohdns.go
  3. 2 0
      common/session/session.go

+ 7 - 1
app/dispatcher/default.go

@@ -258,7 +258,13 @@ func sniffer(ctx context.Context, cReader *cachedReader) (SniffResult, error) {
 
 
 func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
 func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination) {
 	var handler outbound.Handler
 	var handler outbound.Handler
-	if d.router != nil {
+
+	skipRoutePick := false
+	if content := session.ContentFromContext(ctx); content != nil {
+		skipRoutePick = content.SkipRoutePick
+	}
+
+	if d.router != nil && !skipRoutePick {
 		if tag, err := d.router.PickRoute(ctx); err == nil {
 		if tag, err := d.router.PickRoute(ctx); err == nil {
 			if h := d.ohm.GetHandler(tag); h != nil {
 			if h := d.ohm.GetHandler(tag); h != nil {
 				newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))
 				newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx))

+ 38 - 58
app/dns/dohdns.go

@@ -16,13 +16,13 @@ import (
 
 
 	"golang.org/x/net/dns/dnsmessage"
 	"golang.org/x/net/dns/dnsmessage"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common"
-	"v2ray.com/core/common/dice"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/protocol/dns"
 	"v2ray.com/core/common/protocol/dns"
 	"v2ray.com/core/common/session"
 	"v2ray.com/core/common/session"
 	"v2ray.com/core/common/signal/pubsub"
 	"v2ray.com/core/common/signal/pubsub"
 	"v2ray.com/core/common/task"
 	"v2ray.com/core/common/task"
 	"v2ray.com/core/features/routing"
 	"v2ray.com/core/features/routing"
+	"v2ray.com/core/transport/internet"
 )
 )
 
 
 // DoHNameServer implimented DNS over HTTPS (RFC8484) Wire Format,
 // DoHNameServer implimented DNS over HTTPS (RFC8484) Wire Format,
@@ -30,8 +30,6 @@ import (
 // thus most of the DOH implimentation is copied from udpns.go
 // thus most of the DOH implimentation is copied from udpns.go
 type DoHNameServer struct {
 type DoHNameServer struct {
 	sync.RWMutex
 	sync.RWMutex
-	dispatcher routing.Dispatcher
-	dohDests   []net.Destination
 	ips        map[string]record
 	ips        map[string]record
 	pub        *pubsub.Service
 	pub        *pubsub.Service
 	cleanup    *task.Periodic
 	cleanup    *task.Periodic
@@ -45,41 +43,8 @@ type DoHNameServer struct {
 // NewDoHNameServer creates DOH client object for remote resolving
 // NewDoHNameServer creates DOH client object for remote resolving
 func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.IP) (*DoHNameServer, error) {
 func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.IP) (*DoHNameServer, error) {
 
 
-	dohAddr := net.ParseAddress(url.Hostname())
-	dohPort := "443"
-	if url.Port() != "" {
-		dohPort = url.Port()
-	}
-
-	parseIPDest := func(ip net.IP, port string) net.Destination {
-		strIP := ip.String()
-		if len(ip) == net.IPv6len {
-			strIP = fmt.Sprintf("[%s]", strIP)
-		}
-		dest, err := net.ParseDestination(fmt.Sprintf("tcp:%s:%s", strIP, port))
-		common.Must(err)
-		return dest
-	}
-
-	var dests []net.Destination
-	if dohAddr.Family().IsDomain() {
-		// resolve DOH server in advance
-		ips, err := net.LookupIP(dohAddr.Domain())
-		if err != nil || len(ips) == 0 {
-			return nil, err
-		}
-		for _, ip := range ips {
-			dests = append(dests, parseIPDest(ip, dohPort))
-		}
-	} else {
-		ip := dohAddr.IP()
-		dests = append(dests, parseIPDest(ip, dohPort))
-	}
-
-	newError("DNS: created Remote DOH client for ", url.String(), ", preresolved Dests: ", dests).AtInfo().WriteToLog()
+	newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
 	s := baseDOHNameServer(url, "DOH", clientIP)
 	s := baseDOHNameServer(url, "DOH", clientIP)
-	s.dispatcher = dispatcher
-	s.dohDests = dests
 
 
 	// Dispatched connection will be closed (interupted) after each request
 	// Dispatched connection will be closed (interupted) after each request
 	// This makes DOH inefficient without a keeped-alive connection
 	// This makes DOH inefficient without a keeped-alive connection
@@ -88,15 +53,29 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.
 	// Recommand to use NewDoHLocalNameServer (DOHL:) if v2ray instance is running on
 	// Recommand to use NewDoHLocalNameServer (DOHL:) if v2ray instance is running on
 	//  a normal network eg. the server side of v2ray
 	//  a normal network eg. the server side of v2ray
 	tr := &http.Transport{
 	tr := &http.Transport{
-		MaxIdleConns:        10,
+		MaxIdleConns:        30,
 		IdleConnTimeout:     90 * time.Second,
 		IdleConnTimeout:     90 * time.Second,
-		TLSHandshakeTimeout: 10 * time.Second,
-		DialContext:         s.DialContext,
+		TLSHandshakeTimeout: 30 * time.Second,
+		DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+			dest, err := net.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
+			if err != nil {
+				return nil, err
+			}
+
+			link, err := dispatcher.Dispatch(ctx, dest)
+			if err != nil {
+				return nil, err
+			}
+			return net.NewConnection(
+				net.ConnectionInputMulti(link.Writer),
+				net.ConnectionOutputMulti(link.Reader),
+			), nil
+		},
 	}
 	}
 
 
 	dispatchedClient := &http.Client{
 	dispatchedClient := &http.Client{
 		Transport: tr,
 		Transport: tr,
-		Timeout:   16 * time.Second,
+		Timeout:   60 * time.Second,
 	}
 	}
 
 
 	s.httpClient = dispatchedClient
 	s.httpClient = dispatchedClient
@@ -107,8 +86,23 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.
 func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer {
 func NewDoHLocalNameServer(url *url.URL, clientIP net.IP) *DoHNameServer {
 	url.Scheme = "https"
 	url.Scheme = "https"
 	s := baseDOHNameServer(url, "DOHL", clientIP)
 	s := baseDOHNameServer(url, "DOHL", clientIP)
+	tr := &http.Transport{
+		IdleConnTimeout: 90 * time.Second,
+		DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
+			dest, err := net.ParseDestination(fmt.Sprintf("%s:%s", network, addr))
+			if err != nil {
+				return nil, err
+			}
+			conn, err := internet.DialSystem(ctx, dest, nil)
+			if err != nil {
+				return nil, err
+			}
+			return conn, nil
+		},
+	}
 	s.httpClient = &http.Client{
 	s.httpClient = &http.Client{
-		Timeout: time.Second * 180,
+		Timeout:   time.Second * 180,
+		Transport: tr,
 	}
 	}
 	newError("DNS: created Local DOH client for ", url.String()).AtInfo().WriteToLog()
 	newError("DNS: created Local DOH client for ", url.String()).AtInfo().WriteToLog()
 	return s
 	return s
@@ -136,21 +130,6 @@ func (s *DoHNameServer) Name() string {
 	return s.name
 	return s.name
 }
 }
 
 
-// DialContext offer dispatched connection through core routing
-func (s *DoHNameServer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {
-
-	dest := s.dohDests[dice.Roll(len(s.dohDests))]
-
-	link, err := s.dispatcher.Dispatch(ctx, dest)
-	if err != nil {
-		return nil, err
-	}
-	return net.NewConnection(
-		net.ConnectionInputMulti(link.Writer),
-		net.ConnectionOutputMulti(link.Reader),
-	), nil
-}
-
 // Cleanup clears expired items from cache
 // Cleanup clears expired items from cache
 func (s *DoHNameServer) Cleanup() error {
 func (s *DoHNameServer) Cleanup() error {
 	now := time.Now()
 	now := time.Now()
@@ -255,7 +234,8 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO
 			}
 			}
 
 
 			dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
 			dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
-				Protocol: "https",
+				Protocol:      "https",
+				SkipRoutePick: true,
 			})
 			})
 
 
 			// forced to use mux for DOH
 			// forced to use mux for DOH

+ 2 - 0
common/session/session.go

@@ -68,6 +68,8 @@ type Content struct {
 	SniffingRequest SniffingRequest
 	SniffingRequest SniffingRequest
 
 
 	Attributes map[string]interface{}
 	Attributes map[string]interface{}
+
+	SkipRoutePick bool
 }
 }
 
 
 func (c *Content) SetAttribute(name string, value interface{}) {
 func (c *Content) SetAttribute(name string, value interface{}) {