diff --git a/CHANGELOG.md b/CHANGELOG.md index b5edd91b..ca77782c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,11 @@ and this project adheres to - Weaker cipher suites that use the CBC (cipher block chaining) mode of operation have been disabled ([#2993]). +### Added + +- A new HTTP API, `GET /control/blocked_services/services`, that lists all + available blocked services ([#4535]). + ### Deprecated - Ports 784 and 8853 for DNS-over-QUIC in Docker images. Users who still serve @@ -34,6 +39,7 @@ and this project adheres to - Unnecessary logging of non-critical statistics errors ([#4850]). [#2993]: https://github.com/AdguardTeam/AdGuardHome/issues/2993 +[#4535]: https://github.com/AdguardTeam/AdGuardHome/issues/4535 [#4745]: https://github.com/AdguardTeam/AdGuardHome/issues/4745 [#4850]: https://github.com/AdguardTeam/AdGuardHome/issues/4850 @@ -59,7 +65,7 @@ See also the [v0.107.11 GitHub milestone][ms-v0.107.11]. ### Changed -- DNS-over-QUIC connections now use keptalive. +- DNS-over-QUIC connections now use keepalive. ### Fixed diff --git a/internal/filtering/blocked.go b/internal/filtering/blocked.go index 41e8ad04..203407f2 100644 --- a/internal/filtering/blocked.go +++ b/internal/filtering/blocked.go @@ -7,19 +7,21 @@ import ( "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/urlfilter/rules" + "golang.org/x/exp/slices" ) -var serviceRules map[string][]*rules.NetworkRule // service name -> filtering rules - +// svc represents a single blocked service. type svc struct { name string rules []string } +// servicesData contains raw blocked service data. +// // Keep in sync with: -// client/src/helpers/constants.js -// client/src/components/ui/Icons.js -var serviceRulesArray = []svc{{ +// - client/src/helpers/constants.js +// - client/src/components/ui/Icons.js +var servicesData = []svc{{ name: "whatsapp", rules: []string{ "||wa.me^", @@ -365,21 +367,38 @@ var serviceRulesArray = []svc{{ }, }} -// convert array to map +// serviceRules maps a service ID to its filtering rules. +var serviceRules map[string][]*rules.NetworkRule + +// serviceIDs contains service IDs sorted alphabetically. +var serviceIDs []string + +// initBlockedServices initializes package-level blocked service data. func initBlockedServices() { - serviceRules = make(map[string][]*rules.NetworkRule) - for _, s := range serviceRulesArray { - netRules := []*rules.NetworkRule{} + l := len(servicesData) + serviceIDs = make([]string, l) + serviceRules = make(map[string][]*rules.NetworkRule, l) + + for i, s := range servicesData { + netRules := make([]*rules.NetworkRule, 0, len(s.rules)) for _, text := range s.rules { rule, err := rules.NewNetworkRule(text, BlockedSvcsListID) if err != nil { - log.Error("rules.NewNetworkRule: %s rule: %s", err, text) + log.Error("parsing blocked service %q rule %q: %s", s.name, text, err) + continue } + netRules = append(netRules, rule) } + + serviceIDs[i] = s.name serviceRules[s.name] = netRules } + + slices.Sort(serviceIDs) + + log.Debug("filtering: initialized %d services", l) } // BlockedSvcKnown - return TRUE if a blocked service name is known @@ -411,16 +430,11 @@ func (d *DNSFilter) ApplyBlockedServices(setts *Settings, list []string, global } } -func (d *DNSFilter) handleBlockedServicesAvailableServices(w http.ResponseWriter, r *http.Request) { - var list []string - for _, v := range serviceRulesArray { - list = append(list, s.name) - } - +func (d *DNSFilter) handleBlockedServicesAvailableServices(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - err := json.NewEncoder(w).Encode(list) + err := json.NewEncoder(w).Encode(serviceIDs) if err != nil { - aghhttp.Error(r, w, http.StatusInternalServerError, "json.Encode: %s", err) + aghhttp.Error(r, w, http.StatusInternalServerError, "encoding available services: %s", err) return } @@ -434,7 +448,7 @@ func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Req w.Header().Set("Content-Type", "application/json") err := json.NewEncoder(w).Encode(list) if err != nil { - aghhttp.Error(r, w, http.StatusInternalServerError, "json.Encode: %s", err) + aghhttp.Error(r, w, http.StatusInternalServerError, "encoding services: %s", err) return } diff --git a/internal/filtering/filtering.go b/internal/filtering/filtering.go index 4a3e6b28..4d438dfe 100644 --- a/internal/filtering/filtering.go +++ b/internal/filtering/filtering.go @@ -296,9 +296,11 @@ func cloneRewrites(entries []*LegacyRewrite) (clone []*LegacyRewrite) { return clone } -// SetFilters - set new filters (synchronously or asynchronously) -// When filters are set asynchronously, the old filters continue working until the new filters are ready. -// In this case the caller must ensure that the old filter files are intact. +// SetFilters sets new filters, synchronously or asynchronously. When filters +// are set asynchronously, the old filters continue working until the new +// filters are ready. +// +// In this case the caller must ensure that the old filter files are intact. func (d *DNSFilter) SetFilters(blockFilters, allowFilters []Filter, async bool) error { if async { params := filtersInitializerParams{ diff --git a/internal/filtering/rewrites.go b/internal/filtering/rewrites.go index dab4c034..c1557158 100644 --- a/internal/filtering/rewrites.go +++ b/internal/filtering/rewrites.go @@ -130,10 +130,9 @@ func matchDomainWildcard(host, wildcard string) (ok bool) { // // The sorting priority: // -// A and AAAA > CNAME -// wildcard > exact -// lower level wildcard > higher level wildcard -// +// 1. A and AAAA > CNAME +// 2. wildcard > exact +// 3. lower level wildcard > higher level wildcard type rewritesSorted []*LegacyRewrite // Len implements the sort.Interface interface for legacyRewritesSorted.