From 975995a9c7e67cb240c2ff81ac94174ca4bbad90 Mon Sep 17 00:00:00 2001 From: fernvenue Date: Thu, 17 Feb 2022 06:54:38 +0800 Subject: [PATCH 001/228] Update blocked.go Add some items to the filter list for Cloudflare services. - argotunnel.com [src](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/configuration/ports-and-ips) - cloudflare-ipfs.com [src](https://developers.cloudflare.com/distributed-web/ipfs-gateway) - cloudflare-quic.com [src](https://cloudflare-quic.com/) - cloudflareapps.com [src](https://www.cloudflare.com/apps/) - cloudflarewarp.com [src](https://developers.cloudflare.com/cloudflare-one/connections/connect-devices/warp/deployment/mdm-deployment/partners/hexnode) - pages.dev [src](https://pages.cloudflare.com/) - trycloudflare.com [src](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-useful-terms#quick-tunnels) - videodelivery.net [src](https://developers.cloudflare.com/stream/viewing-videos/using-the-stream-player) - workers.dev [src](https://workers.cloudflare.com/) --- internal/filtering/blocked.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/internal/filtering/blocked.go b/internal/filtering/blocked.go index aa0ba979..0efd3998 100644 --- a/internal/filtering/blocked.go +++ b/internal/filtering/blocked.go @@ -86,6 +86,15 @@ var serviceRulesArray = []svc{ "||warp.plus^", "||1.1.1.1^", "||dns4torpnlfs2ifuz2s2yf3fc7rdmsbhm6rw75euj35pac6ap25zgqad.onion^", + "||argotunnel.com^", + "||cloudflare-ipfs.com^", + "||cloudflare-quic.com^", + "||cloudflareapps.com^", + "||cloudflarewarp.com^", + "||pages.dev^", + "||trycloudflare.com^", + "||videodelivery.net^", + "||workers.dev^", }}, {"amazon", []string{ "||amazon.com^", From c346216424d6571f96d80f47a9224da4c47dbbf9 Mon Sep 17 00:00:00 2001 From: bakito Date: Sat, 12 Mar 2022 12:46:15 +0100 Subject: [PATCH 002/228] correct openapi schema --- internal/home/controlfiltering.go | 2 +- openapi/openapi.yaml | 16 +++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/internal/home/controlfiltering.go b/internal/home/controlfiltering.go index 639403d8..51d86146 100644 --- a/internal/home/controlfiltering.go +++ b/internal/home/controlfiltering.go @@ -316,7 +316,7 @@ type filterJSON struct { URL string `json:"url"` Name string `json:"name"` RulesCount uint32 `json:"rules_count"` - LastUpdated string `json:"last_updated"` + LastUpdated string `json:"last_updated,omitempty"` } type filteringConfig struct { diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 8b21a01f..bb3bfe09 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -1396,7 +1396,6 @@ 'required': - 'enabled' - 'id' - - 'last_updated' - 'name' - 'rules_count' - 'url' @@ -1434,6 +1433,10 @@ 'type': 'array' 'items': '$ref': '#/components/schemas/Filter' + 'whitelist_filters': + 'type': 'array' + 'items': + '$ref': '#/components/schemas/Filter' 'user_rules': 'type': 'array' 'items': @@ -1451,14 +1454,7 @@ 'description': 'Filtering URL settings' 'properties': 'data': - 'properties': - 'enabled': - 'type': 'boolean' - 'name': - 'type': 'string' - 'url': - 'type': 'string' - 'type': 'object' + '$ref': '#/components/schemas/Filter' 'url': 'type': 'string' 'whitelist': @@ -1860,6 +1856,8 @@ 'description': 'Previously added URL containing filtering rules' 'type': 'string' 'example': 'https://filters.adtidy.org/windows/filters/15.txt' + 'whitelist': + 'type': 'boolean' 'QueryLogItem': 'type': 'object' 'description': 'Query log item' From cd8206ad9bdf2b888e79f3eba93315919f3c31f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Ib=C3=A1=C3=B1ez?= Date: Wed, 16 Mar 2022 19:09:36 +0100 Subject: [PATCH 003/228] Also honor the user-defined UpstreamMode for the internal DNS proxy --- internal/dnsforward/config.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/internal/dnsforward/config.go b/internal/dnsforward/config.go index 9a050f52..6b82c63d 100644 --- a/internal/dnsforward/config.go +++ b/internal/dnsforward/config.go @@ -376,13 +376,25 @@ func (s *Server) prepareUpstreamSettings() error { // prepareIntlProxy - initializes DNS proxy that we use for internal DNS queries func (s *Server) prepareIntlProxy() { - s.internalProxy = &proxy.Proxy{ + proxyConfig := &proxy.Proxy{ Config: proxy.Config{ CacheEnabled: true, CacheSizeBytes: 4096, UpstreamConfig: s.conf.UpstreamConfig, + MaxGoroutines: int(s.conf.MaxGoroutines), }, } + + if s.conf.AllServers { + proxyConfig.UpstreamMode = proxy.UModeParallel + } else if s.conf.FastestAddr { + proxyConfig.UpstreamMode = proxy.UModeFastestAddr + proxyConfig.FastestPingTimeout = s.conf.FastestTimeout.Duration + } else { + proxyConfig.UpstreamMode = proxy.UModeLoadBalance + } + + s.internalProxy = proxyConfig } // prepareTLS - prepares TLS configuration for the DNS proxy From 047970e5eef1f054441b3ca50becdbe137fb41e4 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 30 Mar 2022 18:02:50 +0800 Subject: [PATCH 004/228] Enable code block syntax hightlight in README.md It'll make it just a little bit easier to read it --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 00664365..7044eece 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ It operates as a DNS server that re-routes tracking domains to a "black hole", t ### Automated install (Linux and Mac) Run the following command in your terminal: -``` +```sh curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v ``` @@ -212,11 +212,11 @@ Check the [`Makefile`](https://github.com/AdguardTeam/AdGuardHome/blob/master/Ma In order to do this, specify `GOOS` and `GOARCH` env variables before running make. For example: -``` +```sh env GOOS='linux' GOARCH='arm64' make ``` Or: -``` +```sh make GOOS='linux' GOARCH='arm64' ``` @@ -228,7 +228,7 @@ You'll need this to prepare a release build: Commands: -``` +```sh make build-release CHANNEL='...' VERSION='...' ``` @@ -271,12 +271,12 @@ There are three options how you can install an unstable version: 3. Standalone builds. Use the automated installation script or look for the available builds below. Beta: -``` +```sh curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c beta ``` Edge: -``` +```sh curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -c edge ``` From 0f2a9f262e5cf021ef5e58e65e92da1288e9da13 Mon Sep 17 00:00:00 2001 From: RoboMagus <68224306+RoboMagus@users.noreply.github.com> Date: Mon, 25 Apr 2022 09:41:31 +0200 Subject: [PATCH 005/228] Add '/blocked_services/services' API --- AGHTechDoc.md | 13 +++++++++++++ client/src/actions/services.js | 15 +++++++++++++++ client/src/api/Api.js | 7 +++++++ client2/src/lib/apis/blockedServices.ts | 12 ++++++++++++ internal/filtering/blocked.go | 16 ++++++++++++++++ openapi/openapi.yaml | 13 +++++++++++++ 6 files changed, 76 insertions(+) diff --git a/AGHTechDoc.md b/AGHTechDoc.md index 4758a0da..8f69e5bd 100644 --- a/AGHTechDoc.md +++ b/AGHTechDoc.md @@ -1355,6 +1355,19 @@ Internally, all supported services are stored as a map: service name -> list of rules +### API: Get blocked services list of available services + +Request: + + GET /control/blocked_services/services + +Response: + + 200 OK + + [ "name1", ... ] + + ### API: Get blocked services list Request: diff --git a/client/src/actions/services.js b/client/src/actions/services.js index 1c95e3a1..650aa330 100644 --- a/client/src/actions/services.js +++ b/client/src/actions/services.js @@ -2,6 +2,21 @@ import { createAction } from 'redux-actions'; import apiClient from '../api/Api'; import { addErrorToast, addSuccessToast } from './toasts'; +export const getBlockedServicesAvailableServicesRequest = createAction('GET_BLOCKED_SERVICES_AVAILABLE_SERVICES_REQUEST'); +export const getBlockedServicesAvailableServicesFailure = createAction('GET_BLOCKED_SERVICES_AVAILABLE_SERVICES_FAILURE'); +export const getBlockedServicesAvailableServicesSuccess = createAction('GET_BLOCKED_SERVICES_AVAILABLE_SERVICES_SUCCESS'); + +export const getBlockedServicesAvailableServices = () => async (dispatch) => { + dispatch(getBlockedServicesAvailableServicesRequest()); + try { + const data = await apiClient.getBlockedServicesAvailableServices(); + dispatch(getBlockedServicesAvailableServicesSuccess(data)); + } catch (error) { + dispatch(addErrorToast({ error })); + dispatch(getBlockedServicesAvailableServicesFailure()); + } +}; + export const getBlockedServicesRequest = createAction('GET_BLOCKED_SERVICES_REQUEST'); export const getBlockedServicesFailure = createAction('GET_BLOCKED_SERVICES_FAILURE'); export const getBlockedServicesSuccess = createAction('GET_BLOCKED_SERVICES_SUCCESS'); diff --git a/client/src/api/Api.js b/client/src/api/Api.js index 1f6b2832..625da9e0 100644 --- a/client/src/api/Api.js +++ b/client/src/api/Api.js @@ -481,10 +481,17 @@ class Api { } // Blocked services + BLOCKED_SERVICES_SERVICES = { path: 'blocked_services/services', method: 'GET' }; + BLOCKED_SERVICES_LIST = { path: 'blocked_services/list', method: 'GET' }; BLOCKED_SERVICES_SET = { path: 'blocked_services/set', method: 'POST' }; + getBlockedServicesAvailableServices() { + const { path, method } = this.BLOCKED_SERVICES_SERVICES; + return this.makeRequest(path, method); + } + getBlockedServices() { const { path, method } = this.BLOCKED_SERVICES_LIST; return this.makeRequest(path, method); diff --git a/client2/src/lib/apis/blockedServices.ts b/client2/src/lib/apis/blockedServices.ts index 381a236d..7daa3344 100644 --- a/client2/src/lib/apis/blockedServices.ts +++ b/client2/src/lib/apis/blockedServices.ts @@ -1,6 +1,18 @@ // This file was autogenerated. Please do not change. // All changes will be overwrited on commit. export default class BlockedServicesApi { + static async blockedServicesAvailableServices(): Promise { + return await fetch(`/control/blocked_services/services`, { + method: 'GET', + }).then(async (res) => { + if (res.status === 200) { + return res.json(); + } else { + return new Error(String(res.status)); + } + }) + } + static async blockedServicesList(): Promise { return await fetch(`/control/blocked_services/list`, { method: 'GET', diff --git a/internal/filtering/blocked.go b/internal/filtering/blocked.go index 1d165cf4..bf990de9 100644 --- a/internal/filtering/blocked.go +++ b/internal/filtering/blocked.go @@ -331,6 +331,21 @@ 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) + } + + 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) + + return + } +} + func (d *DNSFilter) handleBlockedServicesList(w http.ResponseWriter, r *http.Request) { d.confLock.RLock() list := d.Config.BlockedServices @@ -365,6 +380,7 @@ func (d *DNSFilter) handleBlockedServicesSet(w http.ResponseWriter, r *http.Requ // registerBlockedServicesHandlers - register HTTP handlers func (d *DNSFilter) registerBlockedServicesHandlers() { + d.Config.HTTPRegister(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesAvailableServices) d.Config.HTTPRegister(http.MethodGet, "/control/blocked_services/list", d.handleBlockedServicesList) d.Config.HTTPRegister(http.MethodPost, "/control/blocked_services/set", d.handleBlockedServicesSet) } diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 8b21a01f..19193151 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -874,6 +874,19 @@ 'summary': 'Set (dis)allowed clients, blocked hosts, etc.' 'tags': - 'clients' + '/blocked_services/services': + 'get': + 'tags': + - 'blocked_services' + 'operationId': 'blockedServicesAvailableServices' + 'summary': 'Get available services to use for blocking' + 'responses': + '200': + 'description': 'OK.' + 'content': + 'application/json': + 'schema': + '$ref': '#/components/schemas/BlockedServicesArray' '/blocked_services/list': 'get': 'tags': From b7eedb3feb5fadbac3bf780e3429d07f160491b8 Mon Sep 17 00:00:00 2001 From: jumpsmm7 <49514613+jumpsmm7@users.noreply.github.com> Date: Fri, 6 May 2022 21:34:06 -0400 Subject: [PATCH 006/228] Update README.md * Add Asuswrt-Merlin-AdGuardHome-Installer (a.k.a. the installer that made adguardhome available to Asuswrt-Merlin Asus Routers) --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 00664365..316ffa5f 100644 --- a/README.md +++ b/README.md @@ -345,6 +345,7 @@ Here's what you can also do to contribute: * [Prometheus exporter for AdGuard Home](https://github.com/ebrianne/adguard-exporter) by [@ebrianne](https://github.com/ebrianne) * [AdGuard Home on GLInet routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by [Gl-Inet](https://gl-inet.com/) * [Cloudron app](https://git.cloudron.io/cloudron/adguard-home-app) by [@gramakri](https://github.com/gramakri) +* [Asuswrt-Merlin-AdGuardHome-Installer](https://github.com/jumpsmm7/Asuswrt-Merlin-AdGuardHome-Installer) by [@jumpsmm7](https://github.com/jumpsmm7) a.k.a [@SomeWhereOverTheRainBow](https://www.snbforums.com/members/somewhereovertherainbow.64179/) ## Acknowledgments From 3505ce87397372f79bc067f6afa8cead833426a9 Mon Sep 17 00:00:00 2001 From: Ainar Garipov Date: Thu, 7 Jul 2022 19:33:32 +0300 Subject: [PATCH 007/228] Pull request: all: use canonical names for hosts file runtime clients Updates #4683. Squashed commit of the following: commit daa8fdaee574d4ac2171f6b13c5ce3f3fedd9801 Author: Ainar Garipov Date: Thu Jul 7 19:13:29 2022 +0300 all: use canonical names for hosts file runtime clients --- CHANGELOG.md | 6 +- internal/aghnet/hostscontainer.go | 113 +++++++++++++------- internal/aghnet/hostscontainer_test.go | 137 ++++++++++++++----------- internal/aghnet/systemresolvers.go | 2 +- internal/home/clients.go | 16 +-- 5 files changed, 164 insertions(+), 110 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0490a5f1..d5e36004 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,7 +23,7 @@ and this project adheres to ### Added - Support for Discovery of Designated Resolvers (DDR) according to the [RFC - draft][ddr-draft-06] ([#4463]). + draft][ddr-draft] ([#4463]). - `windows/arm64` support ([#3057]). ### Deprecated @@ -32,6 +32,7 @@ and this project adheres to ### Fixed +- Inconsistent names of runtime clients from hosts files ([#4683]). - PTR requests for addresses leased by DHCP will now be resolved into hostnames under `dhcp.local_domain_name` ([#4699]). - Broken service installation on OpenWrt ([#4677]). @@ -39,9 +40,10 @@ and this project adheres to [#2993]: https://github.com/AdguardTeam/AdGuardHome/issues/2993 [#3057]: https://github.com/AdguardTeam/AdGuardHome/issues/3057 [#4677]: https://github.com/AdguardTeam/AdGuardHome/issues/4677 +[#4683]: https://github.com/AdguardTeam/AdGuardHome/issues/4683 [#4699]: https://github.com/AdguardTeam/AdGuardHome/issues/4699 -[ddr-draft-06]: https://www.ietf.org/archive/id/draft-ietf-add-ddr-06.html +[ddr-draft]: https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-08 diff --git a/internal/aghnet/hostscontainer.go b/internal/aghnet/hostscontainer.go index 65c9d3c4..15875ccc 100644 --- a/internal/aghnet/hostscontainer.go +++ b/internal/aghnet/hostscontainer.go @@ -198,7 +198,7 @@ func (hc *HostsContainer) Close() (err error) { } // Upd returns the channel into which the updates are sent. The receivable -// map's values are guaranteed to be of type of *stringutil.Set. +// map's values are guaranteed to be of type of *HostsRecord. func (hc *HostsContainer) Upd() (updates <-chan *netutil.IPMap) { return hc.updates } @@ -290,7 +290,7 @@ func (hp *hostsParser) parseFile(r io.Reader) (patterns []string, cont bool, err continue } - hp.addPairs(ip, hosts) + hp.addRecord(ip, hosts) } return nil, true, s.Err() @@ -335,39 +335,66 @@ func (hp *hostsParser) parseLine(line string) (ip net.IP, hosts []string) { return ip, hosts } -// addPair puts the pair of ip and host to the rules builder if needed. For -// each ip the first member of hosts will become the main one. -func (hp *hostsParser) addPairs(ip net.IP, hosts []string) { +// HostsRecord represents a single hosts file record. +type HostsRecord struct { + Aliases *stringutil.Set + Canonical string +} + +// Equal returns true if all fields of rec are equal to field in other or they +// both are nil. +func (rec *HostsRecord) Equal(other *HostsRecord) (ok bool) { + if rec == nil { + return other == nil + } + + return rec.Canonical == other.Canonical && rec.Aliases.Equal(other.Aliases) +} + +// addRecord puts the record for the IP address to the rules builder if needed. +// The first host is considered to be the canonical name for the IP address. +// hosts must have at least one name. +func (hp *hostsParser) addRecord(ip net.IP, hosts []string) { + line := strings.Join(append([]string{ip.String()}, hosts...), " ") + + var rec *HostsRecord v, ok := hp.table.Get(ip) if !ok { - // This ip is added at the first time. - v = stringutil.NewSet() - hp.table.Set(ip, v) + rec = &HostsRecord{ + Aliases: stringutil.NewSet(), + } + + rec.Canonical, hosts = hosts[0], hosts[1:] + hp.addRules(ip, rec.Canonical, line) + hp.table.Set(ip, rec) + } else { + rec, ok = v.(*HostsRecord) + if !ok { + log.Error("%s: adding pairs: unexpected type %T", hostsContainerPref, v) + + return + } } - var set *stringutil.Set - set, ok = v.(*stringutil.Set) - if !ok { - log.Debug("%s: adding pairs: unexpected value type %T", hostsContainerPref, v) - - return - } - - processed := strings.Join(append([]string{ip.String()}, hosts...), " ") - for _, h := range hosts { - if set.Has(h) { + for _, host := range hosts { + if rec.Canonical == host || rec.Aliases.Has(host) { continue } - set.Add(h) + rec.Aliases.Add(host) - rule, rulePtr := hp.writeRules(h, ip) - hp.translations[rule], hp.translations[rulePtr] = processed, processed - - log.Debug("%s: added ip-host pair %q-%q", hostsContainerPref, ip, h) + hp.addRules(ip, host, line) } } +// addRules adds rules and rule translations for the line. +func (hp *hostsParser) addRules(ip net.IP, host, line string) { + rule, rulePtr := hp.writeRules(host, ip) + hp.translations[rule], hp.translations[rulePtr] = line, line + + log.Debug("%s: added ip-host pair %q-%q", hostsContainerPref, ip, host) +} + // writeRules writes the actual rule for the qtype and the PTR for the host-ip // pair into internal builders. func (hp *hostsParser) writeRules(host string, ip net.IP) (rule, rulePtr string) { @@ -417,6 +444,7 @@ func (hp *hostsParser) writeRules(host string, ip net.IP) (rule, rulePtr string) } // equalSet returns true if the internal hosts table just parsed equals target. +// target's values must be of type *HostsRecord. func (hp *hostsParser) equalSet(target *netutil.IPMap) (ok bool) { if target == nil { // hp.table shouldn't appear nil since it's initialized on each refresh. @@ -427,22 +455,35 @@ func (hp *hostsParser) equalSet(target *netutil.IPMap) (ok bool) { return false } - hp.table.Range(func(ip net.IP, b interface{}) (cont bool) { - // ok is set to true if the target doesn't contain ip or if the - // appropriate hosts set isn't equal to the checked one. - if a, hasIP := target.Get(ip); !hasIP { - ok = true - } else if hosts, aok := a.(*stringutil.Set); aok { - ok = !hosts.Equal(b.(*stringutil.Set)) + hp.table.Range(func(ip net.IP, recVal interface{}) (cont bool) { + var targetVal interface{} + targetVal, ok = target.Get(ip) + if !ok { + return false } - // Continue only if maps has no discrepancies. - return !ok + var rec *HostsRecord + rec, ok = recVal.(*HostsRecord) + if !ok { + log.Error("%s: comparing: unexpected type %T", hostsContainerPref, recVal) + + return false + } + + var targetRec *HostsRecord + targetRec, ok = targetVal.(*HostsRecord) + if !ok { + log.Error("%s: comparing: target: unexpected type %T", hostsContainerPref, targetVal) + + return false + } + + ok = rec.Equal(targetRec) + + return ok }) - // Return true if every value from the IP map has no discrepancies with the - // appropriate one from the target. - return !ok + return ok } // sendUpd tries to send the parsed data to the ch. diff --git a/internal/aghnet/hostscontainer_test.go b/internal/aghnet/hostscontainer_test.go index 42202f43..019c713e 100644 --- a/internal/aghnet/hostscontainer_test.go +++ b/internal/aghnet/hostscontainer_test.go @@ -12,6 +12,7 @@ import ( "github.com/AdguardTeam/AdGuardHome/internal/aghtest" "github.com/AdguardTeam/golibs/errors" + "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/stringutil" "github.com/AdguardTeam/golibs/testutil" "github.com/AdguardTeam/urlfilter" @@ -159,31 +160,47 @@ func TestHostsContainer_refresh(t *testing.T) { require.NoError(t, err) testutil.CleanupAndRequireSuccess(t, hc.Close) - checkRefresh := func(t *testing.T, wantHosts *stringutil.Set) { - upd, ok := <-hc.Upd() - require.True(t, ok) - require.NotNil(t, upd) + checkRefresh := func(t *testing.T, want *HostsRecord) { + t.Helper() + + var ok bool + var upd *netutil.IPMap + select { + case upd, ok = <-hc.Upd(): + require.True(t, ok) + require.NotNil(t, upd) + case <-time.After(1 * time.Second): + t.Fatal("did not receive after 1s") + } assert.Equal(t, 1, upd.Len()) v, ok := upd.Get(ip) require.True(t, ok) - var set *stringutil.Set - set, ok = v.(*stringutil.Set) - require.True(t, ok) + require.IsType(t, (*HostsRecord)(nil), v) - assert.True(t, set.Equal(wantHosts)) + rec, _ := v.(*HostsRecord) + require.NotNil(t, rec) + + assert.Truef(t, rec.Equal(want), "%+v != %+v", rec, want) } t.Run("initial_refresh", func(t *testing.T) { - checkRefresh(t, stringutil.NewSet("hostname")) + checkRefresh(t, &HostsRecord{ + Aliases: stringutil.NewSet(), + Canonical: "hostname", + }) }) t.Run("second_refresh", func(t *testing.T) { testFS["dir/file2"] = &fstest.MapFile{Data: []byte(ipStr + ` alias` + nl)} eventsCh <- event{} - checkRefresh(t, stringutil.NewSet("hostname", "alias")) + + checkRefresh(t, &HostsRecord{ + Aliases: stringutil.NewSet("alias"), + Canonical: "hostname", + }) }) t.Run("double_refresh", func(t *testing.T) { @@ -363,10 +380,15 @@ func TestHostsContainer(t *testing.T) { require.NoError(t, fstest.TestFS(testdata, "etc_hosts")) testCases := []struct { - want []*rules.DNSRewrite - name string req *urlfilter.DNSRequest + name string + want []*rules.DNSRewrite }{{ + req: &urlfilter.DNSRequest{ + Hostname: "simplehost", + DNSType: dns.TypeA, + }, + name: "simple", want: []*rules.DNSRewrite{{ RCode: dns.RcodeSuccess, Value: net.IPv4(1, 0, 0, 1), @@ -376,27 +398,12 @@ func TestHostsContainer(t *testing.T) { Value: net.ParseIP("::1"), RRType: dns.TypeAAAA, }}, - name: "simple", - req: &urlfilter.DNSRequest{ - Hostname: "simplehost", - DNSType: dns.TypeA, - }, }, { - want: []*rules.DNSRewrite{{ - RCode: dns.RcodeSuccess, - Value: net.IPv4(1, 0, 0, 0), - RRType: dns.TypeA, - }, { - RCode: dns.RcodeSuccess, - Value: net.ParseIP("::"), - RRType: dns.TypeAAAA, - }}, - name: "hello_alias", req: &urlfilter.DNSRequest{ Hostname: "hello.world", DNSType: dns.TypeA, }, - }, { + name: "hello_alias", want: []*rules.DNSRewrite{{ RCode: dns.RcodeSuccess, Value: net.IPv4(1, 0, 0, 0), @@ -406,26 +413,41 @@ func TestHostsContainer(t *testing.T) { Value: net.ParseIP("::"), RRType: dns.TypeAAAA, }}, - name: "other_line_alias", + }, { req: &urlfilter.DNSRequest{ Hostname: "hello.world.again", DNSType: dns.TypeA, }, + name: "other_line_alias", + want: []*rules.DNSRewrite{{ + RCode: dns.RcodeSuccess, + Value: net.IPv4(1, 0, 0, 0), + RRType: dns.TypeA, + }, { + RCode: dns.RcodeSuccess, + Value: net.ParseIP("::"), + RRType: dns.TypeAAAA, + }}, }, { - want: []*rules.DNSRewrite{}, - name: "hello_subdomain", req: &urlfilter.DNSRequest{ Hostname: "say.hello", DNSType: dns.TypeA, }, - }, { + name: "hello_subdomain", want: []*rules.DNSRewrite{}, - name: "hello_alias_subdomain", + }, { req: &urlfilter.DNSRequest{ Hostname: "say.hello.world", DNSType: dns.TypeA, }, + name: "hello_alias_subdomain", + want: []*rules.DNSRewrite{}, }, { + req: &urlfilter.DNSRequest{ + Hostname: "for.testing", + DNSType: dns.TypeA, + }, + name: "lots_of_aliases", want: []*rules.DNSRewrite{{ RCode: dns.RcodeSuccess, RRType: dns.TypeA, @@ -435,37 +457,37 @@ func TestHostsContainer(t *testing.T) { RRType: dns.TypeAAAA, Value: net.ParseIP("::2"), }}, - name: "lots_of_aliases", - req: &urlfilter.DNSRequest{ - Hostname: "for.testing", - DNSType: dns.TypeA, - }, }, { + req: &urlfilter.DNSRequest{ + Hostname: "1.0.0.1.in-addr.arpa", + DNSType: dns.TypePTR, + }, + name: "reverse", want: []*rules.DNSRewrite{{ RCode: dns.RcodeSuccess, RRType: dns.TypePTR, Value: "simplehost.", }}, - name: "reverse", - req: &urlfilter.DNSRequest{ - Hostname: "1.0.0.1.in-addr.arpa", - DNSType: dns.TypePTR, - }, }, { - want: []*rules.DNSRewrite{}, - name: "non-existing", req: &urlfilter.DNSRequest{ Hostname: "nonexisting", DNSType: dns.TypeA, }, + name: "non-existing", + want: []*rules.DNSRewrite{}, }, { - want: nil, - name: "bad_type", req: &urlfilter.DNSRequest{ Hostname: "1.0.0.1.in-addr.arpa", DNSType: dns.TypeSRV, }, + name: "bad_type", + want: nil, }, { + req: &urlfilter.DNSRequest{ + Hostname: "domain", + DNSType: dns.TypeA, + }, + name: "issue_4216_4_6", want: []*rules.DNSRewrite{{ RCode: dns.RcodeSuccess, RRType: dns.TypeA, @@ -475,12 +497,12 @@ func TestHostsContainer(t *testing.T) { RRType: dns.TypeAAAA, Value: net.ParseIP("::42"), }}, - name: "issue_4216_4_6", + }, { req: &urlfilter.DNSRequest{ - Hostname: "domain", + Hostname: "domain4", DNSType: dns.TypeA, }, - }, { + name: "issue_4216_4", want: []*rules.DNSRewrite{{ RCode: dns.RcodeSuccess, RRType: dns.TypeA, @@ -490,12 +512,12 @@ func TestHostsContainer(t *testing.T) { RRType: dns.TypeA, Value: net.IPv4(1, 3, 5, 7), }}, - name: "issue_4216_4", - req: &urlfilter.DNSRequest{ - Hostname: "domain4", - DNSType: dns.TypeA, - }, }, { + req: &urlfilter.DNSRequest{ + Hostname: "domain6", + DNSType: dns.TypeAAAA, + }, + name: "issue_4216_6", want: []*rules.DNSRewrite{{ RCode: dns.RcodeSuccess, RRType: dns.TypeAAAA, @@ -505,11 +527,6 @@ func TestHostsContainer(t *testing.T) { RRType: dns.TypeAAAA, Value: net.ParseIP("::31"), }}, - name: "issue_4216_6", - req: &urlfilter.DNSRequest{ - Hostname: "domain6", - DNSType: dns.TypeAAAA, - }, }} stubWatcher := aghtest.FSWatcher{ diff --git a/internal/aghnet/systemresolvers.go b/internal/aghnet/systemresolvers.go index 13fbeb32..5ca8e9be 100644 --- a/internal/aghnet/systemresolvers.go +++ b/internal/aghnet/systemresolvers.go @@ -19,7 +19,7 @@ type SystemResolvers interface { } // NewSystemResolvers returns a SystemResolvers with the cache refresh rate -// defined by refreshIvl. It disables auto-resfreshing if refreshIvl is 0. If +// defined by refreshIvl. It disables auto-refreshing if refreshIvl is 0. If // nil is passed for hostGenFunc, the default generator will be used. func NewSystemResolvers( hostGenFunc HostGenFunc, diff --git a/internal/home/clients.go b/internal/home/clients.go index 4ba6b884..74545a67 100644 --- a/internal/home/clients.go +++ b/internal/home/clients.go @@ -743,8 +743,7 @@ func (clients *clientsContainer) AddHost(ip net.IP, host string, src clientSourc // addHostLocked adds a new IP-hostname pairing. For internal use only. func (clients *clientsContainer) addHostLocked(ip net.IP, host string, src clientSource) (ok bool) { - var rc *RuntimeClient - rc, ok = clients.findRuntimeClientLocked(ip) + rc, ok := clients.findRuntimeClientLocked(ip) if ok { if rc.Source > src { return false @@ -799,25 +798,20 @@ func (clients *clientsContainer) addFromHostsFile(hosts *netutil.IPMap) { n := 0 hosts.Range(func(ip net.IP, v interface{}) (cont bool) { - hosts, ok := v.(*stringutil.Set) + rec, ok := v.(*aghnet.HostsRecord) if !ok { log.Error("dns: bad type %T in ipToRC for %s", v, ip) return true } - hosts.Range(func(name string) (cont bool) { - if clients.addHostLocked(ip, name, ClientSourceHostsFile) { - n++ - } - - return true - }) + clients.addHostLocked(ip, rec.Canonical, ClientSourceHostsFile) + n++ return true }) - log.Debug("clients: added %d client aliases from system hosts-file", n) + log.Debug("clients: added %d client aliases from system hosts file", n) } // addFromSystemARP adds the IP-hostname pairings from the output of the arp -a From 77e5e27d757c6f6110e65f9e99c544614b6c1d1a Mon Sep 17 00:00:00 2001 From: Dimitry Kolyshev Date: Thu, 7 Jul 2022 19:49:47 +0300 Subject: [PATCH 008/228] Pull request: all: updater exe name Merge in DNS/adguard-home from 4219-updater to master Squashed commit of the following: commit f569a5f232330b83c234838a5bff8ae5277f152f Merge: a90b4fa7 3505ce87 Author: Dimitry Kolyshev Date: Thu Jul 7 22:14:50 2022 +0530 Merge remote-tracking branch 'origin/master' into 4219-updater # Conflicts: # CHANGELOG.md commit a90b4fa7782c5ec4531d8e305c0d448e84898239 Author: Dimitry Kolyshev Date: Thu Jul 7 21:56:17 2022 +0530 home: imp code commit da0f96b976e430fffc531072ef3e2384bc8b1f09 Author: Dimitry Kolyshev Date: Thu Jul 7 21:48:40 2022 +0530 updater: exe name commit 246dc9ca3b133cbc93ea59edd272674b87ff8de3 Author: Dimitry Kolyshev Date: Thu Jul 7 19:18:02 2022 +0530 all: imp docs commit 042382d170c4d68ff67fe5544a75371337529623 Author: Dimitry Kolyshev Date: Thu Jul 7 18:02:25 2022 +0530 all: updater exe name commit a180c4673ead66788969865784348634af1a739e Author: Dimitry Kolyshev Date: Thu Jul 7 17:47:46 2022 +0530 docs: updater exe name commit 1a98a6eadbd96add0a488fb8f89fb7d8b0ffb3d0 Author: Dimitry Kolyshev Date: Thu Jul 7 17:40:44 2022 +0530 all: updater exe name commit 1b13f5d85550dc71b08fd8e5b4258f8414a38759 Author: Dimitry Kolyshev Date: Thu Jul 7 17:14:57 2022 +0530 all: updater exe name --- CHANGELOG.md | 3 +++ internal/home/controlupdate.go | 14 ++++++-------- internal/updater/updater.go | 17 +++++++++++------ internal/updater/updater_test.go | 4 ++-- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e36004..21046925 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ and this project adheres to ### Fixed +- Updater no longer expects a hardcoded name for `AdGuardHome` executable + ([#4219]). - Inconsistent names of runtime clients from hosts files ([#4683]). - PTR requests for addresses leased by DHCP will now be resolved into hostnames under `dhcp.local_domain_name` ([#4699]). @@ -39,6 +41,7 @@ and this project adheres to [#2993]: https://github.com/AdguardTeam/AdGuardHome/issues/2993 [#3057]: https://github.com/AdguardTeam/AdGuardHome/issues/3057 +[#4219]: https://github.com/AdguardTeam/AdGuardHome/issues/4219 [#4677]: https://github.com/AdguardTeam/AdGuardHome/issues/4677 [#4683]: https://github.com/AdguardTeam/AdGuardHome/issues/4683 [#4699]: https://github.com/AdguardTeam/AdGuardHome/issues/4699 diff --git a/internal/home/controlupdate.go b/internal/home/controlupdate.go index 08ddd455..067ed83d 100644 --- a/internal/home/controlupdate.go +++ b/internal/home/controlupdate.go @@ -7,7 +7,6 @@ import ( "net/http" "os" "os/exec" - "path/filepath" "runtime" "syscall" "time" @@ -180,11 +179,10 @@ func finishUpdate(ctx context.Context) { cleanup(ctx) cleanupAlways() - exeName := "AdGuardHome" - if runtime.GOOS == "windows" { - exeName = "AdGuardHome.exe" + curBinName, err := os.Executable() + if err != nil { + log.Fatalf("executable path request failed: %s", err) } - curBinName := filepath.Join(Context.workDir, exeName) if runtime.GOOS == "windows" { if Context.runningAsService { @@ -192,7 +190,7 @@ func finishUpdate(ctx context.Context) { // we can't restart the service via "kardianos/service" package - it kills the process first // we can't start a new instance - Windows doesn't allow it cmd := exec.Command("cmd", "/c", "net stop AdGuardHome & net start AdGuardHome") - err := cmd.Start() + err = cmd.Start() if err != nil { log.Fatalf("exec.Command() failed: %s", err) } @@ -204,14 +202,14 @@ func finishUpdate(ctx context.Context) { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr - err := cmd.Start() + err = cmd.Start() if err != nil { log.Fatalf("exec.Command() failed: %s", err) } os.Exit(0) } else { log.Info("Restarting: %v", os.Args) - err := syscall.Exec(curBinName, os.Args, os.Environ()) + err = syscall.Exec(curBinName, os.Args, os.Environ()) if err != nil { log.Fatalf("syscall.Exec() failed: %s", err) } diff --git a/internal/updater/updater.go b/internal/updater/updater.go index d975d977..5077787c 100644 --- a/internal/updater/updater.go +++ b/internal/updater/updater.go @@ -111,7 +111,12 @@ func (u *Updater) Update() (err error) { log.Info("updater: updating") defer func() { log.Info("updater: finished; errors: %v", err) }() - err = u.prepare() + execPath, err := os.Executable() + if err != nil { + return err + } + + err = u.prepare(filepath.Base(execPath)) if err != nil { return err } @@ -162,7 +167,8 @@ func (u *Updater) VersionCheckURL() (vcu string) { return u.versionCheckURL } -func (u *Updater) prepare() (err error) { +// prepare fills all necessary fields in Updater object. +func (u *Updater) prepare(exeName string) (err error) { u.updateDir = filepath.Join(u.workDir, fmt.Sprintf("agh-update-%s", u.newVersion)) _, pkgNameOnly := filepath.Split(u.packageURL) @@ -173,13 +179,13 @@ func (u *Updater) prepare() (err error) { u.packageName = filepath.Join(u.updateDir, pkgNameOnly) u.backupDir = filepath.Join(u.workDir, "agh-backup") - exeName := "AdGuardHome" + updateExeName := "AdGuardHome" if u.goos == "windows" { - exeName = "AdGuardHome.exe" + updateExeName = "AdGuardHome.exe" } u.backupExeName = filepath.Join(u.backupDir, exeName) - u.updateExeName = filepath.Join(u.updateDir, exeName) + u.updateExeName = filepath.Join(u.updateDir, updateExeName) log.Debug( "updater: updating from %s to %s using url: %s", @@ -188,7 +194,6 @@ func (u *Updater) prepare() (err error) { u.packageURL, ) - // TODO(a.garipov): Use os.Args[0] instead? u.currentExeName = filepath.Join(u.workDir, exeName) _, err = os.Stat(u.currentExeName) if err != nil { diff --git a/internal/updater/updater_test.go b/internal/updater/updater_test.go index 771fb6d4..b3268f2f 100644 --- a/internal/updater/updater_test.go +++ b/internal/updater/updater_test.go @@ -131,7 +131,7 @@ func TestUpdate(t *testing.T) { u.newVersion = "v0.103.1" u.packageURL = fakeURL.String() - require.NoError(t, u.prepare()) + require.NoError(t, u.prepare("AdGuardHome")) u.currentExeName = filepath.Join(wd, "AdGuardHome") @@ -209,7 +209,7 @@ func TestUpdateWindows(t *testing.T) { u.newVersion = "v0.103.1" u.packageURL = fakeURL.String() - require.NoError(t, u.prepare()) + require.NoError(t, u.prepare("AdGuardHome.exe")) u.currentExeName = filepath.Join(wd, "AdGuardHome.exe") From a832987f7c6d7f7ad0339f8a280e24263ef1b661 Mon Sep 17 00:00:00 2001 From: Eugene Burkov Date: Fri, 8 Jul 2022 15:17:47 +0300 Subject: [PATCH 009/228] Pull request: 4698 Gateway IP in DHCP Lease Closes #4698. Squashed commit of the following: commit 6be0caee58926f8cea1e10650fbde0c8d97d0dac Author: Ildar Kamalov Date: Fri Jul 8 13:41:50 2022 +0300 update translation commit e0370656d05e8463d73ea73568cae81187c6b2e3 Author: Ildar Kamalov Date: Fri Jul 8 13:40:54 2022 +0300 client: validate static lease ip commit 7f4d00f9f3a54dc93ce5d5c45e9c21745f6e39d1 Merge: 2ee79626 77e5e27d Author: Eugene Burkov Date: Fri Jul 8 13:20:15 2022 +0300 Merge branch 'master' into 4698-lease-with-gateway commit 2ee79626a1b0c7b113dbd22ba4ef6e85ea9913ec Merge: 471b96b8 3505ce87 Author: Eugene Burkov Date: Thu Jul 7 19:34:33 2022 +0300 Merge branch 'master' into 4698-lease-with-gateway commit 471b96b81da8920c1e71b7110050154f912677d2 Author: Eugene Burkov Date: Thu Jul 7 16:07:23 2022 +0300 dhcpd: imp docs commit 67dd6c76f7d2df4712a57281e0f40f2ee1a1efa2 Author: Eugene Burkov Date: Thu Jul 7 15:48:47 2022 +0300 dhcpd: restrict gateway ip for lease --- CHANGELOG.md | 3 + client/src/__locales/en.json | 1 + .../Settings/Dhcp/StaticLeases/Form.js | 3 + .../Settings/Dhcp/StaticLeases/Modal.js | 3 + .../Settings/Dhcp/StaticLeases/index.js | 3 + client/src/components/Settings/Dhcp/index.js | 1 + client/src/helpers/validators.js | 11 ++ internal/dhcpd/http.go | 1 - internal/dhcpd/v4.go | 12 +- internal/dhcpd/v4_test.go | 124 ++++++++++++------ 10 files changed, 120 insertions(+), 42 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21046925..7b8f5d97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ and this project adheres to ### Fixed +- DHCP lease validation incorrectly letting users assign the IP address of the + gateway as the address of the lease ([#4698]). - Updater no longer expects a hardcoded name for `AdGuardHome` executable ([#4219]). - Inconsistent names of runtime clients from hosts files ([#4683]). @@ -44,6 +46,7 @@ and this project adheres to [#4219]: https://github.com/AdguardTeam/AdGuardHome/issues/4219 [#4677]: https://github.com/AdguardTeam/AdGuardHome/issues/4677 [#4683]: https://github.com/AdguardTeam/AdGuardHome/issues/4683 +[#4698]: https://github.com/AdguardTeam/AdGuardHome/issues/4698 [#4699]: https://github.com/AdguardTeam/AdGuardHome/issues/4699 [ddr-draft]: https://datatracker.ietf.org/doc/html/draft-ietf-add-ddr-08 diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index dd885677..9fc65cac 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -47,6 +47,7 @@ "form_error_server_name": "Invalid server name", "form_error_subnet": "Subnet \"{{cidr}}\" does not contain the IP address \"{{ip}}\"", "form_error_positive": "Must be greater than 0", + "form_error_gateway_ip": "Lease can't have the IP address of the gateway", "out_of_range_error": "Must be out of range \"{{start}}\"-\"{{end}}\"", "lower_range_start_error": "Must be lower than range start", "greater_range_start_error": "Must be greater than range start", diff --git a/client/src/components/Settings/Dhcp/StaticLeases/Form.js b/client/src/components/Settings/Dhcp/StaticLeases/Form.js index 625e87e2..0525f6a3 100644 --- a/client/src/components/Settings/Dhcp/StaticLeases/Form.js +++ b/client/src/components/Settings/Dhcp/StaticLeases/Form.js @@ -10,6 +10,7 @@ import { validateMac, validateRequiredValue, validateIpv4InCidr, + validateIpGateway, } from '../../../../helpers/validators'; import { FORM_NAME } from '../../../../helpers/constants'; import { toggleLeaseModal } from '../../../../actions'; @@ -57,6 +58,7 @@ const Form = ({ validateRequiredValue, validateIpv4, validateIpv4InCidr, + validateIpGateway, ]} /> @@ -101,6 +103,7 @@ Form.propTypes = { ip: PropTypes.string.isRequired, hostname: PropTypes.string.isRequired, cidr: PropTypes.string.isRequired, + gatewayIp: PropTypes.string, }), pristine: PropTypes.bool.isRequired, handleSubmit: PropTypes.func.isRequired, diff --git a/client/src/components/Settings/Dhcp/StaticLeases/Modal.js b/client/src/components/Settings/Dhcp/StaticLeases/Modal.js index 8ad0f009..0baf487e 100644 --- a/client/src/components/Settings/Dhcp/StaticLeases/Modal.js +++ b/client/src/components/Settings/Dhcp/StaticLeases/Modal.js @@ -13,6 +13,7 @@ const Modal = ({ cidr, rangeStart, rangeEnd, + gatewayIp, }) => { const dispatch = useDispatch(); @@ -42,6 +43,7 @@ const Modal = ({ cidr, rangeStart, rangeEnd, + gatewayIp, }} onSubmit={handleSubmit} processingAdding={processingAdding} @@ -61,6 +63,7 @@ Modal.propTypes = { cidr: PropTypes.string.isRequired, rangeStart: PropTypes.string, rangeEnd: PropTypes.string, + gatewayIp: PropTypes.string, }; export default withTranslation()(Modal); diff --git a/client/src/components/Settings/Dhcp/StaticLeases/index.js b/client/src/components/Settings/Dhcp/StaticLeases/index.js index a63f78cd..2374f044 100644 --- a/client/src/components/Settings/Dhcp/StaticLeases/index.js +++ b/client/src/components/Settings/Dhcp/StaticLeases/index.js @@ -24,6 +24,7 @@ const StaticLeases = ({ cidr, rangeStart, rangeEnd, + gatewayIp, }) => { const [t] = useTranslation(); const dispatch = useDispatch(); @@ -106,6 +107,7 @@ const StaticLeases = ({ cidr={cidr} rangeStart={rangeStart} rangeEnd={rangeEnd} + gatewayIp={gatewayIp} /> ); @@ -119,6 +121,7 @@ StaticLeases.propTypes = { cidr: PropTypes.string.isRequired, rangeStart: PropTypes.string, rangeEnd: PropTypes.string, + gatewayIp: PropTypes.string, }; cellWrap.propTypes = { diff --git a/client/src/components/Settings/Dhcp/index.js b/client/src/components/Settings/Dhcp/index.js index a84e0a93..bd3a45e3 100644 --- a/client/src/components/Settings/Dhcp/index.js +++ b/client/src/components/Settings/Dhcp/index.js @@ -278,6 +278,7 @@ const Dhcp = () => { cidr={cidr} rangeStart={dhcp?.values?.v4?.range_start} rangeEnd={dhcp?.values?.v4?.range_end} + gatewayIp={dhcp?.values?.v4?.gateway_ip} />
; + const blockButton = ( + <> +
+ + + ); const blockForClientButton = ; const blockClientButton = @@ -37,17 +38,17 @@ const Form = (props) => { type="button" className="btn btn-secondary btn-block" disabled={processing || processingSet} - onClick={() => toggleAllServices(SERVICES, change, false)} + onClick={() => toggleAllServices(blockedServices, change, false)} > unblock_all
- {SERVICES.map((service) => ( + {blockedServices.map((service) => ( { }; Form.propTypes = { + blockedServices: PropTypes.array.isRequired, pristine: PropTypes.bool.isRequired, handleSubmit: PropTypes.func.isRequired, change: PropTypes.func.isRequired, diff --git a/client/src/components/Filters/Services/index.js b/client/src/components/Filters/Services/index.js index be5516bd..09cdd7c8 100644 --- a/client/src/components/Filters/Services/index.js +++ b/client/src/components/Filters/Services/index.js @@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; import Form from './Form'; import Card from '../../ui/Card'; -import { getBlockedServices, setBlockedServices } from '../../../actions/services'; +import { getBlockedServices, getAllBlockedServices, setBlockedServices } from '../../../actions/services'; import PageTitle from '../../ui/PageTitle'; const getInitialDataForServices = (initial) => (initial ? initial.reduce( @@ -21,6 +21,7 @@ const Services = () => { useEffect(() => { dispatch(getBlockedServices()); + dispatch(getAllBlockedServices()); }, []); const handleSubmit = (values) => { @@ -49,6 +50,7 @@ const Services = () => {
state.filtering.filters, shallowEqual); const whitelistFilters = useSelector((state) => state.filtering.whitelistFilters, shallowEqual); const isDetailed = useSelector((state) => state.queryLogs.isDetailed); + const services = useSelector((store) => store?.services); const formattedElapsedMs = formatElapsedMs(elapsedMs, t); @@ -60,8 +61,8 @@ const ResponseCell = ({ install_settings_dns: upstreamString, elapsed: formattedElapsedMs, response_code: status, - ...(service_name - && { service_name: getServiceName(service_name) } + ...(service_name && services.allServices + && { service_name: getServiceName(services.allServices, service_name) } ), ...(rules.length > 0 && { rule_label: getRulesToFilterList(rules, filters, whitelistFilters) } @@ -80,10 +81,10 @@ const ResponseCell = ({ const getDetailedInfo = (reason) => { switch (reason) { case FILTERED_STATUS.FILTERED_BLOCKED_SERVICE: - if (!service_name) { + if (!service_name || !services.allServices) { return formattedElapsedMs; } - return getServiceName(service_name); + return getServiceName(services.allServices, service_name); case FILTERED_STATUS.FILTERED_BLACK_LIST: case FILTERED_STATUS.NOT_FILTERED_WHITE_LIST: return getFilterNames(rules, filters, whitelistFilters).join(', '); diff --git a/client/src/components/Logs/Cells/index.js b/client/src/components/Logs/Cells/index.js index aaffeb07..fe32b104 100644 --- a/client/src/components/Logs/Cells/index.js +++ b/client/src/components/Logs/Cells/index.js @@ -52,6 +52,7 @@ const Row = memo(({ const autoClients = useSelector((state) => state.dashboard.autoClients, shallowEqual); const processingSet = useSelector((state) => state.access.processingSet); const allowedСlients = useSelector((state) => state.access.allowed_clients, shallowEqual); + const services = useSelector((store) => store?.services); const clients = useSelector((state) => state.dashboard.clients); @@ -175,8 +176,8 @@ const Row = memo(({ date: formatDateTime(time, DEFAULT_SHORT_DATE_FORMAT_OPTIONS), encryption_status: isBlocked ?
{requestStatus}
: requestStatus, - ...(FILTERED_STATUS.FILTERED_BLOCKED_SERVICE && service_name - && { service_name: getServiceName(service_name) }), + ...(FILTERED_STATUS.FILTERED_BLOCKED_SERVICE && service_name && services.allServices + && { service_name: getServiceName(services.allServices, service_name) }), domain, type_table_header: type, protocol, diff --git a/client/src/components/Logs/index.js b/client/src/components/Logs/index.js index e9ec100b..b4b4c636 100644 --- a/client/src/components/Logs/index.js +++ b/client/src/components/Logs/index.js @@ -16,6 +16,7 @@ import { getFilteringStatus } from '../../actions/filtering'; import { getClients } from '../../actions'; import { getDnsConfig } from '../../actions/dnsConfig'; import { getAccessList } from '../../actions/access'; +import { getAllBlockedServices } from '../../actions/services'; import { getLogsConfig, resetFilteredLogs, @@ -130,6 +131,7 @@ const Logs = () => { setIsLoading(true); dispatch(getFilteringStatus()); dispatch(getClients()); + dispatch(getAllBlockedServices()); try { await Promise.all([ dispatch(getLogsConfig()), diff --git a/client/src/components/Settings/Clients/ClientsTable.js b/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js similarity index 56% rename from client/src/components/Settings/Clients/ClientsTable.js rename to client/src/components/Settings/Clients/ClientsTable/ClientsTable.js index 2f08eb61..f83c44ab 100644 --- a/client/src/components/Settings/Clients/ClientsTable.js +++ b/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js @@ -1,25 +1,57 @@ -import React, { Component, Fragment } from 'react'; +/* eslint-disable react/display-name */ +/* eslint-disable react/prop-types */ +import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; -import { Trans, withTranslation } from 'react-i18next'; +import { Trans, useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; import ReactTable from 'react-table'; -import { MODAL_TYPE } from '../../../helpers/constants'; -import { splitByNewLine, countClientsStatistics, sortIp } from '../../../helpers/helpers'; -import Card from '../../ui/Card'; -import Modal from './Modal'; -import CellWrap from '../../ui/CellWrap'; -import LogsSearchLink from '../../ui/LogsSearchLink'; +import { getAllBlockedServices } from '../../../../actions/services'; +import { + splitByNewLine, + countClientsStatistics, + sortIp, + getService, +} from '../../../../helpers/helpers'; +import { MODAL_TYPE } from '../../../../helpers/constants'; +import Card from '../../../ui/Card'; +import CellWrap from '../../../ui/CellWrap'; +import LogsSearchLink from '../../../ui/LogsSearchLink'; +import Modal from '../Modal'; -class ClientsTable extends Component { - handleFormAdd = (values) => { - this.props.addClient(values); +const ClientsTable = ({ + clients, + normalizedTopClients, + isModalOpen, + modalClientName, + modalType, + addClient, + updateClient, + deleteClient, + toggleClientModal, + processingAdding, + processingDeleting, + processingUpdating, + getStats, + supportedTags, +}) => { + const [t] = useTranslation(); + const dispatch = useDispatch(); + const services = useSelector((store) => store?.services); + + useEffect(() => { + dispatch(getAllBlockedServices()); + }, []); + + const handleFormAdd = (values) => { + addClient(values); }; - handleFormUpdate = (values, name) => { - this.props.updateClient(values, name); + const handleFormUpdate = (values, name) => { + updateClient(values, name); }; - handleSubmit = (values) => { + const handleSubmit = (values) => { const config = values; if (values) { @@ -42,21 +74,21 @@ class ClientsTable extends Component { } } - if (this.props.modalType === MODAL_TYPE.EDIT_FILTERS) { - this.handleFormUpdate(config, this.props.modalClientName); + if (modalType === MODAL_TYPE.EDIT_FILTERS) { + handleFormUpdate(config, modalClientName); } else { - this.handleFormAdd(config); + handleFormAdd(config); } }; - getOptionsWithLabels = (options) => ( + const getOptionsWithLabels = (options) => ( options.map((option) => ({ value: option, label: option, })) ); - getClient = (name, clients) => { + const getClient = (name, clients) => { const client = clients.find((item) => name === item.name); if (client) { @@ -65,7 +97,7 @@ class ClientsTable extends Component { } = client; return { upstreams: (upstreams && upstreams.join('\n')) || '', - tags: (tags && this.getOptionsWithLabels(tags)) || [], + tags: (tags && getOptionsWithLabels(tags)) || [], ...values, }; } @@ -78,17 +110,17 @@ class ClientsTable extends Component { }; }; - handleDelete = (data) => { + const handleDelete = (data) => { // eslint-disable-next-line no-alert - if (window.confirm(this.props.t('client_confirm_delete', { key: data.name }))) { - this.props.deleteClient(data); - this.props.getStats(); + if (window.confirm(t('client_confirm_delete', { key: data.name }))) { + deleteClient(data); + getStats(); } }; - columns = [ + const columns = [ { - Header: this.props.t('table_client'), + Header: t('table_client'), accessor: 'ids', minWidth: 150, Cell: (row) => { @@ -109,13 +141,13 @@ class ClientsTable extends Component { sortMethod: sortIp, }, { - Header: this.props.t('table_name'), + Header: t('table_name'), accessor: 'name', minWidth: 120, Cell: CellWrap, }, { - Header: this.props.t('settings'), + Header: t('settings'), accessor: 'use_global_settings', minWidth: 120, Cell: ({ value }) => { @@ -133,7 +165,7 @@ class ClientsTable extends Component { }, }, { - Header: this.props.t('blocked_services'), + Header: t('blocked_services'), accessor: 'blocked_services', minWidth: 180, Cell: (row) => { @@ -143,25 +175,40 @@ class ClientsTable extends Component { return settings_global; } + if (value && services.allServices) { + return ( +
+ {value.map((service) => { + const serviceInfo = getService(services.allServices, service); + + if (serviceInfo?.icon_svg) { + return ( +
+ ); + } + + return null; + })} +
+ ); + } + return (
- {value && value.length > 0 - ? value.map((service) => ( - - - - )) - : '–'} + –
); }, }, { - Header: this.props.t('upstreams'), + Header: t('upstreams'), accessor: 'upstreams', minWidth: 120, Cell: ({ value }) => { @@ -179,7 +226,7 @@ class ClientsTable extends Component { }, }, { - Header: this.props.t('tags_title'), + Header: t('tags_title'), accessor: 'tags', minWidth: 140, Cell: (row) => { @@ -203,11 +250,11 @@ class ClientsTable extends Component { }, }, { - Header: this.props.t('requests_count'), + Header: t('requests_count'), id: 'statistics', accessor: (row) => countClientsStatistics( row.ids, - this.props.normalizedTopClients.auto, + normalizedTopClients.auto, ), sortMethod: (a, b) => b - a, minWidth: 120, @@ -222,16 +269,13 @@ class ClientsTable extends Component { }, }, { - Header: this.props.t('actions_table_header'), + Header: t('actions_table_header'), accessor: 'actions', maxWidth: 100, sortable: false, resizable: false, Cell: (row) => { const clientName = row.original.name; - const { - toggleClientModal, processingDeleting, processingUpdating, t, - } = this.props; return (
@@ -253,7 +297,7 @@ class ClientsTable extends Component { - - - - ); - } -} + return ( + + <> + + + + + + ); +}; ClientsTable.propTypes = { - t: PropTypes.func.isRequired, clients: PropTypes.array.isRequired, normalizedTopClients: PropTypes.object.isRequired, toggleClientModal: PropTypes.func.isRequired, @@ -353,4 +382,4 @@ ClientsTable.propTypes = { supportedTags: PropTypes.array.isRequired, }; -export default withTranslation()(ClientsTable); +export default ClientsTable; diff --git a/client/src/components/Settings/Clients/ClientsTable/index.js b/client/src/components/Settings/Clients/ClientsTable/index.js new file mode 100644 index 00000000..fdbe7807 --- /dev/null +++ b/client/src/components/Settings/Clients/ClientsTable/index.js @@ -0,0 +1 @@ +export { default as ClientsTable } from './ClientsTable'; diff --git a/client/src/components/Settings/Clients/Form.js b/client/src/components/Settings/Clients/Form.js index 353b0bea..35d9764d 100644 --- a/client/src/components/Settings/Clients/Form.js +++ b/client/src/components/Settings/Clients/Form.js @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import { connect } from 'react-redux'; +import { connect, useSelector } from 'react-redux'; import PropTypes from 'prop-types'; import { Field, FieldArray, reduxForm, formValueSelector, @@ -19,7 +19,7 @@ import { renderServiceField, } from '../../../helpers/form'; import { validateClientId, validateRequiredValue } from '../../../helpers/validators'; -import { CLIENT_ID_LINK, FORM_NAME, SERVICES } from '../../../helpers/constants'; +import { CLIENT_ID_LINK, FORM_NAME } from '../../../helpers/constants'; import './Service.css'; const settingsCheckboxes = [ @@ -139,6 +139,7 @@ let Form = (props) => { invalid, tagsOptions, } = props; + const services = useSelector((store) => store?.services); const [activeTabLabel, setActiveTabLabel] = useState('settings'); @@ -180,7 +181,9 @@ let Form = (props) => { type="button" className="btn btn-secondary btn-block" disabled={useGlobalServices} - onClick={() => toggleAllServices(SERVICES, change, true)} + onClick={() => ( + toggleAllServices(services.allServices, change, true) + )} > block_all @@ -190,25 +193,29 @@ let Form = (props) => { type="button" className="btn btn-secondary btn-block" disabled={useGlobalServices} - onClick={() => toggleAllServices(SERVICES, change, false)} + onClick={() => ( + toggleAllServices(services.allServices, change, false) + )} > unblock_all
-
- {SERVICES.map((service) => ( - - ))} -
+ {services.allServices.length > 0 && ( +
+ {services.allServices.map((service) => ( + + ))} +
+ )}
, }, diff --git a/client/src/components/Settings/Clients/Service.css b/client/src/components/Settings/Clients/Service.css index 7c1890fe..153cf2d8 100644 --- a/client/src/components/Settings/Clients/Service.css +++ b/client/src/components/Settings/Clients/Service.css @@ -9,6 +9,12 @@ cursor: pointer; } +.service__text { + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; +} + @media screen and (min-width: 768px) { .services { display: flex; @@ -33,7 +39,7 @@ margin-right: 30px; margin-left: 0; } - + .service:nth-child(3n) { margin-right: 0; margin-left: auto; diff --git a/client/src/components/Settings/Clients/index.js b/client/src/components/Settings/Clients/index.js index a6dfc0b1..b7c50ae8 100644 --- a/client/src/components/Settings/Clients/index.js +++ b/client/src/components/Settings/Clients/index.js @@ -2,7 +2,7 @@ import React, { Component, Fragment } from 'react'; import { withTranslation } from 'react-i18next'; import PropTypes from 'prop-types'; -import ClientsTable from './ClientsTable'; +import { ClientsTable } from './ClientsTable'; import AutoClients from './AutoClients'; import PageTitle from '../../ui/PageTitle'; import Loading from '../../ui/Loading'; diff --git a/client/src/components/ui/Icons.js b/client/src/components/ui/Icons.js index 47e71f59..38eff1be 100644 --- a/client/src/components/ui/Icons.js +++ b/client/src/components/ui/Icons.js @@ -98,157 +98,6 @@ const Icons = () => ( d="M15 3C10.57 3 6.701 5.419 4.623 9h2.39a10.063 10.063 0 0 1 4.05-3.19c-.524.89-.961 1.973-1.3 3.19h2.108c.79-2.459 1.998-4 3.129-4s2.339 1.541 3.129 4h2.107c-.338-1.217-.774-2.3-1.299-3.19A10.062 10.062 0 0 1 22.989 9h2.389C23.298 5.419 19.43 3 15 3zm7.035 9.129c-1.372 0-2.264.73-2.264 1.842 0 .896.538 1.463 1.579 1.66l.75.15c.65.13.898.3.898.615 0 .375-.37.635-.91.635-.6 0-1.014-.265-1.049-.68h-1.38c.023 1.097.93 1.776 2.37 1.776 1.491 0 2.399-.717 2.399-1.904 0-.903-.504-1.412-1.63-1.63l-.734-.142c-.6-.118-.851-.3-.851-.611 0-.378.336-.62.844-.62.509 0 .891.28.923.682h1.336c-.024-1.053-.948-1.773-2.28-1.773zm-16.185.148v5.696h2.39c1.712 0 2.662-1.033 2.662-2.903 0-1.779-.966-2.793-2.662-2.793H5.85zm6.933.004v5.692h1.373v-3.235h.076l2.377 3.235h1.149V12.28h-1.373v3.203h-.076l-2.372-3.203h-1.154zm-5.486 1.16h.682c.912 0 1.449.596 1.449 1.657 0 1.128-.51 1.713-1.45 1.713h-.681v-3.37zM4.623 21C6.701 24.581 10.57 27 15 27c4.43 0 8.299-2.419 10.377-6h-2.389a10.063 10.063 0 0 1-4.049 3.19c.524-.89.96-1.973 1.297-3.19H18.13c-.79 2.459-1.996 4-3.127 4-1.131 0-2.339-1.541-3.129-4h-2.11c.339 1.217.776 2.3 1.3 3.19A10.056 10.056 0 0 1 7.013 21h-2.39z"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -356,62 +205,6 @@ const Icons = () => ( d="M8.036 10.93l3.93 4.07 4.068-3.93" /> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ); diff --git a/client/src/components/ui/Tabler.css b/client/src/components/ui/Tabler.css index 34d37622..fac3d80a 100644 --- a/client/src/components/ui/Tabler.css +++ b/client/src/components/ui/Tabler.css @@ -15452,6 +15452,7 @@ a.tag-addon:hover { } .custom-switch-indicator { + flex-shrink: 0; display: inline-block; height: 1.25rem; width: 2.25rem; diff --git a/client/src/helpers/constants.js b/client/src/helpers/constants.js index 943b28ac..c13ab952 100644 --- a/client/src/helpers/constants.js +++ b/client/src/helpers/constants.js @@ -202,158 +202,6 @@ export const FILTERS_URLS = { blocked_services: '/blocked_services', }; -export const SERVICES = [ - { - id: '9gag', - name: '9GAG', - }, - { - id: 'amazon', - name: 'Amazon', - }, - { - id: 'bilibili', - name: 'Bilibili', - }, - { - id: 'cloudflare', - name: 'CloudFlare', - }, - { - id: 'dailymotion', - name: 'Dailymotion', - }, - { - id: 'discord', - name: 'Discord', - }, - { - id: 'disneyplus', - name: 'Disney+', - }, - { - id: 'ebay', - name: 'EBay', - }, - { - id: 'epic_games', - name: 'Epic Games', - }, - { - id: 'facebook', - name: 'Facebook', - }, - { - id: 'hulu', - name: 'Hulu', - }, - { - id: 'imgur', - name: 'Imgur', - }, - { - id: 'instagram', - name: 'Instagram', - }, - { - id: 'mail_ru', - name: 'Mail.ru', - }, - { - id: 'netflix', - name: 'Netflix', - }, - { - id: 'ok', - name: 'OK.ru', - }, - { - id: 'origin', - name: 'Origin', - }, - { - id: 'pinterest', - name: 'Pinterest', - }, - { - id: 'qq', - name: 'QQ', - }, - { - id: 'reddit', - name: 'Reddit', - }, - { - id: 'skype', - name: 'Skype', - }, - { - id: 'snapchat', - name: 'Snapchat', - }, - { - id: 'spotify', - name: 'Spotify', - }, - { - id: 'steam', - name: 'Steam', - }, - { - id: 'telegram', - name: 'Telegram', - }, - { - id: 'tiktok', - name: 'TikTok', - }, - { - id: 'tinder', - name: 'Tinder', - }, - { - id: 'twitch', - name: 'Twitch', - }, - { - id: 'twitter', - name: 'Twitter', - }, - { - id: 'viber', - name: 'Viber', - }, - { - id: 'vimeo', - name: 'Vimeo', - }, - { - id: 'vk', - name: 'VK.com', - }, - { - id: 'wechat', - name: 'WeChat', - }, - { - id: 'weibo', - name: 'Weibo', - }, - { - id: 'whatsapp', - name: 'WhatsApp', - }, - { - id: 'youtube', - name: 'YouTube', - }, -]; - -export const SERVICES_ID_NAME_MAP = SERVICES.reduce((acc, { id, name }) => { - acc[id] = name; - return acc; -}, {}); - export const ENCRYPTION_SOURCE = { PATH: 'path', CONTENT: 'content', diff --git a/client/src/helpers/form.js b/client/src/helpers/form.js index c4f90722..f58aa830 100644 --- a/client/src/helpers/form.js +++ b/client/src/helpers/form.js @@ -1,6 +1,8 @@ import React, { Fragment } from 'react'; import PropTypes from 'prop-types'; import { Trans } from 'react-i18next'; +import cn from 'classnames'; + import { createOnBlurHandler } from './helpers'; import { R_MAC_WITHOUT_COLON, R_UNIX_ABSOLUTE_PATH, R_WIN_ABSOLUTE_PATH } from './constants'; @@ -229,24 +231,34 @@ export const renderServiceField = ({ modifier, icon, meta: { touched, error }, -}) => - - {!disabled && touched && error - && {error}} -; +}) => ( + <> +