From e31905864b23a25ae1797dbc081f36f66d904ac9 Mon Sep 17 00:00:00 2001
From: Eugene Bujak <hmage@hmage.net>
Date: Wed, 5 Dec 2018 20:29:00 +0300
Subject: [PATCH] Get rid of mentions of CoreDNS in code except for upgrading
 and in readme. Add config upgrade.

---
 Makefile   |  2 +-
 README.md  |  6 ++---
 config.go  | 38 ++++++++++++-------------------
 control.go | 65 +++++++++++++++++++++++++++---------------------------
 coredns.go |  6 ++---
 upgrade.go | 49 +++++++++++++++++++++++++++++++++++-----
 6 files changed, 98 insertions(+), 68 deletions(-)

diff --git a/Makefile b/Makefile
index 4ff25301..9e9ae505 100644
--- a/Makefile
+++ b/Makefile
@@ -19,7 +19,7 @@ client/node_modules: client/package.json client/package-lock.json
 $(STATIC): $(JSFILES) client/node_modules
 	npm --prefix client run build-prod
 
-$(TARGET): $(STATIC) *.go coredns_plugin/*.go dnsfilter/*.go
+$(TARGET): $(STATIC) *.go dnsfilter/*.go dnsforward/*.go
 	go get -d .
 	GOOS=$(NATIVE_GOOS) GOARCH=$(NATIVE_GOARCH) GO111MODULE=off go get -v github.com/gobuffalo/packr/...
 	PATH=$(GOPATH)/bin:$(PATH) packr -z
diff --git a/README.md b/README.md
index 37ee39d3..4096ad93 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@ Now open the browser and navigate to http://localhost:3000/ to control your AdGu
 You can run AdGuard Home without superuser privileges, but you need to instruct it to use a different port rather than 53. You can do that by editing `AdGuardHome.yaml` and finding these two lines:
 
 ```yaml
-coredns:
+dns:
   port: 53
 ```
 
@@ -108,7 +108,7 @@ Settings are stored in [YAML format](https://en.wikipedia.org/wiki/YAML), possib
  * `bind_port` — Web interface IP port to listen on
  * `auth_name` — Web interface optional authorization username
  * `auth_pass` — Web interface optional authorization password
- * `coredns` — CoreDNS configuration section
+ * `dns` — DNS configuration section
    * `port` — DNS server port to listen on
    * `filtering_enabled` — Filtering of DNS requests based on filter lists
    * `safebrowsing_enabled` — Filtering of DNS requests based on safebrowsing
@@ -208,6 +208,6 @@ This software wouldn't have been possible without:
    * And many more node.js packages.
  * [whotracks.me data](https://github.com/cliqz-oss/whotracks.me)
 
-You might have seen coredns mentioned here before, but we've stopped using it in AdGuardHome. While we still use it on our servers, it seemed like an overkill and impeded with Home features that we wanted to implement.
+You might have seen that coredns was mentioned here before — we've stopped using it in AdGuardHome. While we still use it on our servers, it seemed like an overkill and it impeded with Home features that we wanted to implement.
 
 For a full list of all node.js packages in use, please take a look at [client/package.json](https://github.com/AdguardTeam/AdGuardHome/blob/master/client/package.json) file.
diff --git a/config.go b/config.go
index 52efdfd9..3bf6fbe4 100644
--- a/config.go
+++ b/config.go
@@ -13,9 +13,8 @@ import (
 )
 
 const (
-	currentSchemaVersion = 1         // used for upgrading from old configs to new config
-	dataDir              = "data"    // data storage
-	filterDir            = "filters" // cache location for downloaded filters, it's under DataDir
+	dataDir   = "data"    // data storage
+	filterDir = "filters" // cache location for downloaded filters, it's under DataDir
 )
 
 // configuration is loaded from YAML
@@ -24,14 +23,14 @@ type configuration struct {
 	ourConfigFilename string // Config filename (can be overriden via the command line arguments)
 	ourBinaryDir      string // Location of our directory, used to protect against CWD being somewhere else
 
-	BindHost  string        `yaml:"bind_host"`
-	BindPort  int           `yaml:"bind_port"`
-	AuthName  string        `yaml:"auth_name"`
-	AuthPass  string        `yaml:"auth_pass"`
-	Language  string        `yaml:"language"` // two-letter ISO 639-1 language code
-	CoreDNS   coreDNSConfig `yaml:"coredns"`
-	Filters   []filter      `yaml:"filters"`
-	UserRules []string      `yaml:"user_rules,omitempty"`
+	BindHost  string    `yaml:"bind_host"`
+	BindPort  int       `yaml:"bind_port"`
+	AuthName  string    `yaml:"auth_name"`
+	AuthPass  string    `yaml:"auth_pass"`
+	Language  string    `yaml:"language"` // two-letter ISO 639-1 language code
+	DNS       dnsConfig `yaml:"dns"`
+	Filters   []filter  `yaml:"filters"`
+	UserRules []string  `yaml:"user_rules,omitempty"`
 
 	sync.RWMutex `yaml:"-"`
 
@@ -39,16 +38,11 @@ type configuration struct {
 }
 
 // field ordering is important -- yaml fields will mirror ordering from here
-type coreDNSConfig struct {
-	binaryFile string
-	coreFile   string
-	Port       int `yaml:"port"`
+type dnsConfig struct {
+	Port int `yaml:"port"`
 
 	dnsforward.FilteringConfig `yaml:",inline"`
 
-	Pprof        string   `yaml:"-"`
-	Cache        string   `yaml:"-"`
-	Prometheus   string   `yaml:"-"`
 	BootstrapDNS string   `yaml:"bootstrap_dns"`
 	UpstreamDNS  []string `yaml:"upstream_dns"`
 }
@@ -60,10 +54,8 @@ var config = configuration{
 	ourConfigFilename: "AdGuardHome.yaml",
 	BindPort:          3000,
 	BindHost:          "127.0.0.1",
-	CoreDNS: coreDNSConfig{
-		Port:       53,
-		binaryFile: "coredns",  // only filename, no path
-		coreFile:   "Corefile", // only filename, no path
+	DNS: dnsConfig{
+		Port: 53,
 		FilteringConfig: dnsforward.FilteringConfig{
 			ProtectionEnabled:  true, // whether or not use any of dnsfilter features
 			FilteringEnabled:   true, // whether or not use filter lists
@@ -74,8 +66,6 @@ var config = configuration{
 		},
 		BootstrapDNS: "8.8.8.8:53",
 		UpstreamDNS:  defaultDNS,
-		Cache:        "cache",
-		Prometheus:   "prometheus :9153",
 	},
 	Filters: []filter{
 		{Filter: dnsfilter.Filter{ID: 1}, Enabled: true, URL: "https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt", Name: "AdGuard Simplified Domain Names filter"},
diff --git a/control.go b/control.go
index 0d57816d..49869ddc 100644
--- a/control.go
+++ b/control.go
@@ -32,9 +32,9 @@ var client = &http.Client{
 }
 
 // -------------------
-// coredns run control
+// dns run control
 // -------------------
-func writeAllConfigsAndReloadCoreDNS() error {
+func writeAllConfigsAndReloadDNS() error {
 	err := writeAllConfigs()
 	if err != nil {
 		log.Printf("Couldn't write all configs: %s", err)
@@ -45,7 +45,7 @@ func writeAllConfigsAndReloadCoreDNS() error {
 }
 
 func httpUpdateConfigReloadDNSReturnOK(w http.ResponseWriter, r *http.Request) {
-	err := writeAllConfigsAndReloadCoreDNS()
+	err := writeAllConfigsAndReloadDNS()
 	if err != nil {
 		errortext := fmt.Sprintf("Couldn't write config file: %s", err)
 		log.Println(errortext)
@@ -67,12 +67,12 @@ func returnOK(w http.ResponseWriter, r *http.Request) {
 func handleStatus(w http.ResponseWriter, r *http.Request) {
 	data := map[string]interface{}{
 		"dns_address":        config.BindHost,
-		"dns_port":           config.CoreDNS.Port,
-		"protection_enabled": config.CoreDNS.ProtectionEnabled,
-		"querylog_enabled":   config.CoreDNS.QueryLogEnabled,
+		"dns_port":           config.DNS.Port,
+		"protection_enabled": config.DNS.ProtectionEnabled,
+		"querylog_enabled":   config.DNS.QueryLogEnabled,
 		"running":            isRunning(),
-		"bootstrap_dns":      config.CoreDNS.BootstrapDNS,
-		"upstream_dns":       config.CoreDNS.UpstreamDNS,
+		"bootstrap_dns":      config.DNS.BootstrapDNS,
+		"upstream_dns":       config.DNS.UpstreamDNS,
 		"version":            VersionString,
 		"language":           config.Language,
 	}
@@ -95,12 +95,12 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
 }
 
 func handleProtectionEnable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.ProtectionEnabled = true
+	config.DNS.ProtectionEnabled = true
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleProtectionDisable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.ProtectionEnabled = false
+	config.DNS.ProtectionEnabled = false
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
@@ -108,12 +108,12 @@ func handleProtectionDisable(w http.ResponseWriter, r *http.Request) {
 // stats
 // -----
 func handleQueryLogEnable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.QueryLogEnabled = true
+	config.DNS.QueryLogEnabled = true
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleQueryLogDisable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.QueryLogEnabled = false
+	config.DNS.QueryLogEnabled = false
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
@@ -135,9 +135,9 @@ func handleSetUpstreamDNS(w http.ResponseWriter, r *http.Request) {
 	hosts := strings.Fields(string(body))
 
 	if len(hosts) == 0 {
-		config.CoreDNS.UpstreamDNS = defaultDNS
+		config.DNS.UpstreamDNS = defaultDNS
 	} else {
-		config.CoreDNS.UpstreamDNS = hosts
+		config.DNS.UpstreamDNS = hosts
 	}
 
 	err = writeAllConfigs()
@@ -243,7 +243,7 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
 
 	resp, err := client.Get(versionCheckURL)
 	if err != nil {
-		errortext := fmt.Sprintf("Couldn't get querylog from coredns: %T %s\n", err, err)
+		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)
 		return
@@ -255,7 +255,7 @@ 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: %s", err)
+		errortext := fmt.Sprintf("Couldn't read response body from %s: %s", versionCheckURL, err)
 		log.Println(errortext)
 		http.Error(w, errortext, http.StatusBadGateway)
 		return
@@ -278,18 +278,18 @@ func handleGetVersionJSON(w http.ResponseWriter, r *http.Request) {
 // ---------
 
 func handleFilteringEnable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.FilteringEnabled = true
+	config.DNS.FilteringEnabled = true
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleFilteringDisable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.FilteringEnabled = false
+	config.DNS.FilteringEnabled = false
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleFilteringStatus(w http.ResponseWriter, r *http.Request) {
 	data := map[string]interface{}{
-		"enabled": config.CoreDNS.FilteringEnabled,
+		"enabled": config.DNS.FilteringEnabled,
 	}
 
 	config.RLock()
@@ -377,7 +377,8 @@ func handleFilteringAddURL(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	// URL is deemed valid, append it to filters, update config, write new filter file and tell coredns to reload it
+	// URL is deemed valid, append it to filters, update config, write new filter file and tell dns to reload it
+	// TODO: since we directly feed filters in-memory, revisit if writing configs is always neccessary
 	config.Filters = append(config.Filters, filter)
 	err = writeAllConfigs()
 	if err != nil {
@@ -537,18 +538,18 @@ func handleFilteringRefresh(w http.ResponseWriter, r *http.Request) {
 // ------------
 
 func handleSafeBrowsingEnable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.SafeBrowsingEnabled = true
+	config.DNS.SafeBrowsingEnabled = true
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleSafeBrowsingDisable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.SafeBrowsingEnabled = false
+	config.DNS.SafeBrowsingEnabled = false
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleSafeBrowsingStatus(w http.ResponseWriter, r *http.Request) {
 	data := map[string]interface{}{
-		"enabled": config.CoreDNS.SafeBrowsingEnabled,
+		"enabled": config.DNS.SafeBrowsingEnabled,
 	}
 	jsonVal, err := json.Marshal(data)
 	if err != nil {
@@ -611,22 +612,22 @@ func handleParentalEnable(w http.ResponseWriter, r *http.Request) {
 		http.Error(w, "Sensitivity must be set to valid value", 400)
 		return
 	}
-	config.CoreDNS.ParentalSensitivity = i
-	config.CoreDNS.ParentalEnabled = true
+	config.DNS.ParentalSensitivity = i
+	config.DNS.ParentalEnabled = true
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleParentalDisable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.ParentalEnabled = false
+	config.DNS.ParentalEnabled = false
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleParentalStatus(w http.ResponseWriter, r *http.Request) {
 	data := map[string]interface{}{
-		"enabled": config.CoreDNS.ParentalEnabled,
+		"enabled": config.DNS.ParentalEnabled,
 	}
-	if config.CoreDNS.ParentalEnabled {
-		data["sensitivity"] = config.CoreDNS.ParentalSensitivity
+	if config.DNS.ParentalEnabled {
+		data["sensitivity"] = config.DNS.ParentalSensitivity
 	}
 	jsonVal, err := json.Marshal(data)
 	if err != nil {
@@ -651,18 +652,18 @@ func handleParentalStatus(w http.ResponseWriter, r *http.Request) {
 // ------------
 
 func handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.SafeSearchEnabled = true
+	config.DNS.SafeSearchEnabled = true
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
-	config.CoreDNS.SafeSearchEnabled = false
+	config.DNS.SafeSearchEnabled = false
 	httpUpdateConfigReloadDNSReturnOK(w, r)
 }
 
 func handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
 	data := map[string]interface{}{
-		"enabled": config.CoreDNS.SafeSearchEnabled,
+		"enabled": config.DNS.SafeSearchEnabled,
 	}
 	jsonVal, err := json.Marshal(data)
 	if err != nil {
diff --git a/coredns.go b/coredns.go
index 119b2371..250c9e37 100644
--- a/coredns.go
+++ b/coredns.go
@@ -31,12 +31,12 @@ func generateServerConfig() dnsforward.ServerConfig {
 	}
 
 	newconfig := dnsforward.ServerConfig{
-		UDPListenAddr:   &net.UDPAddr{Port: config.CoreDNS.Port},
-		FilteringConfig: config.CoreDNS.FilteringConfig,
+		UDPListenAddr:   &net.UDPAddr{Port: config.DNS.Port},
+		FilteringConfig: config.DNS.FilteringConfig,
 		Filters:         filters,
 	}
 
-	for _, u := range config.CoreDNS.UpstreamDNS {
+	for _, u := range config.DNS.UpstreamDNS {
 		upstream, err := dnsforward.GetUpstream(u)
 		if err != nil {
 			log.Printf("Couldn't get upstream: %s", err)
diff --git a/upgrade.go b/upgrade.go
index 4154ee03..1b8c6e34 100644
--- a/upgrade.go
+++ b/upgrade.go
@@ -10,6 +10,8 @@ import (
 	"gopkg.in/yaml.v2"
 )
 
+const currentSchemaVersion = 2 // used for upgrading from old configs to new config
+
 // Performs necessary upgrade operations if needed
 func upgradeConfig() error {
 	// read a config file into an interface map, so we can manipulate values without losing any
@@ -57,7 +59,12 @@ func upgradeConfig() error {
 func upgradeConfigSchema(oldVersion int, diskConfig *map[string]interface{}) error {
 	switch oldVersion {
 	case 0:
-		err := upgradeSchema0to1(diskConfig)
+		err := upgradeSchema0to2(diskConfig)
+		if err != nil {
+			return err
+		}
+	case 1:
+		err := upgradeSchema1to2(diskConfig)
 		if err != nil {
 			return err
 		}
@@ -83,14 +90,13 @@ func upgradeConfigSchema(oldVersion int, diskConfig *map[string]interface{}) err
 	return nil
 }
 
+// The first schema upgrade:
+// No more "dnsfilter.txt", filters are now kept in data/filters/
 func upgradeSchema0to1(diskConfig *map[string]interface{}) error {
 	log.Printf("%s(): called", _Func())
 
-	// The first schema upgrade:
-	// No more "dnsfilter.txt", filters are now kept in data/filters/
 	dnsFilterPath := filepath.Join(config.ourBinaryDir, "dnsfilter.txt")
-	_, err := os.Stat(dnsFilterPath)
-	if !os.IsNotExist(err) {
+	if _, err := os.Stat(dnsFilterPath); !os.IsNotExist(err) {
 		log.Printf("Deleting %s as we don't need it anymore", dnsFilterPath)
 		err = os.Remove(dnsFilterPath)
 		if err != nil {
@@ -103,3 +109,36 @@ func upgradeSchema0to1(diskConfig *map[string]interface{}) error {
 
 	return nil
 }
+
+// Second schema upgrade:
+// coredns is now dns in config
+// delete 'Corefile', since we don't use that anymore
+func upgradeSchema1to2(diskConfig *map[string]interface{}) error {
+	log.Printf("%s(): called", _Func())
+
+	coreFilePath := filepath.Join(config.ourBinaryDir, "Corefile")
+	if _, err := os.Stat(coreFilePath); !os.IsNotExist(err) {
+		log.Printf("Deleting %s as we don't need it anymore", coreFilePath)
+		err = os.Remove(coreFilePath)
+		if err != nil {
+			log.Printf("Cannot remove %s due to %s", coreFilePath, err)
+			// not fatal, move on
+		}
+	}
+
+	(*diskConfig)["dns"] = (*diskConfig)["coredns"]
+	delete((*diskConfig), "coredns")
+	(*diskConfig)["schema_version"] = 2
+
+	return nil
+}
+
+// jump two schemas at once -- this time we just do it sequentially
+func upgradeSchema0to2(diskConfig *map[string]interface{}) error {
+	err := upgradeSchema0to1(diskConfig)
+	if err != nil {
+		return err
+	}
+
+	return upgradeSchema1to2(diskConfig)
+}