From 3929f0da449ce0a8368416ebd46a1f83617fdcbd Mon Sep 17 00:00:00 2001
From: Aleksey Dmitrevskiy <ad@adguard.com>
Date: Thu, 28 Feb 2019 13:01:41 +0300
Subject: [PATCH] [change] control: Handle upstream config with JSON

---
 control.go                 | 89 ++++++++----------------------------
 dnsfilter/dnsfilter.go     |  2 +-
 dnsforward/querylog_top.go |  2 +-
 openapi/openapi.yaml       | 93 ++++----------------------------------
 4 files changed, 31 insertions(+), 155 deletions(-)

diff --git a/control.go b/control.go
index 81befc50..5b1dcf3c 100644
--- a/control.go
+++ b/control.go
@@ -306,35 +306,35 @@ func sortByValue(m map[string]int) []string {
 // upstreams configuration
 // -----------------------
 
+// TODO this struct will become unnecessary after config file rework
+type upstreamConfig struct {
+	upstreams    []string // Upstreams
+	bootstrapDNS []string // Bootstrap DNS
+	allServers   bool     // --all-servers param for dnsproxy
+}
+
 func handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request) {
 	log.Tracef("%s %v", r.Method, r.URL)
-}
+	newconfig := upstreamConfig{}
+	err := json.NewDecoder(r.Body).Decode(&newconfig)
+	if err != nil {
+		httpError(w, http.StatusBadRequest, "Failed to parse new upstreams config json: %s", err)
+		return
+	}
 
-func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) {
-	log.Tracef("%s %v", r.Method, r.URL)
-	setDNSServers(w, r, true)
-}
-
-func handleSetBootstrapDNS(w http.ResponseWriter, r *http.Request) {
-	log.Tracef("%s %v", r.Method, r.URL)
-	setDNSServers(w, r, false)
+	setDNSServers(newconfig.upstreams, true)
+	setDNSServers(newconfig.bootstrapDNS, false)
+	config.DNS.AllServers = newconfig.allServers
+	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 // setDNSServers sets upstream and bootstrap DNS servers
-func setDNSServers(w http.ResponseWriter, r *http.Request, upstreams bool) {
-	body, err := ioutil.ReadAll(r.Body)
-	if err != nil {
-		httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err)
-		return
-	}
-	// if empty body -- user is asking for default servers
-	hosts := strings.Fields(string(body))
-
+func setDNSServers(hosts []string, upstreams bool) {
 	// bootstrap servers are plain DNS only. We should remove tls:// https:// and sdns:// hosts from slice
 	bootstraps := []string{}
 	if !upstreams && len(hosts) > 0 {
 		for _, host := range hosts {
-			err = checkBootstrapDNS(host)
+			err := checkBootstrapDNS(host)
 			if err != nil {
 				log.Tracef("%s can not be used as bootstrap DNS cause: %s", host, err)
 				continue
@@ -360,52 +360,6 @@ func setDNSServers(w http.ResponseWriter, r *http.Request, upstreams bool) {
 			config.DNS.BootstrapDNS = bootstraps
 		}
 	}
-
-	err = writeAllConfigs()
-	if err != nil {
-		httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err)
-		return
-	}
-	err = reconfigureDNSServer()
-	if err != nil {
-		httpError(w, http.StatusInternalServerError, "Couldn't reconfigure the DNS server: %s", err)
-		return
-	}
-	_, err = fmt.Fprintf(w, "OK %d servers\n", count)
-	if err != nil {
-		httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err)
-	}
-}
-
-func handleAllServersEnable(w http.ResponseWriter, r *http.Request) {
-	log.Tracef("%s %v", r.Method, r.URL)
-	config.DNS.AllServers = true
-	httpUpdateConfigReloadDNSReturnOK(w, r)
-}
-
-func handleAllServersDisable(w http.ResponseWriter, r *http.Request) {
-	log.Tracef("%s %v", r.Method, r.URL)
-	config.DNS.AllServers = false
-	httpUpdateConfigReloadDNSReturnOK(w, r)
-}
-
-func handleAllServersStatus(w http.ResponseWriter, r *http.Request) {
-	log.Tracef("%s %v", r.Method, r.URL)
-	data := map[string]interface{}{
-		"enabled": config.DNS.AllServers,
-	}
-	jsonVal, err := json.Marshal(data)
-	if err != nil {
-		httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err)
-		return
-	}
-
-	w.Header().Set("Content-Type", "application/json")
-	_, err = w.Write(jsonVal)
-	if err != nil {
-		httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err)
-		return
-	}
 }
 
 // checkBootstrapDNS checks if host is plain DNS
