1
0
Эх сурвалжийг харах

Fix non-local DoH requests & Apply routing (#147)

Arthur Morgan 4 жил өмнө
parent
commit
11a851f957
1 өөрчлөгдсөн 51 нэмэгдсэн , 40 устгасан
  1. 51 40
      app/dns/dohdns.go

+ 51 - 40
app/dns/dohdns.go

@@ -29,6 +29,7 @@ import (
 // which is compatible with traditional dns over udp(RFC1035),
 // thus most of the DOH implementation is copied from udpns.go
 type DoHNameServer struct {
+	dispatcher routing.Dispatcher
 	sync.RWMutex
 	ips        map[string]record
 	pub        *pubsub.Service
@@ -45,40 +46,8 @@ func NewDoHNameServer(url *url.URL, dispatcher routing.Dispatcher, clientIP net.
 	newError("DNS: created Remote DOH client for ", url.String()).AtInfo().WriteToLog()
 	s := baseDOHNameServer(url, "DOH", clientIP)
 
-	// 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
-	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 := dispatcher.Dispatch(ctx, dest)
-			if err != nil {
-				return nil, err
-			}
-			return cnc.NewConnection(
-				cnc.ConnectionInputMulti(link.Writer),
-				cnc.ConnectionOutputMulti(link.Reader),
-			), nil
-		},
-	}
+	s.dispatcher = dispatcher
 
-	dispatchedClient := &http.Client{
-		Transport: tr,
-		Timeout:   60 * time.Second,
-	}
-
-	s.httpClient = dispatchedClient
 	return s, nil
 }
 
@@ -211,6 +180,11 @@ func (s *DoHNameServer) newReqID() uint16 {
 func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPOption) {
 	newError(s.name, " querying: ", domain).AtInfo().WriteToLog(session.ExportIDToError(ctx))
 
+	if s.name+"." == "DOH//"+domain {
+		newError(s.name, " tries to resolve itself! Use IP or set \"hosts\" instead.").AtError().WriteToLog(session.ExportIDToError(ctx))
+		return
+	}
+
 	reqs := buildReqMsgs(domain, option, s.newReqID, genEDNS0Options(s.clientIP))
 
 	var deadline time.Time
@@ -232,12 +206,12 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO
 			}
 
 			dnsCtx = session.ContextWithContent(dnsCtx, &session.Content{
-				Protocol:      "https",
-				SkipRoutePick: true,
+				Protocol: "https",
+				//SkipRoutePick: true,
 			})
 
 			// forced to use mux for DOH
-			dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true)
+			// dnsCtx = session.ContextWithMuxPrefered(dnsCtx, true)
 
 			var cancel context.CancelFunc
 			dnsCtx, cancel = context.WithDeadline(dnsCtx, deadline)
@@ -245,17 +219,17 @@ func (s *DoHNameServer) sendQuery(ctx context.Context, domain string, option IPO
 
 			b, err := dns.PackMessage(r.msg)
 			if err != nil {
-				newError("failed to pack dns query").Base(err).AtError().WriteToLog()
+				newError("failed to pack dns query for ", domain).Base(err).AtError().WriteToLog()
 				return
 			}
 			resp, err := s.dohHTTPSContext(dnsCtx, b.Bytes())
 			if err != nil {
-				newError("failed to retrieve response").Base(err).AtError().WriteToLog()
+				newError("failed to retrieve response for ", domain).Base(err).AtError().WriteToLog()
 				return
 			}
 			rec, err := parseResponse(resp)
 			if err != nil {
-				newError("failed to handle DOH response").Base(err).AtError().WriteToLog()
+				newError("failed to handle DOH response for ", domain).Base(err).AtError().WriteToLog()
 				return
 			}
 			s.updateIP(r, rec)
@@ -273,7 +247,44 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
 	req.Header.Add("Accept", "application/dns-message")
 	req.Header.Add("Content-Type", "application/dns-message")
 
-	resp, err := s.httpClient.Do(req.WithContext(ctx))
+	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
 	}