From 04a0c0493449441020351bff47930c357618222a Mon Sep 17 00:00:00 2001 From: vcptr <51714622+vcptr@users.noreply.github.com> Date: Mon, 2 Mar 2020 18:07:43 +0800 Subject: [PATCH] doh route strategy optimized --- app/dispatcher/default.go | 8 +++- app/dns/dohdns.go | 96 ++++++++++++++++----------------------- common/session/session.go | 2 + 3 files changed, 47 insertions(+), 59 deletions(-) diff --git a/app/dispatcher/default.go b/app/dispatcher/default.go index ef2ec44c..b74300af 100644 --- a/app/dispatcher/default.go +++ b/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) { 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 h := d.ohm.GetHandler(tag); h != nil { newError("taking detour [", tag, "] for [", destination, "]").WriteToLog(session.ExportIDToError(ctx)) diff --git a/app/dns/dohdns.go b/app/dns/dohdns.go index 483d82c7..892d79ac 100644 --- a/app/dns/dohdns.go +++ b/app/dns/dohdns.go @@ -16,13 +16,13 @@ import ( "golang.org/x/net/dns/dnsmessage" "v2ray.com/core/common" - "v2ray.com/core/common/dice" "v2ray.com/core/common/net" "v2ray.com/core/common/protocol/dns" "v2ray.com/core/common/session" "v2ray.com/core/common/signal/pubsub" "v2ray.com/core/common/task" "v2ray.com/core/features/routing" + "v2ray.com/core/transport/internet" ) // DoHNameServer implimented DNS over HTTPS (RFC8484) Wire Format, @@ -30,8 +30,6 @@ import ( // thus most of the DOH implimentation is copied from udpns.go type DoHNameServer struct { sync.RWMutex - dispatcher routing.Dispatcher - dohDests []net.Destination ips map[string]record pub *pubsub.Service cleanup *task.Periodic @@ -45,41 +43,8 @@ type DoHNameServer struct { // NewDoHNameServer creates DOH client object for remote resolving 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.dispatcher = dispatcher - s.dohDests = dests // Dispatched connection will be closed (interupted) after each request // 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 // a normal network eg. the server side of v2ray tr := &http.Transport{ - MaxIdleConns: 10, + MaxIdleConns: 30, 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{ Transport: tr, - Timeout: 16 * time.Second, + Timeout: 60 * time.Second, } 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 { url.Scheme = "https" 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{ - Timeout: time.Second * 180, + Timeout: time.Second * 180, + Transport: tr, } newError("DNS: created Local DOH client for ", url.String()).AtInfo().WriteToLog() return s @@ -136,21 +130,6 @@ func (s *DoHNameServer) Name() string { 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 func (s *DoHNameServer) Cleanup() error { now := time.Now() @@ -255,7 +234,8 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO } dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{ - Protocol: "https", + Protocol: "https", + SkipRoutePick: true, }) // forced to use mux for DOH diff --git a/common/session/session.go b/common/session/session.go index 8ce25b55..52252648 100644 --- a/common/session/session.go +++ b/common/session/session.go @@ -68,6 +68,8 @@ type Content struct { SniffingRequest SniffingRequest Attributes map[string]interface{} + + SkipRoutePick bool } func (c *Content) SetAttribute(name string, value interface{}) {