From 5bc6d00aa03baa826ed84b3c134ed80db05e1388 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Tue, 26 Feb 2019 18:19:05 +0300 Subject: [PATCH 01/34] 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: From dc05556c5afa69f3dcf360ad650447cd752a09d9 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 11:15:18 +0300 Subject: [PATCH 02/34] Fix #542 - Add Bootstrap DNS resolver settings --- config.go | 3 ++- control.go | 40 ++++++++++++++++++++++++++++++++++++++++ dns.go | 2 +- dnsforward/dnsforward.go | 2 +- openapi/openapi.yaml | 24 ++++++++++++++++++++++++ 5 files changed, 68 insertions(+), 3 deletions(-) diff --git a/config.go b/config.go index 87e5c6a8..4f34f1c9 100644 --- a/config.go +++ b/config.go @@ -61,6 +61,7 @@ type dnsConfig struct { } var defaultDNS = []string{"tls://1.1.1.1", "tls://1.0.0.1"} +var defaultBootstrap = []string{"1.1.1.1"} type tlsConfigSettings struct { Enabled bool `yaml:"enabled" json:"enabled"` // Enabled is the encryption (DOT/DOH/HTTPS) status @@ -114,7 +115,7 @@ var config = configuration{ QueryLogEnabled: true, Ratelimit: 20, RefuseAny: true, - BootstrapDNS: "8.8.8.8:53", + BootstrapDNS: defaultBootstrap, }, UpstreamDNS: defaultDNS, }, diff --git a/control.go b/control.go index fd1759e3..480f9fdc 100644 --- a/control.go +++ b/control.go @@ -437,6 +437,45 @@ func checkDNS(input string) error { return nil } +func handleSetBootstrapDNS(w http.ResponseWriter, r *http.Request) { + body, err := ioutil.ReadAll(r.Body) + if err != nil { + errorText := fmt.Sprintf("Failed to read request body: %s", err) + log.Println(errorText) + http.Error(w, errorText, http.StatusBadRequest) + return + } + // if empty body -- user is asking for default servers + hosts := strings.Fields(string(body)) + + if len(hosts) == 0 { + config.DNS.BootstrapDNS = defaultBootstrap + } else { + config.DNS.BootstrapDNS = hosts + } + + err = writeAllConfigs() + if err != nil { + errorText := fmt.Sprintf("Couldn't write config file: %s", err) + log.Println(errorText) + http.Error(w, errorText, http.StatusInternalServerError) + return + } + err = reconfigureDNSServer() + if err != nil { + errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err) + log.Println(errorText) + http.Error(w, errorText, http.StatusInternalServerError) + return + } + _, err = fmt.Fprintf(w, "OK %d bootsrap servers\n", len(hosts)) + if err != nil { + errorText := fmt.Sprintf("Couldn't write body: %s", err) + log.Println(errorText) + http.Error(w, errorText, http.StatusInternalServerError) + } +} + func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { now := time.Now() if now.Sub(versionCheckLastTime) <= versionCheckPeriod && len(versionCheckJSON) != 0 { @@ -1317,6 +1356,7 @@ 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/set_bootstrap_dns", postInstall(optionalAuth(ensurePOST(handleSetBootstrapDNS)))) 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/dns.go b/dns.go index b7f0d130..c70a4300 100644 --- a/dns.go +++ b/dns.go @@ -61,7 +61,7 @@ func generateServerConfig() dnsforward.ServerConfig { for _, u := range config.DNS.UpstreamDNS { opts := upstream.Options{ Timeout: dnsforward.DefaultTimeout, - Bootstrap: []string{config.DNS.BootstrapDNS}, + Bootstrap: config.DNS.BootstrapDNS, } dnsUpstream, err := upstream.AddressToUpstream(u, opts) if err != nil { diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 99f09e6d..30dd5449 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -66,7 +66,7 @@ type FilteringConfig struct { Ratelimit int `yaml:"ratelimit"` RatelimitWhitelist []string `yaml:"ratelimit_whitelist"` RefuseAny bool `yaml:"refuse_any"` - BootstrapDNS string `yaml:"bootstrap_dns"` + BootstrapDNS []string `yaml:"bootstrap_dns"` dnsfilter.Config `yaml:",inline"` } diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index f1e23f86..067def2a 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -142,6 +142,30 @@ 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: From 5ba43a59f4ea6035b4428c9bde9dfa2c65bb8beb Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 11:31:25 +0300 Subject: [PATCH 03/34] Fix server reconfig --- dns.go | 1 + 1 file changed, 1 insertion(+) diff --git a/dns.go b/dns.go index b7f0d130..6dfa2e8c 100644 --- a/dns.go +++ b/dns.go @@ -71,6 +71,7 @@ func generateServerConfig() dnsforward.ServerConfig { } newconfig.Upstreams = append(newconfig.Upstreams, dnsUpstream) } + newconfig.AllServers = config.DNS.AllServers return newconfig } From bf893d488afbb306e341f5bcc8e651053d92de97 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 12:58:42 +0300 Subject: [PATCH 04/34] Refactoring for set upstream and bootstrap DNS --- control.go | 105 +++++++++++++++++++++++++++++------------------------ 1 file changed, 58 insertions(+), 47 deletions(-) diff --git a/control.go b/control.go index 480f9fdc..726bf2b7 100644 --- a/control.go +++ b/control.go @@ -323,44 +323,94 @@ func sortByValue(m map[string]int) []string { // ----------------------- func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { + setDNSServers(&w, r, true) +} + +func handleSetBootstrapDNS(w http.ResponseWriter, r *http.Request) { + setDNSServers(&w, r, false) +} + +// 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 { errorText := fmt.Sprintf("Failed to read request body: %s", err) log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + http.Error(*w, errorText, http.StatusBadRequest) return } // if empty body -- user is asking for default servers hosts := strings.Fields(string(body)) - if len(hosts) == 0 { - config.DNS.UpstreamDNS = defaultDNS + // 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) + if err != nil { + log.Tracef("%s can not be used as bootstrap DNS cause: %s", host, err) + continue + } + hosts = append(bootstraps, host) + } + } + + // count of upstream or bootstrap servers + var count int + if upstreams { + count = len(hosts) } else { - config.DNS.UpstreamDNS = hosts + count = len(bootstraps) + } + + if upstreams { + if count == 0 { + config.DNS.UpstreamDNS = defaultDNS + } else { + config.DNS.UpstreamDNS = hosts + } + } else { + if count == 0 { + config.DNS.BootstrapDNS = defaultBootstrap + } else { + config.DNS.BootstrapDNS = bootstraps + } } err = writeAllConfigs() if err != nil { errorText := fmt.Sprintf("Couldn't write config file: %s", err) log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + http.Error(*w, errorText, http.StatusInternalServerError) return } err = reconfigureDNSServer() if err != nil { errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err) log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + http.Error(*w, errorText, http.StatusInternalServerError) return } - _, err = fmt.Fprintf(w, "OK %d servers\n", len(hosts)) + + _, err = fmt.Fprintf(*w, "OK %d servers\n", count) if err != nil { errorText := fmt.Sprintf("Couldn't write body: %s", err) log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + http.Error(*w, errorText, http.StatusInternalServerError) } } +func checkBootstrapDNS(host string) error { + // Check if host is ip without port + if net.ParseIP(host) != nil { + return nil + } + + // Check if host is ip with port + _, _, err := net.SplitHostPort(host) + return err +} + func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) if err != nil { @@ -437,45 +487,6 @@ func checkDNS(input string) error { return nil } -func handleSetBootstrapDNS(w http.ResponseWriter, r *http.Request) { - body, err := ioutil.ReadAll(r.Body) - if err != nil { - errorText := fmt.Sprintf("Failed to read request body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) - return - } - // if empty body -- user is asking for default servers - hosts := strings.Fields(string(body)) - - if len(hosts) == 0 { - config.DNS.BootstrapDNS = defaultBootstrap - } else { - config.DNS.BootstrapDNS = hosts - } - - err = writeAllConfigs() - if err != nil { - errorText := fmt.Sprintf("Couldn't write config file: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) - return - } - err = reconfigureDNSServer() - if err != nil { - errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) - return - } - _, err = fmt.Fprintf(w, "OK %d bootsrap servers\n", len(hosts)) - if err != nil { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) - } -} - func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { now := time.Now() if now.Sub(versionCheckLastTime) <= versionCheckPeriod && len(versionCheckJSON) != 0 { From 87c8114291179880a75f83943e33551774ae09f0 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 13:12:06 +0300 Subject: [PATCH 05/34] Use gotools --- control.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control.go b/control.go index 726bf2b7..e9890575 100644 --- a/control.go +++ b/control.go @@ -391,7 +391,6 @@ func setDNSServers(w *http.ResponseWriter, r *http.Request, upstreams bool) { http.Error(*w, errorText, http.StatusInternalServerError) return } - _, err = fmt.Fprintf(*w, "OK %d servers\n", count) if err != nil { errorText := fmt.Sprintf("Couldn't write body: %s", err) @@ -400,6 +399,7 @@ func setDNSServers(w *http.ResponseWriter, r *http.Request, upstreams bool) { } } +// checkBootstrapDNS checks if host is plain DNS func checkBootstrapDNS(host string) error { // Check if host is ip without port if net.ParseIP(host) != nil { From 3a9d436f8ae72a74f9e248d857c3d875aa81fcec Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 16:15:36 +0300 Subject: [PATCH 06/34] Add schema migration --- control.go | 52 ++++++++++++++++++------------------------ dhcp.go | 16 ++++++------- go.mod | 1 + upgrade.go | 66 +++++++++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 91 insertions(+), 44 deletions(-) diff --git a/control.go b/control.go index e9890575..dd368380 100644 --- a/control.go +++ b/control.go @@ -57,10 +57,10 @@ func returnOK(w http.ResponseWriter) { } } -func httpError(w http.ResponseWriter, code int, format string, args ...interface{}) { +func httpError(w *http.ResponseWriter, code int, format string, args ...interface{}) { text := fmt.Sprintf(format, args...) log.Println(text) - http.Error(w, text, code) + http.Error(*w, text, code) } // --------------- @@ -78,7 +78,7 @@ func writeAllConfigsAndReloadDNS() error { func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { err := writeAllConfigsAndReloadDNS() if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) + httpError(&w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } returnOK(w) @@ -334,9 +334,7 @@ func handleSetBootstrapDNS(w http.ResponseWriter, r *http.Request) { func setDNSServers(w *http.ResponseWriter, r *http.Request, upstreams bool) { body, err := ioutil.ReadAll(r.Body) if err != nil { - errorText := fmt.Sprintf("Failed to read request body: %s", err) - log.Println(errorText) - http.Error(*w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) return } // if empty body -- user is asking for default servers @@ -379,23 +377,17 @@ func setDNSServers(w *http.ResponseWriter, r *http.Request, upstreams bool) { err = writeAllConfigs() if err != nil { - errorText := fmt.Sprintf("Couldn't write config file: %s", err) - log.Println(errorText) - http.Error(*w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } err = reconfigureDNSServer() if err != nil { - errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err) - log.Println(errorText) - http.Error(*w, errorText, http.StatusInternalServerError) + 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 { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(*w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } } @@ -574,7 +566,7 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { f := filter{} err := json.NewDecoder(r.Body).Decode(&f) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to parse request body json: %s", err) + httpError(&w, http.StatusBadRequest, "Failed to parse request body json: %s", err) return } @@ -975,7 +967,7 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { ifaces, err := getValidNetInterfacesForWeb() if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) + httpError(&w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) return } @@ -987,7 +979,7 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(data) if err != nil { - httpError(w, http.StatusInternalServerError, "Unable to marshal default addresses to json: %s", err) + httpError(&w, http.StatusInternalServerError, "Unable to marshal default addresses to json: %s", err) return } } @@ -996,7 +988,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { newSettings := firstRunData{} err := json.NewDecoder(r.Body).Decode(&newSettings) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to parse new config json: %s", err) + httpError(&w, http.StatusBadRequest, "Failed to parse new config json: %s", err) return } @@ -1010,14 +1002,14 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { if restartHTTP { err = checkPortAvailable(newSettings.Web.IP, newSettings.Web.Port) if err != nil { - httpError(w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.Web.IP, strconv.Itoa(newSettings.Web.Port)), err) + httpError(&w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.Web.IP, strconv.Itoa(newSettings.Web.Port)), err) return } } err = checkPacketPortAvailable(newSettings.DNS.IP, newSettings.DNS.Port) if err != nil { - httpError(w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.DNS.IP, strconv.Itoa(newSettings.DNS.Port)), err) + httpError(&w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.DNS.IP, strconv.Itoa(newSettings.DNS.Port)), err) return } @@ -1032,7 +1024,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { if config.DNS.Port != 0 { err = startDNSServer() if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't start DNS server: %s", err) + httpError(&w, http.StatusInternalServerError, "Couldn't start DNS server: %s", err) return } } @@ -1057,7 +1049,7 @@ func handleTLSStatus(w http.ResponseWriter, r *http.Request) { func handleTLSValidate(w http.ResponseWriter, r *http.Request) { data, err := unmarshalTLS(r) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) + httpError(&w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) return } @@ -1070,7 +1062,7 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { if !alreadyRunning { err = checkPortAvailable(config.BindHost, data.PortHTTPS) if err != nil { - httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) + httpError(&w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) return } } @@ -1082,7 +1074,7 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { data, err := unmarshalTLS(r) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) + httpError(&w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) return } @@ -1095,7 +1087,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { if !alreadyRunning { err = checkPortAvailable(config.BindHost, data.PortHTTPS) if err != nil { - httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) + httpError(&w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) return } } @@ -1109,7 +1101,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { config.TLS = data err = writeAllConfigsAndReloadDNS() if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) + httpError(&w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } marshalTLS(w, data) @@ -1328,7 +1320,7 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { } err := json.NewEncoder(w).Encode(data) if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) + httpError(&w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) return } } @@ -1338,12 +1330,12 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { // -------------- func handleDOH(w http.ResponseWriter, r *http.Request) { if r.TLS == nil { - httpError(w, http.StatusNotFound, "Not Found") + httpError(&w, http.StatusNotFound, "Not Found") return } if !isRunning() { - httpError(w, http.StatusInternalServerError, "DNS server is not running") + httpError(&w, http.StatusInternalServerError, "DNS server is not running") return } diff --git a/dhcp.go b/dhcp.go index a67b0ef6..6098a1a2 100644 --- a/dhcp.go +++ b/dhcp.go @@ -37,7 +37,7 @@ func handleDHCPStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(status) if err != nil { - httpError(w, http.StatusInternalServerError, "Unable to marshal DHCP status json: %s", err) + httpError(&w, http.StatusInternalServerError, "Unable to marshal DHCP status json: %s", err) return } } @@ -46,14 +46,14 @@ func handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) { newconfig := dhcpd.ServerConfig{} err := json.NewDecoder(r.Body).Decode(&newconfig) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) + httpError(&w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) return } if newconfig.Enabled { err := dhcpServer.Start(&newconfig) if err != nil { - httpError(w, http.StatusBadRequest, "Failed to start DHCP server: %s", err) + httpError(&w, http.StatusBadRequest, "Failed to start DHCP server: %s", err) return } } @@ -72,7 +72,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { ifaces, err := getValidNetInterfaces() if err != nil { - httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) + httpError(&w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) return } @@ -87,7 +87,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { } addrs, err := iface.Addrs() if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err) + httpError(&w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err) return } @@ -105,7 +105,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { ipnet, ok := addr.(*net.IPNet) if !ok { // not an IPNet, should not happen - httpError(w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) + httpError(&w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) return } // ignore link-local @@ -122,7 +122,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { err = json.NewEncoder(w).Encode(response) if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to marshal json with available interfaces: %s", err) + httpError(&w, http.StatusInternalServerError, "Failed to marshal json with available interfaces: %s", err) return } } @@ -153,7 +153,7 @@ func handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(result) if err != nil { - httpError(w, http.StatusInternalServerError, "Failed to marshal DHCP found json: %s", err) + httpError(&w, http.StatusInternalServerError, "Failed to marshal DHCP found json: %s", err) return } } diff --git a/go.mod b/go.mod index cba63387..ebc1b9f3 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414 github.com/miekg/dns v1.1.1 + github.com/pkg/errors v0.8.0 github.com/shirou/gopsutil v2.18.10+incompatible github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/stretchr/testify v1.2.2 diff --git a/upgrade.go b/upgrade.go index 0b3ddc5c..b431dc71 100644 --- a/upgrade.go +++ b/upgrade.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "io/ioutil" "os" @@ -10,7 +11,7 @@ import ( yaml "gopkg.in/yaml.v2" ) -const currentSchemaVersion = 2 // used for upgrading from old configs to new config +const currentSchemaVersion = 3 // used for upgrading from old configs to new config // Performs necessary upgrade operations if needed func upgradeConfig() error { @@ -59,12 +60,17 @@ func upgradeConfig() error { func upgradeConfigSchema(oldVersion int, diskConfig *map[string]interface{}) error { switch oldVersion { case 0: - err := upgradeSchema0to2(diskConfig) + err := upgradeSchema0to3(diskConfig) if err != nil { return err } case 1: - err := upgradeSchema1to2(diskConfig) + err := upgradeSchema1to3(diskConfig) + if err != nil { + return err + } + case 2: + err := upgradeSchema2to3(diskConfig) if err != nil { return err } @@ -135,12 +141,60 @@ func upgradeSchema1to2(diskConfig *map[string]interface{}) error { return nil } -// jump two schemas at once -- this time we just do it sequentially -func upgradeSchema0to2(diskConfig *map[string]interface{}) error { +// Third schema upgrade: +// Bootstrap DNS becomes an array +func upgradeSchema2to3(diskConfig *map[string]interface{}) error { + log.Printf("%s(): called", _Func()) + + // Let's read dns configuration from diskConfig + dnsConfig, ok := (*diskConfig)["dns"] + if !ok { + return errors.New("no DNS configuration in config file") + } + + // Convert interface{} to map[string]interface{} + newDNSConfig := make(map[string]interface{}) + + switch v := dnsConfig.(type) { + case map[interface{}]interface{}: + for k, v := range v { + newDNSConfig[fmt.Sprint(k)] = v + } + default: + return errors.New("DNS configuration is not a map") + } + + // Replace bootstrap_dns value filed with new array contains old bootstrap_dns inside + if bootstrapDNS, ok := (newDNSConfig)["bootstrap_dns"]; ok { + newBootstrapConfig := []string{fmt.Sprint(bootstrapDNS)} + (newDNSConfig)["bootstrap_dns"] = newBootstrapConfig + (*diskConfig)["dns"] = newDNSConfig + } else { + return errors.New("no bootstrap DNS in DNS config") + } + + // Bump schema version + (*diskConfig)["schema_version"] = 3 + + return nil +} + +// jump three schemas at once -- this time we just do it sequentially +func upgradeSchema0to3(diskConfig *map[string]interface{}) error { err := upgradeSchema0to1(diskConfig) if err != nil { return err } - return upgradeSchema1to2(diskConfig) + return upgradeSchema1to3(diskConfig) +} + +// jump two schemas at once -- this time we just do it sequentially +func upgradeSchema1to3(diskConfig *map[string]interface{}) error { + err := upgradeSchema1to2(diskConfig) + if err != nil { + return err + } + + return upgradeSchema2to3(diskConfig) } From 141b14c94a1ef319188e555a51044d4b52f9dddf Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 16:19:45 +0300 Subject: [PATCH 07/34] Remove unuseful library --- go.mod | 1 - upgrade.go | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index ebc1b9f3..cba63387 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,6 @@ require ( github.com/kardianos/service v0.0.0-20181115005516-4c239ee84e7b github.com/krolaw/dhcp4 v0.0.0-20180925202202-7cead472c414 github.com/miekg/dns v1.1.1 - github.com/pkg/errors v0.8.0 github.com/shirou/gopsutil v2.18.10+incompatible github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 // indirect github.com/stretchr/testify v1.2.2 diff --git a/upgrade.go b/upgrade.go index b431dc71..8932b9c7 100644 --- a/upgrade.go +++ b/upgrade.go @@ -1,7 +1,6 @@ package main import ( - "errors" "fmt" "io/ioutil" "os" @@ -149,7 +148,7 @@ func upgradeSchema2to3(diskConfig *map[string]interface{}) error { // Let's read dns configuration from diskConfig dnsConfig, ok := (*diskConfig)["dns"] if !ok { - return errors.New("no DNS configuration in config file") + return fmt.Errorf("no DNS configuration in config file") } // Convert interface{} to map[string]interface{} @@ -161,7 +160,7 @@ func upgradeSchema2to3(diskConfig *map[string]interface{}) error { newDNSConfig[fmt.Sprint(k)] = v } default: - return errors.New("DNS configuration is not a map") + return fmt.Errorf("DNS configuration is not a map") } // Replace bootstrap_dns value filed with new array contains old bootstrap_dns inside @@ -170,7 +169,7 @@ func upgradeSchema2to3(diskConfig *map[string]interface{}) error { (newDNSConfig)["bootstrap_dns"] = newBootstrapConfig (*diskConfig)["dns"] = newDNSConfig } else { - return errors.New("no bootstrap DNS in DNS config") + return fmt.Errorf("no bootstrap DNS in DNS config") } // Bump schema version From 1223965cd43a50b96e7f76f78d936f6995370b65 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 16:42:50 +0300 Subject: [PATCH 08/34] Code simplify --- control.go | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/control.go b/control.go index dd368380..e5d88082 100644 --- a/control.go +++ b/control.go @@ -354,23 +354,19 @@ func setDNSServers(w *http.ResponseWriter, r *http.Request, upstreams bool) { } // count of upstream or bootstrap servers - var count int - if upstreams { - count = len(hosts) - } else { + count := len(hosts) + if !upstreams { count = len(bootstraps) } if upstreams { - if count == 0 { - config.DNS.UpstreamDNS = defaultDNS - } else { + config.DNS.UpstreamDNS = defaultDNS + if count != 0 { config.DNS.UpstreamDNS = hosts } } else { - if count == 0 { - config.DNS.BootstrapDNS = defaultBootstrap - } else { + config.DNS.BootstrapDNS = defaultBootstrap + if count != 0 { config.DNS.BootstrapDNS = bootstraps } } From a9839e95a0f1b3a0d8af09cdaa94308cb2f49c16 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 16:50:19 +0300 Subject: [PATCH 09/34] pointer is unuseful for httpError func --- control.go | 44 ++++++++++++++++++++++---------------------- dhcp.go | 16 ++++++++-------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/control.go b/control.go index e5d88082..3136ae2c 100644 --- a/control.go +++ b/control.go @@ -57,10 +57,10 @@ func returnOK(w http.ResponseWriter) { } } -func httpError(w *http.ResponseWriter, code int, format string, args ...interface{}) { +func httpError(w http.ResponseWriter, code int, format string, args ...interface{}) { text := fmt.Sprintf(format, args...) log.Println(text) - http.Error(*w, text, code) + http.Error(w, text, code) } // --------------- @@ -78,7 +78,7 @@ func writeAllConfigsAndReloadDNS() error { func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { err := writeAllConfigsAndReloadDNS() if err != nil { - httpError(&w, http.StatusInternalServerError, "Couldn't write config file: %s", err) + httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } returnOK(w) @@ -323,15 +323,15 @@ func sortByValue(m map[string]int) []string { // ----------------------- func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { - setDNSServers(&w, r, true) + setDNSServers(w, r, true) } func handleSetBootstrapDNS(w http.ResponseWriter, r *http.Request) { - setDNSServers(&w, r, false) + setDNSServers(w, r, false) } // setDNSServers sets upstream and bootstrap DNS servers -func setDNSServers(w *http.ResponseWriter, r *http.Request, upstreams bool) { +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) @@ -381,7 +381,7 @@ func setDNSServers(w *http.ResponseWriter, r *http.Request, upstreams bool) { httpError(w, http.StatusInternalServerError, "Couldn't reconfigure the DNS server: %s", err) return } - _, err = fmt.Fprintf(*w, "OK %d servers\n", count) + _, err = fmt.Fprintf(w, "OK %d servers\n", count) if err != nil { httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } @@ -562,7 +562,7 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { f := filter{} err := json.NewDecoder(r.Body).Decode(&f) if err != nil { - httpError(&w, http.StatusBadRequest, "Failed to parse request body json: %s", err) + httpError(w, http.StatusBadRequest, "Failed to parse request body json: %s", err) return } @@ -963,7 +963,7 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { ifaces, err := getValidNetInterfacesForWeb() if err != nil { - httpError(&w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) + httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) return } @@ -975,7 +975,7 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(data) if err != nil { - httpError(&w, http.StatusInternalServerError, "Unable to marshal default addresses to json: %s", err) + httpError(w, http.StatusInternalServerError, "Unable to marshal default addresses to json: %s", err) return } } @@ -984,7 +984,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { newSettings := firstRunData{} err := json.NewDecoder(r.Body).Decode(&newSettings) if err != nil { - httpError(&w, http.StatusBadRequest, "Failed to parse new config json: %s", err) + httpError(w, http.StatusBadRequest, "Failed to parse new config json: %s", err) return } @@ -998,14 +998,14 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { if restartHTTP { err = checkPortAvailable(newSettings.Web.IP, newSettings.Web.Port) if err != nil { - httpError(&w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.Web.IP, strconv.Itoa(newSettings.Web.Port)), err) + httpError(w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.Web.IP, strconv.Itoa(newSettings.Web.Port)), err) return } } err = checkPacketPortAvailable(newSettings.DNS.IP, newSettings.DNS.Port) if err != nil { - httpError(&w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.DNS.IP, strconv.Itoa(newSettings.DNS.Port)), err) + httpError(w, http.StatusBadRequest, "Impossible to listen on IP:port %s due to %s", net.JoinHostPort(newSettings.DNS.IP, strconv.Itoa(newSettings.DNS.Port)), err) return } @@ -1020,7 +1020,7 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { if config.DNS.Port != 0 { err = startDNSServer() if err != nil { - httpError(&w, http.StatusInternalServerError, "Couldn't start DNS server: %s", err) + httpError(w, http.StatusInternalServerError, "Couldn't start DNS server: %s", err) return } } @@ -1045,7 +1045,7 @@ func handleTLSStatus(w http.ResponseWriter, r *http.Request) { func handleTLSValidate(w http.ResponseWriter, r *http.Request) { data, err := unmarshalTLS(r) if err != nil { - httpError(&w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) + httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) return } @@ -1058,7 +1058,7 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { if !alreadyRunning { err = checkPortAvailable(config.BindHost, data.PortHTTPS) if err != nil { - httpError(&w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) + httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) return } } @@ -1070,7 +1070,7 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { data, err := unmarshalTLS(r) if err != nil { - httpError(&w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) + httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) return } @@ -1083,7 +1083,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { if !alreadyRunning { err = checkPortAvailable(config.BindHost, data.PortHTTPS) if err != nil { - httpError(&w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) + httpError(w, http.StatusBadRequest, "port %d is not available, cannot enable HTTPS on it", data.PortHTTPS) return } } @@ -1097,7 +1097,7 @@ func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { config.TLS = data err = writeAllConfigsAndReloadDNS() if err != nil { - httpError(&w, http.StatusInternalServerError, "Couldn't write config file: %s", err) + httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } marshalTLS(w, data) @@ -1316,7 +1316,7 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { } err := json.NewEncoder(w).Encode(data) if err != nil { - httpError(&w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) + httpError(w, http.StatusInternalServerError, "Failed to marshal json with TLS status: %s", err) return } } @@ -1326,12 +1326,12 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { // -------------- func handleDOH(w http.ResponseWriter, r *http.Request) { if r.TLS == nil { - httpError(&w, http.StatusNotFound, "Not Found") + httpError(w, http.StatusNotFound, "Not Found") return } if !isRunning() { - httpError(&w, http.StatusInternalServerError, "DNS server is not running") + httpError(w, http.StatusInternalServerError, "DNS server is not running") return } diff --git a/dhcp.go b/dhcp.go index 6098a1a2..a67b0ef6 100644 --- a/dhcp.go +++ b/dhcp.go @@ -37,7 +37,7 @@ func handleDHCPStatus(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(status) if err != nil { - httpError(&w, http.StatusInternalServerError, "Unable to marshal DHCP status json: %s", err) + httpError(w, http.StatusInternalServerError, "Unable to marshal DHCP status json: %s", err) return } } @@ -46,14 +46,14 @@ func handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) { newconfig := dhcpd.ServerConfig{} err := json.NewDecoder(r.Body).Decode(&newconfig) if err != nil { - httpError(&w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) + httpError(w, http.StatusBadRequest, "Failed to parse new DHCP config json: %s", err) return } if newconfig.Enabled { err := dhcpServer.Start(&newconfig) if err != nil { - httpError(&w, http.StatusBadRequest, "Failed to start DHCP server: %s", err) + httpError(w, http.StatusBadRequest, "Failed to start DHCP server: %s", err) return } } @@ -72,7 +72,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { ifaces, err := getValidNetInterfaces() if err != nil { - httpError(&w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) + httpError(w, http.StatusInternalServerError, "Couldn't get interfaces: %s", err) return } @@ -87,7 +87,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { } addrs, err := iface.Addrs() if err != nil { - httpError(&w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err) + httpError(w, http.StatusInternalServerError, "Failed to get addresses for interface %s: %s", iface.Name, err) return } @@ -105,7 +105,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { ipnet, ok := addr.(*net.IPNet) if !ok { // not an IPNet, should not happen - httpError(&w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) + httpError(w, http.StatusInternalServerError, "SHOULD NOT HAPPEN: got iface.Addrs() element %s that is not net.IPNet, it is %T", addr, addr) return } // ignore link-local @@ -122,7 +122,7 @@ func handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { err = json.NewEncoder(w).Encode(response) if err != nil { - httpError(&w, http.StatusInternalServerError, "Failed to marshal json with available interfaces: %s", err) + httpError(w, http.StatusInternalServerError, "Failed to marshal json with available interfaces: %s", err) return } } @@ -153,7 +153,7 @@ func handleDHCPFindActiveServer(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") err = json.NewEncoder(w).Encode(result) if err != nil { - httpError(&w, http.StatusInternalServerError, "Failed to marshal DHCP found json: %s", err) + httpError(w, http.StatusInternalServerError, "Failed to marshal DHCP found json: %s", err) return } } From 523c5ef10a064cd9f75742fc84d6ecbf0a764e58 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 17:28:10 +0300 Subject: [PATCH 10/34] Refactor httpErrors --- control.go | 206 ++++++++++++++--------------------------------------- 1 file changed, 55 insertions(+), 151 deletions(-) diff --git a/control.go b/control.go index 70ff0e7c..23caf56e 100644 --- a/control.go +++ b/control.go @@ -51,9 +51,7 @@ var client = &http.Client{ func returnOK(w http.ResponseWriter) { _, err := fmt.Fprintf(w, "OK\n") if err != nil { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } } @@ -101,17 +99,13 @@ func handleStatus(w http.ResponseWriter, r *http.Request) { 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) + 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 { - errorText := fmt.Sprintf("Unable to write response json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } @@ -144,18 +138,14 @@ func handleQueryLog(w http.ResponseWriter, r *http.Request) { jsonVal, err := json.Marshal(data) if err != nil { - errorText := fmt.Sprintf("Couldn't marshal data into json: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't marshal data into json: %s", err) 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, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) } } @@ -200,9 +190,7 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") _, err := w.Write(statsJSON.Bytes()) if err != nil { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } } @@ -211,9 +199,7 @@ func handleStatsReset(w http.ResponseWriter, r *http.Request) { dnsServer.PurgeStats() _, err := fmt.Fprintf(w, "OK\n") if err != nil { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } } @@ -223,17 +209,13 @@ func handleStats(w http.ResponseWriter, r *http.Request) { statsJSON, err := json.Marshal(summed) if err != nil { - errorText := fmt.Sprintf("Unable to marshal status json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err) return } w.Header().Set("Content-Type", "application/json") _, err = w.Write(statsJSON) if err != nil { - errorText := fmt.Sprintf("Unable to write response json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } @@ -260,40 +242,31 @@ func handleStatsHistory(w http.ResponseWriter, r *http.Request) { // parse start and end time startTime, err := time.Parse(time.RFC3339, r.URL.Query().Get("start_time")) if err != nil { - errorText := fmt.Sprintf("Must specify valid start_time parameter: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Must specify valid start_time parameter: %s", err) return } endTime, err := time.Parse(time.RFC3339, r.URL.Query().Get("end_time")) if err != nil { - errorText := fmt.Sprintf("Must specify valid end_time parameter: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Must specify valid end_time parameter: %s", err) return } data, err := dnsServer.GetStatsHistory(timeUnit, startTime, endTime) if err != nil { - errorText := fmt.Sprintf("Cannot get stats history: %s", err) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Cannot get stats history: %s", err) return } statsJSON, err := json.Marshal(data) if err != nil { - errorText := fmt.Sprintf("Unable to marshal status json: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err) return } w.Header().Set("Content-Type", "application/json") _, err = w.Write(statsJSON) if err != nil { - errorText := fmt.Sprintf("Unable to write response json: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } @@ -326,9 +299,7 @@ func sortByValue(m map[string]int) []string { func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) if err != nil { - errorText := fmt.Sprintf("Failed to read request body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) return } // if empty body -- user is asking for default servers @@ -342,23 +313,17 @@ func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { err = writeAllConfigs() if err != nil { - errorText := fmt.Sprintf("Couldn't write config file: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } err = reconfigureDNSServer() if err != nil { - errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't reconfigure the DNS server: %s", err) return } _, err = fmt.Fprintf(w, "OK %d servers\n", len(hosts)) if err != nil { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } } @@ -378,18 +343,14 @@ func handleAllServersStatus(w http.ResponseWriter, r *http.Request) { } 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) + 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 { - errorText := fmt.Sprintf("Unable to write response json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } @@ -397,17 +358,13 @@ func handleAllServersStatus(w http.ResponseWriter, r *http.Request) { func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) if err != nil { - errorText := fmt.Sprintf("Failed to read request body: %s", err) - log.Println(errorText) - http.Error(w, errorText, 400) + httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) return } hosts := strings.Fields(string(body)) if len(hosts) == 0 { - errorText := fmt.Sprintf("No servers specified") - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "No servers specified") return } @@ -425,18 +382,14 @@ func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { jsonVal, err := json.Marshal(result) if err != nil { - errorText := fmt.Sprintf("Unable to marshal status json: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + 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 { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } } @@ -481,9 +434,7 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { resp, err := client.Get(versionCheckURL) if err != nil { - errorText := fmt.Sprintf("Couldn't get version check json from %s: %T %s\n", versionCheckURL, err, err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadGateway) + httpError(w, http.StatusBadGateway, "Couldn't get version check json from %s: %T %s\n", versionCheckURL, err, err) return } if resp != nil && resp.Body != nil { @@ -493,18 +444,14 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { // read the body entirely body, err := ioutil.ReadAll(resp.Body) if err != nil { - errorText := fmt.Sprintf("Couldn't read response body from %s: %s", versionCheckURL, err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadGateway) + httpError(w, http.StatusBadGateway, "Couldn't read response body from %s: %s", versionCheckURL, err) return } w.Header().Set("Content-Type", "application/json") _, err = w.Write(body) if err != nil { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } versionCheckLastTime = now @@ -537,18 +484,14 @@ func handleFilteringStatus(w http.ResponseWriter, r *http.Request) { config.RUnlock() if err != nil { - errorText := fmt.Sprintf("Unable to marshal status json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + 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 { - errorText := fmt.Sprintf("Unable to write response json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } @@ -562,21 +505,19 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { } if len(f.URL) == 0 { - http.Error(w, "URL parameter was not specified", 400) + http.Error(w, "URL parameter was not specified", http.StatusBadRequest) return } if valid := govalidator.IsRequestURL(f.URL); !valid { - http.Error(w, "URL parameter is not valid request URL", 400) + http.Error(w, "URL parameter is not valid request URL", http.StatusBadRequest) return } // Check for duplicates for i := range config.Filters { if config.Filters[i].URL == f.URL { - errorText := fmt.Sprintf("Filter URL already added -- %s", f.URL) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Filter URL already added -- %s", f.URL) return } } @@ -588,30 +529,22 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { // Download the filter contents ok, err := f.update(true) if err != nil { - errorText := fmt.Sprintf("Couldn't fetch filter from url %s: %s", f.URL, err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Couldn't fetch filter from url %s: %s", f.URL, err) return } if f.RulesCount == 0 { - errorText := fmt.Sprintf("Filter at the url %s has no rules (maybe it points to blank page?)", f.URL) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Filter at the url %s has no rules (maybe it points to blank page?)", f.URL) return } if !ok { - errorText := fmt.Sprintf("Filter at the url %s is invalid (maybe it points to blank page?)", f.URL) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Filter at the url %s is invalid (maybe it points to blank page?)", f.URL) return } // Save the filter contents err = f.save() if err != nil { - errorText := fmt.Sprintf("Failed to save filter %d due to %s", f.ID, err) - log.Println(errorText) - http.Error(w, errorText, http.StatusBadRequest) + httpError(w, http.StatusBadRequest, "Failed to save filter %d due to %s", f.ID, err) return } @@ -620,44 +553,36 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { config.Filters = append(config.Filters, f) err = writeAllConfigs() if err != nil { - errorText := fmt.Sprintf("Couldn't write config file: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write config file: %s", err) return } err = reconfigureDNSServer() if err != nil { - errorText := fmt.Sprintf("Couldn't reconfigure the DNS server: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't reconfigure the DNS server: %s", err) } _, err = fmt.Fprintf(w, "OK %d rules\n", f.RulesCount) if err != nil { - errorText := fmt.Sprintf("Couldn't write body: %s", err) - log.Println(errorText) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't write body: %s", err) } } func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) { parameters, err := parseParametersFromBody(r.Body) if err != nil { - errorText := fmt.Sprintf("failed to parse parameters from body: %s", err) - log.Println(errorText) - http.Error(w, errorText, 400) + httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) return } url, ok := parameters["url"] if !ok { - http.Error(w, "URL parameter was not specified", 400) + http.Error(w, "URL parameter was not specified", http.StatusBadRequest) return } if valid := govalidator.IsRequestURL(url); !valid { - http.Error(w, "URL parameter is not valid request URL", 400) + http.Error(w, "URL parameter is not valid request URL", http.StatusBadRequest) return } @@ -670,8 +595,7 @@ func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) { // Remove the filter file err := os.Remove(filter.Path()) if err != nil && !os.IsNotExist(err) { - errorText := fmt.Sprintf("Couldn't remove the filter file: %s", err) - http.Error(w, errorText, http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError, "Couldn't remove the filter file: %s", err) return } } @@ -684,15 +608,13 @@ func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) { func handleFilteringEnableURL(w http.ResponseWriter, r *http.Request) { parameters, err := parseParametersFromBody(r.Body) if err != nil { - errorText := fmt.Sprintf("failed to parse parameters from body: %s", err) - log.Println(errorText) - http.Error(w, errorText, 400) + httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) return } url, ok := parameters["url"] if !ok { - http.Error(w, "URL parameter was not specified", 400) + http.Error(w, "URL parameter was not specified", http.StatusBadRequest) return } @@ -723,15 +645,13 @@ func handleFilteringEnableURL(w http.ResponseWriter, r *http.Request) { func handleFilteringDisableURL(w http.ResponseWriter, r *http.Request) { parameters, err := parseParametersFromBody(r.Body) if err != nil { - errorText := fmt.Sprintf("failed to parse parameters from body: %s", err) - log.Println(errorText) - http.Error(w, errorText, 400) + httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) return } url, ok := parameters["url"] if !ok { - http.Error(w, "URL parameter was not specified", 400) + http.Error(w, "URL parameter was not specified", http.StatusBadRequest) return } @@ -760,9 +680,7 @@ func handleFilteringDisableURL(w http.ResponseWriter, r *http.Request) { func handleFilteringSetRules(w http.ResponseWriter, r *http.Request) { body, err := ioutil.ReadAll(r.Body) if err != nil { - errorText := fmt.Sprintf("Failed to read request body: %s", err) - log.Println(errorText) - http.Error(w, errorText, 400) + httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) return } @@ -796,17 +714,13 @@ func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) { } 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) + httpError(w, http.StatusInternalServerError, "Unable to marshal status json: %s", err) } 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) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } @@ -817,9 +731,7 @@ func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) { func handleParentalEnable(w http.ResponseWriter, r *http.Request) { parameters, err := parseParametersFromBody(r.Body) if err != nil { - errorText := fmt.Sprintf("failed to parse parameters from body: %s", err) - log.Println(errorText) - http.Error(w, errorText, 400) + httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) return } @@ -874,18 +786,14 @@ func handleParentalStatus(w http.ResponseWriter, r *http.Request) { } 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) + 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 { - errorText := fmt.Sprintf("Unable to write response json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } @@ -910,18 +818,14 @@ func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) { } 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) + 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 { - errorText := fmt.Sprintf("Unable to write response json: %s", err) - log.Println(errorText) - http.Error(w, errorText, 500) + httpError(w, http.StatusInternalServerError, "Unable to write response json: %s", err) return } } From 1ed3a9673dfca48deed23099105d336ee632a9ff Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 17:39:07 +0300 Subject: [PATCH 11/34] Add handles logging --- control.go | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/control.go b/control.go index 23caf56e..b17e1c2e 100644 --- a/control.go +++ b/control.go @@ -83,6 +83,7 @@ func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { } func handleStatus(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := map[string]interface{}{ "dns_address": config.DNS.BindHost, "http_port": config.BindPort, @@ -111,11 +112,13 @@ func handleStatus(w http.ResponseWriter, r *http.Request) { } func handleProtectionEnable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.ProtectionEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleProtectionDisable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.ProtectionEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } @@ -124,16 +127,19 @@ func handleProtectionDisable(w http.ResponseWriter, r *http.Request) { // stats // ----- func handleQueryLogEnable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.QueryLogEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleQueryLogDisable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.QueryLogEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleQueryLog(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := dnsServer.GetQueryLog() jsonVal, err := json.Marshal(data) @@ -150,6 +156,7 @@ func handleQueryLog(w http.ResponseWriter, r *http.Request) { } func handleStatsTop(w http.ResponseWriter, r *http.Request) { + log.Tracef("") s := dnsServer.GetStatsTop() // use manual json marshalling because we want maps to be sorted by value @@ -196,6 +203,7 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) { // handleStatsReset resets the stats caches func handleStatsReset(w http.ResponseWriter, r *http.Request) { + log.Tracef("") dnsServer.PurgeStats() _, err := fmt.Fprintf(w, "OK\n") if err != nil { @@ -205,6 +213,7 @@ func handleStatsReset(w http.ResponseWriter, r *http.Request) { // handleStats returns aggregated stats data for the 24 hours func handleStats(w http.ResponseWriter, r *http.Request) { + log.Tracef("") summed := dnsServer.GetAggregatedStats() statsJSON, err := json.Marshal(summed) @@ -222,6 +231,7 @@ func handleStats(w http.ResponseWriter, r *http.Request) { // HandleStatsHistory returns historical stats data for the 24 hours func handleStatsHistory(w http.ResponseWriter, r *http.Request) { + log.Tracef("") // handle time unit and prepare our time window size timeUnitString := r.URL.Query().Get("time_unit") var timeUnit time.Duration @@ -297,6 +307,7 @@ func sortByValue(m map[string]int) []string { // ----------------------- func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { + log.Tracef("") body, err := ioutil.ReadAll(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) @@ -328,16 +339,19 @@ func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { } func handleAllServersEnable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.AllServers = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleAllServersDisable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.AllServers = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleAllServersStatus(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := map[string]interface{}{ "enabled": config.DNS.AllServers, } @@ -356,6 +370,7 @@ func handleAllServersStatus(w http.ResponseWriter, r *http.Request) { } func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { + log.Tracef("") body, err := ioutil.ReadAll(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) @@ -424,6 +439,7 @@ func checkDNS(input string) error { } func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { + log.Tracef("") now := time.Now() if now.Sub(versionCheckLastTime) <= versionCheckPeriod && len(versionCheckJSON) != 0 { // return cached copy @@ -463,16 +479,19 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { // --------- func handleFilteringEnable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.FilteringEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleFilteringDisable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.FilteringEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleFilteringStatus(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := map[string]interface{}{ "enabled": config.DNS.FilteringEnabled, } @@ -497,6 +516,7 @@ func handleFilteringStatus(w http.ResponseWriter, r *http.Request) { } func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { + log.Tracef("") f := filter{} err := json.NewDecoder(r.Body).Decode(&f) if err != nil { @@ -569,6 +589,7 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) { + log.Tracef("") parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -606,6 +627,7 @@ func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringEnableURL(w http.ResponseWriter, r *http.Request) { + log.Tracef("") parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -643,6 +665,7 @@ func handleFilteringEnableURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringDisableURL(w http.ResponseWriter, r *http.Request) { + log.Tracef("") parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -678,6 +701,7 @@ func handleFilteringDisableURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringSetRules(w http.ResponseWriter, r *http.Request) { + log.Tracef("") body, err := ioutil.ReadAll(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) @@ -689,6 +713,7 @@ func handleFilteringSetRules(w http.ResponseWriter, r *http.Request) { } func handleFilteringRefresh(w http.ResponseWriter, r *http.Request) { + log.Tracef("") force := r.URL.Query().Get("force") updated := refreshFiltersIfNecessary(force != "") fmt.Fprintf(w, "OK %d filters updated\n", updated) @@ -699,16 +724,19 @@ func handleFilteringRefresh(w http.ResponseWriter, r *http.Request) { // ------------ func handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.SafeBrowsingEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.SafeBrowsingEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := map[string]interface{}{ "enabled": config.DNS.SafeBrowsingEnabled, } @@ -729,6 +757,7 @@ func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) { // parental // -------- func handleParentalEnable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -773,11 +802,13 @@ func handleParentalEnable(w http.ResponseWriter, r *http.Request) { } func handleParentalDisable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.ParentalEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleParentalStatus(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := map[string]interface{}{ "enabled": config.DNS.ParentalEnabled, } @@ -803,16 +834,19 @@ func handleParentalStatus(w http.ResponseWriter, r *http.Request) { // ------------ func handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.SafeSearchEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) { + log.Tracef("") config.DNS.SafeSearchEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := map[string]interface{}{ "enabled": config.DNS.SafeSearchEnabled, } @@ -845,6 +879,7 @@ type firstRunData struct { } func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data := firstRunData{} // find out if port 80 is available -- if not, fall back to 3000 @@ -880,6 +915,7 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { } func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { + log.Tracef("") newSettings := firstRunData{} err := json.NewDecoder(r.Body).Decode(&newSettings) if err != nil { @@ -938,10 +974,12 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { // TLS // --- func handleTLSStatus(w http.ResponseWriter, r *http.Request) { + log.Tracef("") marshalTLS(w, config.TLS) } func handleTLSValidate(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data, err := unmarshalTLS(r) if err != nil { httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) @@ -967,6 +1005,7 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { } func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { + log.Tracef("") data, err := unmarshalTLS(r) if err != nil { httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) @@ -1224,6 +1263,7 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { // DNS-over-HTTPS // -------------- func handleDOH(w http.ResponseWriter, r *http.Request) { + log.Tracef("") if r.TLS == nil { httpError(w, http.StatusNotFound, "Not Found") return From 1626b6bd5aace36f86cc7ea9c399396b4aa1f73b Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 18:09:57 +0300 Subject: [PATCH 12/34] Fix empty logging --- control.go | 80 +++++++++++++++++++++++++++--------------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/control.go b/control.go index b17e1c2e..ed5fc682 100644 --- a/control.go +++ b/control.go @@ -83,7 +83,7 @@ func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) { } func handleStatus(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data := map[string]interface{}{ "dns_address": config.DNS.BindHost, "http_port": config.BindPort, @@ -112,13 +112,13 @@ func handleStatus(w http.ResponseWriter, r *http.Request) { } func handleProtectionEnable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.ProtectionEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleProtectionDisable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.ProtectionEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } @@ -127,19 +127,19 @@ func handleProtectionDisable(w http.ResponseWriter, r *http.Request) { // stats // ----- func handleQueryLogEnable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.QueryLogEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleQueryLogDisable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.QueryLogEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleQueryLog(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data := dnsServer.GetQueryLog() jsonVal, err := json.Marshal(data) @@ -156,7 +156,7 @@ func handleQueryLog(w http.ResponseWriter, r *http.Request) { } func handleStatsTop(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) s := dnsServer.GetStatsTop() // use manual json marshalling because we want maps to be sorted by value @@ -203,7 +203,7 @@ func handleStatsTop(w http.ResponseWriter, r *http.Request) { // handleStatsReset resets the stats caches func handleStatsReset(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) dnsServer.PurgeStats() _, err := fmt.Fprintf(w, "OK\n") if err != nil { @@ -213,7 +213,7 @@ func handleStatsReset(w http.ResponseWriter, r *http.Request) { // handleStats returns aggregated stats data for the 24 hours func handleStats(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) summed := dnsServer.GetAggregatedStats() statsJSON, err := json.Marshal(summed) @@ -231,7 +231,7 @@ func handleStats(w http.ResponseWriter, r *http.Request) { // HandleStatsHistory returns historical stats data for the 24 hours func handleStatsHistory(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) // handle time unit and prepare our time window size timeUnitString := r.URL.Query().Get("time_unit") var timeUnit time.Duration @@ -307,7 +307,7 @@ func sortByValue(m map[string]int) []string { // ----------------------- func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) body, err := ioutil.ReadAll(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) @@ -339,19 +339,19 @@ func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { } func handleAllServersEnable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + 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("") + 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("") + log.Tracef("%s %v", r.Method, r.URL) data := map[string]interface{}{ "enabled": config.DNS.AllServers, } @@ -370,7 +370,7 @@ func handleAllServersStatus(w http.ResponseWriter, r *http.Request) { } func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) body, err := ioutil.ReadAll(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) @@ -439,7 +439,7 @@ func checkDNS(input string) error { } func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) now := time.Now() if now.Sub(versionCheckLastTime) <= versionCheckPeriod && len(versionCheckJSON) != 0 { // return cached copy @@ -479,19 +479,19 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) { // --------- func handleFilteringEnable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.FilteringEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleFilteringDisable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.FilteringEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleFilteringStatus(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data := map[string]interface{}{ "enabled": config.DNS.FilteringEnabled, } @@ -516,7 +516,7 @@ func handleFilteringStatus(w http.ResponseWriter, r *http.Request) { } func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) f := filter{} err := json.NewDecoder(r.Body).Decode(&f) if err != nil { @@ -589,7 +589,7 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -627,7 +627,7 @@ func handleFilteringRemoveURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringEnableURL(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -665,7 +665,7 @@ func handleFilteringEnableURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringDisableURL(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -701,7 +701,7 @@ func handleFilteringDisableURL(w http.ResponseWriter, r *http.Request) { } func handleFilteringSetRules(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) body, err := ioutil.ReadAll(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) @@ -713,7 +713,7 @@ func handleFilteringSetRules(w http.ResponseWriter, r *http.Request) { } func handleFilteringRefresh(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) force := r.URL.Query().Get("force") updated := refreshFiltersIfNecessary(force != "") fmt.Fprintf(w, "OK %d filters updated\n", updated) @@ -724,19 +724,19 @@ func handleFilteringRefresh(w http.ResponseWriter, r *http.Request) { // ------------ func handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.SafeBrowsingEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.SafeBrowsingEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data := map[string]interface{}{ "enabled": config.DNS.SafeBrowsingEnabled, } @@ -757,7 +757,7 @@ func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) { // parental // -------- func handleParentalEnable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) parameters, err := parseParametersFromBody(r.Body) if err != nil { httpError(w, http.StatusBadRequest, "failed to parse parameters from body: %s", err) @@ -802,13 +802,13 @@ func handleParentalEnable(w http.ResponseWriter, r *http.Request) { } func handleParentalDisable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.ParentalEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleParentalStatus(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data := map[string]interface{}{ "enabled": config.DNS.ParentalEnabled, } @@ -834,19 +834,19 @@ func handleParentalStatus(w http.ResponseWriter, r *http.Request) { // ------------ func handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.SafeSearchEnabled = true httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) config.DNS.SafeSearchEnabled = false httpUpdateConfigReloadDNSReturnOK(w, r) } func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data := map[string]interface{}{ "enabled": config.DNS.SafeSearchEnabled, } @@ -879,7 +879,7 @@ type firstRunData struct { } func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data := firstRunData{} // find out if port 80 is available -- if not, fall back to 3000 @@ -915,7 +915,7 @@ func handleInstallGetAddresses(w http.ResponseWriter, r *http.Request) { } func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) newSettings := firstRunData{} err := json.NewDecoder(r.Body).Decode(&newSettings) if err != nil { @@ -974,12 +974,12 @@ func handleInstallConfigure(w http.ResponseWriter, r *http.Request) { // TLS // --- func handleTLSStatus(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) marshalTLS(w, config.TLS) } func handleTLSValidate(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data, err := unmarshalTLS(r) if err != nil { httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) @@ -1005,7 +1005,7 @@ func handleTLSValidate(w http.ResponseWriter, r *http.Request) { } func handleTLSConfigure(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) data, err := unmarshalTLS(r) if err != nil { httpError(w, http.StatusBadRequest, "Failed to unmarshal TLS config: %s", err) @@ -1263,7 +1263,7 @@ func marshalTLS(w http.ResponseWriter, data tlsConfig) { // DNS-over-HTTPS // -------------- func handleDOH(w http.ResponseWriter, r *http.Request) { - log.Tracef("") + log.Tracef("%s %v", r.Method, r.URL) if r.TLS == nil { httpError(w, http.StatusNotFound, "Not Found") return From bc325de13f2714961fdf32ef79fc3451d8db1f83 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Wed, 27 Feb 2019 18:49:53 +0300 Subject: [PATCH 13/34] Add missed logging --- control.go | 1 + 1 file changed, 1 insertion(+) diff --git a/control.go b/control.go index 52f7932f..2a32b0b4 100644 --- a/control.go +++ b/control.go @@ -338,6 +338,7 @@ func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { } func handleSetBootstrapDNS(w http.ResponseWriter, r *http.Request) { + log.Tracef("%s %v", r.Method, r.URL) setDNSServers(w, r, false) } From 6b2baba3c76b756337872bb38fb802e30fbc1d89 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Thu, 28 Feb 2019 11:10:43 +0300 Subject: [PATCH 14/34] Add set_upstreams_config function --- control.go | 5 +++++ openapi/openapi.yaml | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/control.go b/control.go index d27a7cfc..d57114ce 100644 --- a/control.go +++ b/control.go @@ -306,6 +306,10 @@ func sortByValue(m map[string]int) []string { // upstreams configuration // ----------------------- +func handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request) { + log.Tracef("%s %v", r.Method, r.URL) +} + func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) { log.Tracef("%s %v", r.Method, r.URL) body, err := ioutil.ReadAll(r.Body) @@ -1293,6 +1297,7 @@ func registerControlHandlers() { 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/all_servers/enable", postInstall(optionalAuth(ensurePOST(handleAllServersEnable)))) http.HandleFunc("/control/all_servers/disable", postInstall(optionalAuth(ensurePOST(handleAllServersDisable)))) diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index e83d6360..f0fb68ee 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -87,6 +87,25 @@ paths: 200: description: OK + /set_upstreams_config: + post: + tags: + - global + operationId: setUpstreamsConfig + summary: "Updates the current upstreams configuration" + consumes: + - application/json + parameters: + - in: "body" + name: "body" + description: "Upstreams configuration JSON" + required: true + schema: + $ref: "#/definitions/UpstreamsConfig" + responses: + 200: + description: OK + /set_upstream_dns: post: tags: @@ -831,6 +850,25 @@ definitions: language: type: "string" example: "en" + UpstreamsConfig: + type: "object" + description: "Upstreams configuration" + required: + - "bootstrap_dns" + - "upstream_dns" + properties: + bootstrap_dns: + type: "string" + example: "8.8.8.8:53" + upstream_dns: + type: "array" + items: + type: "string" + example: + - "tls://1.1.1.1" + - "tls://1.0.0.1" + all_servers: + type: "boolean" Filter: type: "object" description: "Filter subscription info" From 3929f0da449ce0a8368416ebd46a1f83617fdcbd Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Thu, 28 Feb 2019 13:01:41 +0300 Subject: [PATCH 15/34] [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" From acb4a984664b95d280522792be56bec2146da430 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Thu, 28 Feb 2019 13:40:40 +0300 Subject: [PATCH 16/34] [change] dnsforward: Add comments for public fields --- dnsforward/dnsforward.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 4f910bc8..7b8430ed 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -62,12 +62,12 @@ type FilteringConfig struct { ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features FilteringEnabled bool `yaml:"filtering_enabled"` // whether or not use filter lists BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600) - QueryLogEnabled bool `yaml:"querylog_enabled"` - Ratelimit int `yaml:"ratelimit"` - RatelimitWhitelist []string `yaml:"ratelimit_whitelist"` - RefuseAny bool `yaml:"refuse_any"` - BootstrapDNS []string `yaml:"bootstrap_dns"` - AllServers bool `yaml:"all_servers"` + QueryLogEnabled bool `yaml:"querylog_enabled"` // if true, query log is enabled + Ratelimit int `yaml:"ratelimit"` // max number of requests per second from a given IP (0 to disable) + RatelimitWhitelist []string `yaml:"ratelimit_whitelist"` // a list of whitelisted client IP addresses + RefuseAny bool `yaml:"refuse_any"` // if true, refuse ANY requests + BootstrapDNS []string `yaml:"bootstrap_dns"` // a list of bootstrap DNS for DoH and DoT (plain DNS only) + AllServers bool `yaml:"all_servers"` // if true, parallel queries to all configured upstream servers are enabled dnsfilter.Config `yaml:",inline"` } From a8812908c1062e4dcfe6e30e9cf9612e41967aa1 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Thu, 28 Feb 2019 15:06:30 +0300 Subject: [PATCH 17/34] [change] control: Fix issues from review --- control.go | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/control.go b/control.go index 5b1dcf3c..85775c1d 100644 --- a/control.go +++ b/control.go @@ -322,44 +322,31 @@ func handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request) { return } - setDNSServers(newconfig.upstreams, true) - setDNSServers(newconfig.bootstrapDNS, false) - config.DNS.AllServers = newconfig.allServers - httpUpdateConfigReloadDNSReturnOK(w, r) -} + config.DNS.UpstreamDNS = defaultDNS + if len(newconfig.upstreams) > 0 { + config.DNS.UpstreamDNS = newconfig.upstreams + } -// setDNSServers sets upstream and bootstrap DNS servers -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 { + if len(newconfig.bootstrapDNS) > 0 { + for _, host := range newconfig.bootstrapDNS { err := checkBootstrapDNS(host) if err != nil { log.Tracef("%s can not be used as bootstrap DNS cause: %s", host, err) continue } - hosts = append(bootstraps, host) + bootstraps = append(bootstraps, host) } } - // count of upstream or bootstrap servers - count := len(hosts) - if !upstreams { - count = len(bootstraps) + config.DNS.BootstrapDNS = defaultBootstrap + if len(bootstraps) > 0 { + config.DNS.BootstrapDNS = bootstraps } - if upstreams { - config.DNS.UpstreamDNS = defaultDNS - if count != 0 { - config.DNS.UpstreamDNS = hosts - } - } else { - config.DNS.BootstrapDNS = defaultBootstrap - if count != 0 { - config.DNS.BootstrapDNS = bootstraps - } - } + config.DNS.AllServers = newconfig.allServers + httpUpdateConfigReloadDNSReturnOK(w, r) } // checkBootstrapDNS checks if host is plain DNS From 91f8ab0549f07815a0d72301d9b60bc903338ba3 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Thu, 28 Feb 2019 15:18:51 +0300 Subject: [PATCH 18/34] [change] control: Remove unuseful check --- control.go | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/control.go b/control.go index 85775c1d..7b17321b 100644 --- a/control.go +++ b/control.go @@ -329,15 +329,13 @@ func handleSetUpstreamConfig(w http.ResponseWriter, r *http.Request) { // bootstrap servers are plain DNS only. We should remove tls:// https:// and sdns:// hosts from slice bootstraps := []string{} - if len(newconfig.bootstrapDNS) > 0 { - for _, host := range newconfig.bootstrapDNS { - err := checkBootstrapDNS(host) - if err != nil { - log.Tracef("%s can not be used as bootstrap DNS cause: %s", host, err) - continue - } - bootstraps = append(bootstraps, host) + for _, host := range newconfig.bootstrapDNS { + err := checkBootstrapDNS(host) + if err != nil { + log.Tracef("%s can not be used as bootstrap DNS cause: %s", host, err) + continue } + bootstraps = append(bootstraps, host) } config.DNS.BootstrapDNS = defaultBootstrap From d3185459138e4fd0cb8ae21869d6c2fe9d73c5f3 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Thu, 28 Feb 2019 20:41:18 +0300 Subject: [PATCH 19/34] [change] upgrade_test: add test for upgrade --- upgrade_test.go | 183 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 upgrade_test.go diff --git a/upgrade_test.go b/upgrade_test.go new file mode 100644 index 00000000..e474cc1f --- /dev/null +++ b/upgrade_test.go @@ -0,0 +1,183 @@ +package main + +import ( + "fmt" + "testing" +) + +func TestUpgrade1to2(t *testing.T) { + // Let's create test config + diskConfig := createTestDiskConfig(1) + oldDNSConfig := createTestDNSConfig(1) + + err := upgradeSchema1to2(&diskConfig) + if err != nil { + t.Fatalf("Can't upgrade schema version from 1 to 2") + } + + _, ok := diskConfig["coredns"] + if ok { + t.Fatalf("Core DNS config was not removed after upgrade schema version from 1 to 2") + } + + dnsMap, ok := diskConfig["dns"] + if !ok { + t.Fatalf("No DNS config after upgrade schema version from 1 to 2") + } + + // Cast dns configuration to map + newDNSConfig := make(map[string]interface{}) + switch v := dnsMap.(type) { + case map[interface{}]interface{}: + if len(oldDNSConfig) != len(v) { + t.Fatalf("We loose some data") + } + for key, value := range v { + newDNSConfig[fmt.Sprint(key)] = value + } + default: + t.Fatalf("DNS configuration is not a map") + } + + _, v, err := compareConfigs(oldDNSConfig, newDNSConfig) + if err != nil { + t.Fatalf("Wrong data %s, %s", v, err) + } +} + +func TestUpgrade2to3(t *testing.T) { + // Let's create test config + diskConfig := createTestDiskConfig(2) + oldDNSConfig := createTestDNSConfig(2) + + // Upgrade schema from 2 to 3 + err := upgradeSchema2to3(&diskConfig) + if err != nil { + t.Fatalf("Can't update schema version from 2 to 3: %s", err) + } + + // Check new schema version + newSchemaVersion := diskConfig["schema_version"] + switch v := newSchemaVersion.(type) { + case int: + if v != 3 { + t.Fatalf("Wrong schema version in new config file") + } + default: + t.Fatalf("Schema version is not an integer after update") + } + + // Let's get new dns configuration + dnsMap, ok := diskConfig["dns"] + if !ok { + t.Fatalf("No dns config in new configuration") + } + + // Cast dns configuration to map + newDNSConfig := make(map[string]interface{}) + switch v := dnsMap.(type) { + case map[string]interface{}: + for key, value := range v { + newDNSConfig[fmt.Sprint(key)] = value + } + default: + t.Fatalf("DNS configuration is not a map") + } + + // Check if bootstrap DNS becomes an array + bootstrapDNS := newDNSConfig["bootstrap_dns"] + switch v := bootstrapDNS.(type) { + case []string: + if len(v) != 1 { + t.Fatalf("Wrong count of bootsrap DNS servers") + } + + if v[0] != "8.8.8.8:53" { + t.Fatalf("Wrong bootsrap DNS servers") + } + default: + t.Fatalf("Wrong type for bootsrap DNS") + } + + // Set old value for bootstrap_dns and compare old and new configurations + newDNSConfig["bootstrap_dns"] = "8.8.8.8:53" + _, v, err := compareConfigs(oldDNSConfig, newDNSConfig) + if err != nil { + t.Fatalf("%s value is wrong: %s", v, err) + } +} + +// TODO add comparation for all possible types +func compareConfigs(oldConfig map[interface{}]interface{}, newConfig map[string]interface{}) (string, interface{}, error) { + oldConfigCasted := make(map[string]interface{}) + for k, v := range oldConfig { + oldConfigCasted[fmt.Sprint(k)] = v + } + + // Check old data and new data + for k, v := range newConfig { + switch value := v.(type) { + case string: + case int: + if value != oldConfigCasted[k] { + + } + case []string: + for i, s := range value { + if oldConfigCasted[k].([]string)[i] != s { + return k, v, fmt.Errorf("wrong data for %s", k) + } + } + case bool: + if v != oldConfigCasted[k].(bool) { + return k, v, fmt.Errorf("wrong data for %s", k) + } + default: + return k, v, fmt.Errorf("unknown type in DNS configuration for %s", k) + } + } + + return "", nil, nil +} + +func createTestDiskConfig(schemaVersion int) (diskConfig map[string]interface{}) { + diskConfig = make(map[string]interface{}) + diskConfig["language"] = "en" + diskConfig["filters"] = []filter{} + diskConfig["user_rules"] = []string{} + diskConfig["schema_version"] = schemaVersion + diskConfig["bind_host"] = "0.0.0.0" + diskConfig["bind_port"] = 80 + diskConfig["auth_name"] = "name" + diskConfig["auth_pass"] = "pass" + dnsConfig := createTestDNSConfig(schemaVersion) + if schemaVersion > 1 { + diskConfig["dns"] = dnsConfig + } else { + diskConfig["coredns"] = dnsConfig + } + return diskConfig +} + +func createTestDNSConfig(schemaVersion int) map[interface{}]interface{} { + dnsConfig := make(map[interface{}]interface{}) + dnsConfig["port"] = 53 + dnsConfig["blocked_response_ttl"] = 10 + dnsConfig["querylog_enabled"] = true + dnsConfig["ratelimit"] = 20 + dnsConfig["bootstrap_dns"] = "8.8.8.8:53" + if schemaVersion > 2 { + dnsConfig["bootstrap_dns"] = []string{"8.8.8.8:53"} + } + dnsConfig["parental_sensitivity"] = 13 + dnsConfig["ratelimit_whitelist"] = []string{} + dnsConfig["upstream_dns"] = []string{"tls://1.1.1.1", "tls://1.0.0.1", "8.8.8.8"} + dnsConfig["filtering_enabled"] = true + dnsConfig["refuse_any"] = true + dnsConfig["parental_enabled"] = true + dnsConfig["bind_host"] = "0.0.0.0" + dnsConfig["protection_enabled"] = true + dnsConfig["safesearch_enabled"] = true + dnsConfig["safebrowsing_enabled"] = true + return dnsConfig +} From d9d641941cf00035d8bdef9f27cd8288a86b64d9 Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Fri, 1 Mar 2019 11:27:15 +0300 Subject: [PATCH 20/34] [change] upgrade_test: rework tests --- upgrade_test.go | 187 ++++++++++++++++++++++++++++++------------------ 1 file changed, 117 insertions(+), 70 deletions(-) diff --git a/upgrade_test.go b/upgrade_test.go index e474cc1f..8980eda9 100644 --- a/upgrade_test.go +++ b/upgrade_test.go @@ -6,144 +6,191 @@ import ( ) func TestUpgrade1to2(t *testing.T) { - // Let's create test config + // let's create test config for 1 schema version diskConfig := createTestDiskConfig(1) - oldDNSConfig := createTestDNSConfig(1) + // update config err := upgradeSchema1to2(&diskConfig) if err != nil { t.Fatalf("Can't upgrade schema version from 1 to 2") } + // ensure that schema version was bumped + compareSchemaVersion(t, diskConfig["schema_version"], 2) + + // old coredns entry should be removed _, ok := diskConfig["coredns"] if ok { t.Fatalf("Core DNS config was not removed after upgrade schema version from 1 to 2") } + // pull out new dns config dnsMap, ok := diskConfig["dns"] if !ok { t.Fatalf("No DNS config after upgrade schema version from 1 to 2") } - // Cast dns configuration to map - newDNSConfig := make(map[string]interface{}) - switch v := dnsMap.(type) { - case map[interface{}]interface{}: - if len(oldDNSConfig) != len(v) { - t.Fatalf("We loose some data") - } - for key, value := range v { - newDNSConfig[fmt.Sprint(key)] = value - } - default: - t.Fatalf("DNS configuration is not a map") - } + // cast dns configurations to maps and compare them + oldDNSConfig := castInterfaceToMap(t, createTestDNSConfig(1)) + newDNSConfig := castInterfaceToMap(t, dnsMap) + compareConfigs(t, &oldDNSConfig, &newDNSConfig) - _, v, err := compareConfigs(oldDNSConfig, newDNSConfig) - if err != nil { - t.Fatalf("Wrong data %s, %s", v, err) - } + // exclude dns config and schema version from disk config comparison + oldExcludedEntries := []string{"coredns", "schema_version"} + newExcludedEntries := []string{"dns", "schema_version"} + oldDiskConfig := createTestDiskConfig(1) + compareConfigsWithoutEntries(t, &oldDiskConfig, &diskConfig, oldExcludedEntries, newExcludedEntries) } func TestUpgrade2to3(t *testing.T) { - // Let's create test config + // let's create test config diskConfig := createTestDiskConfig(2) - oldDNSConfig := createTestDNSConfig(2) - // Upgrade schema from 2 to 3 + // upgrade schema from 2 to 3 err := upgradeSchema2to3(&diskConfig) if err != nil { t.Fatalf("Can't update schema version from 2 to 3: %s", err) } - // Check new schema version - newSchemaVersion := diskConfig["schema_version"] - switch v := newSchemaVersion.(type) { - case int: - if v != 3 { - t.Fatalf("Wrong schema version in new config file") - } - default: - t.Fatalf("Schema version is not an integer after update") - } + // check new schema version + compareSchemaVersion(t, diskConfig["schema_version"], 3) - // Let's get new dns configuration + // pull out new dns configuration dnsMap, ok := diskConfig["dns"] if !ok { t.Fatalf("No dns config in new configuration") } - // Cast dns configuration to map - newDNSConfig := make(map[string]interface{}) - switch v := dnsMap.(type) { - case map[string]interface{}: - for key, value := range v { - newDNSConfig[fmt.Sprint(key)] = value - } - default: - t.Fatalf("DNS configuration is not a map") - } + // cast dns configuration to map + newDNSConfig := castInterfaceToMap(t, dnsMap) - // Check if bootstrap DNS becomes an array + // check if bootstrap DNS becomes an array bootstrapDNS := newDNSConfig["bootstrap_dns"] switch v := bootstrapDNS.(type) { case []string: if len(v) != 1 { - t.Fatalf("Wrong count of bootsrap DNS servers") + t.Fatalf("Wrong count of bootsrap DNS servers: %d", len(v)) } if v[0] != "8.8.8.8:53" { - t.Fatalf("Wrong bootsrap DNS servers") + t.Fatalf("Bootsrap DNS server is not 8.8.8.8:53 : %s", v[0]) } default: - t.Fatalf("Wrong type for bootsrap DNS") + t.Fatalf("Wrong type for bootsrap DNS: %T", v) } - // Set old value for bootstrap_dns and compare old and new configurations - newDNSConfig["bootstrap_dns"] = "8.8.8.8:53" - _, v, err := compareConfigs(oldDNSConfig, newDNSConfig) - if err != nil { - t.Fatalf("%s value is wrong: %s", v, err) - } + // exclude bootstrap DNS from DNS configs comparison + excludedEntries := []string{"bootstrap_dns"} + oldDNSConfig := castInterfaceToMap(t, createTestDNSConfig(2)) + compareConfigsWithoutEntries(t, &oldDNSConfig, &newDNSConfig, excludedEntries, excludedEntries) + + // excluded dns config and schema version from disk config comparison + excludedEntries = []string{"dns", "schema_version"} + oldDiskConfig := createTestDiskConfig(2) + compareConfigsWithoutEntries(t, &oldDiskConfig, &diskConfig, excludedEntries, excludedEntries) } -// TODO add comparation for all possible types -func compareConfigs(oldConfig map[interface{}]interface{}, newConfig map[string]interface{}) (string, interface{}, error) { - oldConfigCasted := make(map[string]interface{}) - for k, v := range oldConfig { - oldConfigCasted[fmt.Sprint(k)] = v +func castInterfaceToMap(t *testing.T, oldConfig interface{}) (newConfig map[string]interface{}) { + newConfig = make(map[string]interface{}) + switch v := oldConfig.(type) { + case map[interface{}]interface{}: + for key, value := range v { + newConfig[fmt.Sprint(key)] = value + } + case map[string]interface{}: + for key, value := range v { + newConfig[key] = value + } + default: + t.Fatalf("DNS configuration is not a map") + } + return +} + +// compareConfigsWithoutEntry removes entries from configs and returns result of compareConfigs +func compareConfigsWithoutEntries(t *testing.T, oldConfig, newConfig *map[string]interface{}, oldKey, newKey []string) { + for _, k := range oldKey { + delete(*oldConfig, k) + } + for _, k := range newKey { + delete(*newConfig, k) + } + compareConfigs(t, oldConfig, newConfig) +} + +// compares configs before and after schema upgrade +func compareConfigs(t *testing.T, oldConfig, newConfig *map[string]interface{}) { + if len(*oldConfig) != len(*newConfig) { + t.Fatalf("wrong config entries count! Before upgrade: %d; After upgrade: %d", len(*oldConfig), len(*oldConfig)) } - // Check old data and new data - for k, v := range newConfig { + // Check old and new entries + for k, v := range *newConfig { switch value := v.(type) { case string: + if value != (*oldConfig)[k] { + t.Fatalf("wrong value for string %s. Before update: %s; After update: %s", k, (*oldConfig)[k], value) + } case int: - if value != oldConfigCasted[k] { - + if value != (*oldConfig)[k] { + t.Fatalf("wrong value for int %s. Before update: %d; After update: %d", k, (*oldConfig)[k], value) } case []string: - for i, s := range value { - if oldConfigCasted[k].([]string)[i] != s { - return k, v, fmt.Errorf("wrong data for %s", k) + for i, line := range value { + if len((*oldConfig)[k].([]string)) != len(value) { + t.Fatalf("wrong array length for %s. Before update: %d; After update: %d", k, len((*oldConfig)[k].([]string)), len(value)) + } + if (*oldConfig)[k].([]string)[i] != line { + t.Fatalf("wrong data for string array %s. Before update: %s; After update: %s", k, (*oldConfig)[k].([]string)[i], line) } } case bool: - if v != oldConfigCasted[k].(bool) { - return k, v, fmt.Errorf("wrong data for %s", k) + if v != (*oldConfig)[k].(bool) { + t.Fatalf("wrong boolean value for %s", k) + } + case []filter: + if len((*oldConfig)[k].([]filter)) != len(value) { + t.Fatalf("wrong filters count. Before update: %d; After update: %d", len((*oldConfig)[k].([]filter)), len(value)) + } + for i, newFilter := range value { + oldFilter := (*oldConfig)[k].([]filter)[i] + if oldFilter.Enabled != newFilter.Enabled || oldFilter.Name != newFilter.Name || oldFilter.RulesCount != newFilter.RulesCount { + t.Fatalf("old filter %s not equals new filter %s", oldFilter.Name, newFilter.Name) + } } default: - return k, v, fmt.Errorf("unknown type in DNS configuration for %s", k) + t.Fatalf("uknown data type for %s: %T", k, value) } } +} - return "", nil, nil +// compareSchemaVersion check if newSchemaVersion equals schemaVersion +func compareSchemaVersion(t *testing.T, newSchemaVersion interface{}, schemaVersion int) { + switch v := newSchemaVersion.(type) { + case int: + if v != schemaVersion { + t.Fatalf("Wrong schema version in new config file") + } + default: + t.Fatalf("Schema version is not an integer after update") + } } func createTestDiskConfig(schemaVersion int) (diskConfig map[string]interface{}) { diskConfig = make(map[string]interface{}) diskConfig["language"] = "en" - diskConfig["filters"] = []filter{} + diskConfig["filters"] = []filter{ + { + URL: "https://filters.adtidy.org/android/filters/111_optimized.txt", + Name: "Latvian filter", + RulesCount: 100, + }, + { + URL: "https://easylist.to/easylistgermany/easylistgermany.txt", + Name: "Germany filter", + RulesCount: 200, + }, + } diskConfig["user_rules"] = []string{} diskConfig["schema_version"] = schemaVersion diskConfig["bind_host"] = "0.0.0.0" From e973c4b174d9249cb05f01faa9d7662f2f04d55e Mon Sep 17 00:00:00 2001 From: Aleksey Dmitrevskiy Date: Tue, 5 Mar 2019 12:29:52 +0300 Subject: [PATCH 21/34] [change] control, openapi: Handle upstreams test with JSON --- control.go | 4 ++-- openapi/openapi.yaml | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/control.go b/control.go index 7b17321b..0bc6fc66 100644 --- a/control.go +++ b/control.go @@ -361,12 +361,12 @@ func checkBootstrapDNS(host string) error { func handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { log.Tracef("%s %v", r.Method, r.URL) - body, err := ioutil.ReadAll(r.Body) + hosts := []string{} + err := json.NewDecoder(r.Body).Decode(&hosts) if err != nil { httpError(w, http.StatusBadRequest, "Failed to read request body: %s", err) return } - hosts := strings.Fields(string(body)) if len(hosts) == 0 { httpError(w, http.StatusBadRequest, "No servers specified") diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 6d0aafb4..d84e90ec 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -111,21 +111,15 @@ paths: tags: - global operationId: testUpstreamDNS - summary: 'Test upstream DNS' + summary: "Test upstream DNS" consumes: - - text/plain + - application/json parameters: - - in: body - name: upstream - description: 'Upstream servers, separated by newline or space, port is optional after colon' + - in: "body" + name: "body" + description: "Upstream servers to be tested" 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 + $ref: "#/definitions/TestUpstreams" responses: 200: description: 'Status of testing each requested server, with "OK" meaning that server works, any other text means an error.' @@ -820,6 +814,20 @@ definitions: all_servers: type: "boolean" description: "If true, parallel queries to all configured upstream servers are enabled" + TestUpstreams: + type: "object" + description: "Upstream servers to be tested" + required: + - "upstream_dns" + properties: + upstream_dns: + type: "array" + description: 'Upstream servers, port is optional after colon' + items: + type: "string" + example: + - "tls://1.1.1.1" + - "tls://1.0.0.1" Filter: type: "object" description: "Filter subscription info" From 5d6c980ac7a33e5a4b8d3c376d8984e36cba1f83 Mon Sep 17 00:00:00 2001 From: Ildar Kamalov Date: Wed, 6 Mar 2019 14:45:21 +0300 Subject: [PATCH 22/34] * client: upstream form --- client/src/__locales/en.json | 5 +- client/src/actions/index.js | 13 +- client/src/api/Api.js | 6 +- client/src/components/Settings/Upstream.js | 97 -------------- .../components/Settings/Upstream/Examples.js | 32 +++++ .../src/components/Settings/Upstream/Form.js | 120 ++++++++++++++++++ .../src/components/Settings/Upstream/index.js | 67 ++++++++++ client/src/components/Settings/index.js | 29 +---- client/src/components/ui/Checkbox.css | 4 + client/src/helpers/form.js | 2 +- client/src/helpers/helpers.js | 2 + client/src/reducers/index.js | 8 +- 12 files changed, 256 insertions(+), 129 deletions(-) delete mode 100644 client/src/components/Settings/Upstream.js create mode 100644 client/src/components/Settings/Upstream/Examples.js create mode 100644 client/src/components/Settings/Upstream/Form.js create mode 100644 client/src/components/Settings/Upstream/index.js diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 45c33828..b70cdd37 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -246,5 +246,8 @@ "form_error_equal": "Shouldn't be equal", "form_error_password": "Password mismatched", "reset_settings": "Reset settings", - "update_announcement": "AdGuard Home {{version}} is now available! <0>Click here for more info." + "update_announcement": "AdGuard Home {{version}} is now available! <0>Click here for more info.", + "upstream_parallel": "Use parallel queries to speed up resolving by simultaneously querying all upstream servers", + "bootstrap_dns": "Bootstrap DNS", + "bootstrap_dns_desc": "Bootstrap DNS for DNS-over-HTTPS and DNS-over-TLS servers" } diff --git a/client/src/actions/index.js b/client/src/actions/index.js index 1bb99064..0bb99940 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -3,7 +3,7 @@ import round from 'lodash/round'; import { t } from 'i18next'; import { showLoading, hideLoading } from 'react-redux-loading-bar'; -import { normalizeHistory, normalizeFilteringStatus, normalizeLogs } from '../helpers/helpers'; +import { normalizeHistory, normalizeFilteringStatus, normalizeLogs, normalizeTextarea } from '../helpers/helpers'; import { SETTINGS_NAMES } from '../helpers/constants'; import Api from '../api/Api'; @@ -452,10 +452,14 @@ export const setUpstreamRequest = createAction('SET_UPSTREAM_REQUEST'); export const setUpstreamFailure = createAction('SET_UPSTREAM_FAILURE'); export const setUpstreamSuccess = createAction('SET_UPSTREAM_SUCCESS'); -export const setUpstream = url => async (dispatch) => { +export const setUpstream = config => async (dispatch) => { dispatch(setUpstreamRequest()); try { - await apiClient.setUpstream(url); + const values = { ...config }; + values.bootstrap_dns = (values.bootstrap_dns && normalizeTextarea(values.bootstrap_dns)) || ''; + values.upstream_dns = (values.upstream_dns && normalizeTextarea(values.upstream_dns)) || ''; + + await apiClient.setUpstream(values); dispatch(addSuccessToast('updated_upstream_dns_toast')); dispatch(setUpstreamSuccess()); } catch (error) { @@ -468,9 +472,10 @@ export const testUpstreamRequest = createAction('TEST_UPSTREAM_REQUEST'); export const testUpstreamFailure = createAction('TEST_UPSTREAM_FAILURE'); export const testUpstreamSuccess = createAction('TEST_UPSTREAM_SUCCESS'); -export const testUpstream = servers => async (dispatch) => { +export const testUpstream = values => async (dispatch) => { dispatch(testUpstreamRequest()); try { + const servers = normalizeTextarea(values); const upstreamResponse = await apiClient.testUpstream(servers); const testMessages = Object.keys(upstreamResponse).map((key) => { diff --git a/client/src/api/Api.js b/client/src/api/Api.js index d20a334a..17df1221 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -34,7 +34,7 @@ export default class Api { GLOBAL_QUERY_LOG = { path: 'querylog', method: 'GET' }; GLOBAL_QUERY_LOG_ENABLE = { path: 'querylog_enable', method: 'POST' }; GLOBAL_QUERY_LOG_DISABLE = { path: 'querylog_disable', method: 'POST' }; - GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstream_dns', method: 'POST' }; + GLOBAL_SET_UPSTREAM_DNS = { path: 'set_upstreams_config', method: 'POST' }; GLOBAL_TEST_UPSTREAM_DNS = { path: 'test_upstream_dns', method: 'POST' }; GLOBAL_VERSION = { path: 'version.json', method: 'GET' }; GLOBAL_ENABLE_PROTECTION = { path: 'enable_protection', method: 'POST' }; @@ -110,7 +110,7 @@ export default class Api { const { path, method } = this.GLOBAL_SET_UPSTREAM_DNS; const config = { data: url, - header: { 'Content-Type': 'text/plain' }, + headers: { 'Content-Type': 'application/json' }, }; return this.makeRequest(path, method, config); } @@ -119,7 +119,7 @@ export default class Api { const { path, method } = this.GLOBAL_TEST_UPSTREAM_DNS; const config = { data: servers, - header: { 'Content-Type': 'text/plain' }, + headers: { 'Content-Type': 'application/json' }, }; return this.makeRequest(path, method, config); } diff --git a/client/src/components/Settings/Upstream.js b/client/src/components/Settings/Upstream.js deleted file mode 100644 index fe24c4d7..00000000 --- a/client/src/components/Settings/Upstream.js +++ /dev/null @@ -1,97 +0,0 @@ -import React, { Component } from 'react'; -import PropTypes from 'prop-types'; -import classnames from 'classnames'; -import { Trans, withNamespaces } from 'react-i18next'; -import Card from '../ui/Card'; - -class Upstream extends Component { - handleChange = (e) => { - const { value } = e.currentTarget; - this.props.handleUpstreamChange(value); - }; - - handleSubmit = (e) => { - e.preventDefault(); - this.props.handleUpstreamSubmit(); - }; - - handleTest = () => { - this.props.handleUpstreamTest(); - } - - render() { - const testButtonClass = classnames({ - 'btn btn-primary btn-standard mr-2': true, - 'btn btn-primary btn-standard mr-2 btn-loading': this.props.processingTestUpstream, - }); - const { t } = this.props; - - return ( - -
-
-
-