Merge branch 'master' into 4923-gopacket-dhcp-vol.8

This commit is contained in:
Eugene Burkov 2024-07-03 15:12:43 +03:00
commit 0bfccf8bc1
43 changed files with 402 additions and 367 deletions

View File

@ -1,7 +1,7 @@
'name': 'build'
'env':
'GO_VERSION': '1.22.4'
'GO_VERSION': '1.22.5'
'NODE_VERSION': '16'
'on':

View File

@ -1,7 +1,7 @@
'name': 'lint'
'env':
'GO_VERSION': '1.22.4'
'GO_VERSION': '1.22.5'
'on':
'push':

View File

@ -27,6 +27,11 @@ See also the [v0.107.52 GitHub milestone][ms-v0.107.52].
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Security
- Go version has been updated to prevent the possibility of exploiting the Go
vulnerabilities fixed in [Go 1.22.5][go-1.22.5].
### Added
- The ability to disable logging using the new `log.enabled` configuration
@ -56,6 +61,8 @@ NOTE: Add new changes BELOW THIS COMMENT.
### Fixed
- Unnecessary validation call on the encryption page.
- Missing version in the footer.
- Panic caused by missing user-specific blocked services object in configuration
file ([#7069]).
- Tracking `/etc/hosts` file changes causing panics within particular
@ -66,6 +73,7 @@ NOTE: Add new changes BELOW THIS COMMENT.
[#7076]: https://github.com/AdguardTeam/AdGuardHome/issues/7076
[#7079]: https://github.com/AdguardTeam/AdGuardHome/issues/7079
[go-1.22.5]: https://groups.google.com/g/golang-announce/c/gyb7aM1C9H4
[install-script]: https://github.com/AdguardTeam/AdGuardHome/?tab=readme-ov-file#automated-install-linux-and-mac
<!--

View File

@ -27,7 +27,7 @@ DIST_DIR = dist
GOAMD64 = v1
GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct
GOSUMDB = sum.golang.google.cn
GOTOOLCHAIN = go1.22.4
GOTOOLCHAIN = go1.22.5
GPG_KEY = devteam@adguard.com
GPG_KEY_PASSPHRASE = not-a-real-password
NPM = npm

View File

@ -7,8 +7,8 @@
# Make sure to sync any changes with the branch overrides below.
'variables':
'channel': 'edge'
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'stages':
- 'Build frontend':
@ -266,7 +266,7 @@
'variables':
'channel': 'beta'
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.5--1'
# release-vX.Y.Z branches are the branches from which the actual final
# release is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@ -282,4 +282,4 @@
'variables':
'channel': 'release'
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.5--1'

View File

@ -5,8 +5,8 @@
'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests'
'variables':
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerFrontend': 'adguard/home-js-builder:2.0'
'dockerGo': 'adguard/go-builder:1.22.5--1'
'channel': 'development'
'stages':
@ -195,5 +195,5 @@
# may need to build a few of these.
'variables':
'dockerFrontend': '${bamboo.adguardRegistryBasePath}/home-js-builder:2.0'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.4--1'
'dockerGo': '${bamboo.adguardRegistryBasePath}/go-builder:1.22.5--1'
'channel': 'candidate'

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Použijte paralelní požadavky na urychlení řešení simultánním dotazováním na všechny navazující servery.",
"parallel_requests": "Paralelní požadavky",
"load_balancing": "Optimalizace vytížení",
"load_balancing_desc": "Optimalizovaný dotaz na odchozí server. AdGuard Home použije vážený náhodný algoritmus k výběru serveru, takže nejrychlejší server je používán častěji.",
"load_balancing_desc": "Dotazy jednoho odchozího serveru ve stejný čas. AdGuard Home používá náhodný algoritmus pro výběr serverů s nejnižším počtem neúspěšných vyhledávání a nejnižší průměrnou dobou vyhledávání.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy DNS serverů používaných k překladu IP adres řešitelů DoH/DoT, které zadáte jako odchozí servery. Komentáře nejsou povoleny.",
"fallback_dns_title": "Záložní DNS servery",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Brug parallelforespørgsler til at accelerere fortolkningen ved at forespørge alle upstream-servere samtidigt.",
"parallel_requests": "Parallelle forespørgsler",
"load_balancing": "Belastningsfordeling",
"load_balancing_desc": "Forespørg én server ad gangen. AdGuard Home vil bruge en vægtet randomiseringsalgoritme til valg af server, så den hurtigste server oftere anvendes.",
"load_balancing_desc": "Forespørg én upstream-server ad gangen. AdGuard Home bruger en vægtet tilfældighedsalgoritme til vælg af servere med det laveste antal fejlslagne opslag og den laveste gennemsnitlige opslagstid.",
"bootstrap_dns": "Bootstrap DNS-servere",
"bootstrap_dns_desc": "IP-adresser på DNS-servere, som bruges til at opløse IP-adresser på de DoH/DoT-opløsere, som angives som upstreams. Kommentarer er ikke tilladt.",
"fallback_dns_title": "Reserve DNS-servere",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Parallele Abfragen verwenden, um das Auflösen zu beschleunigen, indem alle Upstream-Server gleichzeitig abgefragt werden.",
"parallel_requests": "Paralleles Abfragen",
"load_balancing": "Lastverteilung",
"load_balancing_desc": "Einen Server nach dem anderen abfragen. AdGuard Home verwendet den gewichteten Zufallsalgorithmus, um den Server so auszuwählen, dass der schnellste Server häufiger verwendet wird.",
"load_balancing_desc": "Es wird jeweils ein Upstream-Server abgefragt. AdGuard Home verwendet einen gewichteten Zufallsalgorithmus, um die Server mit der geringsten Anzahl an fehlgeschlagenen Suchvorgängen und der niedrigsten durchschnittlichen Suchzeit auszuwählen.",
"bootstrap_dns": "Bootstrap-DNS-Server",
"bootstrap_dns_desc": "IP-Adressen der DNS-Server, die zum Auflösen der IP-Adressen von DoH/DoT Upstream-Servern verwendet werden, die Sie angegeben haben. Kommentare sind nicht erlaubt.",
"fallback_dns_title": "Fallback-DNS-Server",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar la resolución al consultar simultáneamente a todos los servidores DNS de subida.",
"parallel_requests": "Consultas paralelas",
"load_balancing": "Balanceo de carga",
"load_balancing_desc": "Consulta un servidor DNS de subida a la vez. AdGuard Home utiliza su algoritmo aleatorio ponderado para elegir el servidor más rápido y sea utilizado con más frecuencia.",
"load_balancing_desc": "Consulta un servidor upstream a la vez. AdGuard Home utiliza un algoritmo aleatorio ponderado para seleccionar los servidores con el menor número de fallos y el menor tiempo medio de búsqueda.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Direcciones IP de servidores DNS utilizadas para resolver direcciones IP de los solucionadores DoH/DoT que especifiques como ascendentes. No se permiten comentarios.",
"fallback_dns_title": "Servidores DNS alternativos",

View File

@ -589,6 +589,7 @@
"cache_optimistic_desc": "AdGuard Home را وادار می کند که از سمت حافظه پنهان پاسخ دهد حتی وقتی که موارد وارد شده منقضی شده باشد و همچنین سعی بر تازه کردن آنها می کند.",
"filter_category_general": "General",
"filter_category_security": "مسدودسازی بدافزار و فیشینگ",
"filter_category_regional": "منطقه‌ای",
"filter_category_other": "ساير",
"use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید",
"parental_control": "نظارت والدین",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Utilisez des requêtes parallèles pour accélérer la résolution en requêtant simultanément tous les serveurs en amont.",
"parallel_requests": "Requêtes en parallèle",
"load_balancing": "Équilibrage de charge",
"load_balancing_desc": "Interroger un serveur en amont à la fois. AdGuard Home utilise son algorithme aléatoire pondéré pour choisir le serveur de sorte que le serveur le plus rapide soit utilisé plus souvent.",
"load_balancing_desc": "Une requête par serveur en amont à la fois. AdGuard Home utilise un algorithme aléatoire pondéré pour sélectionner les serveurs avec le plus petit nombre d'échecs de recherche et le temps de recherche moyen le plus bas.",
"bootstrap_dns": "Serveurs DNS d'amorçage",
"bootstrap_dns_desc": "Les adresses IP des serveurs DNS utilisées pour résoudre les adresses IP des résolveurs DoH/DoT que vous spécifiez comme en amont. Les commentaires ne sont pas autorisés.",
"fallback_dns_title": "Serveurs DNS de repli",

View File

@ -13,14 +13,14 @@
"fallback_dns_desc": "Popis rezervnih DNS poslužitelja koji se koriste kada uzvodni DNS poslužitelji ne odgovaraju. Sintaksa je ista kao u gornjem polju glavnog uzvodnog toka.",
"fallback_dns_placeholder": "Unesite jedan rezervni DNS poslužitelj po retku",
"local_ptr_title": "Privatni obrnuti DNS poslužitelji",
"local_ptr_desc": "DNS poslužitelji koje AdGuard Home koristi za lokalne PTR upite. Ti se poslužitelji koriste za razrješavanje naziva glavnog računala klijenata s privatnim IP adresama, na primjer \"192.168.12.34\", koristeći obrnuti DNS. Ako nije postavljeno, AdGuard Home koristi adrese zadanih DNS razrješivača vašeg OS-a, osim za adrese samog AdGuard Homea.",
"local_ptr_desc": "DNS poslužitelji koje koristi AdGuard Home za privatne PTR, SOA i NS zahtjeve. Zahtjev se smatra privatnim ako traži ARPA domenu koja sadrži podmrežu unutar privatnih IP raspona (kao što je \"192.168.12.34\") i dolazi od klijenta s privatnom IP adresom. Ako nije postavljeno, koristit će se zadani DNS rezolveri vašeg OS-a, osim za AdGuard Home IP adrese.",
"local_ptr_default_resolver": "Prema zadanim postavkama AdGuard Home koristi sljedeće obrnute DNS razrješivače: {{ip}}.",
"local_ptr_no_default_resolver": "AdGuard Home nije mogao odrediti prikladne privatne obrnute DNS razrješivače za ovaj sustav.",
"local_ptr_placeholder": "Unesite jednu adresu poslužitelja po retku",
"resolve_clients_title": "Omogući obrnuto rješavanje IP adresa klijenata",
"resolve_clients_desc": "Obrnuto razriješite IP adrese klijenata u nazive glavnih računala slanjem PTR upita odgovarajućim razrješivačima (privatni DNS poslužitelji za lokalne klijente, uzvodni poslužitelji za klijente s javnim IP adresama).",
"use_private_ptr_resolvers_title": "Koristi privatne reverzne DNS razrješivače",
"use_private_ptr_resolvers_desc": "Izvršite obrnuta DNS traženja za lokalno poslužene adrese pomoću ovih uzlaznih poslužitelja. Ako je onemogućen, AdGuard Home odgovara S NXDOMAIN-om na sve takve PTR zahtjeve osim za klijente poznate iz DHCP-a, /etc/hosts i tako dalje.",
"use_private_ptr_resolvers_desc": "Razriješi PTR, SOA i NS zahtjeve za ARPA domene koje sadrže privatne IP adrese putem privatnih uzvodnih poslužitelja, DHCP-a, /etc/hostova itd. Ako je onemogućeno, AdGuard Home će na sve takve zahtjeve odgovoriti s NXDOMAIN.",
"check_dhcp_servers": "Provjera DHCP poslužitelja",
"save_config": "Spremi konfiguraciju",
"enabled_dhcp": "DHCP poslužitelj je omogućen",
@ -425,6 +425,9 @@
"encryption_hostnames": "Nazivi računala",
"encryption_reset": "Jeste li sigurni da želite poništiti postavke šifriranja?",
"encryption_warning": "Upozorenje",
"encryption_plain_dns_enable": "Omogući obični DNS",
"encryption_plain_dns_desc": "Obični DNS je omogućen prema zadanim postavkama. Možete ga onemogućiti kako biste prisilili sve uređaje da koriste šifrirani DNS. Da biste to učinili, morate omogućiti barem jedan kriptirani DNS protokol",
"encryption_plain_dns_error": "Da biste onemogućili obični DNS, omogućite barem jedan kriptirani DNS protokol",
"topline_expiring_certificate": "Vaš SSL certifikat uskoro ističe. Ažurirajte <0>Postavke šifriranja</0>.",
"topline_expired_certificate": "Vaš SSL certifikat je istekao. Ažurirajte <0>Postavke šifriranja</0>.",
"form_error_port_range": "Unesite broj porta od 80 do 65536",
@ -675,7 +678,7 @@
"use_saved_key": "Korištenje prethodno spremljenog ključa",
"parental_control": "Roditeljska zaštita",
"safe_browsing": "Sigurno surfanje",
"served_from_cache": "{{value}} <i>(dohvaćeno iz predmemorije)</i>",
"served_from_cache_label": "Posluženo iz predmemorije",
"form_error_password_length": "Lozinka mora sadržavati od {{min}} do {{max}} znakova",
"anonymizer_notification": "<0>Napomena:</0>IP anonimizacija je omogućena. Možete ju onemogućiti u <1>općim postavkama</1>.",
"confirm_dns_cache_clear": "Jeste li sigurni da želite očistiti DNS predmemoriju?",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Utilizza richieste parallele per accelerare la risoluzione interrogando simultaneamente tutti i server upstream.",
"parallel_requests": "Richieste parallele",
"load_balancing": "Bilanciamento del carico",
"load_balancing_desc": "Interroga un server upstream per volta. AdGuard Home utilizzerà un algoritmo casuale ponderato per la selezione del server, in maniera tale da scegliere spesso il più veloce.",
"load_balancing_desc": "Esegui una query su un server upstream alla volta. AdGuard Home utilizza un algoritmo casuale ponderato per selezionare i server con il minor numero di ricerche fallite e il tempo medio di ricerca più basso.",
"bootstrap_dns": "Server DNS bootstrap",
"bootstrap_dns_desc": "Indirizzi IP dei server DNS utilizzati per risolvere gli indirizzi IP dei resolver DoH/DoT specificati come upstream. I commenti non sono ammessi.",
"fallback_dns_title": "Server DNS di fallback",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "並列リクエストを使用する(同時にすべてのアップストリームサーバーに処理要求することで解決スピードが向上)",
"parallel_requests": "並列リクエスト",
"load_balancing": "ロードバランシング",
"load_balancing_desc": "一度に1つのアップストリームサーバに処理要求します。 AdGuard Homeは、重み付きランダムアルゴリズムweighted random algorithmを使用してサーバを選択するため、最速のサーバがより頻繁に使用されます。",
"load_balancing_desc": "一度に1つのアップストリームサーバーをクエリします。AdGuard Home は、重み付き乱択アルゴリズムを使用して、ルックアップに失敗した回数が最も少なく、平均ルックアップ時間が最も短いサーバーを選択します。",
"bootstrap_dns": "ブートストラップDNSサーバ",
"bootstrap_dns_desc": "アップストリームとして指定したDoH/DoTリゾルバのIPアドレスを解決するために使用されるDNSサーバーのIPアドレスです。コメントは許可されていません",
"fallback_dns_title": "フォールバックDNSサーバー",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "쿼리 처리 속도를 높이려면 모든 업스트림 서버에서 동시에 병렬 쿼리를 사용해주세요.",
"parallel_requests": "병렬 처리 요청",
"load_balancing": "로드 밸런싱",
"load_balancing_desc": "한 번에 하나의 서버씩 질의합니다. AdGuard Home은 가중 랜덤 알고리즘를 사용해서 가장 빠른 서버가 자주 사용되도록 서버를 선택합니다.",
"load_balancing_desc": "한 번에 하나의 업스트림 서버를 쿼리합니다. AdGuard Home은 가중 무작위 알고리즘을 사용하여 조회 실패 횟수가 가장 적고 평균 조회 시간이 가장 짧은 서버를 선택합니다.",
"bootstrap_dns": "부트스트랩 DNS 서버",
"bootstrap_dns_desc": "업스트림으로 지정한 DoH/DoT 리졸버의 IP 주소를 확인하는 데 사용되는 DNS 서버의 IP 주소입니다. 주석은 허용되지 않습니다.",
"fallback_dns_title": "폴백 DNS 서버",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Parallelle verzoeken gebruiken om te versnellen door gelijktijdig verzoeken te sturen naar alle upstream servers.",
"parallel_requests": "Parallelle verzoeken",
"load_balancing": "Volume balanceren",
"load_balancing_desc": "Eén server per keer bevragen. AdGuard Home gebruikt hiervoor een gewogen willekeurig algoritme om de server te kiezen zodat de snelste server meer zal gebruikt worden.",
"load_balancing_desc": "Voer zoekopdrachten uit op één upstream-server tegelijk. AdGuard Home gebruikt een gewogen willekeurig algoritme om servers te selecteren met het laagste aantal mislukte zoekopdrachten en de laagste gemiddelde opzoektijd.",
"bootstrap_dns": "Bootstrap DNS-servers",
"bootstrap_dns_desc": "IP-adressen van DNS-servers die worden gebruikt om IP-adressen om te zetten van de DoH/DoT-resolvers die je opgeeft als upstreams. Opmerkingen zijn niet toegestaan.",
"fallback_dns_title": "Back-up DNS-servers",
@ -495,7 +495,7 @@
"setup_dns_privacy_2": "<0>DNS-via-HTTPS:</0> Gebruik <1>{{address}}</1> string.",
"setup_dns_privacy_3": "<0>Hou er rekening mee dat het beveiligde DNS protocol alleen beschikbaar is voor Android 9. U moet dus extra software installeren voor andere besturingssystemen.</0><0>Hier is een lijst van te gebruiken software.</0>",
"setup_dns_privacy_4": "Op een iOS 14 of macOS Big Sur apparaat kan je een speciaal '.mobileconfig'-bestand downloaden dat <highlight>DNS-via-HTTPS</highlight> of <highlight>DNS-via-TLS</highlight> servers aan de DNS-instellingen toevoegt.",
"setup_dns_privacy_android_1": "Android 9 ondersteunt native DNS-via-TLS. Om het te configureren, ga naar Instellingen → Netwerk & internet → Geavanceerd → Privé DNS en voer daar je domeinnaam in.",
"setup_dns_privacy_android_1": "Android 9 ondersteunt native DNS-via-TLS. Om het te configureren, ga naar Instellingen → Netwerk & internet → Geavanceerd → Privé-DNS en voer daar je domeinnaam in.",
"setup_dns_privacy_android_2": "<0>AdGuard voor Android</0>ondersteunt<1>DNS-via-HTTPS </1>en<1>DNS-via-TLS</1>.",
"setup_dns_privacy_android_3": "<0> Intra </0> voegt <1> DNS-via-HTTPS</1> ondersteuning toe aan Android.",
"setup_dns_privacy_ios_1": "<0>DNSCloak</0> ondersteunt <1> DNS-via-HTTPS </1>, maar om het te configureren op jouw eigen server moet er een <2> DNS-stempel </2> gegenereerd worden.",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS primário",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulte um servidor DNS primário por vez. O AdGuard Home usa seu algoritmo aleatório ponderado para escolher o servidor para que o servidor mais rápido seja usado com mais frequência.",
"load_balancing_desc": "Consulte um servidor upstream por vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de falhas e o menor tempo médio de consulta.",
"bootstrap_dns": "Servidores DNS de inicialização",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS Fallback",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Usar consultas paralelas para acelerar a resolução consultando simultaneamente todos os servidores DNS",
"parallel_requests": "Solicitações paralelas",
"load_balancing": "Balanceamento de carga",
"load_balancing_desc": "Consulte um servidor DNS primário por vez. O AdGuard Home usa seu algoritmo aleatório ponderado para escolher o servidor para que o servidor mais rápido seja usado com mais frequência.",
"load_balancing_desc": "Consulta um servidor a montante de cada vez. O AdGuard Home usa um algoritmo aleatório ponderado para selecionar servidores com o menor número de pesquisas com falha e o menor tempo médio de pesquisa.",
"bootstrap_dns": "Servidores DNS de arranque",
"bootstrap_dns_desc": "Endereços IP de servidores DNS usados para resolver endereços IP dos resolvedores DoH/DoT que você especifica como upstreams. Comentários não são permitidos.",
"fallback_dns_title": "Servidores DNS de fallback",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Использовать параллельные запросы ко всем серверам одновременно для ускорения обработки запроса.",
"parallel_requests": "Параллельные запросы",
"load_balancing": "Распределение нагрузки\n",
"load_balancing_desc": "Запрашивать по одному серверу за раз. AdGuard Home использует алгоритм взвешенного случайного выбора сервера, так что самый быстрый сервер используется чаще.",
"load_balancing_desc": "Запрашивайте по одному серверу за раз. AdGuard Home использует алгоритм случайной выборки с учётом веса для выбора серверов с наименьшим количеством неудачных запросов и наименьшим средним временем выполнения запроса.",
"bootstrap_dns": "Bootstrap DNS-серверы",
"bootstrap_dns_desc": "IP-адреса DNS-серверов, используемых для поиска IP-адресов DoH/DoT upstream-серверов, которые вы указали. Комментарии не допускаются.",
"fallback_dns_title": "Резервные DNS-серверы",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Používať paralelné dopyty na zrýchlenie súčasným dopytovaním všetkých upstream serverov súčasne.",
"parallel_requests": "Paralelné dopyty",
"load_balancing": "Vyrovnávanie záťaže",
"load_balancing_desc": "Dopytovať len jeden server v danom čase. AdGuard Home použije na výber servera vážený náhodný algoritmus, aby sa najrýchlejší server používal častejšie.",
"load_balancing_desc": "Dopytuje sa súčasne len jeden upstream server. AdGuard Home používa vážený náhodný algoritmus na výber serverov s najnižším počtom neúspešných vyhľadávaní a najnižším priemerným časom vyhľadávania.",
"bootstrap_dns": "Bootstrap DNS servery",
"bootstrap_dns_desc": "IP adresy serverov DNS používaných na rozlíšenie IP adries prekladačov DoH/DoT, ktoré zadáte ako upstream. Komentáre nie sú povolené.",
"fallback_dns_title": "Záložné servery DNS",
@ -89,7 +89,7 @@
"form_enter_hostname": "Zadajte meno hostiteľa",
"error_details": "Podrobnosti chyby",
"response_details": "Podrobnosti odpovede",
"request_details": "Podrobnosti požiadavky",
"request_details": "Podrobnosti dopytu",
"client_details": "Podrobnosti klienta",
"details": "Podrobnosti",
"back": "Naspäť",
@ -308,7 +308,7 @@
"form_enter_rate_limit": "Zadajte rýchlostný limit",
"rate_limit": "Rýchlostný limit",
"edns_enable": "Povoliť klientsku podsiete EDNS",
"edns_cs_desc": "Pridáva možnosť EDNS Client Subnet (ECS) do upstream požiadaviek a zapíše hodnoty odoslané klientmi do denníka dopytov.",
"edns_cs_desc": "Pridáva možnosť EDNS Client Subnet (ECS) do upstream dopytov a zapíše hodnoty odoslané klientami do denníka dopytov.",
"edns_use_custom_ip": "Použiť vlastnú IP adresu pre EDNS",
"edns_use_custom_ip_desc": "Povoliť používanie vlastnej IP adresy pre EDNS",
"rate_limit_desc": "Počet požiadaviek za sekundu, ktoré môže jeden klient vykonať. Nastavenie na hodnotu 0 znamená neobmedzene.",
@ -480,9 +480,9 @@
"access_title": "Nastavenia prístupu",
"access_desc": "Tu môžete konfigurovať pravidlá prístupu pre server DNS AdGuard Home.",
"access_allowed_title": "Povolení klienti",
"access_allowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home bude akceptovať požiadavky iba od týchto klientov.",
"access_allowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home bude akceptovať dopyty iba od týchto klientov.",
"access_disallowed_title": "Nepovolení klienti",
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home zruší požiadavky od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
"access_disallowed_desc": "Zoznam CIDR, IP adries alebo <a>ClientID</a>. Ak tento zoznam obsahuje položky, AdGuard Home zruší dopyty od týchto klientov. Toto pole sa ignoruje, ak sú v poli Povolení klienti položky.",
"access_blocked_title": "Nepovolené domény",
"access_blocked_desc": "Nesmie byť zamieňaná s filtrami. AdGuard Home zruší DNS dopyty, ktoré sa zhodujú s týmito doménami, a tieto dopyty sa nezobrazia ani v denníku dopytov. Môžete určiť presné názvy domén, zástupné znaky alebo pravidlá filtrovania URL adries, napr. \"example.org\", \"*.example.org\" alebo ||example.org^\" zodpovedajúcim spôsobom.",
"access_settings_saved": "Nastavenia prístupu úspešne uložené",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "Tüm üst sunucuları eş zamanlı sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
"parallel_requests": "Paralel istekler",
"load_balancing": "Yük dengeleme",
"load_balancing_desc": "Her seferde bir üst sunucuyu sorgulayın. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
"load_balancing_desc": "Aynı anda bir üst kaynak sunucusunu sorgulayın. AdGuard Home, en düşük başarısız arama sayısına ve en düşük ortalama arama süresine sahip sunucuları seçmek için ağırlıklı rastgele bir algoritma kullanır.",
"bootstrap_dns": "DNS Önyükleme sunucuları",
"bootstrap_dns_desc": "Üst kaynak olarak belirttiğiniz DoH/DoT çözümleyicilerin IP adreslerini çözümlemek için kullanılan DNS sunucularının IP adresleri. Yorumlara izin verilmez.",
"fallback_dns_title": "Yedek DNS sunucuları",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "使用并行请求以同时查询所有上游服务器来加快解析速度。",
"parallel_requests": "并行请求",
"load_balancing": "负载均衡",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 将使用加权随机算法来选择服务器,以便更常使用最快的服务器。",
"load_balancing_desc": "一次查询一台服务器。AdGuard Home 使用加权随机算法来选择具有最少失败查找和最低平均查找时间的服务器。",
"bootstrap_dns": "Bootstrap DNS 服务器",
"bootstrap_dns_desc": "DNS 服务器的 IP 地址,用于解析指定为上游的 DoH/DoT 解析器的 IP 地址。不允许添加注释。",
"fallback_dns_title": "后备 DNS 服务器",

View File

@ -6,7 +6,7 @@
"upstream_parallel": "透過同時地查詢所有上游的伺服器,使用並行的查詢以加速解析。",
"parallel_requests": "並行的請求",
"load_balancing": "負載平衡",
"load_balancing_desc": "每次查詢一個上游伺服器。AdGuard Home 使用它的加權隨機的演算法來選擇伺服器,以便最快的伺服器被更常使用。",
"load_balancing_desc": "一次查詢一台伺服器。AdGuard Home 使用加權隨機演算法來選擇具有最少失敗查詢和最低平均查詢時間的伺服器。",
"bootstrap_dns": "自我啟動BootstrapDNS 伺服器",
"bootstrap_dns_desc": "DNS 伺服器的 IP 位址,用於解析您指定為上游伺服器的 DoH/DoT 解析器的 IP 位址。不允許註釋。",
"fallback_dns_title": "應變 DNS 伺服器",

View File

@ -38,7 +38,7 @@ class Encryption extends Component<EncryptionProps> {
handleFormChange = debounce((values) => {
const submitValues = this.getSubmitValues(values);
if (submitValues.enabled || submitValues.serve_plain_dns) {
if (submitValues.enabled) {
this.props.validateTlsConfig(submitValues);
}
}, DEBOUNCE_TIMEOUT);

View File

@ -12,12 +12,11 @@ const Version = () => {
const dashboard = useSelector((state: RootState) => state.dashboard, shallowEqual);
const install = useSelector((state: RootState) => state.install, shallowEqual);
if (!dashboard || !install) {
if (!dashboard && !install) {
return null;
}
const { dnsVersion, processingVersion, checkUpdateFlag } = dashboard;
const version = dnsVersion || install?.dnsVersion;
const version = dashboard?.dnsVersion || install?.dnsVersion;
const onClick = () => {
dispatch(getVersion(true));
@ -35,12 +34,12 @@ const Version = () => {
</>
)}
{checkUpdateFlag && (
{dashboard?.checkUpdateFlag && (
<button
type="button"
className="btn btn-icon btn-icon-sm btn-outline-primary btn-sm ml-2"
onClick={onClick}
disabled={processingVersion}
disabled={dashboard?.processingVersion}
title={t('check_updates_now')}>
<svg className="icons icon12">
<use xlinkHref="#refresh" />

View File

@ -1,5 +1,5 @@
{
"timeUpdated": "2024-03-01T00:10:14.031Z",
"timeUpdated": "2024-07-01T00:11:48.891Z",
"categories": {
"0": "audio_video_player",
"1": "comments",
@ -1182,6 +1182,13 @@
"url": "https://www.admitad.com/en/#",
"companyId": "admitad"
},
"admixer": {
"name": "Admixer",
"categoryId": 4,
"url": "https://admixer.com/",
"companyId": "admixer",
"source": "AdGuard"
},
"admixer.net": {
"name": "Admixer",
"categoryId": 4,
@ -1473,6 +1480,13 @@
"url": "http://www.adplus.co.id/",
"companyId": "adplus"
},
"adprofex": {
"name": "AdProfex",
"categoryId": 4,
"url": "https://adprofex.com/",
"companyId": "adprofex",
"source": "AdGuard"
},
"adprofy": {
"name": "AdProfy",
"categoryId": 4,
@ -2019,6 +2033,13 @@
"url": "https://hybridtheory.com/",
"companyId": "affectv"
},
"affilbox": {
"name": "Affilbox",
"categoryId": 4,
"url": "https://affilbox.com/",
"companyId": "affilbox",
"source": "AdGuard"
},
"affiliate-b": {
"name": "Affiliate-B",
"categoryId": 4,
@ -2373,6 +2394,13 @@
"url": "http://www.amadesa.com/",
"companyId": "amadesa"
},
"amap": {
"name": "Amap",
"categoryId": 2,
"url": "https://www.amap.com/",
"companyId": "softbank",
"source": "AdGuard"
},
"amazon": {
"name": "Amazon.com",
"categoryId": 8,
@ -4591,6 +4619,13 @@
"url": "http://clickaider.com/",
"companyId": "clickaider"
},
"clickaine": {
"name": "Clickaine",
"categoryId": 4,
"url": "https://clickaine.com/",
"companyId": "clickaine",
"source": "AdGuard"
},
"clickbank": {
"name": "ClickBank",
"categoryId": 4,
@ -12470,6 +12505,13 @@
"url": "http://nolix.ru/",
"companyId": "nolix"
},
"nonli": {
"name": "Nonli",
"categoryId": 4,
"url": "https://www.nonli.com/",
"companyId": "nonli",
"source": "AdGuard"
},
"nonstop_consulting": {
"name": "Resolution Media",
"categoryId": 4,
@ -12869,6 +12911,13 @@
"url": "http://opensharecount.com/",
"companyId": "open_share_count"
},
"openai": {
"name": "OpenAI",
"categoryId": 8,
"url": "https://openai.com/",
"companyId": "openai",
"source": "AdGuard"
},
"openload": {
"name": "Openload",
"categoryId": 9,
@ -20339,6 +20388,7 @@
"adguard.org": "adguard",
"adtidy.org": "adguard",
"agrd.io": "adguard",
"agrd.eu": "adguard",
"adguard-dns.com": "adguard_dns",
"adguard-dns.io": "adguard_dns",
"adguard-vpn.com": "adguard_vpn",
@ -20413,7 +20463,8 @@
"admicro.vn": "admicro",
"vcmedia.vn": "admicro",
"admitad.com": "admitad.com",
"admixer.net": "admixer.net",
"admixer.net": "admixer",
"admixer.com": "admixer",
"admized.com": "admized",
"admo.tv": "admo.tv",
"a.admob.com": "admob",
@ -20493,6 +20544,8 @@
"advg.jp": "adplan",
"c.p-advg.com": "adplan",
"adplus.co.id": "adplus",
"adprofex.com": "adprofex",
"ads2.bid": "adprofex",
"adframesrc.com": "adprofy",
"adserve.adpulse.ir": "adpulse",
"ads.adpv.com": "adpv",
@ -20616,6 +20669,8 @@
"affectv.com": "affectv",
"go.affec.tv": "affectv",
"hybridtheory.com": "affectv",
"affilbox.com": "affilbox",
"affilbox.cz": "affilbox",
"track.affiliate-b.com": "affiliate-b",
"affiliate4you.nl": "affiliate4you",
"ads.affbuzzads.com": "affiliatebuzz",
@ -20704,6 +20759,7 @@
"inputs.alooma.com": "alooma",
"arena.altitude-arena.com": "altitude_digital",
"amadesa.com": "amadesa",
"amap.com": "amap",
"amazon.ca": "amazon",
"amazon.co.jp": "amazon",
"amazon.co.uk": "amazon",
@ -21231,6 +21287,7 @@
"clickandchat.com": "click_and_chat",
"software.clickback.com": "click_back",
"hit.clickaider.com": "clickaider",
"clickaine.com": "clickaine",
"clickbank.net": "clickbank",
"cbproads.com": "clickbank_proads",
"adtoll.com": "clickbooth",
@ -22242,6 +22299,7 @@
"withgoogle.com": "google",
"googleadservices.com": "google_adservices",
"google-analytics.com": "google_analytics",
"app-analytics-services.com": "google_analytics",
"ssl-google-analytics.l.google.com": "google_analytics",
"www-googletagmanager.l.google.com": "google_analytics",
"appspot.com": "google_appspot",
@ -23396,6 +23454,8 @@
"noaa.gov": "noaa.gov",
"track.noddus.com": "noddus",
"contextbar.ru": "nolix",
"nonli.com": "nonli",
"non.li": "nonli",
"trkme.net": "nonstop_consulting",
"noop.style": "noop.style",
"nosto.com": "nosto.com",
@ -23473,6 +23533,10 @@
"realmedia.com": "open_adstream",
"realmediadigital.com": "open_adstream",
"opensharecount.com": "open_share_count",
"chatgpt.com": "openai",
"oaistatic.com": "openai",
"oaiusercontent.com": "openai",
"openai.com": "openai",
"oloadcdn.net": "openload",
"openload.co": "openload",
"openstat.net": "openstat",
@ -23649,6 +23713,7 @@
"popads.net": "popads",
"popadscdn.net": "popads",
"popcash.net": "popcash",
"popcashjs.b-cdn.net": "popcash",
"desv383oqqc0.cloudfront.net": "popcorn_metrics",
"popin.cc": "popin.cc",
"cdn.popmyads.com": "popmyads",
@ -24356,6 +24421,10 @@
"spoteffects.net": "spoteffect",
"scdn.co": "spotify",
"spotify.com": "spotify",
"pscdn.co": "spotify",
"spotifycdn.com": "spotify",
"spotifycdn.net": "spotify",
"spotilocal.com": "spotify",
"embed.spotify.com": "spotify_embed",
"spotscenered.info": "spotscenered.info",
"spotx.tv": "spotxchange",
@ -24675,6 +24744,8 @@
"t.co": "twitter",
"twimg.com": "twitter",
"twitter.com": "twitter",
"twttr.com": "twitter",
"x.com": "twitter",
"ads-twitter.com": "twitter_ads",
"analytics.twitter.com": "twitter_analytics",
"tellapart.com": "twitter_for_business",

2
go.mod
View File

@ -1,6 +1,6 @@
module github.com/AdguardTeam/AdGuardHome
go 1.22.4
go 1.22.5
require (
github.com/AdguardTeam/dnsproxy v0.71.2

View File

@ -30,8 +30,8 @@ func macToKey(mac net.HardwareAddr) (key macKey) {
}
}
// Index stores all information about persistent clients.
type Index struct {
// index stores all information about persistent clients.
type index struct {
// nameToUID maps client name to UID.
nameToUID map[string]UID
@ -51,9 +51,9 @@ type Index struct {
subnetToUID aghalg.SortedMap[netip.Prefix, UID]
}
// NewIndex initializes the new instance of client index.
func NewIndex() (ci *Index) {
return &Index{
// newIndex initializes the new instance of client index.
func newIndex() (ci *index) {
return &index{
nameToUID: map[string]UID{},
clientIDToUID: map[string]UID{},
ipToUID: map[netip.Addr]UID{},
@ -63,9 +63,9 @@ func NewIndex() (ci *Index) {
}
}
// Add stores information about a persistent client in the index. c must be
// add stores information about a persistent client in the index. c must be
// non-nil, have a UID, and contain at least one identifier.
func (ci *Index) Add(c *Persistent) {
func (ci *index) add(c *Persistent) {
if (c.UID == UID{}) {
panic("client must contain uid")
}
@ -92,9 +92,9 @@ func (ci *Index) Add(c *Persistent) {
ci.uidToClient[c.UID] = c
}
// ClashesUID returns existing persistent client with the same UID as c. Note
// clashesUID returns existing persistent client with the same UID as c. Note
// that this is only possible when configuration contains duplicate fields.
func (ci *Index) ClashesUID(c *Persistent) (err error) {
func (ci *index) clashesUID(c *Persistent) (err error) {
p, ok := ci.uidToClient[c.UID]
if ok {
return fmt.Errorf("another client %q uses the same uid", p.Name)
@ -103,9 +103,9 @@ func (ci *Index) ClashesUID(c *Persistent) (err error) {
return nil
}
// Clashes returns an error if the index contains a different persistent client
// clashes returns an error if the index contains a different persistent client
// with at least a single identifier contained by c. c must be non-nil.
func (ci *Index) Clashes(c *Persistent) (err error) {
func (ci *index) clashes(c *Persistent) (err error) {
if p := ci.clashesName(c); p != nil {
return fmt.Errorf("another client uses the same name %q", p.Name)
}
@ -139,8 +139,8 @@ func (ci *Index) Clashes(c *Persistent) (err error) {
// clashesName returns existing persistent client with the same name as c or
// nil. c must be non-nil.
func (ci *Index) clashesName(c *Persistent) (existing *Persistent) {
existing, ok := ci.FindByName(c.Name)
func (ci *index) clashesName(c *Persistent) (existing *Persistent) {
existing, ok := ci.findByName(c.Name)
if !ok {
return nil
}
@ -154,7 +154,7 @@ func (ci *Index) clashesName(c *Persistent) (existing *Persistent) {
// clashesIP returns a previous client with the same IP address as c. c must be
// non-nil.
func (ci *Index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
func (ci *index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
for _, ip := range c.IPs {
existing, ok := ci.ipToUID[ip]
if ok && existing != c.UID {
@ -167,7 +167,7 @@ func (ci *Index) clashesIP(c *Persistent) (p *Persistent, ip netip.Addr) {
// clashesSubnet returns a previous client with the same subnet as c. c must be
// non-nil.
func (ci *Index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
func (ci *index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
for _, s = range c.Subnets {
var existing UID
var ok bool
@ -193,7 +193,7 @@ func (ci *Index) clashesSubnet(c *Persistent) (p *Persistent, s netip.Prefix) {
// clashesMAC returns a previous client with the same MAC address as c. c must
// be non-nil.
func (ci *Index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr) {
func (ci *index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr) {
for _, mac = range c.MACs {
k := macToKey(mac)
existing, ok := ci.macToUID[k]
@ -205,9 +205,9 @@ func (ci *Index) clashesMAC(c *Persistent) (p *Persistent, mac net.HardwareAddr)
return nil, nil
}
// Find finds persistent client by string representation of the client ID, IP
// find finds persistent client by string representation of the client ID, IP
// address, or MAC.
func (ci *Index) Find(id string) (c *Persistent, ok bool) {
func (ci *index) find(id string) (c *Persistent, ok bool) {
uid, found := ci.clientIDToUID[id]
if found {
return ci.uidToClient[uid], true
@ -224,14 +224,14 @@ func (ci *Index) Find(id string) (c *Persistent, ok bool) {
mac, err := net.ParseMAC(id)
if err == nil {
return ci.FindByMAC(mac)
return ci.findByMAC(mac)
}
return nil, false
}
// FindByName finds persistent client by name.
func (ci *Index) FindByName(name string) (c *Persistent, found bool) {
// findByName finds persistent client by name.
func (ci *index) findByName(name string) (c *Persistent, found bool) {
uid, found := ci.nameToUID[name]
if found {
return ci.uidToClient[uid], true
@ -241,7 +241,7 @@ func (ci *Index) FindByName(name string) (c *Persistent, found bool) {
}
// findByIP finds persistent client by IP address.
func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
func (ci *index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
uid, found := ci.ipToUID[ip]
if found {
return ci.uidToClient[uid], true
@ -266,8 +266,8 @@ func (ci *Index) findByIP(ip netip.Addr) (c *Persistent, found bool) {
return nil, false
}
// FindByMAC finds persistent client by MAC.
func (ci *Index) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
// findByMAC finds persistent client by MAC.
func (ci *index) findByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
k := macToKey(mac)
uid, found := ci.macToUID[k]
if found {
@ -277,13 +277,13 @@ func (ci *Index) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
return nil, false
}
// FindByIPWithoutZone finds a persistent client by IP address without zone. It
// findByIPWithoutZone finds a persistent client by IP address without zone. It
// strips the IPv6 zone index from the stored IP addresses before comparing,
// because querylog entries don't have it. See TODO on [querylog.logEntry.IP].
//
// Note that multiple clients can have the same IP address with different zones.
// Therefore, the result of this method is indeterminate.
func (ci *Index) FindByIPWithoutZone(ip netip.Addr) (c *Persistent) {
func (ci *index) findByIPWithoutZone(ip netip.Addr) (c *Persistent) {
if (ip == netip.Addr{}) {
return nil
}
@ -297,9 +297,9 @@ func (ci *Index) FindByIPWithoutZone(ip netip.Addr) (c *Persistent) {
return nil
}
// Delete removes information about persistent client from the index. c must be
// remove removes information about persistent client from the index. c must be
// non-nil.
func (ci *Index) Delete(c *Persistent) {
func (ci *index) remove(c *Persistent) {
delete(ci.nameToUID, c.Name)
for _, id := range c.ClientIDs {
@ -322,24 +322,14 @@ func (ci *Index) Delete(c *Persistent) {
delete(ci.uidToClient, c.UID)
}
// Size returns the number of persistent clients.
func (ci *Index) Size() (n int) {
// size returns the number of persistent clients.
func (ci *index) size() (n int) {
return len(ci.uidToClient)
}
// Range calls f for each persistent client, unless cont is false. The order is
// undefined.
func (ci *Index) Range(f func(c *Persistent) (cont bool)) {
for _, c := range ci.uidToClient {
if !f(c) {
return
}
}
}
// RangeByName is like [Index.Range] but sorts the persistent clients by name
// rangeByName is like [Index.Range] but sorts the persistent clients by name
// before iterating ensuring a predictable order.
func (ci *Index) RangeByName(f func(c *Persistent) (cont bool)) {
func (ci *index) rangeByName(f func(c *Persistent) (cont bool)) {
cs := maps.Values(ci.uidToClient)
slices.SortFunc(cs, func(a, b *Persistent) (n int) {
return strings.Compare(a.Name, b.Name)
@ -352,10 +342,10 @@ func (ci *Index) RangeByName(f func(c *Persistent) (cont bool)) {
}
}
// CloseUpstreams closes upstream configurations of persistent clients.
func (ci *Index) CloseUpstreams() (err error) {
// closeUpstreams closes upstream configurations of persistent clients.
func (ci *index) closeUpstreams() (err error) {
var errs []error
ci.RangeByName(func(c *Persistent) (cont bool) {
ci.rangeByName(func(c *Persistent) (cont bool) {
err = c.CloseUpstreams()
if err != nil {
errs = append(errs, err)

View File

@ -11,12 +11,12 @@ import (
// newIDIndex is a helper function that returns a client index filled with
// persistent clients from the m. It also generates a UID for each client.
func newIDIndex(m []*Persistent) (ci *Index) {
ci = NewIndex()
func newIDIndex(m []*Persistent) (ci *index) {
ci = newIndex()
for _, c := range m {
c.UID = MustNewUID()
ci.Add(c)
ci.add(c)
}
return ci
@ -110,7 +110,7 @@ func TestClientIndex_Find(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, id := range tc.ids {
c, ok := ci.Find(id)
c, ok := ci.find(id)
require.True(t, ok)
assert.Equal(t, tc.want, c)
@ -119,7 +119,7 @@ func TestClientIndex_Find(t *testing.T) {
}
t.Run("not_found", func(t *testing.T) {
_, ok := ci.Find(cliIPNone)
_, ok := ci.find(cliIPNone)
assert.False(t, ok)
})
}
@ -171,11 +171,11 @@ func TestClientIndex_Clashes(t *testing.T) {
clone := tc.client.ShallowClone()
clone.UID = MustNewUID()
err := ci.Clashes(clone)
err := ci.clashes(clone)
require.Error(t, err)
ci.Delete(tc.client)
err = ci.Clashes(clone)
ci.remove(tc.client)
err = ci.clashes(clone)
require.NoError(t, err)
})
}
@ -293,7 +293,7 @@ func TestIndex_FindByIPWithoutZone(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
c := ci.FindByIPWithoutZone(tc.ip.WithZone(""))
c := ci.findByIPWithoutZone(tc.ip.WithZone(""))
require.Equal(t, tc.want, c)
})
}
@ -339,7 +339,7 @@ func TestClientIndex_RangeByName(t *testing.T) {
ci := newIDIndex(tc.want)
var got []*Persistent
ci.RangeByName(func(c *Persistent) (cont bool) {
ci.rangeByName(func(c *Persistent) (cont bool) {
got = append(got, c)
return true

View File

@ -65,6 +65,7 @@ type Persistent struct {
// upstream must be used.
UpstreamConfig *proxy.CustomUpstreamConfig
// SafeSearch handles search engine hosts rewrites.
SafeSearch filtering.SafeSearch
// BlockedServices is the configuration of blocked services of a client. It
@ -74,29 +75,62 @@ type Persistent struct {
// Name of the persistent client. Must not be empty.
Name string
// Tags is a list of client tags that categorize the client.
Tags []string
// Upstreams is a list of custom upstream DNS servers for the client.
Upstreams []string
// IPs is a list of IP addresses that identify the client. The client must
// have at least one ID (IP, subnet, MAC, or ClientID).
IPs []netip.Addr
// Subnets identifying the client. The client must have at least one ID
// (IP, subnet, MAC, or ClientID).
//
// TODO(s.chzhen): Use netutil.Prefix.
Subnets []netip.Prefix
// MACs identifying the client. The client must have at least one ID (IP,
// subnet, MAC, or ClientID).
MACs []net.HardwareAddr
// ClientIDs identifying the client. The client must have at least one ID
// (IP, subnet, MAC, or ClientID).
ClientIDs []string
// UID is the unique identifier of the persistent client.
UID UID
// UpstreamsCacheSize is the cache size for custom upstreams.
UpstreamsCacheSize uint32
// UpstreamsCacheEnabled specifies whether custom upstreams are used.
UpstreamsCacheEnabled bool
// UseOwnSettings specifies whether custom filtering settings are used.
UseOwnSettings bool
// FilteringEnabled specifies whether filtering is enabled.
FilteringEnabled bool
// SafeBrowsingEnabled specifies whether safe browsing is enabled.
SafeBrowsingEnabled bool
// ParentalEnabled specifies whether parental control is enabled.
ParentalEnabled bool
// UseOwnBlockedServices specifies whether custom services are blocked.
UseOwnBlockedServices bool
// IgnoreQueryLog specifies whether the client requests are logged.
IgnoreQueryLog bool
// IgnoreStatistics specifies whether the client requests are counted.
IgnoreStatistics bool
// SafeSearchConf is the safe search filtering configuration.
//
// TODO(d.kolyshev): Make SafeSearchConf a pointer.
SafeSearchConf filtering.SafeSearchConfig
}
@ -134,21 +168,6 @@ func (c *Persistent) validate(allTags *container.MapSet[string]) (err error) {
return nil
}
// SetTags sets the tags if they are known, otherwise logs an unknown tag.
func (c *Persistent) SetTags(tags []string, known *container.MapSet[string]) {
for _, t := range tags {
if !known.Has(t) {
log.Info("skipping unknown tag %q", t)
continue
}
c.Tags = append(c.Tags, t)
}
slices.Sort(c.Tags)
}
// SetIDs parses a list of strings into typed fields and returns an error if
// there is one.
func (c *Persistent) SetIDs(ids []string) (err error) {

View File

@ -4,6 +4,7 @@ import (
"net/netip"
"testing"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@ -126,13 +127,19 @@ func TestPersistent_EqualIDs(t *testing.T) {
}
func TestPersistent_Validate(t *testing.T) {
// TODO(s.chzhen): Add test cases.
const (
allowedTag = "allowed_tag"
notAllowedTag = "not_allowed_tag"
)
allowedTags := container.NewMapSet(allowedTag)
testCases := []struct {
name string
cli *Persistent
wantErrMsg string
}{{
name: "basic",
name: "success",
cli: &Persistent{
Name: "basic",
IPs: []netip.Addr{
@ -162,11 +169,24 @@ func TestPersistent_Validate(t *testing.T) {
},
},
wantErrMsg: "uid required",
}, {
name: "not_allowed_tag",
cli: &Persistent{
Name: "basic",
IPs: []netip.Addr{
netip.MustParseAddr("1.2.3.4"),
},
UID: MustNewUID(),
Tags: []string{
notAllowedTag,
},
},
wantErrMsg: `invalid tag: "` + notAllowedTag + `"`,
}}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
err := tc.cli.validate(nil)
err := tc.cli.validate(allowedTags)
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
})
}

View File

@ -11,6 +11,14 @@ import (
"github.com/AdguardTeam/golibs/log"
)
// Config is the client storage configuration structure.
//
// TODO(s.chzhen): Expand.
type Config struct {
// AllowedTags is a list of all allowed client tags.
AllowedTags []string
}
// Storage contains information about persistent and runtime clients.
type Storage struct {
// allowedTags is a set of all allowed tags.
@ -20,18 +28,22 @@ type Storage struct {
mu *sync.Mutex
// index contains information about persistent clients.
index *Index
index *index
// runtimeIndex contains information about runtime clients.
//
// TODO(s.chzhen): Use it.
runtimeIndex *RuntimeIndex
}
// NewStorage returns initialized client storage.
func NewStorage(allowedTags *container.MapSet[string]) (s *Storage) {
// NewStorage returns initialized client storage. conf must not be nil.
func NewStorage(conf *Config) (s *Storage) {
allowedTags := container.NewMapSet(conf.AllowedTags...)
return &Storage{
allowedTags: allowedTags,
mu: &sync.Mutex{},
index: NewIndex(),
index: newIndex(),
runtimeIndex: NewRuntimeIndex(),
}
}
@ -49,40 +61,45 @@ func (s *Storage) Add(p *Persistent) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
err = s.index.ClashesUID(p)
err = s.index.clashesUID(p)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
err = s.index.Clashes(p)
err = s.index.clashes(p)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
s.index.Add(p)
s.index.add(p)
log.Debug("client storage: added %q: IDs: %q [%d]", p.Name, p.IDs(), s.index.Size())
log.Debug("client storage: added %q: IDs: %q [%d]", p.Name, p.IDs(), s.index.size())
return nil
}
// FindByName finds persistent client by name.
func (s *Storage) FindByName(name string) (c *Persistent, found bool) {
// FindByName finds persistent client by name. And returns its shallow copy.
func (s *Storage) FindByName(name string) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
return s.index.FindByName(name)
p, ok = s.index.findByName(name)
if ok {
return p.ShallowClone(), ok
}
return nil, false
}
// Find finds persistent client by string representation of the client ID, IP
// address, or MAC. And returns it shallow copy.
// address, or MAC. And returns its shallow copy.
func (s *Storage) Find(id string) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok = s.index.Find(id)
p, ok = s.index.find(id)
if ok {
return p.ShallowClone(), ok
}
@ -101,12 +118,12 @@ func (s *Storage) FindLoose(ip netip.Addr, id string) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok = s.index.Find(id)
p, ok = s.index.find(id)
if ok {
return p.ShallowClone(), ok
}
p = s.index.FindByIPWithoutZone(ip)
p = s.index.findByIPWithoutZone(ip)
if p != nil {
return p.ShallowClone(), true
}
@ -114,12 +131,17 @@ func (s *Storage) FindLoose(ip netip.Addr, id string) (p *Persistent, ok bool) {
return nil, false
}
// FindByMAC finds persistent client by MAC.
func (s *Storage) FindByMAC(mac net.HardwareAddr) (c *Persistent, found bool) {
// FindByMAC finds persistent client by MAC and returns its shallow copy.
func (s *Storage) FindByMAC(mac net.HardwareAddr) (p *Persistent, ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
return s.index.FindByMAC(mac)
p, ok = s.index.findByMAC(mac)
if ok {
return p.ShallowClone(), ok
}
return nil, false
}
// RemoveByName removes persistent client information. ok is false if no such
@ -128,7 +150,7 @@ func (s *Storage) RemoveByName(name string) (ok bool) {
s.mu.Lock()
defer s.mu.Unlock()
p, ok := s.index.FindByName(name)
p, ok := s.index.findByName(name)
if !ok {
return false
}
@ -137,7 +159,7 @@ func (s *Storage) RemoveByName(name string) (ok bool) {
log.Error("client storage: removing client %q: %s", p.Name, err)
}
s.index.Delete(p)
s.index.remove(p)
return true
}
@ -156,7 +178,7 @@ func (s *Storage) Update(name string, p *Persistent) (err error) {
s.mu.Lock()
defer s.mu.Unlock()
stored, ok := s.index.FindByName(name)
stored, ok := s.index.findByName(name)
if !ok {
return fmt.Errorf("client %q is not found", name)
}
@ -166,14 +188,14 @@ func (s *Storage) Update(name string, p *Persistent) (err error) {
// TODO(s.chzhen): Remove when frontend starts handling UIDs.
p.UID = stored.UID
err = s.index.Clashes(p)
err = s.index.clashes(p)
if err != nil {
// Don't wrap the error since there is already an annotation deferred.
return err
}
s.index.Delete(stored)
s.index.Add(p)
s.index.remove(stored)
s.index.add(p)
return nil
}
@ -184,7 +206,7 @@ func (s *Storage) RangeByName(f func(c *Persistent) (cont bool)) {
s.mu.Lock()
defer s.mu.Unlock()
s.index.RangeByName(f)
s.index.rangeByName(f)
}
// Size returns the number of persistent clients.
@ -192,7 +214,7 @@ func (s *Storage) Size() (n int) {
s.mu.Lock()
defer s.mu.Unlock()
return s.index.Size()
return s.index.size()
}
// CloseUpstreams closes upstream configurations of persistent clients.
@ -200,11 +222,13 @@ func (s *Storage) CloseUpstreams() (err error) {
s.mu.Lock()
defer s.mu.Unlock()
return s.index.CloseUpstreams()
return s.index.closeUpstreams()
}
// ClientRuntime returns a copy of the saved runtime client by ip. If no such
// client exists, returns nil.
//
// TODO(s.chzhen): Use it.
func (s *Storage) ClientRuntime(ip netip.Addr) (rc *Runtime) {
s.mu.Lock()
defer s.mu.Unlock()
@ -214,6 +238,8 @@ func (s *Storage) ClientRuntime(ip netip.Addr) (rc *Runtime) {
// AddRuntime saves the runtime client information in the storage. IP address
// of a client must be unique. rc must not be nil.
//
// TODO(s.chzhen): Use it.
func (s *Storage) AddRuntime(rc *Runtime) {
s.mu.Lock()
defer s.mu.Unlock()
@ -222,6 +248,8 @@ func (s *Storage) AddRuntime(rc *Runtime) {
}
// SizeRuntime returns the number of the runtime clients.
//
// TODO(s.chzhen): Use it.
func (s *Storage) SizeRuntime() (n int) {
s.mu.Lock()
defer s.mu.Unlock()
@ -230,6 +258,8 @@ func (s *Storage) SizeRuntime() (n int) {
}
// RangeRuntime calls f for each runtime client in an undefined order.
//
// TODO(s.chzhen): Use it.
func (s *Storage) RangeRuntime(f func(rc *Runtime) (cont bool)) {
s.mu.Lock()
defer s.mu.Unlock()
@ -238,6 +268,8 @@ func (s *Storage) RangeRuntime(f func(rc *Runtime) (cont bool)) {
}
// DeleteRuntime removes the runtime client by ip.
//
// TODO(s.chzhen): Use it.
func (s *Storage) DeleteRuntime(ip netip.Addr) {
s.mu.Lock()
defer s.mu.Unlock()
@ -247,6 +279,8 @@ func (s *Storage) DeleteRuntime(ip netip.Addr) {
// DeleteBySource removes all runtime clients that have information only from
// the specified source and returns the number of removed clients.
//
// TODO(s.chzhen): Use it.
func (s *Storage) DeleteBySource(src Source) (n int) {
s.mu.Lock()
defer s.mu.Unlock()

View File

@ -16,7 +16,9 @@ import (
func newStorage(tb testing.TB, m []*client.Persistent) (s *client.Storage) {
tb.Helper()
s = client.NewStorage(nil)
s = client.NewStorage(&client.Config{
AllowedTags: nil,
})
for _, c := range m {
c.UID = client.MustNewUID()
@ -57,7 +59,9 @@ func TestStorage_Add(t *testing.T) {
UID: existingClientUID,
}
s := client.NewStorage(nil)
s := client.NewStorage(&client.Config{
AllowedTags: nil,
})
err := s.Add(existingClient)
require.NoError(t, err)
@ -137,7 +141,9 @@ func TestStorage_RemoveByName(t *testing.T) {
UID: client.MustNewUID(),
}
s := client.NewStorage(nil)
s := client.NewStorage(&client.Config{
AllowedTags: nil,
})
err := s.Add(existingClient)
require.NoError(t, err)
@ -162,7 +168,9 @@ func TestStorage_RemoveByName(t *testing.T) {
}
t.Run("duplicate_remove", func(t *testing.T) {
s = client.NewStorage(nil)
s = client.NewStorage(&client.Config{
AllowedTags: nil,
})
err = s.Add(existingClient)
require.NoError(t, err)

View File

@ -532,7 +532,7 @@ var blockedServices = []blockedService{{
},
}, {
ID: "cloudflare",
Name: "CloudFlare",
Name: "Cloudflare",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M38 10.813l-.906 3.78-1.907-3.405v1.718c2.899 2.301 4.926 5.79 5.126 9.688.699-.2 1.3-.188 2-.188 1.374 0 2.667.297 3.812.875l-1.031-.593 3.812-.875-3.812-.907L48.5 19h-3.813l2.813-2.688-3.688 1.094 2-3.312-3.312 2 1.094-3.688-2.688 2.781.094-3.874-2 3.28zM27 11c-5 0-9.414 2.992-11.313 7.594-.699-.399-1.687-.688-2.687-.688-3.2 0-5.906 2.606-5.906 5.907v.5c-3.899.3-7.094 3.68-7.094 7.78 0 .802.113 1.52.313 2.22.101.398.5.687 1 .687h47c.398 0 .675-.195.874-.594.5-1.101.813-2.207.813-3.406 0-4.2-3.488-7.594-7.688-7.594-.8 0-1.511.082-2.312.282l4.906 6.625-5.5-4.5L22 29.593l15.094-4.905L28.5 21.5l10.688 1.813v-.125C39.188 16.488 33.699 11 27 11zm19.781 12.656c.434.274.844.586 1.219.938h.5z\" /></svg>"),
Rules: []string{
"||argotunnel.com^",
@ -709,7 +709,7 @@ var blockedServices = []blockedService{{
},
}, {
ID: "ebay",
Name: "EBay",
Name: "eBay",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 12.601563 13.671875 L 12.636719 24.058594 C 12.632813 22.457031 12.128906 18.101563 6.464844 18.097656 C 0.210938 18.097656 -0.03125 22.964844 0.00390625 24.230469 C 0.00390625 24.230469 -0.304688 29.917969 6.3125 29.917969 C 11.996094 29.917969 12.277344 26.347656 12.277344 26.347656 L 9.664063 26.355469 C 9.664063 26.355469 9.152344 28.320313 6.320313 28.265625 C 2.683594 28.199219 2.546875 24.675781 2.546875 24.675781 L 12.621094 24.675781 C 12.621094 24.675781 12.628906 24.566406 12.636719 24.425781 L 12.644531 26.960938 C 12.644531 26.960938 12.628906 28.507813 12.535156 29.53125 L 14.984375 29.53125 L 15.089844 28.039063 C 15.089844 28.039063 16.230469 29.917969 19.566406 29.917969 C 22.902344 29.917969 25.535156 27.863281 25.609375 24.050781 C 25.675781 20.242188 22.761719 18.117188 19.617188 18.097656 C 16.472656 18.082031 15.121094 19.960938 15.121094 19.960938 L 15.121094 13.671875 Z M 31.054688 18.046875 C 29.566406 18.097656 26.539063 18.558594 26.132813 21.460938 L 28.796875 21.460938 C 28.796875 21.460938 29 19.6875 31.703125 19.738281 C 34.257813 19.785156 34.722656 21.039063 34.707031 22.578125 C 34.707031 22.578125 32.519531 22.585938 31.785156 22.59375 C 30.46875 22.597656 25.863281 22.742188 25.433594 25.550781 C 24.917969 28.890625 27.898438 29.933594 30.230469 29.917969 C 32.5625 29.90625 33.890625 29.207031 34.878906 27.953125 L 34.984375 29.511719 L 37.300781 29.496094 C 37.300781 29.496094 37.242188 28.628906 37.25 26.90625 C 37.257813 25.1875 37.308594 23.65625 37.25 22.574219 C 37.183594 21.316406 37.304688 18.285156 31.875 18.0625 C 31.875 18.0625 31.550781 18.03125 31.054688 18.046875 Z M 35.871094 18.519531 L 41.675781 29.496094 L 39.4375 33.71875 L 42.265625 33.71875 L 50 18.519531 L 47.359375 18.519531 L 43.074219 27.046875 L 38.796875 18.519531 Z M 6.402344 19.765625 C 9.984375 19.761719 9.984375 22.949219 9.984375 22.949219 L 2.628906 22.949219 C 2.628906 22.949219 2.804688 19.765625 6.402344 19.765625 Z M 19.035156 19.800781 C 23.078125 19.699219 22.949219 24.097656 22.949219 24.097656 C 22.949219 24.097656 23.011719 28.167969 19.042969 28.21875 C 15.070313 28.269531 15.136719 24.011719 15.136719 24.011719 C 15.136719 24.011719 14.992188 19.90625 19.035156 19.800781 Z M 34.734375 24.265625 C 34.734375 24.269531 35.195313 28.371094 30.664063 28.3125 C 30.664063 28.3125 28.136719 28.3125 27.988281 26.296875 C 27.832031 24.140625 31.875 24.269531 31.875 24.269531 Z\" /></svg>"),
Rules: []string{
"|ebay-*.s3-us-west-1.amazonaws.com^",

View File

@ -19,7 +19,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/whois"
"github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/dnsproxy/upstream"
"github.com/AdguardTeam/golibs/container"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/hostsfile"
"github.com/AdguardTeam/golibs/log"
@ -45,14 +44,12 @@ type DHCP interface {
// clientsContainer is the storage of all runtime and persistent clients.
type clientsContainer struct {
// clientIndex stores information about persistent clients.
clientIndex *client.Index
// storage stores information about persistent clients.
storage *client.Storage
// runtimeIndex stores information about runtime clients.
runtimeIndex *client.RuntimeIndex
allTags *container.MapSet[string]
// dhcp is the DHCP service implementation.
dhcp DHCP
@ -104,15 +101,15 @@ func (clients *clientsContainer) Init(
filteringConf *filtering.Config,
) (err error) {
// TODO(s.chzhen): Refactor it.
if clients.clientIndex != nil {
if clients.storage != nil {
return errors.Error("clients container already initialized")
}
clients.runtimeIndex = client.NewRuntimeIndex()
clients.clientIndex = client.NewIndex()
clients.allTags = container.NewMapSet(clientTags...)
clients.storage = client.NewStorage(&client.Config{
AllowedTags: clientTags,
})
// TODO(e.burkov): Use [dhcpsvc] implementation when it's ready.
clients.dhcp = dhcpServer
@ -217,7 +214,6 @@ type clientObject struct {
// toPersistent returns an initialized persistent client if there are no errors.
func (o *clientObject) toPersistent(
filteringConf *filtering.Config,
allTags *container.MapSet[string],
) (cli *client.Persistent, err error) {
cli = &client.Persistent{
Name: o.Name,
@ -274,7 +270,7 @@ func (o *clientObject) toPersistent(
cli.BlockedServices = o.BlockedServices.Clone()
cli.SetTags(o.Tags, allTags)
cli.Tags = slices.Clone(o.Tags)
return cli, nil
}
@ -287,22 +283,14 @@ func (clients *clientsContainer) addFromConfig(
) (err error) {
for i, o := range objects {
var cli *client.Persistent
cli, err = o.toPersistent(filteringConf, clients.allTags)
cli, err = o.toPersistent(filteringConf)
if err != nil {
return fmt.Errorf("clients: init persistent client at index %d: %w", i, err)
}
// TODO(s.chzhen): Consider moving to the client index constructor.
err = clients.clientIndex.ClashesUID(cli)
err = clients.storage.Add(cli)
if err != nil {
return fmt.Errorf("adding client %s at index %d: %w", cli.Name, i, err)
}
err = clients.add(cli)
if err != nil {
// TODO(s.chzhen): Return an error instead of logging if more
// stringent requirements are implemented.
log.Error("clients: adding client %s at index %d: %s", cli.Name, i, err)
return fmt.Errorf("adding client %q at index %d: %w", cli.Name, i, err)
}
}
@ -315,8 +303,8 @@ func (clients *clientsContainer) forConfig() (objs []*clientObject) {
clients.lock.Lock()
defer clients.lock.Unlock()
objs = make([]*clientObject, 0, clients.clientIndex.Size())
clients.clientIndex.RangeByName(func(cli *client.Persistent) (cont bool) {
objs = make([]*clientObject, 0, clients.storage.Size())
clients.storage.RangeByName(func(cli *client.Persistent) (cont bool) {
objs = append(objs, &clientObject{
Name: cli.Name,
@ -360,7 +348,7 @@ func (clients *clientsContainer) periodicUpdate() {
// clientSource checks if client with this IP address already exists and returns
// the source which updated it last. It returns [client.SourceNone] if the
// client doesn't exist.
// client doesn't exist. Note that it is only used in tests.
func (clients *clientsContainer) clientSource(ip netip.Addr) (src client.Source) {
clients.lock.Lock()
defer clients.lock.Unlock()
@ -419,12 +407,8 @@ func (clients *clientsContainer) clientOrArtificial(
}
}()
cli, ok := clients.find(id)
if !ok {
cli = clients.clientIndex.FindByIPWithoutZone(ip)
}
if cli != nil {
cli, ok := clients.storage.FindLoose(ip, id)
if ok {
return &querylog.Client{
Name: cli.Name,
IgnoreQueryLog: cli.IgnoreQueryLog,
@ -456,7 +440,7 @@ func (clients *clientsContainer) find(id string) (c *client.Persistent, ok bool)
return nil, false
}
return c.ShallowClone(), true
return c, true
}
// shouldCountClient is a wrapper around [clientsContainer.find] to make it a
@ -530,7 +514,7 @@ func (clients *clientsContainer) UpstreamConfigByID(
// findLocked searches for a client by its ID. clients.lock is expected to be
// locked.
func (clients *clientsContainer) findLocked(id string) (c *client.Persistent, ok bool) {
c, ok = clients.clientIndex.Find(id)
c, ok = clients.storage.Find(id)
if ok {
return c, true
}
@ -552,7 +536,7 @@ func (clients *clientsContainer) findDHCP(ip netip.Addr) (c *client.Persistent,
return nil, false
}
return clients.clientIndex.FindByMAC(foundMAC)
return clients.storage.FindByMAC(foundMAC)
}
// runtimeClient returns a runtime client from internal index. Note that it
@ -586,114 +570,6 @@ func (clients *clientsContainer) findRuntimeClient(ip netip.Addr) (rc *client.Ru
return rc
}
// check validates the client. It also sorts the client tags.
func (clients *clientsContainer) check(c *client.Persistent) (err error) {
switch {
case c == nil:
return errors.Error("client is nil")
case c.Name == "":
return errors.Error("invalid name")
case c.IDsLen() == 0:
return errors.Error("id required")
default:
// Go on.
}
for _, t := range c.Tags {
if !clients.allTags.Has(t) {
return fmt.Errorf("invalid tag: %q", t)
}
}
// TODO(s.chzhen): Move to the constructor.
slices.Sort(c.Tags)
_, err = proxy.ParseUpstreamsConfig(c.Upstreams, &upstream.Options{})
if err != nil {
return fmt.Errorf("invalid upstream servers: %w", err)
}
return nil
}
// add adds a persistent client or returns an error.
func (clients *clientsContainer) add(c *client.Persistent) (err error) {
err = clients.check(c)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
clients.lock.Lock()
defer clients.lock.Unlock()
err = clients.clientIndex.Clashes(c)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
clients.addLocked(c)
log.Debug("clients: added %q: ID:%q [%d]", c.Name, c.IDs(), clients.clientIndex.Size())
return nil
}
// addLocked c to the indexes. clients.lock is expected to be locked.
func (clients *clientsContainer) addLocked(c *client.Persistent) {
clients.clientIndex.Add(c)
}
// remove removes a client. ok is false if there is no such client.
func (clients *clientsContainer) remove(name string) (ok bool) {
clients.lock.Lock()
defer clients.lock.Unlock()
c, ok := clients.clientIndex.FindByName(name)
if !ok {
return false
}
clients.removeLocked(c)
return true
}
// removeLocked removes c from the indexes. clients.lock is expected to be
// locked.
func (clients *clientsContainer) removeLocked(c *client.Persistent) {
if err := c.CloseUpstreams(); err != nil {
log.Error("client container: removing client %s: %s", c.Name, err)
}
// Update the ID index.
clients.clientIndex.Delete(c)
}
// update updates a client by its name.
func (clients *clientsContainer) update(prev, c *client.Persistent) (err error) {
err = clients.check(c)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
clients.lock.Lock()
defer clients.lock.Unlock()
err = clients.clientIndex.Clashes(c)
if err != nil {
// Don't wrap the error since it's informative enough as is.
return err
}
clients.removeLocked(prev)
clients.addLocked(c)
return nil
}
// setWHOISInfo sets the WHOIS information for a client. clients.lock is
// expected to be locked.
func (clients *clientsContainer) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
@ -855,5 +731,5 @@ func (clients *clientsContainer) addFromSystemARP() {
// close gracefully closes all the client-specific upstream configurations of
// the persistent clients.
func (clients *clientsContainer) close() (err error) {
return clients.clientIndex.CloseUpstreams()
return clients.storage.CloseUpstreams()
}

View File

@ -72,7 +72,7 @@ func TestClients(t *testing.T) {
IPs: []netip.Addr{cli1IP, cliIPv6},
}
err := clients.add(c)
err := clients.storage.Add(c)
require.NoError(t, err)
c = &client.Persistent{
@ -81,7 +81,7 @@ func TestClients(t *testing.T) {
IPs: []netip.Addr{cli2IP},
}
err = clients.add(c)
err = clients.storage.Add(c)
require.NoError(t, err)
c, ok := clients.find(cli1)
@ -107,7 +107,7 @@ func TestClients(t *testing.T) {
})
t.Run("add_fail_name", func(t *testing.T) {
err := clients.add(&client.Persistent{
err := clients.storage.Add(&client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
IPs: []netip.Addr{netip.MustParseAddr("1.2.3.5")},
@ -116,7 +116,7 @@ func TestClients(t *testing.T) {
})
t.Run("add_fail_ip", func(t *testing.T) {
err := clients.add(&client.Persistent{
err := clients.storage.Add(&client.Persistent{
Name: "client3",
UID: client.MustNewUID(),
})
@ -124,7 +124,7 @@ func TestClients(t *testing.T) {
})
t.Run("update_fail_ip", func(t *testing.T) {
err := clients.update(&client.Persistent{Name: "client1"}, &client.Persistent{
err := clients.storage.Update("client1", &client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
})
@ -139,11 +139,11 @@ func TestClients(t *testing.T) {
cliNewIP = netip.MustParseAddr(cliNew)
)
prev, ok := clients.clientIndex.FindByName("client1")
prev, ok := clients.storage.FindByName("client1")
require.True(t, ok)
require.NotNil(t, prev)
err := clients.update(prev, &client.Persistent{
err := clients.storage.Update("client1", &client.Persistent{
Name: "client1",
UID: prev.UID,
IPs: []netip.Addr{cliNewIP},
@ -155,11 +155,11 @@ func TestClients(t *testing.T) {
assert.Equal(t, clients.clientSource(cliNewIP), client.SourcePersistent)
prev, ok = clients.clientIndex.FindByName("client1")
prev, ok = clients.storage.FindByName("client1")
require.True(t, ok)
require.NotNil(t, prev)
err = clients.update(prev, &client.Persistent{
err = clients.storage.Update("client1", &client.Persistent{
Name: "client1-renamed",
UID: prev.UID,
IPs: []netip.Addr{cliNewIP},
@ -173,7 +173,7 @@ func TestClients(t *testing.T) {
assert.Equal(t, "client1-renamed", c.Name)
assert.True(t, c.UseOwnSettings)
nilCli, ok := clients.clientIndex.FindByName("client1")
nilCli, ok := clients.storage.FindByName("client1")
require.False(t, ok)
assert.Nil(t, nilCli)
@ -184,7 +184,7 @@ func TestClients(t *testing.T) {
})
t.Run("del_success", func(t *testing.T) {
ok := clients.remove("client1-renamed")
ok := clients.storage.RemoveByName("client1-renamed")
require.True(t, ok)
_, ok = clients.find("1.1.1.2")
@ -192,7 +192,7 @@ func TestClients(t *testing.T) {
})
t.Run("del_fail", func(t *testing.T) {
ok := clients.remove("client3")
ok := clients.storage.RemoveByName("client3")
assert.False(t, ok)
})
@ -261,7 +261,7 @@ func TestClientsWHOIS(t *testing.T) {
t.Run("can't_set_manually-added", func(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.2")
err := clients.add(&client.Persistent{
err := clients.storage.Add(&client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.2")},
@ -272,7 +272,7 @@ func TestClientsWHOIS(t *testing.T) {
rc := clients.runtimeIndex.Client(ip)
require.Nil(t, rc)
assert.True(t, clients.remove("client1"))
assert.True(t, clients.storage.RemoveByName("client1"))
})
}
@ -283,7 +283,7 @@ func TestClientsAddExisting(t *testing.T) {
ip := netip.MustParseAddr("1.1.1.1")
// Add a client.
err := clients.add(&client.Persistent{
err := clients.storage.Add(&client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
IPs: []netip.Addr{ip, netip.MustParseAddr("1:2:3::4")},
@ -333,7 +333,7 @@ func TestClientsAddExisting(t *testing.T) {
require.NoError(t, err)
// Add a new client with the same IP as for a client with MAC.
err = clients.add(&client.Persistent{
err = clients.storage.Add(&client.Persistent{
Name: "client2",
UID: client.MustNewUID(),
IPs: []netip.Addr{ip},
@ -341,7 +341,7 @@ func TestClientsAddExisting(t *testing.T) {
require.NoError(t, err)
// Add a new client with the IP from the first client's IP range.
err = clients.add(&client.Persistent{
err = clients.storage.Add(&client.Persistent{
Name: "client3",
UID: client.MustNewUID(),
IPs: []netip.Addr{netip.MustParseAddr("2.2.2.2")},
@ -354,7 +354,7 @@ func TestClientsCustomUpstream(t *testing.T) {
clients := newClientsContainer(t)
// Add client with upstreams.
err := clients.add(&client.Persistent{
err := clients.storage.Add(&client.Persistent{
Name: "client1",
UID: client.MustNewUID(),
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("1:2:3::4")},

View File

@ -96,7 +96,7 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
clients.lock.Lock()
defer clients.lock.Unlock()
clients.clientIndex.Range(func(c *client.Persistent) (cont bool) {
clients.storage.RangeByName(func(c *client.Persistent) (cont bool) {
cj := clientToJSON(c)
data.Clients = append(data.Clients, cj)
@ -336,7 +336,7 @@ func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http.
return
}
err = clients.add(c)
err = clients.storage.Add(c)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
@ -364,7 +364,7 @@ func (clients *clientsContainer) handleDelClient(w http.ResponseWriter, r *http.
return
}
if !clients.remove(cj.Name) {
if !clients.storage.RemoveByName(cj.Name) {
aghhttp.Error(r, w, http.StatusBadRequest, "Client not found")
return
@ -399,30 +399,14 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
return
}
var prev *client.Persistent
var ok bool
func() {
clients.lock.Lock()
defer clients.lock.Unlock()
prev, ok = clients.clientIndex.FindByName(dj.Name)
}()
if !ok {
aghhttp.Error(r, w, http.StatusBadRequest, "client not found")
return
}
c, err := clients.jsonToClient(dj.Data, prev)
c, err := clients.jsonToClient(dj.Data, nil)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
return
}
err = clients.update(prev, c)
err = clients.storage.Update(dj.Name, c)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)

View File

@ -198,11 +198,11 @@ func TestClientsContainer_HandleDelClient(t *testing.T) {
clients := newClientsContainer(t)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.add(clientOne)
err := clients.storage.Add(clientOne)
require.NoError(t, err)
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
err = clients.add(clientTwo)
err = clients.storage.Add(clientTwo)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
@ -260,7 +260,7 @@ func TestClientsContainer_HandleUpdateClient(t *testing.T) {
clients := newClientsContainer(t)
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.add(clientOne)
err := clients.storage.Add(clientOne)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne})
@ -342,11 +342,11 @@ func TestClientsContainer_HandleFindClient(t *testing.T) {
}
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
err := clients.add(clientOne)
err := clients.storage.Add(clientOne)
require.NoError(t, err)
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
err = clients.add(clientTwo)
err = clients.storage.Add(clientTwo)
require.NoError(t, err)
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})

View File

@ -13,17 +13,21 @@ import (
var testIPv4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
// newIDIndex is a helper function that returns a client index filled with
// persistent clients from the m. It also generates a UID for each client.
func newIDIndex(m []*client.Persistent) (ci *client.Index) {
ci = client.NewIndex()
// newStorage is a helper function that returns a client storage filled with
// persistent clients. It also generates a UID for each client.
func newStorage(tb testing.TB, clients []*client.Persistent) (s *client.Storage) {
tb.Helper()
for _, c := range m {
c.UID = client.MustNewUID()
ci.Add(c)
s = client.NewStorage(&client.Config{
AllowedTags: nil,
})
for _, p := range clients {
p.UID = client.MustNewUID()
require.NoError(tb, s.Add(p))
}
return ci
return s
}
func TestApplyAdditionalFiltering(t *testing.T) {
@ -36,7 +40,8 @@ func TestApplyAdditionalFiltering(t *testing.T) {
}, nil)
require.NoError(t, err)
Context.clients.clientIndex = newIDIndex([]*client.Persistent{{
Context.clients.storage = newStorage(t, []*client.Persistent{{
Name: "default",
ClientIDs: []string{"default"},
UseOwnSettings: false,
SafeSearchConf: filtering.SafeSearchConfig{Enabled: false},
@ -44,6 +49,7 @@ func TestApplyAdditionalFiltering(t *testing.T) {
SafeBrowsingEnabled: false,
ParentalEnabled: false,
}, {
Name: "custom_filtering",
ClientIDs: []string{"custom_filtering"},
UseOwnSettings: true,
SafeSearchConf: filtering.SafeSearchConfig{Enabled: true},
@ -51,6 +57,7 @@ func TestApplyAdditionalFiltering(t *testing.T) {
SafeBrowsingEnabled: true,
ParentalEnabled: true,
}, {
Name: "partial_custom_filtering",
ClientIDs: []string{"partial_custom_filtering"},
UseOwnSettings: true,
SafeSearchConf: filtering.SafeSearchConfig{Enabled: true},
@ -121,16 +128,19 @@ func TestApplyAdditionalFiltering_blockedServices(t *testing.T) {
}, nil)
require.NoError(t, err)
Context.clients.clientIndex = newIDIndex([]*client.Persistent{{
Context.clients.storage = newStorage(t, []*client.Persistent{{
Name: "default",
ClientIDs: []string{"default"},
UseOwnBlockedServices: false,
}, {
Name: "no_services",
ClientIDs: []string{"no_services"},
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
},
UseOwnBlockedServices: true,
}, {
Name: "services",
ClientIDs: []string{"services"},
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
@ -138,6 +148,7 @@ func TestApplyAdditionalFiltering_blockedServices(t *testing.T) {
},
UseOwnBlockedServices: true,
}, {
Name: "invalid_services",
ClientIDs: []string{"invalid_services"},
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.EmptyWeekly(),
@ -145,6 +156,7 @@ func TestApplyAdditionalFiltering_blockedServices(t *testing.T) {
},
UseOwnBlockedServices: true,
}, {
Name: "allow_all",
ClientIDs: []string{"allow_all"},
BlockedServices: &filtering.BlockedServices{
Schedule: schedule.FullWeekly(),

View File

@ -133,7 +133,14 @@ func webCheckPortAvailable(port uint16) (ok bool) {
addrPort := netip.AddrPortFrom(config.HTTPConfig.Address.Addr(), port)
return aghnet.CheckPort("tcp", addrPort) == nil
err := aghnet.CheckPort("tcp", addrPort)
if err != nil {
log.Info("web: warning: checking https port: %s", err)
return false
}
return true
}
// tlsConfigChanged updates the TLS configuration and restarts the HTTPS server

View File

@ -4,14 +4,14 @@ go 1.22.4
require (
github.com/fzipp/gocyclo v0.6.0
github.com/golangci/misspell v0.5.1
github.com/golangci/misspell v0.6.0
github.com/gordonklaus/ineffassign v0.1.0
github.com/kisielk/errcheck v1.7.0
github.com/kyoh86/looppointer v0.2.1
github.com/securego/gosec/v2 v2.20.0
github.com/uudashr/gocognit v1.1.2
golang.org/x/tools v0.22.0
golang.org/x/vuln v1.1.1
golang.org/x/vuln v1.1.2
honnef.co/go/tools v0.4.7
mvdan.cc/gofumpt v0.6.0
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f
@ -26,9 +26,10 @@ require (
github.com/kyoh86/nolint v0.0.1 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/exp/typeparams v0.0.0-20240604190554-fc45aab8b7f8 // indirect
golang.org/x/exp/typeparams v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/mod v0.18.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect
golang.org/x/telemetry v0.0.0-20240701175443-4e29c7872ac1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

View File

@ -12,8 +12,8 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/golangci/misspell v0.5.1 h1:/SjR1clj5uDjNLwYzCahHwIOPmQgoH04AyQIiWGbhCM=
github.com/golangci/misspell v0.5.1/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@ -63,8 +63,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/typeparams v0.0.0-20240604190554-fc45aab8b7f8 h1:WKP3FgLqWfVutBnw/dr+LNg4fzjyTQP5o+ELTIyoBrs=
golang.org/x/exp/typeparams v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/exp/typeparams v0.0.0-20240613232115-7f521ea00fb8 h1:+ZJmEdDFzH5H0CnzOrwgbH3elHctfTecW9X0k2tkn5M=
golang.org/x/exp/typeparams v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@ -95,6 +95,8 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/telemetry v0.0.0-20240701175443-4e29c7872ac1 h1:jveUVYFLPlIma1aZBg9rrUN+Dqk4e6QbVSGiZGwA/2Y=
golang.org/x/telemetry v0.0.0-20240701175443-4e29c7872ac1/go.mod h1:n38mvGdgc4dA684EC4NwQwoPKSw4jyKw8/DgZHDA1Dk=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -109,8 +111,8 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/vuln v1.1.1 h1:4nYQg4OSr7uYQMtjuuYqLAEVuTjY4k/CPMYqvv5OPcI=
golang.org/x/vuln v1.1.1/go.mod h1:hNgE+SKMSp2wHVUpW0Ow2ejgKpNJePdML+4YjxrVxik=
golang.org/x/vuln v1.1.2 h1:UkLxe+kAMcrNBpGrFbU0Mc5l7cX97P2nhy21wx5+Qbk=
golang.org/x/vuln v1.1.2/go.mod h1:2o3fRKD8Uz9AraAL3lwd/grWBv+t+SeJnPcqBUJrY24=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=