@@ -1343,13 +1297,8 @@ func registerControlHandlers() {
 	http.HandleFunc("/control/querylog", postInstall(optionalAuth(ensureGET(handleQueryLog))))
 	http.HandleFunc("/control/querylog_enable", postInstall(optionalAuth(ensurePOST(handleQueryLogEnable))))
 	http.HandleFunc("/control/querylog_disable", postInstall(optionalAuth(ensurePOST(handleQueryLogDisable))))
-	http.HandleFunc("/control/set_upstream_dns", postInstall(optionalAuth(ensurePOST(handleSetUpstreamDNS))))
 	http.HandleFunc("/control/set_upstreams_config", postInstall(optionalAuth(ensurePOST(handleSetUpstreamConfig))))
 	http.HandleFunc("/control/test_upstream_dns", postInstall(optionalAuth(ensurePOST(handleTestUpstreamDNS))))
-	http.HandleFunc("/control/set_bootstrap_dns", postInstall(optionalAuth(ensurePOST(handleSetBootstrapDNS))))
-	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/dnsfilter/dnsfilter.go b/dnsfilter/dnsfilter.go
index 561ccb04..b33465f9 100644
--- a/dnsfilter/dnsfilter.go
+++ b/dnsfilter/dnsfilter.go
@@ -16,8 +16,8 @@ import (
 	"sync/atomic"
 	"time"
 
-	"github.com/bluele/gcache"
 	"github.com/AdguardTeam/golibs/log"
+	"github.com/bluele/gcache"
 	"golang.org/x/net/publicsuffix"
 )
 
diff --git a/dnsforward/querylog_top.go b/dnsforward/querylog_top.go
index a2ffffdc..b381984c 100644
--- a/dnsforward/querylog_top.go
+++ b/dnsforward/querylog_top.go
@@ -9,8 +9,8 @@ import (
 	"sync"
 	"time"
 
-	"github.com/bluele/gcache"
 	"github.com/AdguardTeam/golibs/log"
+	"github.com/bluele/gcache"
 	"github.com/miekg/dns"
 )
 
diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml
index ed788794..6d0aafb4 100644
--- a/openapi/openapi.yaml
+++ b/openapi/openapi.yaml
@@ -106,63 +106,6 @@ paths:
                 200:
                     description: OK
 
-    /set_upstream_dns:
-        post:
-            tags:
-                - global
-            operationId: setUpstreamDNS
-            summary: 'Set upstream DNS for coredns, empty value will reset it to default values'
-            consumes:
-                - text/plain
-            parameters:
-                -   in: body
-                    name: upstream
-                    description: 'Upstream servers, separated by newline or space, port is optional after colon'
-                    schema:
-                        # TODO: use JSON
-                        type: string
-                        example: |
-                            1.1.1.1
-                            1.0.0.1
-                            8.8.8.8 8.8.4.4
-                            192.168.1.104:53535
-            responses:
-                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:
@@ -194,30 +137,6 @@ paths:
                             8.8.4.4: OK
                             "192.168.1.104:53535": "Couldn't communicate with DNS server"
 
-    /set_bootstrap_dns:
-        post:
-            tags:
-                - global
-            operationId: setBootstrapDNS
-            summary: 'Set bootstrap DNS for DNS-over-HTTPS and DNS-over-TLS upstreams, empty value will reset it to default values'
-            consumes:
-                - text/plain
-            parameters:
-                -   in: body
-                    name: upstream
-                    description: 'Bootstrap servers, separated by newline or space, port is optional after colon'
-                    schema:
-                        # TODO: use JSON
-                        type: string
-                        example: |
-                            1.1.1.1
-                            1.0.0.1
-                            8.8.8.8 8.8.4.4
-                            192.168.1.104:53535
-            responses:
-                200:
-                    description: OK
-
     /version.json:
         get:
             tags:
@@ -880,12 +799,19 @@ definitions:
         required:
             - "bootstrap_dns"
             - "upstream_dns"
+            - "all_servers"
         properties:
             bootstrap_dns:
-                type: "string"
-                example: "8.8.8.8:53"
+                type: "array"
+                description: 'Bootstrap servers, port is optional after colon. Empty value will reset it to default values'
+                items:
+                    type: "string"
+                example:
+                    - "8.8.8.8:53"
+                    - "1.1.1.1:53"
             upstream_dns:
                 type: "array"
+                description: 'Upstream servers, port is optional after colon. Empty value will reset it to default values'
                 items:
                     type: "string"
                 example:
@@ -893,6 +819,7 @@ definitions:
                     - "tls://1.0.0.1"
             all_servers:
                 type: "boolean"
+                description: "If true, parallel queries to all configured upstream servers are enabled"
     Filter:
         type: "object"
         description: "Filter subscription info"