diff --git a/AGHTechDoc.md b/AGHTechDoc.md index e5e582ec..196515a5 100644 --- a/AGHTechDoc.md +++ b/AGHTechDoc.md @@ -940,7 +940,7 @@ Error response (Client not found): ### API: Find clients by IP This method returns the list of clients (manual and auto-clients) matching the IP list. -For auto-clients only `name`, `ids` and `whois_info` fields are set. Other fields are empty. +For auto-clients only `name`, `ids`, `whois_info`, `disallowed` fields are set. Other fields are empty. Request: @@ -966,11 +966,18 @@ Response: key: "value" ... } + + "disallowed": "..." } } ... ] +`disallowed`: +* "": IP is allowed +* not "", e.g. "127.0.0.0/24" - IP is disallowed by "disallowed IP list", and the string contains the matched rule (IP or CIDR) +* "not-in-allowed-list" - IP is disallowed by "allowed IP list" + ## DNS general settings diff --git a/dnsforward/access.go b/dnsforward/access.go index eadd1141..1a56dc84 100644 --- a/dnsforward/access.go +++ b/dnsforward/access.go @@ -80,43 +80,43 @@ func processIPCIDRArray(dst *map[string]bool, dstIPNet *[]net.IPNet, src []strin } // IsBlockedIP - return TRUE if this client should be blocked -func (a *accessCtx) IsBlockedIP(ip string) bool { +func (a *accessCtx) IsBlockedIP(ip string) (bool, string) { a.lock.Lock() defer a.lock.Unlock() if len(a.allowedClients) != 0 || len(a.allowedClientsIPNet) != 0 { _, ok := a.allowedClients[ip] if ok { - return false + return false, "" } if len(a.allowedClientsIPNet) != 0 { ipAddr := net.ParseIP(ip) for _, ipnet := range a.allowedClientsIPNet { if ipnet.Contains(ipAddr) { - return false + return false, "" } } } - return true + return true, "not-in-allowed-list" } _, ok := a.disallowedClients[ip] if ok { - return true + return true, ip } if len(a.disallowedClientsIPNet) != 0 { ipAddr := net.ParseIP(ip) for _, ipnet := range a.disallowedClientsIPNet { if ipnet.Contains(ipAddr) { - return true + return true, ipnet.String() } } } - return false + return false, "" } // IsBlockedDomain - return TRUE if this domain should be blocked diff --git a/dnsforward/dnsforward.go b/dnsforward/dnsforward.go index 39d09a39..6815a7cd 100644 --- a/dnsforward/dnsforward.go +++ b/dnsforward/dnsforward.go @@ -298,3 +298,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { p.ServeHTTP(w, r) } } + +// IsBlockedIP - return TRUE if this client should be blocked +func (s *Server) IsBlockedIP(ip string) (bool, string) { + return s.access.IsBlockedIP(ip) +} diff --git a/dnsforward/dnsforward_test.go b/dnsforward/dnsforward_test.go index a3eb94f1..6099fed0 100644 --- a/dnsforward/dnsforward_test.go +++ b/dnsforward/dnsforward_test.go @@ -878,20 +878,28 @@ func TestIsBlockedIPAllowed(t *testing.T) { a := &accessCtx{} assert.True(t, a.Init([]string{"1.1.1.1", "2.2.0.0/16"}, nil, nil) == nil) - assert.True(t, !a.IsBlockedIP("1.1.1.1")) - assert.True(t, a.IsBlockedIP("1.1.1.2")) - assert.True(t, !a.IsBlockedIP("2.2.1.1")) - assert.True(t, a.IsBlockedIP("2.3.1.1")) + disallowed, _ := a.IsBlockedIP("1.1.1.1") + assert.False(t, disallowed) + disallowed, _ = a.IsBlockedIP("1.1.1.2") + assert.True(t, disallowed) + disallowed, _ = a.IsBlockedIP("2.2.1.1") + assert.False(t, disallowed) + disallowed, _ = a.IsBlockedIP("2.3.1.1") + assert.True(t, disallowed) } func TestIsBlockedIPDisallowed(t *testing.T) { a := &accessCtx{} assert.True(t, a.Init(nil, []string{"1.1.1.1", "2.2.0.0/16"}, nil) == nil) - assert.True(t, a.IsBlockedIP("1.1.1.1")) - assert.True(t, !a.IsBlockedIP("1.1.1.2")) - assert.True(t, a.IsBlockedIP("2.2.1.1")) - assert.True(t, !a.IsBlockedIP("2.3.1.1")) + disallowed, _ := a.IsBlockedIP("1.1.1.1") + assert.True(t, disallowed) + disallowed, _ = a.IsBlockedIP("1.1.1.2") + assert.False(t, disallowed) + disallowed, _ = a.IsBlockedIP("2.2.1.1") + assert.True(t, disallowed) + disallowed, _ = a.IsBlockedIP("2.3.1.1") + assert.False(t, disallowed) } func TestIsBlockedIPBlockedDomain(t *testing.T) { diff --git a/dnsforward/filter.go b/dnsforward/filter.go index 068c5112..e4900acb 100644 --- a/dnsforward/filter.go +++ b/dnsforward/filter.go @@ -12,7 +12,8 @@ import ( func (s *Server) beforeRequestHandler(_ *proxy.Proxy, d *proxy.DNSContext) (bool, error) { ip := ipFromAddr(d.Addr) - if s.access.IsBlockedIP(ip) { + disallowed, _ := s.access.IsBlockedIP(ip) + if disallowed { log.Tracef("Client IP %s is blocked by settings", ip) return false, nil } diff --git a/home/clients.go b/home/clients.go index 1e6ae413..81ee9bec 100644 --- a/home/clients.go +++ b/home/clients.go @@ -80,6 +80,8 @@ type clientsContainer struct { // dhcpServer is used for looking up clients IP addresses by MAC addresses dhcpServer *dhcpd.Server + DNSServer *dnsforward.Server + autoHosts *util.AutoHosts // get entries from system hosts-files testing bool // if TRUE, this object is used for internal tests diff --git a/home/clients_http.go b/home/clients_http.go index c48e4937..40130d42 100644 --- a/home/clients_http.go +++ b/home/clients_http.go @@ -21,6 +21,13 @@ type clientJSON struct { BlockedServices []string `json:"blocked_services"` Upstreams []string `json:"upstreams"` + + WhoisInfo map[string]interface{} `json:"whois_info"` + + // * "": IP is allowed + // * not "", e.g. "127.0.0.0/24" - IP is disallowed by "disallowed IP list", and the string contains the matched rule (IP or CIDR) + // * "not-in-allowed-list" - IP is disallowed by "allowed IP list" + Disallowed string `json:"disallowed"` } type clientHostJSON struct { @@ -123,15 +130,9 @@ func clientToJSON(c *Client) clientJSON { return cj } -type clientHostJSONWithID struct { - IDs []string `json:"ids"` - Name string `json:"name"` - WhoisInfo map[string]interface{} `json:"whois_info"` -} - // Convert ClientHost object to JSON -func clientHostToJSON(ip string, ch ClientHost) clientHostJSONWithID { - cj := clientHostJSONWithID{ +func clientHostToJSON(ip string, ch ClientHost) clientJSON { + cj := clientJSON{ Name: ch.Host, IDs: []string{ip}, } @@ -255,9 +256,21 @@ func (clients *clientsContainer) handleFindClient(w http.ResponseWriter, r *http continue // a client with this IP isn't found } cj := clientHostToJSON(ip, ch) + + disallowed, disallowedRule := clients.DNSServer.IsBlockedIP(ip) + if disallowed { + cj.Disallowed = disallowedRule + } + el[ip] = cj } else { cj := clientToJSON(&c) + + disallowed, disallowedRule := clients.DNSServer.IsBlockedIP(ip) + if disallowed { + cj.Disallowed = disallowedRule + } + el[ip] = cj } diff --git a/home/dns.go b/home/dns.go index 2d647f94..e44c1ff1 100644 --- a/home/dns.go +++ b/home/dns.go @@ -70,6 +70,7 @@ func initDNSServer() error { p.DHCPServer = Context.dhcpServer } Context.dnsServer = dnsforward.NewServer(p) + Context.clients.DNSServer = Context.dnsServer dnsConfig := generateServerConfig() err = Context.dnsServer.Prepare(&dnsConfig) if err != nil { diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 57d63c70..b9be01c3 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -1757,7 +1757,57 @@ components: properties: 1.2.3.4: items: - $ref: "#/components/schemas/Client" + $ref: "#/components/schemas/ClientFindSubEntry" + + ClientFindSubEntry: + type: object + properties: + name: + type: string + description: Name + example: localhost + ids: + type: array + description: IP, CIDR or MAC address + items: + type: string + use_global_settings: + type: boolean + filtering_enabled: + type: boolean + parental_enabled: + type: boolean + safebrowsing_enabled: + type: boolean + safesearch_enabled: + type: boolean + use_global_blocked_services: + type: boolean + blocked_services: + type: array + items: + type: string + upstreams: + type: array + items: + type: string + whois_info: + type: array + items: + $ref: "#/components/schemas/WhoisInfo" + disallowed: + description: > + * "": IP is allowed + * not "", e.g. "127.0.0.0/24" - IP is disallowed by "disallowed IP list", and the string contains the matched rule (IP or CIDR) + * "not-in-allowed-list" - IP is disallowed by "allowed IP list" + type: string + + WhoisInfo: + type: object + properties: + key: + type: string + Clients: type: object properties: