diff --git a/AGHTechDoc.md b/AGHTechDoc.md index 58a39dc6..4ac88137 100644 --- a/AGHTechDoc.md +++ b/AGHTechDoc.md @@ -29,6 +29,9 @@ Contents: * Static IP check/set * Add a static lease * API: Reset DHCP configuration +* DNS general settings + * API: Get DNS general settings + * API: Set DNS general settings * DNS access settings * List access settings * Set access settings @@ -801,6 +804,42 @@ Response: ] +## DNS general settings + +### API: Get DNS general settings + +Request: + + GET /control/dns_info + +Response: + + 200 OK + + { + "protection_enabled": true | false, + "ratelimit": 1234, + "blocking_mode": "nxdomain" | "null_ip", + } + + +### API: Set DNS general settings + +Request: + + POST /control/dns_config + + { + "protection_enabled": true | false, + "ratelimit": 1234, + "blocking_mode": "nxdomain" | "null_ip", + } + +Response: + + 200 OK + + ## DNS access settings There are low-level settings that can block undesired DNS requests. "Blocking" means not responding to request. diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index a3dabc01..e4947229 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -101,7 +101,7 @@ type FilteringConfig struct { BlockingMode string `yaml:"blocking_mode"` // mode how to answer filtered requests BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600) - Ratelimit int `yaml:"ratelimit"` // max number of requests per second from a given IP (0 to disable) + Ratelimit uint32 `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) @@ -214,7 +214,7 @@ func (s *Server) prepare(config *ServerConfig) error { proxyConfig := proxy.Config{ UDPListenAddr: s.conf.UDPListenAddr, TCPListenAddr: s.conf.TCPListenAddr, - Ratelimit: s.conf.Ratelimit, + Ratelimit: int(s.conf.Ratelimit), RatelimitWhitelist: s.conf.RatelimitWhitelist, RefuseAny: s.conf.RefuseAny, CacheEnabled: true, diff --git a/dnsforward/dnsforward_http.go b/dnsforward/dnsforward_http.go index 3941aba1..b284d1c2 100644 --- a/dnsforward/dnsforward_http.go +++ b/dnsforward/dnsforward_http.go @@ -20,6 +20,62 @@ func httpError(r *http.Request, w http.ResponseWriter, code int, format string, http.Error(w, text, code) } +type dnsConfigJSON struct { + ProtectionEnabled bool `json:"protection_enabled"` + RateLimit uint32 `json:"ratelimit"` + BlockingMode string `json:"blocking_mode"` +} + +func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { + resp := dnsConfigJSON{} + s.RLock() + resp.ProtectionEnabled = s.conf.ProtectionEnabled + resp.BlockingMode = s.conf.BlockingMode + resp.RateLimit = s.conf.Ratelimit + s.RUnlock() + + js, err := json.Marshal(resp) + if err != nil { + httpError(r, w, http.StatusInternalServerError, "json.Marshal: %s", err) + return + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write(js) +} + +func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) { + req := dnsConfigJSON{} + err := json.NewDecoder(r.Body).Decode(&req) + if err != nil { + httpError(r, w, http.StatusBadRequest, "json.Decode: %s", err) + return + } + + if !(req.BlockingMode == "nxdomain" || req.BlockingMode == "null_ip") { + httpError(r, w, http.StatusBadRequest, "blocking_mode: value not supported") + return + } + + restart := false + s.Lock() + s.conf.ProtectionEnabled = req.ProtectionEnabled + s.conf.BlockingMode = req.BlockingMode + if s.conf.Ratelimit != req.RateLimit { + restart = true + } + s.conf.Ratelimit = req.RateLimit + s.Unlock() + s.conf.ConfigModified() + + if restart { + err = s.Restart() + if err != nil { + httpError(r, w, http.StatusInternalServerError, "%s", err) + return + } + } +} + func (s *Server) handleProtectionEnable(w http.ResponseWriter, r *http.Request) { s.conf.ProtectionEnabled = true s.conf.ConfigModified() @@ -270,6 +326,8 @@ func checkDNS(input string, bootstrap []string) error { } func (s *Server) registerHandlers() { + s.conf.HTTPRegister("GET", "/control/dns_info", s.handleGetConfig) + s.conf.HTTPRegister("POST", "/control/dns_config", s.handleSetConfig) s.conf.HTTPRegister("POST", "/control/enable_protection", s.handleProtectionEnable) s.conf.HTTPRegister("POST", "/control/disable_protection", s.handleProtectionDisable) s.conf.HTTPRegister("POST", "/control/set_upstreams_config", s.handleSetUpstreamConfig) @@ -277,5 +335,4 @@ func (s *Server) registerHandlers() { s.conf.HTTPRegister("GET", "/control/access/list", s.handleAccessList) s.conf.HTTPRegister("POST", "/control/access/set", s.handleAccessSet) - }