From 5bc6d00aa03baa826ed84b3c134ed80db05e1388 Mon Sep 17 00:00:00 2001
From: Aleksey Dmitrevskiy <ad@adguard.com>
Date: Tue, 26 Feb 2019 18:19:05 +0300
Subject: [PATCH] Fix #596 - Intelligent Optimal DNS Resolution

---
 config.go                |  1 +
 control.go               | 36 ++++++++++++++++++++++++++++++++++++
 dnsforward/dnsforward.go |  2 ++
 openapi/openapi.yaml     | 33 +++++++++++++++++++++++++++++++++
 4 files changed, 72 insertions(+)

diff --git a/config.go b/config.go
index 87e5c6a8..2df9ec07 100644
--- a/config.go
+++ b/config.go
@@ -114,6 +114,7 @@ var config = configuration{
 			QueryLogEnabled:    true,
 			Ratelimit:          20,
 			RefuseAny:          true,
+			AllServers:         false,
 			BootstrapDNS:       "8.8.8.8:53",
 		},
 		UpstreamDNS: defaultDNS,
diff --git a/control.go b/control.go
index fd1759e3..70ff0e7c 100644
--- a/control.go
+++ b/control.go
@@ -94,6 +94,7 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
 		"running":            isRunning(),
 		"bootstrap_dns":      config.DNS.BootstrapDNS,
 		"upstream_dns":       config.DNS.UpstreamDNS,
+		"all_servers":        config.DNS.AllServers,
 		"version":            VersionString,
 		"language":           config.Language,
 	}
@@ -361,6 +362,38 @@ func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) {
 	}
 }
 
+func handleAllServersEnable(w http.ResponseWriter, r *http.Request) {
+	config.DNS.AllServers = true
+	httpUpdateConfigReloadDNSReturnOK(w, r)
+}
+
+func handleAllServersDisable(w http.ResponseWriter, r *http.Request) {
+	config.DNS.AllServers = false
+	httpUpdateConfigReloadDNSReturnOK(w, r)
+}
+
+func handleAllServersStatus(w http.ResponseWriter, r *http.Request) {
+	data := map[string]interface{}{
+		"enabled": config.DNS.AllServers,
+	}
+	jsonVal, err := json.Marshal(data)
+	if err != nil {
+		errorText := fmt.Sprintf("Unable to marshal status json: %s", err)
+		log.Println(errorText)
+		http.Error(w, errorText, 500)
+		return
+	}
+
+	w.Header().Set("Content-Type", "application/json")
+	_, err = w.Write(jsonVal)
+	if err != nil {
+		errorText := fmt.Sprintf("Unable to write response json: %s", err)
+		log.Println(errorText)
+		http.Error(w, errorText, 500)
+		return
+	}
+}
+
 func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) {
 	body, err := ioutil.ReadAll(r.Body)
 	if err != nil {
@@ -1317,6 +1350,9 @@ func registerControlHandlers() {
 	http.HandleFunc("/control/querylog_disable", postInstall(optionalAuth(ensurePOST(handleQueryLogDisable))))
 	http.HandleFunc("/control/set_upstream_dns", postInstall(optionalAuth(ensurePOST(handleSetUpstreamDNS))))
 	http.HandleFunc("/control/test_upstream_dns", postInstall(optionalAuth(ensurePOST(handleTestUpstreamDNS))))
+	http.HandleFunc("/control/all_servers/enable", postInstall(optionalAuth(ensurePOST(handleAllServersEnable))))
+	http.HandleFunc("/control/all_servers/disable", postInstall(optionalAuth(ensurePOST(handleAllServersDisable))))
+	http.HandleFunc("/control/all_servers/status", postInstall(optionalAuth(ensureGET(handleAllServersStatus))))
 	http.HandleFunc("/control/i18n/change_language", postInstall(optionalAuth(ensurePOST(handleI18nChangeLanguage))))
 	http.HandleFunc("/control/i18n/current_language", postInstall(optionalAuth(ensureGET(handleI18nCurrentLanguage))))
 	http.HandleFunc("/control/stats_top", postInstall(optionalAuth(ensureGET(handleStatsTop))))
diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go
index 99f09e6d..bf975ac5 100644
--- a/dnsforward/dnsforward.go
+++ b/dnsforward/dnsforward.go
@@ -67,6 +67,7 @@ type FilteringConfig struct {
 	RatelimitWhitelist []string `yaml:"ratelimit_whitelist"`
 	RefuseAny          bool     `yaml:"refuse_any"`
 	BootstrapDNS       string   `yaml:"bootstrap_dns"`
+	AllServers         bool     `yaml:"all_servers"`
 
 	dnsfilter.Config `yaml:",inline"`
 }
@@ -163,6 +164,7 @@ func (s *Server) startInternal(config *ServerConfig) error {
 		CacheEnabled:       true,
 		Upstreams:          s.Upstreams,
 		Handler:            s.handleDNSRequest,
+		AllServers:         s.AllServers,
 	}
 
 	if s.TLSListenAddr != nil && s.CertificateChain != "" && s.PrivateKey != "" {
diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml
index f1e23f86..e83d6360 100644
--- a/openapi/openapi.yaml
+++ b/openapi/openapi.yaml
@@ -111,6 +111,39 @@ paths:
                 200:
                     description: OK
 
+    /all_servers/enable:
+        post:
+            tags:
+                - global
+            operationId: allServersEnable
+            summary: 'Enable parallel queries'
+            responses:
+                200:
+                    description: OK
+
+    /all_servers/disable:
+        post:
+            tags:
+                - global
+            operationId: allServersDisable
+            summary: 'Disable parallel queries'
+            responses:
+                200:
+                    description: OK
+
+    /all_servers/status:
+        get:
+            tags:
+                - global
+            operationId: allServersStatus
+            summary: 'Get parallel queries status'
+            responses:
+                200:
+                    description: OK
+                    examples:
+                        application/json:
+                            enabled: false
+
     /test_upstream_dns:
         post:
             tags: