diff --git a/AGHTechDoc.md b/AGHTechDoc.md index 4ac88137..0aade0a7 100644 --- a/AGHTechDoc.md +++ b/AGHTechDoc.md @@ -819,7 +819,9 @@ Response: { "protection_enabled": true | false, "ratelimit": 1234, - "blocking_mode": "nxdomain" | "null_ip", + "blocking_mode": "nxdomain" | "null_ip" | "custom_ip", + "blocking_ipv4": "1.2.3.4", + "blocking_ipv6": "1:2:3::4", } @@ -832,13 +834,17 @@ Request: { "protection_enabled": true | false, "ratelimit": 1234, - "blocking_mode": "nxdomain" | "null_ip", + "blocking_mode": "nxdomain" | "null_ip" | "custom_ip", + "blocking_ipv4": "1.2.3.4", + "blocking_ipv6": "1:2:3::4", } Response: 200 OK +`blocking_ipv4` and `blocking_ipv6` values are active when `blocking_mode` is set to `custom_ip`. + ## DNS access settings diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index e4947229..2630770e 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -99,7 +99,12 @@ type FilteringConfig struct { ProtectionEnabled bool `yaml:"protection_enabled"` // whether or not use any of dnsfilter features - BlockingMode string `yaml:"blocking_mode"` // mode how to answer filtered requests + BlockingMode string `yaml:"blocking_mode"` // mode how to answer filtered requests + BlockingIPv4 string `yaml:"blocking_ipv4"` // IP address to be returned for a blocked A request + BlockingIPv6 string `yaml:"blocking_ipv6"` // IP address to be returned for a blocked AAAA request + BlockingIPAddrv4 net.IP `yaml:"-"` + BlockingIPAddrv6 net.IP `yaml:"-"` + BlockedResponseTTL uint32 `yaml:"blocked_response_ttl"` // if 0, then default is used (3600) 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 @@ -657,6 +662,14 @@ func (s *Server) genDNSFilterMessage(d *proxy.DNSContext, result *dnsfilter.Resu case dns.TypeAAAA: return s.genAAAARecord(m, net.IPv6zero) } + + } else if s.conf.BlockingMode == "custom_ip" { + switch m.Question[0].Qtype { + case dns.TypeA: + return s.genARecord(m, s.conf.BlockingIPAddrv4) + case dns.TypeAAAA: + return s.genAAAARecord(m, s.conf.BlockingIPAddrv6) + } } return s.genNXDomain(m) diff --git a/dnsforward/dnsforward_http.go b/dnsforward/dnsforward_http.go index b284d1c2..a2660b81 100644 --- a/dnsforward/dnsforward_http.go +++ b/dnsforward/dnsforward_http.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/AdguardTeam/dnsproxy/upstream" + "github.com/AdguardTeam/golibs/jsonutil" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/utils" "github.com/miekg/dns" @@ -24,6 +25,8 @@ type dnsConfigJSON struct { ProtectionEnabled bool `json:"protection_enabled"` RateLimit uint32 `json:"ratelimit"` BlockingMode string `json:"blocking_mode"` + BlockingIPv4 string `json:"blocking_ipv4"` + BlockingIPv6 string `json:"blocking_ipv6"` } func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { @@ -31,6 +34,8 @@ func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { s.RLock() resp.ProtectionEnabled = s.conf.ProtectionEnabled resp.BlockingMode = s.conf.BlockingMode + resp.BlockingIPv4 = s.conf.BlockingIPv4 + resp.BlockingIPv6 = s.conf.BlockingIPv6 resp.RateLimit = s.conf.Ratelimit s.RUnlock() @@ -43,27 +48,68 @@ func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) { _, _ = w.Write(js) } +func checkBlockingMode(req dnsConfigJSON) bool { + bm := req.BlockingMode + if !(bm == "nxdomain" || bm == "null_ip" || bm == "custom_ip") { + return false + } + + if bm == "custom_ip" { + ip := net.ParseIP(req.BlockingIPv4) + if ip == nil || ip.To4() == nil { + return false + } + + ip = net.ParseIP(req.BlockingIPv6) + if ip == nil { + return false + } + } + + return true +} + func (s *Server) handleSetConfig(w http.ResponseWriter, r *http.Request) { req := dnsConfigJSON{} - err := json.NewDecoder(r.Body).Decode(&req) + js, err := jsonutil.DecodeObject(&req, r.Body) 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") + if js.Exists("blocking_mode") && !checkBlockingMode(req) { + httpError(r, w, http.StatusBadRequest, "blocking_mode: incorrect value") return } restart := false s.Lock() - s.conf.ProtectionEnabled = req.ProtectionEnabled - s.conf.BlockingMode = req.BlockingMode - if s.conf.Ratelimit != req.RateLimit { - restart = true + + if js.Exists("protection_enabled") { + s.conf.ProtectionEnabled = req.ProtectionEnabled } - s.conf.Ratelimit = req.RateLimit + + if js.Exists("blocking_mode") { + s.conf.BlockingMode = req.BlockingMode + if req.BlockingMode == "custom_ip" { + if js.Exists("blocking_ipv4") { + s.conf.BlockingIPv4 = req.BlockingIPv4 + s.conf.BlockingIPAddrv4 = net.ParseIP(req.BlockingIPv4) + } + if js.Exists("blocking_ipv6") { + s.conf.BlockingIPv6 = req.BlockingIPv6 + s.conf.BlockingIPAddrv6 = net.ParseIP(req.BlockingIPv6) + } + } + } + + if js.Exists("ratelimit") { + if s.conf.Ratelimit != req.RateLimit { + restart = true + } + s.conf.Ratelimit = req.RateLimit + } + s.Unlock() s.conf.ConfigModified()