Compare commits

..

2 Commits

Author SHA1 Message Date
Stanislav Chzhen
b34a8c169c filtering: imp tests 2023-05-15 10:33:52 +03:00
Stanislav Chzhen
1f2ba07eae filtering: wildcard interference 2023-05-12 12:25:33 +03:00
60 changed files with 1101 additions and 1955 deletions

View File

@@ -89,54 +89,21 @@
'validations': 'validations':
'required': true 'required': true
- 'attributes': - 'attributes':
'description': 'Please provide a set of steps to reproduce the issue' 'description': 'Please describe the bug'
'label': 'Issue Details' 'label': 'Description'
'placeholder':
'value': | 'value': |
Steps to reproduce: #### What did you do?
1.
2. #### Expected result
3.
'id': 'what-happened' #### Actual result
#### Screenshots (if applicable)
#### Additional information
'id': 'description'
'type': 'textarea' 'type': 'textarea'
'validations': 'validations':
'required': true 'required': true
- 'attributes': 'description': 'File a bug report'
'label': 'Expected Behavior' 'name': 'Bug'
'description': 'Describe the expected behavior'
'placeholder': |
A clear and concise description of what you expected to happen.
'id': 'expected-behavior'
'type': 'textarea'
'validations':
'required': false
- 'attributes':
'label': 'Actual Behavior'
'description': 'Describe the actual behavior'
'placeholder': 'A clear description of what happened instead.'
'id': 'actual-behavior'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'label': 'Screenshots'
'description': |
If applicable add screenshots explaining your problem.
You can drag and drop images or paste them from clipboard.
Use `<details> </details>` tag to hide screenshots under the spoiler.
'placeholder': 'If applicable add screenshots explaining your problem.'
'value': |
<details><summary>Screenshot 1:</summary>
<!-- paste screenshot here -->
</details>
'id': 'screenshots'
'type': 'textarea'
'validations':
'required': false
'description': >
Open a bug report. Please do not open bug reports for questions. If you
want to ask a question, ask in in the Discussions section.
'name': 'Bug report'
'labels': [ 'bug' ]

View File

@@ -23,32 +23,19 @@
'id': 'prerequisites' 'id': 'prerequisites'
'type': 'checkboxes' 'type': 'checkboxes'
- 'attributes': - 'attributes':
'description': 'What happened?' 'description': 'Please describe the request'
'placeholder': | 'label': 'Description'
Is your feature request related to a problem? Please add a clear and 'value': |
concise description of what the problem is. #### What problem are you trying to solve?
'label': 'Issue Details'
'id': 'what-happened' #### Proposed solution
#### Alternatives considered
#### Additional information
'id': 'description'
'type': 'textarea' 'type': 'textarea'
'validations': 'validations':
'required': true 'required': true
- 'attributes':
'description': 'What do you propose to change in AdGuard Home?'
'placeholder': |
Describe the solution you'd like in a clear and concise manner.
'label': 'Proposed solution'
'id': 'proposed-solution'
'type': 'textarea'
'validations':
'required': true
- 'attributes':
'description': 'Are there any other ways to solve the problem?'
'placeholder': |
A clear and concise description of any alternative solutions or features
you've considered.
'label': 'Alternative solution'
'id': 'alternative-solution'
'type': 'textarea'
'description': 'Suggest a feature or an enhancement for AdGuard Home' 'description': 'Suggest a feature or an enhancement for AdGuard Home'
'name': 'Feature request or enhancement' 'name': 'Feature request or enhancement'
'labels': [ 'feature request' ]

View File

@@ -1,7 +1,7 @@
'name': 'build' 'name': 'build'
'env': 'env':
'GO_VERSION': '1.19.10' 'GO_VERSION': '1.19.8'
'NODE_VERSION': '14' 'NODE_VERSION': '14'
'on': 'on':

View File

@@ -1,7 +1,7 @@
'name': 'lint' 'name': 'lint'
'env': 'env':
'GO_VERSION': '1.19.10' 'GO_VERSION': '1.19.8'
'on': 'on':
'push': 'push':

View File

@@ -1,34 +0,0 @@
'name': 'potential-duplicates'
'on':
'issues':
'types': [ 'opened', 'edited' ]
'jobs':
'run':
'runs-on': 'ubuntu-latest'
'steps':
- 'uses': 'wow-actions/potential-duplicates@v1'
'with':
'GITHUB_TOKEN': ${{ secrets.GITHUB_TOKEN }}
# Issue title filter work with https://www.npmjs.com/package/anymatch.
# Any matched issue will stop detection immediately.
# You can specify multi filters in each line.
# 'filter': ''
# Exclude keywords in title before detecting.
# 'exclude': ''
# Label to set, when potential duplicates are detected.
'label': 'potential-duplicate'
# Get issues with state to compare. Supported state: 'all', 'closed',
# 'open'.
'state': 'all'
# If similarity is higher than this threshold([0,1]), issue will be
# marked as duplicate.
'threshold': 0.6
# Reactions to be add to comment when potential duplicates are
# detected. Available reactions: "-1", "+1", "confused", "laugh",
# "heart", "hooray", "rocket", "eyes".
# 'reactions': 'eyes, confused'
# Comment to post when potential duplicates are detected.
'comment': >
Potential duplicates: {{#issues}}
- [#{{ number }}] {{ title }} ({{ accuracy }}%)
{{/issues}}

View File

@@ -14,60 +14,14 @@ and this project adheres to
<!-- <!--
## [v0.108.0] - TBA ## [v0.108.0] - TBA
## [v0.107.32] - 2023-06-28 (APPROX.) ## [v0.107.30] - 2023-04-26 (APPROX.)
See also the [v0.107.32 GitHub milestone][ms-v0.107.32].
[ms-v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/milestone/68?closed=1
NOTE: Add new changes BELOW THIS COMMENT.
-->
### Added
- The ability to edit rewrite rules via `PUT /control/rewrite/update` HTTP API
([#1577]).
### Fixed
- DNSCrypt upstream not resetting the client and resolver information on
dialing errors ([#5872]).
[#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577
<!--
NOTE: Add new changes ABOVE THIS COMMENT.
-->
## [v0.107.31] - 2023-06-08
See also the [v0.107.31 GitHub milestone][ms-v0.107.31].
### Fixed
- Startup errors on OpenWrt ([#5872]).
- Plain-UDP upstreams always falling back to TCP, causing outages and slowdowns
([#5873], [#5874]).
[#5872]: https://github.com/AdguardTeam/AdGuardHome/issues/5872
[#5873]: https://github.com/AdguardTeam/AdGuardHome/issues/5873
[#5874]: https://github.com/AdguardTeam/AdGuardHome/issues/5874
[ms-v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/milestone/67?closed=1
## [v0.107.30] - 2023-06-07
See also the [v0.107.30 GitHub milestone][ms-v0.107.30]. See also the [v0.107.30 GitHub milestone][ms-v0.107.30].
### Security [ms-v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/milestone/66?closed=1
- Go version has been updated to prevent the possibility of exploiting the NOTE: Add new changes BELOW THIS COMMENT.
CVE-2023-29402, CVE-2023-29403, and CVE-2023-29404 Go vulnerabilities fixed in -->
[Go 1.19.10][go-1.19.10].
### Fixed ### Fixed
@@ -83,8 +37,9 @@ See also the [v0.107.30 GitHub milestone][ms-v0.107.30].
[#5716]: https://github.com/AdguardTeam/AdGuardHome/issues/5716 [#5716]: https://github.com/AdguardTeam/AdGuardHome/issues/5716
[go-1.19.10]: https://groups.google.com/g/golang-announce/c/q5135a9d924/m/j0ZoAJOHAwAJ <!--
[ms-v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/milestone/66?closed=1 NOTE: Add new changes ABOVE THIS COMMENT.
-->
@@ -2009,13 +1964,11 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
<!-- <!--
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...HEAD
[v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32 [v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30
--> -->
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...HEAD [Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...HEAD
[v0.107.31]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...v0.107.31
[v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30
[v0.107.29]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.28...v0.107.29 [v0.107.29]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.28...v0.107.29
[v0.107.28]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.27...v0.107.28 [v0.107.28]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.27...v0.107.28
[v0.107.27]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...v0.107.27 [v0.107.27]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...v0.107.27

View File

@@ -466,10 +466,6 @@ bug or implementing the feature.
Home](https://github.com/ebrianne/adguard-exporter) by Home](https://github.com/ebrianne/adguard-exporter) by
[@ebrianne](https://github.com/ebrianne). [@ebrianne](https://github.com/ebrianne).
* [Terminal-based, real-time traffic monitoring and statistics for your AdGuard Home
instance](https://github.com/Lissy93/AdGuardian-Term) by
[@Lissy93](https://github.com/Lissy93)
* [AdGuard Home on GLInet * [AdGuard Home on GLInet
routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by
[Gl-Inet](https://gl-inet.com/). [Gl-Inet](https://gl-inet.com/).

View File

@@ -7,7 +7,7 @@
# Make sure to sync any changes with the branch overrides below. # Make sure to sync any changes with the branch overrides below.
'variables': 'variables':
'channel': 'edge' 'channel': 'edge'
'dockerGo': 'adguard/golang-ubuntu:6.7' 'dockerGo': 'adguard/golang-ubuntu:6.3'
'stages': 'stages':
- 'Build frontend': - 'Build frontend':
@@ -249,7 +249,8 @@
env\ env\
SNAPCRAFT_CHANNEL="$snapchannel"\ SNAPCRAFT_CHANNEL="$snapchannel"\
SNAPCRAFT_EMAIL="${bamboo.snapcraftEmail}"\ SNAPCRAFT_EMAIL="${bamboo.snapcraftEmail}"\
SNAPCRAFT_STORE_CREDENTIALS="${bamboo.snapcraftMacaroonPassword}"\ SNAPCRAFT_MACAROON="${bamboo.snapcraftMacaroonPassword}"\
SNAPCRAFT_UBUNTU_DISCHARGE="${bamboo.snapcraftUbuntuDischargePassword}"\
../bamboo-deploy-publisher/deploy.sh adguard-home-snap ../bamboo-deploy-publisher/deploy.sh adguard-home-snap
'final-tasks': 'final-tasks':
- 'clean' - 'clean'
@@ -330,7 +331,7 @@
# need to build a few of these. # need to build a few of these.
'variables': 'variables':
'channel': 'beta' 'channel': 'beta'
'dockerGo': 'adguard/golang-ubuntu:6.7' 'dockerGo': 'adguard/golang-ubuntu:6.3'
# release-vX.Y.Z branches are the branches from which the actual final release # release-vX.Y.Z branches are the branches from which the actual final release
# is built. # is built.
- '^release-v[0-9]+\.[0-9]+\.[0-9]+': - '^release-v[0-9]+\.[0-9]+\.[0-9]+':
@@ -345,4 +346,4 @@
# are the ones that actually get released. # are the ones that actually get released.
'variables': 'variables':
'channel': 'release' 'channel': 'release'
'dockerGo': 'adguard/golang-ubuntu:6.7' 'dockerGo': 'adguard/golang-ubuntu:6.3'

View File

@@ -5,7 +5,7 @@
'key': 'AHBRTSPECS' 'key': 'AHBRTSPECS'
'name': 'AdGuard Home - Build and run tests' 'name': 'AdGuard Home - Build and run tests'
'variables': 'variables':
'dockerGo': 'adguard/golang-ubuntu:6.7' 'dockerGo': 'adguard/golang-ubuntu:6.3'
'stages': 'stages':
- 'Tests': - 'Tests':

View File

@@ -150,7 +150,7 @@
"dns_allowlists": "Белыя спісы DNS", "dns_allowlists": "Белыя спісы DNS",
"dns_blocklists_desc": "AdGuard Home будзе блакаваць дамены з чорных спісаў.", "dns_blocklists_desc": "AdGuard Home будзе блакаваць дамены з чорных спісаў.",
"dns_allowlists_desc": "Дамены з белых спісаў DNS будуць дазволены, нават калі яны знаходзяцца ў любым з чорных спісаў.", "dns_allowlists_desc": "Дамены з белых спісаў DNS будуць дазволены, нават калі яны знаходзяцца ў любым з чорных спісаў.",
"custom_filtering_rules": "Карыстальніцкія правілы фільтрацыі", "custom_filtering_rules": "Карыстацкія правілы фільтрацыі",
"encryption_settings": "Налады шыфравання", "encryption_settings": "Налады шыфравання",
"dhcp_settings": "Налады DHCP", "dhcp_settings": "Налады DHCP",
"upstream_dns": "Upstream DNS-серверы", "upstream_dns": "Upstream DNS-серверы",
@@ -247,7 +247,7 @@
"loading_table_status": "Загрузка...", "loading_table_status": "Загрузка...",
"page_table_footer_text": "Старонка", "page_table_footer_text": "Старонка",
"rows_table_footer_text": "радкоў", "rows_table_footer_text": "радкоў",
"updated_custom_filtering_toast": "Карыстальніцкія правілы паспяхова захаваны", "updated_custom_filtering_toast": "Занесены змены ў карыстацкія правілы",
"rule_removed_from_custom_filtering_toast": "Карыстацкае правіла выдалена: {{rule}}", "rule_removed_from_custom_filtering_toast": "Карыстацкае правіла выдалена: {{rule}}",
"rule_added_to_custom_filtering_toast": "Карыстацкае правіла дададзена: {{rule}}", "rule_added_to_custom_filtering_toast": "Карыстацкае правіла дададзена: {{rule}}",
"query_log_response_status": "Статус: {{value}}", "query_log_response_status": "Статус: {{value}}",
@@ -568,7 +568,7 @@
"check_desc": "Праверыць фільтрацыю імя хаста", "check_desc": "Праверыць фільтрацыю імя хаста",
"check": "Праверыць", "check": "Праверыць",
"form_enter_host": "Увядзіце імя хаста", "form_enter_host": "Увядзіце імя хаста",
"filtered_custom_rules": "Адфільтраваны з дапамогай карыстальніцкіх правіл фільтрацыі", "filtered_custom_rules": "Адфільтраваны з дапамогай карыстацкіх правілаў фільтрацыі",
"choose_from_list": "Абраць са спіса", "choose_from_list": "Абраць са спіса",
"add_custom_list": "Дадаць свой спіс", "add_custom_list": "Дадаць свой спіс",
"host_whitelisted": "Хост занесены ў белы спіс", "host_whitelisted": "Хост занесены ў белы спіс",

View File

@@ -268,8 +268,6 @@
"blocking_mode_nxdomain": "NXDOMAIN: پاسخ با کُد NXDOMAIN", "blocking_mode_nxdomain": "NXDOMAIN: پاسخ با کُد NXDOMAIN",
"blocking_mode_null_ip": "Null IP: پاسخ با آدرس آی پی صفر(0.0.0.0 برای A; :: برای AAAA)", "blocking_mode_null_ip": "Null IP: پاسخ با آدرس آی پی صفر(0.0.0.0 برای A; :: برای AAAA)",
"blocking_mode_custom_ip": "آی پی دستی: پاسخ با آدرس آی پی دستی تنظیم شده", "blocking_mode_custom_ip": "آی پی دستی: پاسخ با آدرس آی پی دستی تنظیم شده",
"theme_light": "پوسته روشن",
"theme_dark": "پوسته تیره",
"upstream_dns_client_desc": "اگر این فیلد را خالی نگه دارید، AdGuard Home از سرور پیکربندی شده در <0> تنظیماتDNS </0> استفاده می کند.", "upstream_dns_client_desc": "اگر این فیلد را خالی نگه دارید، AdGuard Home از سرور پیکربندی شده در <0> تنظیماتDNS </0> استفاده می کند.",
"tracker_source": "منبع ردیاب", "tracker_source": "منبع ردیاب",
"source_label": "منبع", "source_label": "منبع",
@@ -569,6 +567,5 @@
"use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید", "use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید",
"parental_control": "نظارت والدین", "parental_control": "نظارت والدین",
"safe_browsing": "وب گردی اَمن", "safe_browsing": "وب گردی اَمن",
"form_error_password_length": "رمزعبور باید حداقل {{value}} کاراکتر باشد.", "form_error_password_length": "رمزعبور باید حداقل {{value}} کاراکتر باشد."
"protection_section_label": "حفاظت"
} }

View File

@@ -86,7 +86,7 @@
"request_details": "Pyynnön tiedot", "request_details": "Pyynnön tiedot",
"client_details": "Päätelaitteen tiedot", "client_details": "Päätelaitteen tiedot",
"details": "Yksityiskohdat", "details": "Yksityiskohdat",
"back": "Palaa takaisin", "back": "Takaisin",
"dashboard": "Tila", "dashboard": "Tila",
"settings": "Asetukset", "settings": "Asetukset",
"filters": "Suodattimet", "filters": "Suodattimet",
@@ -146,8 +146,8 @@
"no_servers_specified": "Palvelimia ei ole määritetty", "no_servers_specified": "Palvelimia ei ole määritetty",
"general_settings": "Yleiset asetukset", "general_settings": "Yleiset asetukset",
"dns_settings": "DNS-asetukset", "dns_settings": "DNS-asetukset",
"dns_blocklists": "DNS-estot", "dns_blocklists": "DNS-estolistat",
"dns_allowlists": "DNS-sallinnat", "dns_allowlists": "DNS-sallittujen listat",
"dns_blocklists_desc": "AdGuard Home estää estolistalla olevat verkkotunnukset.", "dns_blocklists_desc": "AdGuard Home estää estolistalla olevat verkkotunnukset.",
"dns_allowlists_desc": "DNS-sallittujen listalla olevat verkkotunnukset sallitaan myös silloin, jos ne ovat jollain muulla estolistalla.", "dns_allowlists_desc": "DNS-sallittujen listalla olevat verkkotunnukset sallitaan myös silloin, jos ne ovat jollain muulla estolistalla.",
"custom_filtering_rules": "Omat suodatussäännöt", "custom_filtering_rules": "Omat suodatussäännöt",
@@ -627,7 +627,7 @@
"cache_optimistic": "Optimistinen välimuisti", "cache_optimistic": "Optimistinen välimuisti",
"cache_optimistic_desc": "Pakota AdGuard Home vastaamaan välimuistista vaikka tiedot olisivat vanhentuneet. Pyri samalla myös päivittämään tiedot.", "cache_optimistic_desc": "Pakota AdGuard Home vastaamaan välimuistista vaikka tiedot olisivat vanhentuneet. Pyri samalla myös päivittämään tiedot.",
"filter_category_general": "Yleiset", "filter_category_general": "Yleiset",
"filter_category_security": "Tietoturva", "filter_category_security": "Turvallisuus",
"filter_category_regional": "Alueelliset", "filter_category_regional": "Alueelliset",
"filter_category_other": "Muut", "filter_category_other": "Muut",
"filter_category_general_desc": "Listat, jotka estävät seurannan ja mainokset useimmilla laitteilla", "filter_category_general_desc": "Listat, jotka estävät seurannan ja mainokset useimmilla laitteilla",

View File

@@ -282,8 +282,6 @@
"blocking_mode_null_ip": "Null IP: Svar med en 0-IP-adresse (0.0.0.0 for A; :: for AAAA)", "blocking_mode_null_ip": "Null IP: Svar med en 0-IP-adresse (0.0.0.0 for A; :: for AAAA)",
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt valgt IP-adresse", "blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt valgt IP-adresse",
"theme_auto": "Auto", "theme_auto": "Auto",
"theme_light": "Lyst tema",
"theme_dark": "Mørkt tema",
"upstream_dns_client_desc": "Hvis dette feltet holdes tomt, vil AdGuard Home bruke tjenerne som er satt opp i <0>DNS-innstillingene</0>.", "upstream_dns_client_desc": "Hvis dette feltet holdes tomt, vil AdGuard Home bruke tjenerne som er satt opp i <0>DNS-innstillingene</0>.",
"tracker_source": "Sporerkilde", "tracker_source": "Sporerkilde",
"source_label": "Kilde", "source_label": "Kilde",

View File

@@ -222,7 +222,7 @@
"all_lists_up_to_date_toast": "Wszystkie listy są już aktualne", "all_lists_up_to_date_toast": "Wszystkie listy są już aktualne",
"updated_upstream_dns_toast": "Serwery nadrzędne zostały pomyślnie zapisane", "updated_upstream_dns_toast": "Serwery nadrzędne zostały pomyślnie zapisane",
"dns_test_ok_toast": "Określone serwery DNS działają poprawnie", "dns_test_ok_toast": "Określone serwery DNS działają poprawnie",
"dns_test_not_ok_toast": "Serwer \"{{key}}\": nie może być użyte, sprawdź, czy zapisano go poprawnie", "dns_test_not_ok_toast": "Serwer \"{{key}}\": nie można go użyć, sprawdź, czy napisałeś go poprawnie",
"dns_test_warning_toast": "Upstream \"{{key}}\" nie odpowiada na zapytania testowe i może nie działać prawidłowo", "dns_test_warning_toast": "Upstream \"{{key}}\" nie odpowiada na zapytania testowe i może nie działać prawidłowo",
"unblock": "Odblokuj", "unblock": "Odblokuj",
"block": "Zablokuj", "block": "Zablokuj",
@@ -346,7 +346,7 @@
"install_devices_windows_list_2": "Przejdź do kategorii Sieć i Internet, a następnie do Centrum sieci i udostępniania.", "install_devices_windows_list_2": "Przejdź do kategorii Sieć i Internet, a następnie do Centrum sieci i udostępniania.",
"install_devices_windows_list_3": "W lewym panelu kliknij \"Zmień ustawienia adaptera\".", "install_devices_windows_list_3": "W lewym panelu kliknij \"Zmień ustawienia adaptera\".",
"install_devices_windows_list_4": "Kliknij prawym przyciskiem myszy aktywne połączenie i wybierz Właściwości.", "install_devices_windows_list_4": "Kliknij prawym przyciskiem myszy aktywne połączenie i wybierz Właściwości.",
"install_devices_windows_list_5": "Znajdź na liście \"Protokół internetowy w wersji 4 (TCP/IPv4)\" (lub w przypadku IPv6 \"Protokół internetowy w wersji 6 (TCP/IPv6)\"), zaznacz go i ponownie kliknij Właściwości.", "install_devices_windows_list_5": "Znajdź na liście \"Protokół internetowy w wersji 4 (TCP/IPv4)\" (lub w przypadku IPv6 \"Protokół internetowy w wersji 6 (TCP/IPv6)\"), zaznacz go i ponownie kliknij na Właściwości.",
"install_devices_windows_list_6": "Wybierz opcję \"Użyj następujących adresów serwerów DNS\" i wprowadź adresy serwerów AdGuard Home.", "install_devices_windows_list_6": "Wybierz opcję \"Użyj następujących adresów serwerów DNS\" i wprowadź adresy serwerów AdGuard Home.",
"install_devices_macos_list_1": "Kliknij ikonę Apple i przejdź do Preferencje systemowe.", "install_devices_macos_list_1": "Kliknij ikonę Apple i przejdź do Preferencje systemowe.",
"install_devices_macos_list_2": "Kliknij Sieć.", "install_devices_macos_list_2": "Kliknij Sieć.",
@@ -396,7 +396,7 @@
"encryption_issuer": "Zgłaszający", "encryption_issuer": "Zgłaszający",
"encryption_hostnames": "Nazwy hostów", "encryption_hostnames": "Nazwy hostów",
"encryption_reset": "Czy na pewno chcesz zresetować ustawienia szyfrowania?", "encryption_reset": "Czy na pewno chcesz zresetować ustawienia szyfrowania?",
"encryption_warning": "Uwaga", "encryption_warning": "Uwaga!",
"topline_expiring_certificate": "Twój certyfikat SSL wkrótce wygaśnie. Zaktualizuj <0>Ustawienia szyfrowania</0>.", "topline_expiring_certificate": "Twój certyfikat SSL wkrótce wygaśnie. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"topline_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania</0>.", "topline_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
"form_error_port_range": "Wpisz numer portu z zakresu 80-65535", "form_error_port_range": "Wpisz numer portu z zakresu 80-65535",
@@ -542,7 +542,7 @@
"password_placeholder": "Wpisz hasło", "password_placeholder": "Wpisz hasło",
"sign_in": "Zaloguj się", "sign_in": "Zaloguj się",
"sign_out": "Wyloguj się", "sign_out": "Wyloguj się",
"forgot_password": "Zapomniano hasła?", "forgot_password": "Zapomniałeś hasła?",
"forgot_password_desc": "Wykonaj <0>te kroki</0>, aby utworzyć nowe hasło do konta użytkownika.", "forgot_password_desc": "Wykonaj <0>te kroki</0>, aby utworzyć nowe hasło do konta użytkownika.",
"location": "Lokalizacja", "location": "Lokalizacja",
"orgname": "Nazwa firmy", "orgname": "Nazwa firmy",

View File

@@ -529,7 +529,7 @@
"ignore_domains": "Domínios ignorados (separados por nova linha)", "ignore_domains": "Domínios ignorados (separados por nova linha)",
"ignore_domains_title": "Domínios ignorados", "ignore_domains_title": "Domínios ignorados",
"ignore_domains_desc_stats": "As consultas para esses domínios não são gravadas nas estatísticas", "ignore_domains_desc_stats": "As consultas para esses domínios não são gravadas nas estatísticas",
"ignore_domains_desc_query": "As consultas para esses domínios não são gravadas no registro de consulta", "ignore_domains_desc_query": "As consultas para esses domínios não são gravadas no log de consulta",
"interval_hours": "{{count}} hora", "interval_hours": "{{count}} hora",
"interval_hours_plural": "{{count}} horas", "interval_hours_plural": "{{count}} horas",
"filters_configuration": "Configuração de filtros", "filters_configuration": "Configuração de filtros",

View File

@@ -529,7 +529,7 @@
"ignore_domains": "Domínios ignorados (separados por nova linha)", "ignore_domains": "Domínios ignorados (separados por nova linha)",
"ignore_domains_title": "Domínios ignorados", "ignore_domains_title": "Domínios ignorados",
"ignore_domains_desc_stats": "As consultas para estes domínios não aparecem nas estatísticas", "ignore_domains_desc_stats": "As consultas para estes domínios não aparecem nas estatísticas",
"ignore_domains_desc_query": "As consultas para estes domínios não aparecem no registo de consultas", "ignore_domains_desc_query": "As consultas para estes domínios nãoaparecem no registo de consultas",
"interval_hours": "{{count}} hora", "interval_hours": "{{count}} hora",
"interval_hours_plural": "{{count}} horas", "interval_hours_plural": "{{count}} horas",
"filters_configuration": "Definição dos filtros", "filters_configuration": "Definição dos filtros",

View File

@@ -167,7 +167,6 @@
"enabled_parental_toast": "«Батьківський контроль» увімкнено", "enabled_parental_toast": "«Батьківський контроль» увімкнено",
"disabled_safe_search_toast": "Безпечний пошук вимкнено", "disabled_safe_search_toast": "Безпечний пошук вимкнено",
"enabled_save_search_toast": "Безпечний пошук увімкнено", "enabled_save_search_toast": "Безпечний пошук увімкнено",
"updated_save_search_toast": "Налаштування Безпечного пошуку оновлено",
"enabled_table_header": "Увімкнено", "enabled_table_header": "Увімкнено",
"name_table_header": "Назва", "name_table_header": "Назва",
"list_url_table_header": "URL списку", "list_url_table_header": "URL списку",
@@ -291,8 +290,6 @@
"rate_limit": "Обмеження швидкості", "rate_limit": "Обмеження швидкості",
"edns_enable": "Увімкнути відправку EDNS Client Subnet", "edns_enable": "Увімкнути відправку EDNS Client Subnet",
"edns_cs_desc": "Додавати параметр EDNS Client Subnet (ECS) до запитів до upstream-серверів, а також записувати в журнал значення, що надсилаються клієнтами.", "edns_cs_desc": "Додавати параметр EDNS Client Subnet (ECS) до запитів до upstream-серверів, а також записувати в журнал значення, що надсилаються клієнтами.",
"edns_use_custom_ip": "Використання користувацької IP-адреси для EDNS",
"edns_use_custom_ip_desc": "Дозволити використовувати користувацьку IP-адресу для EDNS",
"rate_limit_desc": "Кількість запитів в секунду, які може робити один клієнт. Встановлене значення «0» означатиме необмежену кількість.", "rate_limit_desc": "Кількість запитів в секунду, які може робити один клієнт. Встановлене значення «0» означатиме необмежену кількість.",
"blocking_ipv4_desc": "IP-адреса, яку потрібно видати для заблокованого A запиту", "blocking_ipv4_desc": "IP-адреса, яку потрібно видати для заблокованого A запиту",
"blocking_ipv6_desc": "IP-адреса, яку потрібно видати для заблокованого АААА запиту", "blocking_ipv6_desc": "IP-адреса, яку потрібно видати для заблокованого АААА запиту",
@@ -526,10 +523,6 @@
"statistics_retention_confirm": "Ви впевнені, що хочете змінити тривалість статистики? Якщо зменшити значення інтервалу, деякі дані будуть втрачені", "statistics_retention_confirm": "Ви впевнені, що хочете змінити тривалість статистики? Якщо зменшити значення інтервалу, деякі дані будуть втрачені",
"statistics_cleared": "Статистику успішно очищено", "statistics_cleared": "Статистику успішно очищено",
"statistics_enable": "Увімкнути статистику", "statistics_enable": "Увімкнути статистику",
"ignore_domains": "Ігноровані домени (по одному на рядок)",
"ignore_domains_title": "Ігноровані домени",
"ignore_domains_desc_stats": "Запити для цих доменів в статистику не пишуться",
"ignore_domains_desc_query": "Запити для цих доменів не записуються до журналу запитів",
"interval_hours": "{{count}} година", "interval_hours": "{{count}} година",
"interval_hours_plural": "{{count}} годин(и)", "interval_hours_plural": "{{count}} годин(и)",
"filters_configuration": "Конфігурація фільтрів", "filters_configuration": "Конфігурація фільтрів",
@@ -650,29 +643,5 @@
"confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?", "confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?",
"cache_cleared": "Кеш DNS успішно очищено", "cache_cleared": "Кеш DNS успішно очищено",
"clear_cache": "Очистити кеш", "clear_cache": "Очистити кеш",
"make_static": "Зробити статичним", "protection_section_label": "Захист"
"theme_auto_desc": "Автоматична (на основі теми вашого пристрою)",
"theme_dark_desc": "Темна тема",
"theme_light_desc": "Світла тема",
"disable_for_seconds": "На {{count}} секунду",
"disable_for_seconds_plural": "На {{count}} секунд",
"disable_for_minutes": "На {{count}} хвилину",
"disable_for_minutes_plural": "На {{count}} хвилин",
"disable_for_hours": "На {{count}} годину",
"disable_for_hours_plural": "На {{count}} годин",
"disable_until_tomorrow": "До завтра",
"disable_notify_for_seconds": "Вимкнення захисту на {{count}} секунду",
"disable_notify_for_seconds_plural": "Вимкнення захисту на {{count}} секунд",
"disable_notify_for_minutes": "Вимкнення захисту на {{count}} хвилину",
"disable_notify_for_minutes_plural": "Вимкнення захисту на {{count}} хвилин",
"disable_notify_for_hours": "Вимкнення захисту на {{count}} годину",
"disable_notify_for_hours_plural": "Вимкнення захисту на {{count}} годин",
"disable_notify_until_tomorrow": "Відключення захисту до завтра",
"enable_protection_timer": "Захист буде ввімкнено о {{time}}",
"custom_retention_input": "Введіть час в годинах",
"custom_rotation_input": "Введіть час в годинах",
"protection_section_label": "Захист",
"log_and_stats_section_label": "Журнал запитів і статистика",
"ignore_query_log": "Ігнорувати цей клієнт у журналі запитів",
"ignore_statistics": "Ігноруйте цей клієнт в статистиці"
} }

View File

@@ -100,12 +100,6 @@ export default {
"homepage": "https://github.com/DandelionSprout/adfilt", "homepage": "https://github.com/DandelionSprout/adfilt",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_13.txt" "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_13.txt"
}, },
"POL_cert_polska_list_of_malicious_domains": {
"name": "POL: CERT Polska List of malicious domains",
"categoryId": "regional",
"homepage": "https://cert.pl/posts/2020/03/ostrzezenia_phishing/",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_41.txt"
},
"POL_polish_filters_for_pi_hole": { "POL_polish_filters_for_pi_hole": {
"name": "POL: Polish filters for Pi-hole", "name": "POL: Polish filters for Pi-hole",
"categoryId": "regional", "categoryId": "regional",
@@ -124,12 +118,6 @@ export default {
"homepage": "https://github.com/bkrucarci/turk-adlist", "homepage": "https://github.com/bkrucarci/turk-adlist",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_26.txt" "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_26.txt"
}, },
"TUR_turkish_ad_hosts": {
"name": "TUR: Turkish Ad Hosts",
"categoryId": "regional",
"homepage": "https://github.com/symbuzzer/Turkish-Ad-Hosts",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_40.txt"
},
"VNM_abpvn": { "VNM_abpvn": {
"name": "VNM: ABPVN List", "name": "VNM: ABPVN List",
"categoryId": "regional", "categoryId": "regional",
@@ -226,12 +214,6 @@ export default {
"homepage": "https://github.com/durablenapkin/scamblocklist", "homepage": "https://github.com/durablenapkin/scamblocklist",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_10.txt" "source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_10.txt"
}, },
"shadowwhisperers_malware_list": {
"name": "ShadowWhisperer's Malware List",
"categoryId": "security",
"homepage": "https://github.com/ShadowWhisperer/BlockLists",
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_42.txt"
},
"staklerware_indicators_list": { "staklerware_indicators_list": {
"name": "Stalkerware Indicators List", "name": "Stalkerware Indicators List",
"categoryId": "security", "categoryId": "security",

View File

@@ -1,5 +1,5 @@
{ {
"timeUpdated": "2023-06-01T00:12:12.660Z", "timeUpdated": "2023-04-06T10:46:09.881Z",
"categories": { "categories": {
"0": "audio_video_player", "0": "audio_video_player",
"1": "comments", "1": "comments",
@@ -19526,13 +19526,6 @@
"companyId": "qualcomm", "companyId": "qualcomm",
"source": "AdGuard" "source": "AdGuard"
}, },
"qualcomm_location_service": {
"name": "Qualcomm Location Service",
"categoryId": 15,
"url": "https://www.qualcomm.com/site/privacy/services",
"companyId": "qualcomm",
"source": "AdGuard"
},
"recaptcha": { "recaptcha": {
"name": "reCAPTCHA", "name": "reCAPTCHA",
"categoryId": 8, "categoryId": 8,
@@ -19540,55 +19533,6 @@
"companyId": "google", "companyId": "google",
"source": "AdGuard" "source": "AdGuard"
}, },
"samsung": {
"name": "Samsung",
"categoryId": 8,
"url": "https://www.samsung.com/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungads": {
"name": "Samsung Ads",
"categoryId": 4,
"url": "https://www.samsung.com/business/samsungads/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungapps": {
"name": "Samsung Apps",
"categoryId": 101,
"url": "https://www.samsung.com/au/apps/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungmobile": {
"name": "Samsung Mobile",
"categoryId": 101,
"url": "https://www.samsung.com/mobile/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungpush": {
"name": "Samsung Push",
"categoryId": 8,
"url": null,
"companyId": "samsung",
"source": "AdGuard"
},
"samsungsds": {
"name": "Samsung SDS",
"categoryId": 10,
"url": "https://www.samsungsds.com/",
"companyId": "samsung",
"source": "AdGuard"
},
"samsungtv": {
"name": "Samsung TV",
"categoryId": 15,
"url": "https://www.samsung.com/au/tvs/",
"companyId": "samsung",
"source": "AdGuard"
},
"sectigo": { "sectigo": {
"name": "Sectigo Limited", "name": "Sectigo Limited",
"categoryId": 5, "categoryId": 5,
@@ -19645,13 +19589,6 @@
"companyId": "telstra", "companyId": "telstra",
"source": "AdGuard" "source": "AdGuard"
}, },
"ubuntu": {
"name": "Ubuntu",
"categoryId": 8,
"url": "https://ubuntu.com/",
"companyId": "ubuntu",
"source": "AdGuard"
},
"unity_ads": { "unity_ads": {
"name": "Unity Ads", "name": "Unity Ads",
"categoryId": 4, "categoryId": 4,
@@ -19714,13 +19651,6 @@
"url": "https://www.3gpp.org/", "url": "https://www.3gpp.org/",
"companyId": "3gpp", "companyId": "3gpp",
"source": "AdGuard" "source": "AdGuard"
},
"7plus": {
"name": "7plus",
"categoryId": 0,
"url": "https://7plus.com.au/",
"companyId": "7plus",
"source": "AdGuard"
} }
}, },
"trackerDomains": { "trackerDomains": {
@@ -19913,8 +19843,8 @@
"adfreestyle.pl": "adfreestyle", "adfreestyle.pl": "adfreestyle",
"adfront.org": "adfront", "adfront.org": "adfront",
"adfrontiers.com": "adfrontiers", "adfrontiers.com": "adfrontiers",
"adgear.com": "samsungads", "adgear.com": "adgear",
"adgrx.com": "samsungads", "adgrx.com": "adgear",
"adgebra.co.in": "adgebra", "adgebra.co.in": "adgebra",
"adgenie.co.uk": "adgenie", "adgenie.co.uk": "adgenie",
"ad.adgile.com": "adgile", "ad.adgile.com": "adgile",
@@ -24126,10 +24056,6 @@
"safebrowsing.g.applimg.com": "apple", "safebrowsing.g.applimg.com": "apple",
"applvn.com": "applovin", "applvn.com": "applovin",
"applovin.com": "applovin", "applovin.com": "applovin",
"bitbucket.org": "atlassian.net",
"jira.com": "atlassian.net",
"ss-inf.net": "atlassian.net",
"stspg-customer.com": "statuspage.io",
"blob.core.windows.net": "azure_blob_storage", "blob.core.windows.net": "azure_blob_storage",
"azure.com": "azure", "azure.com": "azure",
"trafficmanager.net": "azure", "trafficmanager.net": "azure",
@@ -24137,21 +24063,6 @@
"mobileapptracking.com": "branch", "mobileapptracking.com": "branch",
"bttn.io": "button", "bttn.io": "button",
"cloudflare-dns.com": "cloudflare", "cloudflare-dns.com": "cloudflare",
"cloudflare-dm-cmpimg.com": "cloudflare",
"cloudflare-ipfs.com": "cloudflare",
"cloudflare-quic.com": "cloudflare",
"cloudflare-terms-of-service-abuse.com": "cloudflare",
"cloudflare.tv": "cloudflare",
"cloudflareaccess.com": "cloudflare",
"cloudflareclient.com": "cloudflare",
"cloudflareinsights.com": "cloudflare",
"cloudflareok.com": "cloudflare",
"cloudflareportal.com": "cloudflare",
"cloudflareresolve.com": "cloudflare",
"cloudflaressl.com": "cloudflare",
"cloudflarestatus.com": "cloudflare",
"pacloudflare.com": "cloudflare",
"sn-cloudflare.com": "cloudflare",
"crashlytics.com": "crashlytics", "crashlytics.com": "crashlytics",
"phicdn.net": "digicert_trust_seal", "phicdn.net": "digicert_trust_seal",
"domain.glass": "domainglass", "domain.glass": "domainglass",
@@ -24181,9 +24092,6 @@
"qy.net": "iqiyi", "qy.net": "iqiyi",
"iqiyi.com": "iqiyi", "iqiyi.com": "iqiyi",
"iq.com": "iqiyi", "iq.com": "iqiyi",
"ironsrc.com": "ironsource",
"ironsrc.net": "ironsource",
"supersonicads.com": "ironsource",
"karambasecurity.com": "karambasecurity", "karambasecurity.com": "karambasecurity",
"kik.com": "kik", "kik.com": "kik",
"apikik.com": "kik", "apikik.com": "kik",
@@ -24213,23 +24121,6 @@
"mozilla.com": "mozilla", "mozilla.com": "mozilla",
"mozilla.net": "mozilla", "mozilla.net": "mozilla",
"mozilla.org": "mozilla", "mozilla.org": "mozilla",
"flxvpn.net": "netflix",
"netflix.ca": "netflix",
"netflix.com.au": "netflix",
"netflix.net": "netflix",
"netflixdnstest1.com": "netflix",
"netflixdnstest10.com": "netflix",
"netflixdnstest2.com": "netflix",
"netflixdnstest3.com": "netflix",
"netflixdnstest4.com": "netflix",
"netflixdnstest5.com": "netflix",
"netflixdnstest6.com": "netflix",
"netflixdnstest7.com": "netflix",
"netflixdnstest8.com": "netflix",
"netflixdnstest9.com": "netflix",
"netflixinvestor.com": "netflix",
"netflixstudios.com": "netflix",
"netflixtechblog.com": "netflix",
"nflximg.com": "netflix", "nflximg.com": "netflix",
"netify.ai": "netify", "netify.ai": "netify",
"nab.com": "nab", "nab.com": "nab",
@@ -24253,69 +24144,9 @@
"oztam.com.au": "oztam", "oztam.com.au": "oztam",
"plex.tv": "plex", "plex.tv": "plex",
"plex.direct": "plex", "plex.direct": "plex",
"xtracloud.net": "qualcomm",
"qualcomm.com": "qualcomm", "qualcomm.com": "qualcomm",
"gpsonextra.net": "qualcomm_location_service",
"izatcloud.net": "qualcomm_location_service",
"xtracloud.net": "qualcomm_location_service",
"recaptcha.net": "recaptcha", "recaptcha.net": "recaptcha",
"samsungacr.com": "samsungads",
"samsungadhub.com": "samsungads",
"samsungads.com": "samsungads",
"samsungtifa.com": "samsungads",
"game-mode.net": "samsung",
"gos-gsp.io": "samsung",
"lldns.net": "samsung",
"pavv.co.kr": "samsung",
"remotesamsung.com": "samsung",
"samsung-gamelauncher.com": "samsung",
"samsung.co.kr": "samsung",
"samsung.com": "samsung",
"samsung.com.cn": "samsung",
"samsungcloud.com": "samsung",
"samsungcloudcdn.com": "samsung",
"samsungcloudprint.com": "samsung",
"samsungcloudsolution.com": "samsung",
"samsungcloudsolution.net": "samsung",
"samsungelectronics.com": "samsung",
"samsunghealth.com": "samsung",
"samsungiotcloud.com": "samsung",
"samsungknox.com": "samsung",
"samsungnyc.com": "samsung",
"samsungosp.com": "samsung",
"samsungotn.net": "samsung",
"samsungpositioning.com": "samsung",
"samsungqbe.com": "samsung",
"samsungrm.net": "samsung",
"samsungrs.com": "samsung",
"samsungsemi.com": "samsung",
"samsungsetup.com": "samsung",
"samsungusa.com": "samsung",
"secb2b.com": "samsung",
"smartthings.com": "samsung",
"ospserver.net": "samsungmobile",
"samsungdms.net": "samsungmobile",
"samsungmax.com": "samsungmobile",
"samsungmobile.com": "samsungmobile",
"secmobilesvc.com": "samsungmobile",
"internetat.tv": "samsungtv",
"samsungcloud.tv": "samsungtv",
"samsungsds.com": "samsungsds",
"push.samsungosp.com": "samsungpush",
"pushmessage.samsung.com": "samsungpush",
"scs.samsungqbe.com": "samsungpush",
"ssp.samsung.com": "samsungpush",
"aibixby.com": "samsungapps",
"findmymobile.samsung.com": "samsungapps",
"samsapps.cust.lldns.net": "samsungapps",
"samsung-omc.com": "samsungapps",
"samsungapps.com": "samsungapps",
"samsungdiroute.net": "samsungapps",
"samsungdive.com": "samsungapps",
"samsungdm.com": "samsungapps",
"samsungdmroute.com": "samsungapps",
"samsungmdec.com": "samsungapps",
"samsungvisioncloud.com": "samsungapps",
"sbixby.com": "samsungapps",
"sectigo.com": "sectigo", "sectigo.com": "sectigo",
"showrss.info": "showrss", "showrss.info": "showrss",
"similarweb.io": "similarweb", "similarweb.io": "similarweb",
@@ -24340,13 +24171,6 @@
"telstra.com.au": "telstra", "telstra.com.au": "telstra",
"telstra.com": "telstra", "telstra.com": "telstra",
"usertrust.com": "trustlogo", "usertrust.com": "trustlogo",
"canonical.com": "ubuntu",
"launchpad.net": "ubuntu",
"launchpadcontent.net": "ubuntu",
"snapcraft.io": "ubuntu",
"snapcraftcontent.com": "ubuntu",
"ubuntu.com": "ubuntu",
"ubuntucompanyservices.co.za": "ubuntu",
"unityads.unity3d.com": "unity_ads", "unityads.unity3d.com": "unity_ads",
"exp-tas.com": "vscode", "exp-tas.com": "vscode",
"vscode-unpkg.net": "vscode", "vscode-unpkg.net": "vscode",
@@ -24366,7 +24190,6 @@
"yandex.kz": "yandex", "yandex.kz": "yandex",
"appmetrica.yandex.com": "yandex_appmetrica", "appmetrica.yandex.com": "yandex_appmetrica",
"3gppnetwork.org": "3gpp", "3gppnetwork.org": "3gpp",
"3gpp.org": "3gpp", "3gpp.org": "3gpp"
"swm.digital": "7plus"
} }
} }

30
go.mod
View File

@@ -3,7 +3,7 @@ module github.com/AdguardTeam/AdGuardHome
go 1.19 go 1.19
require ( require (
github.com/AdguardTeam/dnsproxy v0.50.2 github.com/AdguardTeam/dnsproxy v0.49.1
github.com/AdguardTeam/golibs v0.13.2 github.com/AdguardTeam/golibs v0.13.2
github.com/AdguardTeam/urlfilter v0.16.1 github.com/AdguardTeam/urlfilter v0.16.1
github.com/NYTimes/gziphandler v1.1.1 github.com/NYTimes/gziphandler v1.1.1
@@ -16,24 +16,24 @@ require (
github.com/google/gopacket v1.1.19 github.com/google/gopacket v1.1.19
github.com/google/renameio v1.0.1 github.com/google/renameio v1.0.1
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
github.com/kardianos/service v1.2.2 github.com/kardianos/service v1.2.2
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
github.com/mdlayher/netlink v1.7.2 github.com/mdlayher/netlink v1.7.1
github.com/mdlayher/packet v1.1.2 github.com/mdlayher/packet v1.1.1
// TODO(a.garipov): This package is deprecated; find a new one or use our // TODO(a.garipov): This package is deprecated; find a new one or use our
// own code for that. Perhaps, use gopacket. // own code for that. Perhaps, use gopacket.
github.com/mdlayher/raw v0.1.0 github.com/mdlayher/raw v0.1.0
github.com/miekg/dns v1.1.54 github.com/miekg/dns v1.1.53
github.com/quic-go/quic-go v0.35.1 github.com/quic-go/quic-go v0.33.0
github.com/stretchr/testify v1.8.2 github.com/stretchr/testify v1.8.2
github.com/ti-mo/netfilter v0.5.0 github.com/ti-mo/netfilter v0.5.0
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
golang.org/x/crypto v0.9.0 golang.org/x/crypto v0.8.0
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 golang.org/x/exp v0.0.0-20230321023759-10a507213a29
golang.org/x/net v0.10.0 golang.org/x/net v0.9.0
golang.org/x/sys v0.8.0 golang.org/x/sys v0.7.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
howett.net/plist v1.0.0 howett.net/plist v1.0.0
@@ -48,9 +48,9 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect
github.com/mdlayher/socket v0.4.1 // indirect github.com/mdlayher/socket v0.4.0 // indirect
github.com/onsi/ginkgo/v2 v2.10.0 // indirect github.com/onsi/ginkgo/v2 v2.9.2 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pierrec/lz4/v4 v4.1.17 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
@@ -60,7 +60,7 @@ require (
github.com/quic-go/qtls-go1-20 v0.2.2 // indirect github.com/quic-go/qtls-go1-20 v0.2.2 // indirect
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
golang.org/x/mod v0.10.0 // indirect golang.org/x/mod v0.10.0 // indirect
golang.org/x/sync v0.2.0 // indirect golang.org/x/sync v0.1.0 // indirect
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
golang.org/x/tools v0.9.3 // indirect golang.org/x/tools v0.8.0 // indirect
) )

103
go.sum
View File

@@ -1,5 +1,5 @@
github.com/AdguardTeam/dnsproxy v0.50.2 h1:p1471SsMZ6SMo7T51Olw4aNluahvMwSLMorwxYV18ts= github.com/AdguardTeam/dnsproxy v0.49.1 h1:JpStBK05uCgA3ldleaNLRmIwE9V7vRg7/kVJQSdnQYg=
github.com/AdguardTeam/dnsproxy v0.50.2/go.mod h1:CQhZTkqC8X0ID6glrtyaxgqRRdiYfn1gJulC1cZ5Dn8= github.com/AdguardTeam/dnsproxy v0.49.1/go.mod h1:Y7g7jRTd/u7+KJ/QvnGI2PCE8vnisp6EsW47/Sz0DZw=
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4= github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw= github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
github.com/AdguardTeam/golibs v0.13.2 h1:BPASsyQKmb+b8VnvsNOHp7bKfcZl9Z+Z2UhPjOiupSc= github.com/AdguardTeam/golibs v0.13.2 h1:BPASsyQKmb+b8VnvsNOHp7bKfcZl9Z+Z2UhPjOiupSc=
@@ -31,9 +31,10 @@ github.com/digineo/go-ipset/v2 v2.2.1 h1:k6skY+0fMqeUjjeWO/m5OuWPSZUAn7AucHMnQ1M
github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU= github.com/digineo/go-ipset/v2 v2.2.1/go.mod h1:wBsNzJlZlABHUITkesrggFnZQtgW5wkqw1uo8Qxe0VU=
github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ= github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ=
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw= github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
github.com/fanliao/go-promise v0.0.0-20141029170127-1890db352a72/go.mod h1:PjfxuH4FZdUyfMdtBio2lsRr1AKEaVPwelzuHuh8Lqc=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw= github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
@@ -44,54 +45,72 @@ github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= github.com/google/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ=
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU= github.com/google/renameio v1.0.1 h1:Lh/jXZmvZxb0BBeSY5VKEfidcbcbenKjZFzM/q0fSeU=
github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk= github.com/google/renameio v1.0.1/go.mod h1:t/HQoYBZSsWSNK35C6CO/TpPLDVWvxOHboWUAweKUpk=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q= github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4= github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8 h1:Z72DOke2yOK0Ms4Z2LK1E1OrRJXOxSj5DllTz2FYTRg=
github.com/insomniacslk/dhcp v0.0.0-20221215072855-de60144f33f8/go.mod h1:m5WMe03WCvWcXjRnhvaAbAAXdCnu20J5P+mmH44ZzpE=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk= github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8= github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86/go.mod h1:aFAMtuldEgx/4q7iSGazk22+IcgvtiC+HIimFO9XlS8=
github.com/jsimonetti/rtnetlink v0.0.0-20190606172950-9527aa82566a/go.mod h1:Oz+70psSo5OFh8DBl0Zv2ACw7Esh6pPUphlvZG9x7uw=
github.com/jsimonetti/rtnetlink v0.0.0-20200117123717-f846d4f6c1f4/go.mod h1:WGuG/smIU4J/54PblvSbh+xvCZmpJnFgr3ds6Z55XMQ=
github.com/jsimonetti/rtnetlink v0.0.0-20201009170750-9c6f07d100c1/go.mod h1:hqoO/u39cqLeBLebZ8fWdE96O7FxrAsRYhnVOdgHxok=
github.com/jsimonetti/rtnetlink v0.0.0-20201110080708-d2c240429e6c/go.mod h1:huN4d1phzjhlOsNIjFsw2SVRbwIHj3fJDMEU2SDPTmg=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60= github.com/kardianos/service v1.2.2 h1:ZvePhAHfvo0A7Mftk/tEzqEZ7Q4lgnR8sGz4xu1YX60=
github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM= github.com/kardianos/service v1.2.2/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7/go.mod h1:U6ZQobyTjI/tJyq2HG+i/dfSoFUt8/aZCM+GKtmFk/Y=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE= github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118 h1:2oDp6OOhLxQ9JBoUuysVz9UZ9uI6oLUbvAZu0x8o+vE=
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og= github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA= github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/netlink v1.0.0/go.mod h1:KxeJAFOFLG6AjpyDkQ/iIhxygIUKD+vcwqcnu43w/+M=
github.com/mdlayher/netlink v1.1.0/go.mod h1:H4WCitaheIsdF9yOYu8CFmCgQthAPIWZmcKp9uZHgmY=
github.com/mdlayher/netlink v1.1.1/go.mod h1:WTYpFb/WTvlRJAyKhZL5/uy69TDDpHHu2VZmb2XgV7o=
github.com/mdlayher/netlink v1.7.1 h1:FdUaT/e33HjEXagwELR8R3/KL1Fq5x3G5jgHLp/BTmg=
github.com/mdlayher/netlink v1.7.1/go.mod h1:nKO5CSjE/DJjVhk/TNp6vCE1ktVxEA8VEh8drhZzxsQ=
github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU= github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
github.com/mdlayher/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY= github.com/mdlayher/packet v1.1.1 h1:7Fv4OEMYqPl7//uBm04VgPpnSNi8fbBZznppgh6WMr8=
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4= github.com/mdlayher/packet v1.1.1/go.mod h1:DRvYY5mH4M4lUqAnMg04E60U4fjUKMZ/4g2cHElZkKo=
github.com/mdlayher/raw v0.0.0-20190606142536-fef19f00fc18/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.0.0-20191009151244-50f2db8cc065/go.mod h1:7EpbotpCmVZcu+KCX4g9WaRNuu11uyhiW7+Le1dKawg=
github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y= github.com/mdlayher/raw v0.1.0 h1:K4PFMVy+AFsp0Zdlrts7yNhxc/uXoPVHi9RzRvtZF2Y=
github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg= github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5s9Sg=
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4= github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI= github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY= github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs= github.com/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE= github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
@@ -108,10 +127,12 @@ github.com/quic-go/qtls-go1-19 v0.3.2 h1:tFxjCFcTQzK+oMxG6Zcvp4Dq8dx4yD3dDiIiyc8
github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI= github.com/quic-go/qtls-go1-19 v0.3.2/go.mod h1:ySOI96ew8lnoKPtSqx2BlI5wCpUVPT05RMAlajtnyOI=
github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E= github.com/quic-go/qtls-go1-20 v0.2.2 h1:WLOPx6OY/hxtTxKV1Zrq20FtXtDEkeY00CGQm8GEa3E=
github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM= github.com/quic-go/qtls-go1-20 v0.2.2/go.mod h1:JKtK6mjbAVcUTN/9jZpvLbGxvdWIKS8uT7EiStoU1SM=
github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo= github.com/quic-go/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g= github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA= github.com/shirou/gopsutil/v3 v3.21.8 h1:nKct+uP0TV8DjjNiHanKf8SAuub+GNsbrOtM9Nl9biA=
github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ= github.com/shirou/gopsutil/v3 v3.21.8/go.mod h1:YWp/H8Qs5fVmf17v7JNZzA0mPJ+mS2e9JdiUF9LlKzQ=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
@@ -131,6 +152,7 @@ github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs= github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ= github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8= github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
github.com/u-root/uio v0.0.0-20221213070652-c3537552635f/go.mod h1:IogEAUBXDEwX7oR/BMmCctShYs80ql4hF0ySdzGxf7E=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg= github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264= github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
@@ -138,10 +160,11 @@ go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= 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/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -149,25 +172,40 @@ golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191007182048-72f939374954/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190411185658-b44545bcd369/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190418153312-f0ce4c0180be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606122018-79a91cf218c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -181,8 +219,8 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -192,11 +230,12 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -304,7 +304,7 @@ func tryConn6(req *dhcpv6.Message, c net.PacketConn) (ok, next bool, err error)
if !(response.Type() == dhcpv6.MessageTypeAdvertise && if !(response.Type() == dhcpv6.MessageTypeAdvertise &&
msg.TransactionID == req.TransactionID && msg.TransactionID == req.TransactionID &&
rcid != nil && rcid != nil &&
cid.Equal(rcid)) { cid.Equal(*rcid)) {
log.Debug("dhcpv6: received message from server doesn't match our request") log.Debug("dhcpv6: received message from server doesn't match our request")

View File

@@ -1,60 +1,46 @@
# Testing DHCP Server # DHCP server
Contents: Contents:
* [Test setup with Virtual Box](#vbox) * [Test setup with Virtual Box](#vbox)
* [Quick test with DHCPTest](#dhcptest)
## <a href="#vbox" id="vbox" name="vbox">Test setup with Virtual Box</a> <a id="vbox"></a>
## Test setup with Virtual Box
### Prerequisites To set up a test environment for DHCP server you need:
To set up a test environment for DHCP server you will need: * Linux host machine
* Virtual Box
* Virtual machine (guest OS doesn't matter)
* Linux AG Home host machine (Virtual). ### Configure client
* Virtual Box.
* Virtual machine (guest OS doesn't matter).
### Configure Virtual Box 1. Install Virtual Box and run the following command to create a Host-Only network:
1. Install Virtual Box and run the following command to create a Host-Only
network:
```sh
$ VBoxManage hostonlyif create $ VBoxManage hostonlyif create
```
You can check its status by `ip a` command. You can check its status by `ip a` command.
You can also set up Host-Only network using Virtual Box menu: You can also set up Host-Only network using Virtual Box menu:
```
File -> Host Network Manager... File -> Host Network Manager...
```
2. Create your virtual machine and set up its network: 2. Create your virtual machine and set up its network:
```
VM Settings -> Network -> Host-only Adapter VM Settings -> Network -> Host-only Adapter
```
3. Start your VM, install an OS. Configure your network interface to use 3. Start your VM, install an OS. Configure your network interface to use DHCP and the OS should ask for a IP address from our DHCP server.
DHCP and the OS should ask for a IP address from our DHCP server.
4. To see the current IP addresses on client OS you can use `ip a` command on 4. To see the current IP address on client OS you can use `ip a` command on Linux or `ipconfig` on Windows.
Linux or `ipconfig` on Windows.
5. To force the client OS to request an IP from DHCP server again, you can 5. To force the client OS to request an IP from DHCP server again, you can use `dhclient` on Linux or `ipconfig /release` on Windows.
use `dhclient` on Linux or `ipconfig /release` on Windows.
### Configure server ### Configure server
1. Edit server configuration file `AdGuardHome.yaml`, for example: 1. Edit server configuration file 'AdGuardHome.yaml', for example:
```yaml
dhcp: dhcp:
enabled: true enabled: true
interface_name: vboxnet0 interface_name: vboxnet0
local_domain_name: lan
dhcpv4: dhcpv4:
gateway_ip: 192.168.56.1 gateway_ip: 192.168.56.1
subnet_mask: 255.255.255.0 subnet_mask: 255.255.255.0
@@ -68,29 +54,11 @@ To set up a test environment for DHCP server you will need:
lease_duration: 86400 lease_duration: 86400
ra_slaac_only: false ra_slaac_only: false
ra_allow_slaac: false ra_allow_slaac: false
```
2. Start the server 2. Start the server
```sh ./AdGuardHome
./AdGuardHome -v
```
There should be a message in log which shows that DHCP server is ready: There should be a message in log which shows that DHCP server is ready:
```
[info] DHCP: listening on 0.0.0.0:67 [info] DHCP: listening on 0.0.0.0:67
```
## <a href="#dhcptest" id="dhcptest" name="dhcptest">Quick test with DHCPTest utility</a>
### Prerequisites
* [DHCP test utility][dhcptest-gh].
### Quick test
The DHCP server could be tested for DISCOVER-OFFER packets with in
interactive mode.
[dhcptest-gh]: https://github.com/CyberShadow/dhcptest

View File

@@ -1,4 +1,4 @@
//go:build darwin || freebsd || openbsd //go:build darwin
package dhcpd package dhcpd

View File

@@ -1,4 +1,4 @@
//go:build darwin || freebsd || openbsd //go:build darwin
package dhcpd package dhcpd

View File

@@ -1,4 +1,4 @@
//go:build linux //go:build freebsd || linux || openbsd
package dhcpd package dhcpd

View File

@@ -1,4 +1,4 @@
//go:build linux //go:build freebsd || linux || openbsd
package dhcpd package dhcpd

View File

@@ -239,16 +239,36 @@ func Create(conf *ServerConfig) (s *server, err error) {
// [aghhttp.RegisterFunc]. // [aghhttp.RegisterFunc].
s.registerHandlers() s.registerHandlers()
v4Enabled, v6Enabled, err := s.setServers(conf) v4conf := conf.Conf4
v4conf.InterfaceName = s.conf.InterfaceName
v4conf.notify = s.onNotify
v4conf.Enabled = s.conf.Enabled && v4conf.RangeStart.IsValid()
s.srv4, err = v4Create(&v4conf)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. if v4conf.Enabled {
return nil, err return nil, fmt.Errorf("creating dhcpv4 srv: %w", err)
}
log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err)
}
v6conf := conf.Conf6
v6conf.Enabled = s.conf.Enabled
if len(v6conf.RangeStart) == 0 {
v6conf.Enabled = false
}
v6conf.InterfaceName = s.conf.InterfaceName
v6conf.notify = s.onNotify
s.srv6, err = v6Create(v6conf)
if err != nil {
return nil, fmt.Errorf("creating dhcpv6 srv: %w", err)
} }
s.conf.Conf4 = conf.Conf4 s.conf.Conf4 = conf.Conf4
s.conf.Conf6 = conf.Conf6 s.conf.Conf6 = conf.Conf6
if s.conf.Enabled && !v4Enabled && !v6Enabled { if s.conf.Enabled && !v4conf.Enabled && !v6conf.Enabled {
return nil, fmt.Errorf("neither dhcpv4 nor dhcpv6 srv is configured") return nil, fmt.Errorf("neither dhcpv4 nor dhcpv6 srv is configured")
} }
@@ -269,39 +289,6 @@ func Create(conf *ServerConfig) (s *server, err error) {
return s, nil return s, nil
} }
// setServers updates DHCPv4 and DHCPv6 servers created from the provided
// configuration conf.
func (s *server) setServers(conf *ServerConfig) (v4Enabled, v6Enabled bool, err error) {
v4conf := conf.Conf4
v4conf.InterfaceName = s.conf.InterfaceName
v4conf.notify = s.onNotify
v4conf.Enabled = s.conf.Enabled && v4conf.RangeStart.IsValid()
s.srv4, err = v4Create(&v4conf)
if err != nil {
if v4conf.Enabled {
return true, false, fmt.Errorf("creating dhcpv4 srv: %w", err)
}
log.Debug("dhcpd: warning: creating dhcpv4 srv: %s", err)
}
v6conf := conf.Conf6
v6conf.InterfaceName = s.conf.InterfaceName
v6conf.notify = s.onNotify
v6conf.Enabled = s.conf.Enabled
if len(v6conf.RangeStart) == 0 {
v6conf.Enabled = false
}
s.srv6, err = v6Create(v6conf)
if err != nil {
return v4conf.Enabled, v6conf.Enabled, fmt.Errorf("creating dhcpv6 srv: %w", err)
}
return v4conf.Enabled, v6conf.Enabled, nil
}
// Enabled returns true when the server is enabled. // Enabled returns true when the server is enabled.
func (s *server) Enabled() (ok bool) { func (s *server) Enabled() (ok bool) {
return s.conf.Enabled return s.conf.Enabled

View File

@@ -16,7 +16,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghnet" "github.com/AdguardTeam/AdGuardHome/internal/aghnet"
"github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil"
) )
type v4ServerConfJSON struct { type v4ServerConfJSON struct {
@@ -264,28 +263,6 @@ func (s *server) handleDHCPSetConfigV6(
return srv6, enabled, err return srv6, enabled, err
} }
// createServers returns DHCPv4 and DHCPv6 servers created from the provided
// configuration conf.
func (s *server) createServers(conf *dhcpServerConfigJSON) (srv4, srv6 DHCPServer, err error) {
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv4 configuration: %s", err)
}
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
if err != nil {
return nil, nil, fmt.Errorf("bad dhcpv6 configuration: %s", err)
}
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
return nil, nil, fmt.Errorf("dhcpv4 or dhcpv6 configuration must be complete")
}
return srv4, srv6, nil
}
// handleDHCPSetConfig is the handler for the POST /control/dhcp/set_config
// HTTP API.
func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) { func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
conf := &dhcpServerConfigJSON{} conf := &dhcpServerConfigJSON{}
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled) conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
@@ -298,9 +275,22 @@ func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
return return
} }
srv4, srv6, err := s.createServers(conf) srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) aghhttp.Error(r, w, http.StatusBadRequest, "bad dhcpv4 configuration: %s", err)
return
}
srv6, v6Enabled, err := s.handleDHCPSetConfigV6(conf)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "bad dhcpv6 configuration: %s", err)
return
}
if conf.Enabled == aghalg.NBTrue && !v4Enabled && !v6Enabled {
aghhttp.Error(r, w, http.StatusBadRequest, "dhcpv4 or dhcpv6 configuration must be complete")
return return
} }
@@ -360,10 +350,10 @@ type netInterfaceJSON struct {
Addrs6 []netip.Addr `json:"ipv6_addresses"` Addrs6 []netip.Addr `json:"ipv6_addresses"`
} }
// handleDHCPInterfaces is the handler for the GET /control/dhcp/interfaces // handleDHCPInterfaces is the handler for the GET /control/dhcp/interfaces HTTP
// HTTP API. // API.
func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) { func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
resp := map[string]*netInterfaceJSON{} resp := map[string]netInterfaceJSON{}
ifaces, err := net.Interfaces() ifaces, err := net.Interfaces()
if err != nil { if err != nil {
@@ -374,86 +364,73 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
for _, iface := range ifaces { for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback != 0 { if iface.Flags&net.FlagLoopback != 0 {
// It's a loopback, skip it. // it's a loopback, skip it
continue continue
} }
if iface.Flags&net.FlagBroadcast == 0 { if iface.Flags&net.FlagBroadcast == 0 {
// This interface doesn't support broadcast, skip it. // this interface doesn't support broadcast, skip it
continue continue
} }
jsonIface, iErr := newNetInterfaceJSON(iface) var addrs []net.Addr
if iErr != nil { addrs, err = iface.Addrs()
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", iErr) if err != nil {
aghhttp.Error(
r,
w,
http.StatusInternalServerError,
"Failed to get addresses for interface %s: %s",
iface.Name,
err,
)
return return
} }
if jsonIface != nil { jsonIface := netInterfaceJSON{
resp[iface.Name] = jsonIface
}
}
_ = aghhttp.WriteJSONResponse(w, r, resp)
}
// newNetInterfaceJSON creates a JSON object from a [net.Interface] iface.
func newNetInterfaceJSON(iface net.Interface) (out *netInterfaceJSON, err error) {
addrs, err := iface.Addrs()
if err != nil {
return nil, fmt.Errorf(
"failed to get addresses for interface %s: %s",
iface.Name,
err,
)
}
out = &netInterfaceJSON{
Name: iface.Name, Name: iface.Name,
HardwareAddr: iface.HardwareAddr.String(), HardwareAddr: iface.HardwareAddr.String(),
} }
if iface.Flags != 0 { if iface.Flags != 0 {
out.Flags = iface.Flags.String() jsonIface.Flags = iface.Flags.String()
} }
// we don't want link-local addresses in json, so skip them
// We don't want link-local addresses in JSON, so skip them.
for _, addr := range addrs { for _, addr := range addrs {
ipNet, ok := addr.(*net.IPNet) ipnet, ok := addr.(*net.IPNet)
if !ok { if !ok {
// Not an IPNet, should not happen. // not an IPNet, should not happen
return nil, fmt.Errorf("got iface.Addrs() element %[1]s that is not"+ aghhttp.Error(
" net.IPNet, it is %[1]T", addr) r,
} w,
http.StatusInternalServerError,
"got iface.Addrs() element %[1]s that is not net.IPNet, it is %[1]T",
addr)
// Ignore link-local. return
}
// ignore link-local
// //
// TODO(e.burkov): Try to listen DHCP on LLA as well. // TODO(e.burkov): Try to listen DHCP on LLA as well.
if ipNet.IP.IsLinkLocalUnicast() { if ipnet.IP.IsLinkLocalUnicast() {
continue continue
} }
vAddr, iErr := netutil.IPToAddrNoMapped(ipNet.IP) if ip4 := ipnet.IP.To4(); ip4 != nil {
if iErr != nil { addr := netip.AddrFrom4(*(*[4]byte)(ip4))
// Not an IPNet, should not happen. jsonIface.Addrs4 = append(jsonIface.Addrs4, addr)
return nil, fmt.Errorf("failed to convert IP address %[1]s: %w", addr, iErr)
}
if vAddr.Is4() {
out.Addrs4 = append(out.Addrs4, vAddr)
} else { } else {
out.Addrs6 = append(out.Addrs6, vAddr) addr := netip.AddrFrom16(*(*[16]byte)(ipnet.IP))
jsonIface.Addrs6 = append(jsonIface.Addrs6, addr)
}
}
if len(jsonIface.Addrs4)+len(jsonIface.Addrs6) != 0 {
jsonIface.GatewayIP = aghnet.GatewayIP(iface.Name)
resp[iface.Name] = jsonIface
} }
} }
if len(out.Addrs4)+len(out.Addrs6) == 0 { _ = aghhttp.WriteJSONResponse(w, r, resp)
return nil, nil
}
out.GatewayIP = aghnet.GatewayIP(iface.Name)
return out, nil
} }
// dhcpSearchOtherResult contains information about other DHCP server for // dhcpSearchOtherResult contains information about other DHCP server for

View File

@@ -7,7 +7,6 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/netutil"
"golang.org/x/net/icmp" "golang.org/x/net/icmp"
@@ -196,7 +195,7 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
return data, nil return data, nil
} }
// Init initializes RA module. // Init - initialize RA module
func (ra *raCtx) Init() (err error) { func (ra *raCtx) Init() (err error) {
ra.stop.Store(0) ra.stop.Store(0)
ra.conn = nil ra.conn = nil
@@ -204,7 +203,8 @@ func (ra *raCtx) Init() (err error) {
return nil return nil
} }
log.Debug("dhcpv6 ra: source IP address: %s DNS IP address: %s", ra.ipAddr, ra.dnsIPAddr) log.Debug("dhcpv6 ra: source IP address: %s DNS IP address: %s",
ra.ipAddr, ra.dnsIPAddr)
params := icmpv6RA{ params := icmpv6RA{
managedAddressConfiguration: !ra.raSLAACOnly, managedAddressConfiguration: !ra.raSLAACOnly,
@@ -223,15 +223,18 @@ func (ra *raCtx) Init() (err error) {
return fmt.Errorf("creating packet: %w", err) return fmt.Errorf("creating packet: %w", err)
} }
success := false
ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName
ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope) ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope)
if err != nil { if err != nil {
return fmt.Errorf("dhcpv6 ra: icmp.ListenPacket: %w", err) return fmt.Errorf("dhcpv6 ra: icmp.ListenPacket: %w", err)
} }
defer func() { defer func() {
if err != nil { if !success {
err = errors.WithDeferred(err, ra.Close()) derr := ra.Close()
if derr != nil {
log.Error("closing context: %s", derr)
}
} }
}() }()
@@ -266,6 +269,7 @@ func (ra *raCtx) Init() (err error) {
log.Debug("dhcpv6 ra: loop exit") log.Debug("dhcpv6 ra: loop exit")
}() }()
success = true
return nil return nil
} }

View File

@@ -342,8 +342,8 @@ func (s *v4Server) rmLease(lease *Lease) (err error) {
// server to be configured and it's not. // server to be configured and it's not.
const ErrUnconfigured errors.Error = "server is unconfigured" const ErrUnconfigured errors.Error = "server is unconfigured"
// AddStaticLease implements the DHCPServer interface for *v4Server. It is // AddStaticLease implements the DHCPServer interface for *v4Server. It is safe
// safe for concurrent use. // for concurrent use.
func (s *v4Server) AddStaticLease(l *Lease) (err error) { func (s *v4Server) AddStaticLease(l *Lease) (err error) {
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }() defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
@@ -354,23 +354,21 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
l.IP = l.IP.Unmap() l.IP = l.IP.Unmap()
if !l.IP.Is4() { if !l.IP.Is4() {
return fmt.Errorf("invalid IP %q: only IPv4 is supported", l.IP) return fmt.Errorf("invalid ip %q, only ipv4 is supported", l.IP)
} else if gwIP := s.conf.GatewayIP; gwIP == l.IP { } else if gwIP := s.conf.GatewayIP; gwIP == l.IP {
return fmt.Errorf("can't assign the gateway IP %q to the lease", gwIP) return fmt.Errorf("can't assign the gateway IP %s to the lease", gwIP)
} }
l.IsStatic = true l.IsStatic = true
err = netutil.ValidateMAC(l.HWAddr) err = netutil.ValidateMAC(l.HWAddr)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err return err
} }
if hostname := l.Hostname; hostname != "" { if hostname := l.Hostname; hostname != "" {
hostname, err = normalizeHostname(hostname) hostname, err = normalizeHostname(hostname)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err return err
} }
@@ -388,33 +386,37 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
l.Hostname = hostname l.Hostname = hostname
} }
err = s.updateStaticLease(l) // Perform the following actions in an anonymous function to make sure
if err != nil { // that the lock gets unlocked before the notification step.
// Don't wrap the error, because it's informative enough as is. func() {
return err
}
s.conf.notify(LeaseChangedDBStore)
s.conf.notify(LeaseChangedAddedStatic)
return nil
}
// updateStaticLease safe removes dynamic lease with the same properties and
// then adds a static lease l.
func (s *v4Server) updateStaticLease(l *Lease) (err error) {
s.leasesLock.Lock() s.leasesLock.Lock()
defer s.leasesLock.Unlock() defer s.leasesLock.Unlock()
err = s.rmDynamicLease(l) err = s.rmDynamicLease(l)
if err != nil { if err != nil {
return fmt.Errorf("removing dynamic leases for %s (%s): %w", l.IP, l.HWAddr, err) err = fmt.Errorf(
"removing dynamic leases for %s (%s): %w",
l.IP,
l.HWAddr,
err,
)
return
} }
err = s.addLease(l) err = s.addLease(l)
if err != nil { if err != nil {
return fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err) err = fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
return
} }
}()
if err != nil {
return err
}
s.conf.notify(LeaseChangedDBStore)
s.conf.notify(LeaseChangedAddedStatic)
return nil return nil
} }
@@ -892,9 +894,24 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
reqIP = req.ClientIPAddr reqIP = req.ClientIPAddr
} }
oldLease := s.findLeaseForIP(reqIP, mac) netIP, ok := netip.AddrFromSlice(reqIP)
if !ok {
log.Info("dhcpv4: invalid IP: %s", reqIP)
return nil
}
var oldLease *Lease
for _, l := range s.leases {
if bytes.Equal(l.HWAddr, mac) && l.IP == netIP {
oldLease = l
break
}
}
if oldLease == nil { if oldLease == nil {
log.Info("dhcpv4: lease with IP %s for %s not found", reqIP, mac) log.Info("dhcpv4: lease with ip %s for %s not found", reqIP, mac)
return nil return nil
} }
@@ -908,7 +925,7 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
if err != nil { if err != nil {
return fmt.Errorf("allocating new lease for %s: %w", mac, err) return fmt.Errorf("allocating new lease for %s: %w", mac, err)
} else if newLease == nil { } else if newLease == nil {
log.Info("dhcpv4: allocating new lease for %s: no more IP addresses", mac) log.Info("dhcpv4: allocating new lease for %s: no more ip addresses", mac)
resp.YourIPAddr = make([]byte, 4) resp.YourIPAddr = make([]byte, 4)
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck)) resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
@@ -924,32 +941,15 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
return fmt.Errorf("adding new lease for %s: %w", mac, err) return fmt.Errorf("adding new lease for %s: %w", mac, err)
} }
log.Info("dhcpv4: changed IP from %s to %s for %s", reqIP, newLease.IP, mac) log.Info("dhcpv4: changed ip from %s to %s for %s", reqIP, newLease.IP, mac)
resp.YourIPAddr = net.IP(newLease.IP.AsSlice())
resp.YourIPAddr = newLease.IP.AsSlice()
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck)) resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
return nil return nil
} }
// findLeaseForIP returns a lease for provided ip and mac.
func (s *v4Server) findLeaseForIP(ip net.IP, mac net.HardwareAddr) (l *Lease) {
netIP, ok := netip.AddrFromSlice(ip)
if !ok {
log.Info("dhcpv4: invalid IP: %s", ip)
return nil
}
for _, il := range s.leases {
if bytes.Equal(il.HWAddr, mac) && il.IP == netIP {
return il
}
}
return nil
}
// handleRelease is the handler for the DHCP Release request. // handleRelease is the handler for the DHCP Release request.
func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) { func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
mac := req.ClientHWAddr mac := req.ClientHWAddr
@@ -995,80 +995,11 @@ func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
return nil return nil
} }
// messageHandler describes a DHCPv4 message handler function. // Find a lease associated with MAC and prepare response
type messageHandler func(s *v4Server, req, resp *dhcpv4.DHCPv4) (rCode int, l *Lease, err error) // Return 1: OK
// Return 0: error; reply with Nak
// messageHandlers is a map of handlers for various messages with message types // Return -1: error; don't reply
// keys. func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
var messageHandlers = map[dhcpv4.MessageType]messageHandler{
dhcpv4.MessageTypeDiscover: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
l, err = s.handleDiscover(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling discover: %s", err)
}
if l == nil {
return 0, nil, nil
}
return 1, l, nil
},
dhcpv4.MessageTypeRequest: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
var toReply bool
l, toReply = s.handleRequest(req, resp)
if l == nil {
if toReply {
return 0, nil, nil
}
// Drop the packet.
return -1, nil, nil
}
return 1, l, nil
},
dhcpv4.MessageTypeDecline: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
err = s.handleDecline(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling decline: %s", err)
}
return 1, nil, nil
},
dhcpv4.MessageTypeRelease: func(
s *v4Server,
req *dhcpv4.DHCPv4,
resp *dhcpv4.DHCPv4,
) (rCode int, l *Lease, err error) {
err = s.handleRelease(req, resp)
if err != nil {
return 0, nil, fmt.Errorf("handling release: %s", err)
}
return 1, nil, nil
},
}
// handle processes request, it finds a lease associated with MAC address and
// prepares response.
//
// Possible return values are:
// - "1": OK,
// - "0": error, reply with Nak,
// - "-1": error, don't reply.
func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) (rCode int) {
var err error var err error
// Include server's identifier option since any reply should contain it. // Include server's identifier option since any reply should contain it.
@@ -1076,26 +1007,47 @@ func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) (rCode int) {
// See https://datatracker.ietf.org/doc/html/rfc2131#page-29. // See https://datatracker.ietf.org/doc/html/rfc2131#page-29.
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0].AsSlice())) resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0].AsSlice()))
handler := messageHandlers[req.MessageType()] // TODO(a.garipov): Refactor this into handlers.
if handler == nil { var l *Lease
s.updateOptions(req, resp) switch mt := req.MessageType(); mt {
case dhcpv4.MessageTypeDiscover:
return 1 l, err = s.handleDiscover(req, resp)
}
rCode, l, err := handler(s, req, resp)
if err != nil { if err != nil {
log.Error("dhcpv4: %s", err) log.Error("dhcpv4: handling discover: %s", err)
return 0 return 0
} }
if rCode != 1 { if l == nil {
return rCode return 0
}
case dhcpv4.MessageTypeRequest:
var toReply bool
l, toReply = s.handleRequest(req, resp)
if l == nil {
if toReply {
return 0
}
return -1 // drop packet
}
case dhcpv4.MessageTypeDecline:
err = s.handleDecline(req, resp)
if err != nil {
log.Error("dhcpv4: handling decline: %s", err)
return 0
}
case dhcpv4.MessageTypeRelease:
err = s.handleRelease(req, resp)
if err != nil {
log.Error("dhcpv4: handling release: %s", err)
return 0
}
} }
if l != nil { if l != nil {
resp.YourIPAddr = l.IP.AsSlice() resp.YourIPAddr = net.IP(l.IP.AsSlice())
} }
s.updateOptions(req, resp) s.updateOptions(req, resp)
@@ -1210,8 +1162,23 @@ func (s *v4Server) Start() (err error) {
// No available IP addresses which may appear later. // No available IP addresses which may appear later.
return nil return nil
} }
// Update the value of Domain Name Server option separately from others if
// not assigned yet since its value is available only at server's start.
//
// TODO(e.burkov): Initialize as implicit option with the rest of default
// options when it will be possible to do before the call to Start.
if !s.explicitOpts.Has(dhcpv4.OptionDomainNameServer) {
s.implicitOpts.Update(dhcpv4.OptDNS(dnsIPAddrs...))
}
s.configureDNSIPAddrs(dnsIPAddrs) for _, ip := range dnsIPAddrs {
ip = ip.To4()
if ip == nil {
continue
}
s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, netip.AddrFrom4(*(*[4]byte)(ip)))
}
var c net.PacketConn var c net.PacketConn
if c, err = s.newDHCPConn(iface); err != nil { if c, err = s.newDHCPConn(iface); err != nil {
@@ -1232,10 +1199,10 @@ func (s *v4Server) Start() (err error) {
log.Info("dhcpv4: listening") log.Info("dhcpv4: listening")
go func() { go func() {
if sErr := s.srv.Serve(); errors.Is(sErr, net.ErrClosed) { if serr := s.srv.Serve(); errors.Is(serr, net.ErrClosed) {
log.Info("dhcpv4: server is closed") log.Info("dhcpv4: server is closed")
} else if sErr != nil { } else if serr != nil {
log.Error("dhcpv4: srv.Serve: %s", sErr) log.Error("dhcpv4: srv.Serve: %s", serr)
} }
}() }()
@@ -1246,28 +1213,6 @@ func (s *v4Server) Start() (err error) {
return nil return nil
} }
// configureDNSIPAddrs updates v4Server configuration with provided slice of
// dns IP addresses.
func (s *v4Server) configureDNSIPAddrs(dnsIPAddrs []net.IP) {
// Update the value of Domain Name Server option separately from others if
// not assigned yet since its value is available only at server's start.
//
// TODO(e.burkov): Initialize as implicit option with the rest of default
// options when it will be possible to do before the call to Start.
if !s.explicitOpts.Has(dhcpv4.OptionDomainNameServer) {
s.implicitOpts.Update(dhcpv4.OptDNS(dnsIPAddrs...))
}
for _, ip := range dnsIPAddrs {
vAddr, err := netutil.IPToAddr(ip, netutil.AddrFamilyIPv4)
if err != nil {
continue
}
s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, vAddr)
}
}
// Stop - stop server // Stop - stop server
func (s *v4Server) Stop() (err error) { func (s *v4Server) Stop() (err error) {
if s.srv == nil { if s.srv == nil {

View File

@@ -227,7 +227,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
}, },
name: "with_gateway_ip", name: "with_gateway_ip",
wantErrMsg: "dhcpv4: adding static lease: " + wantErrMsg: "dhcpv4: adding static lease: " +
`can't assign the gateway IP "192.168.10.1" to the lease`, "can't assign the gateway IP 192.168.10.1 to the lease",
}, { }, {
lease: &Lease{ lease: &Lease{
Hostname: "ip6.local", Hostname: "ip6.local",
@@ -236,7 +236,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
}, },
name: "ipv6", name: "ipv6",
wantErrMsg: `dhcpv4: adding static lease: ` + wantErrMsg: `dhcpv4: adding static lease: ` +
`invalid IP "ffff::1": only IPv4 is supported`, `invalid ip "ffff::1", only ipv4 is supported`,
}, { }, {
lease: &Lease{ lease: &Lease{
Hostname: "bad-mac.local", Hostname: "bad-mac.local",

View File

@@ -30,7 +30,7 @@ type v6Server struct {
leasesLock sync.Mutex leasesLock sync.Mutex
leases []*Lease leases []*Lease
ipAddrs [256]byte ipAddrs [256]byte
sid dhcpv6.DUID sid dhcpv6.Duid
ra raCtx // RA module ra raCtx // RA module
@@ -586,31 +586,9 @@ func (s *v6Server) packetHandler(conn net.PacketConn, peer net.Addr, req dhcpv6.
} }
} }
// configureDNSIPAddrs updates v6Server configuration with the slice of DNS IP // initialize RA module
// addresses of provided interface iface. Initializes RA module. func (s *v6Server) initRA(iface *net.Interface) error {
func (s *v6Server) configureDNSIPAddrs(iface *net.Interface) (ok bool, err error) { // choose the source IP address - should be link-local-unicast
dnsIPAddrs, err := aghnet.IfaceDNSIPAddrs(
iface,
aghnet.IPVersion6,
defaultMaxAttempts,
defaultBackoff,
)
if err != nil {
return false, fmt.Errorf("interface %s: %w", iface.Name, err)
}
if len(dnsIPAddrs) == 0 {
return false, nil
}
s.conf.dnsIPAddrs = dnsIPAddrs
return true, s.initRA(iface)
}
// initRA initializes RA module.
func (s *v6Server) initRA(iface *net.Interface) (err error) {
// Choose the source IP address - should be link-local-unicast.
s.ra.ipAddr = s.conf.dnsIPAddrs[0] s.ra.ipAddr = s.conf.dnsIPAddrs[0]
for _, ip := range s.conf.dnsIPAddrs { for _, ip := range s.conf.dnsIPAddrs {
if ip.IsLinkLocalUnicast() { if ip.IsLinkLocalUnicast() {
@@ -626,7 +604,6 @@ func (s *v6Server) initRA(iface *net.Interface) (err error) {
s.ra.ifaceName = s.conf.InterfaceName s.ra.ifaceName = s.conf.InterfaceName
s.ra.iface = iface s.ra.iface = iface
s.ra.packetSendPeriod = 1 * time.Second s.ra.packetSendPeriod = 1 * time.Second
return s.ra.Init() return s.ra.Init()
} }
@@ -646,47 +623,63 @@ func (s *v6Server) Start() (err error) {
log.Debug("dhcpv6: starting...") log.Debug("dhcpv6: starting...")
ok, err := s.configureDNSIPAddrs(iface) dnsIPAddrs, err := aghnet.IfaceDNSIPAddrs(
iface,
aghnet.IPVersion6,
defaultMaxAttempts,
defaultBackoff,
)
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. return fmt.Errorf("interface %s: %w", ifaceName, err)
return err
} }
if !ok { if len(dnsIPAddrs) == 0 {
// No available IP addresses which may appear later. // No available IP addresses which may appear later.
return nil return nil
} }
// Don't initialize DHCPv6 server if we must force the clients to use SLAAC. s.conf.dnsIPAddrs = dnsIPAddrs
err = s.initRA(iface)
if err != nil {
return err
}
// don't initialize DHCPv6 server if we must force the clients to use SLAAC
if s.conf.RASLAACOnly { if s.conf.RASLAACOnly {
log.Debug("not starting dhcpv6 server due to ra_slaac_only=true") log.Debug("not starting dhcpv6 server due to ra_slaac_only=true")
return nil return nil
} }
log.Debug("dhcpv6: listening...")
err = netutil.ValidateMAC(iface.HardwareAddr) err = netutil.ValidateMAC(iface.HardwareAddr)
if err != nil { if err != nil {
return fmt.Errorf("validating interface %s: %w", iface.Name, err) return fmt.Errorf("validating interface %s: %w", iface.Name, err)
} }
s.sid = &dhcpv6.DUIDLLT{ s.sid = dhcpv6.Duid{
HWType: iana.HWTypeEthernet, Type: dhcpv6.DUID_LLT,
HwType: iana.HWTypeEthernet,
LinkLayerAddr: iface.HardwareAddr, LinkLayerAddr: iface.HardwareAddr,
Time: dhcpv6.GetTime(), Time: dhcpv6.GetTime(),
} }
s.srv, err = server6.NewServer(iface.Name, nil, s.packetHandler, server6.WithDebugLogger()) laddr := &net.UDPAddr{
IP: net.ParseIP("::"),
Port: dhcpv6.DefaultServerPort,
}
s.srv, err = server6.NewServer(iface.Name, laddr, s.packetHandler, server6.WithDebugLogger())
if err != nil { if err != nil {
return err return err
} }
log.Debug("dhcpv6: listening...")
go func() { go func() {
if sErr := s.srv.Serve(); errors.Is(sErr, net.ErrClosed) { if serr := s.srv.Serve(); errors.Is(serr, net.ErrClosed) {
log.Info("dhcpv6: server is closed") log.Info("dhcpv6: server is closed")
} else if sErr != nil { } else if serr != nil {
log.Error("dhcpv6: srv.Serve: %s", sErr) log.Error("dhcpv6: srv.Serve: %s", serr)
} }
}() }()

View File

@@ -121,8 +121,9 @@ func TestV6GetLease(t *testing.T) {
dnsAddr := net.ParseIP("2000::1") dnsAddr := net.ParseIP("2000::1")
s.conf.dnsIPAddrs = []net.IP{dnsAddr} s.conf.dnsIPAddrs = []net.IP{dnsAddr}
s.sid = &dhcpv6.DUIDLL{ s.sid = dhcpv6.Duid{
HWType: iana.HWTypeEthernet, Type: dhcpv6.DUID_LLT,
HwType: iana.HWTypeEthernet,
LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}, LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
} }
@@ -215,8 +216,9 @@ func TestV6GetDynamicLease(t *testing.T) {
dnsAddr := net.ParseIP("2000::1") dnsAddr := net.ParseIP("2000::1")
s.conf.dnsIPAddrs = []net.IP{dnsAddr} s.conf.dnsIPAddrs = []net.IP{dnsAddr}
s.sid = &dhcpv6.DUIDLL{ s.sid = dhcpv6.Duid{
HWType: iana.HWTypeEthernet, Type: dhcpv6.DUID_LLT,
HwType: iana.HWTypeEthernet,
LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}, LinkLayerAddr: net.HardwareAddr{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
} }

View File

@@ -487,8 +487,7 @@ func TestServer_handleTestUpstreaDNS(t *testing.T) {
}, },
wantResp: map[string]any{ wantResp: map[string]any{
badUps: `upstream "` + badUps + `" fails to exchange: ` + badUps: `upstream "` + badUps + `" fails to exchange: ` +
`couldn't communicate with upstream: exchanging with ` + `couldn't communicate with upstream: dns: id mismatch`,
badUps + ` over tcp: dns: id mismatch`,
}, },
name: "broken", name: "broken",
}, { }, {
@@ -498,8 +497,7 @@ func TestServer_handleTestUpstreaDNS(t *testing.T) {
wantResp: map[string]any{ wantResp: map[string]any{
goodUps: "OK", goodUps: "OK",
badUps: `upstream "` + badUps + `" fails to exchange: ` + badUps: `upstream "` + badUps + `" fails to exchange: ` +
`couldn't communicate with upstream: exchanging with ` + `couldn't communicate with upstream: dns: id mismatch`,
badUps + ` over tcp: dns: id mismatch`,
}, },
name: "both", name: "both",
}} }}

View File

@@ -3,6 +3,7 @@ package filtering
import ( import (
"github.com/AdguardTeam/urlfilter/rules" "github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns" "github.com/miekg/dns"
"golang.org/x/exp/slices"
) )
// DNSRewriteResult is the result of application of $dnsrewrite rules. // DNSRewriteResult is the result of application of $dnsrewrite rules.
@@ -24,7 +25,13 @@ func (d *DNSFilter) processDNSRewrites(dnsr []*rules.NetworkRule) (res Result) {
Response: DNSRewriteResultResponse{}, Response: DNSRewriteResultResponse{},
} }
for _, nr := range dnsr { slices.SortFunc(dnsr, rewriteSortsBefore)
for i, nr := range dnsr {
if i > 0 && containsWildcard(nr) {
break
}
dr := nr.DNSRewrite dr := nr.DNSRewrite
if dr.NewCNAME != "" { if dr.NewCNAME != "" {
// NewCNAME rules have a higher priority than other rules. // NewCNAME rules have a higher priority than other rules.
@@ -73,3 +80,19 @@ func (d *DNSFilter) processDNSRewrites(dnsr []*rules.NetworkRule) (res Result) {
Reason: RewrittenRule, Reason: RewrittenRule,
} }
} }
func rewriteSortsBefore(a, b *rules.NetworkRule) (sortsBefore bool) {
return len(a.Shortcut) > len(b.Shortcut)
}
func containsWildcard(r *rules.NetworkRule) (ok bool) {
for _, c := range r.RuleText {
if c == '*' {
return true
} else if c == '^' {
break
}
}
return false
}

View File

@@ -5,6 +5,7 @@ import (
"path" "path"
"testing" "testing"
"github.com/AdguardTeam/urlfilter"
"github.com/miekg/dns" "github.com/miekg/dns"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@@ -202,3 +203,32 @@ func TestDNSFilter_CheckHostRules_dnsrewrite(t *testing.T) {
assert.Equal(t, "new-ptr-with-dot.", ptr) assert.Equal(t, "new-ptr-with-dot.", ptr)
}) })
} }
func TestDNSFilter_ProcessDNSRewrites(t *testing.T) {
const text = `
|www.example.com^$dnsrewrite=127.0.0.1
|*.example.com^$dnsrewrite=127.0.0.2
`
host := "www.example.com"
rrtype := dns.TypeA
f, _ := newForTest(t, nil, []Filter{{ID: 0, Data: []byte(text)}})
setts := &Settings{
FilteringEnabled: true,
}
ufReq := &urlfilter.DNSRequest{
Hostname: host,
SortedClientTags: setts.ClientTags,
ClientIP: setts.ClientIP.String(),
ClientName: setts.ClientName,
DNSType: rrtype,
}
dres, matched := f.filteringEngine.MatchRequest(ufReq)
require.False(t, matched)
res := f.processDNSResultRewrites(dres, host)
assert.Len(t, res.Rules, 1)
}

View File

@@ -555,7 +555,6 @@ func (d *DNSFilter) RegisterFilteringHandlers() {
registerHTTP(http.MethodGet, "/control/rewrite/list", d.handleRewriteList) registerHTTP(http.MethodGet, "/control/rewrite/list", d.handleRewriteList)
registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd) registerHTTP(http.MethodPost, "/control/rewrite/add", d.handleRewriteAdd)
registerHTTP(http.MethodPut, "/control/rewrite/update", d.handleRewriteUpdate)
registerHTTP(http.MethodPost, "/control/rewrite/delete", d.handleRewriteDelete) registerHTTP(http.MethodPost, "/control/rewrite/delete", d.handleRewriteDelete)
registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs) registerHTTP(http.MethodGet, "/control/blocked_services/services", d.handleBlockedServicesIDs)

View File

@@ -6,7 +6,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/slices"
) )
// TODO(d.kolyshev): Use [rewrite.Item] instead. // TODO(d.kolyshev): Use [rewrite.Item] instead.
@@ -92,62 +91,3 @@ func (d *DNSFilter) handleRewriteDelete(w http.ResponseWriter, r *http.Request)
d.Config.ConfigModified() d.Config.ConfigModified()
} }
// rewriteUpdateJSON is a struct for JSON object with rewrite rule update info.
type rewriteUpdateJSON struct {
Target rewriteEntryJSON `json:"target"`
Update rewriteEntryJSON `json:"update"`
}
// handleRewriteUpdate is the handler for the PUT /control/rewrite/update HTTP
// API.
func (d *DNSFilter) handleRewriteUpdate(w http.ResponseWriter, r *http.Request) {
updateJSON := rewriteUpdateJSON{}
err := json.NewDecoder(r.Body).Decode(&updateJSON)
if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "json.Decode: %s", err)
return
}
rwDel := &LegacyRewrite{
Domain: updateJSON.Target.Domain,
Answer: updateJSON.Target.Answer,
}
rwAdd := &LegacyRewrite{
Domain: updateJSON.Update.Domain,
Answer: updateJSON.Update.Answer,
}
err = rwAdd.normalize()
if err != nil {
// Shouldn't happen currently, since normalize only returns a non-nil
// error when a rewrite is nil, but be change-proof.
aghhttp.Error(r, w, http.StatusBadRequest, "normalizing: %s", err)
return
}
index := -1
defer func() {
if index >= 0 {
d.Config.ConfigModified()
}
}()
d.confLock.Lock()
defer d.confLock.Unlock()
index = slices.IndexFunc(d.Config.Rewrites, rwDel.equal)
if index == -1 {
aghhttp.Error(r, w, http.StatusBadRequest, "target rule not found")
return
}
d.Config.Rewrites = slices.Replace(d.Config.Rewrites, index, index+1, rwAdd)
log.Debug("rewrite: removed element: %s -> %s", rwDel.Domain, rwDel.Answer)
log.Debug("rewrite: added element: %s -> %s", rwAdd.Domain, rwAdd.Answer)
}

View File

@@ -1,237 +0,0 @@
package filtering_test
import (
"bytes"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TODO(d.kolyshev): Use [rewrite.Item] instead.
type rewriteJSON struct {
Domain string `json:"domain"`
Answer string `json:"answer"`
}
type rewriteUpdateJSON struct {
Target rewriteJSON `json:"target"`
Update rewriteJSON `json:"update"`
}
const (
// testTimeout is the common timeout for tests.
testTimeout = 100 * time.Millisecond
listURL = "/control/rewrite/list"
addURL = "/control/rewrite/add"
deleteURL = "/control/rewrite/delete"
updateURL = "/control/rewrite/update"
decodeErrorMsg = "json.Decode: json: cannot unmarshal string into Go value of type" +
" filtering.rewriteEntryJSON\n"
)
func TestDNSFilter_handleRewriteHTTP(t *testing.T) {
confModCh := make(chan struct{})
reqCh := make(chan struct{})
testRewrites := []*rewriteJSON{
{Domain: "example.local", Answer: "example.rewrite"},
{Domain: "one.local", Answer: "one.rewrite"},
}
testRewritesJSON, mErr := json.Marshal(testRewrites)
require.NoError(t, mErr)
testCases := []struct {
reqData any
name string
url string
method string
wantList []*rewriteJSON
wantBody string
wantConfMod bool
wantStatus int
}{{
name: "list",
url: listURL,
method: http.MethodGet,
reqData: nil,
wantConfMod: false,
wantStatus: http.StatusOK,
wantBody: string(testRewritesJSON) + "\n",
wantList: testRewrites,
}, {
name: "add",
url: addURL,
method: http.MethodPost,
reqData: rewriteJSON{Domain: "add.local", Answer: "add.rewrite"},
wantConfMod: true,
wantStatus: http.StatusOK,
wantBody: "",
wantList: append(
testRewrites,
&rewriteJSON{Domain: "add.local", Answer: "add.rewrite"},
),
}, {
name: "add_error",
url: addURL,
method: http.MethodPost,
reqData: "invalid_json",
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: decodeErrorMsg,
wantList: testRewrites,
}, {
name: "delete",
url: deleteURL,
method: http.MethodPost,
reqData: rewriteJSON{Domain: "one.local", Answer: "one.rewrite"},
wantConfMod: true,
wantStatus: http.StatusOK,
wantBody: "",
wantList: []*rewriteJSON{{Domain: "example.local", Answer: "example.rewrite"}},
}, {
name: "delete_error",
url: deleteURL,
method: http.MethodPost,
reqData: "invalid_json",
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: decodeErrorMsg,
wantList: testRewrites,
}, {
name: "update",
url: updateURL,
method: http.MethodPut,
reqData: rewriteUpdateJSON{
Target: rewriteJSON{Domain: "one.local", Answer: "one.rewrite"},
Update: rewriteJSON{Domain: "upd.local", Answer: "upd.rewrite"},
},
wantConfMod: true,
wantStatus: http.StatusOK,
wantBody: "",
wantList: []*rewriteJSON{
{Domain: "example.local", Answer: "example.rewrite"},
{Domain: "upd.local", Answer: "upd.rewrite"},
},
}, {
name: "update_error",
url: updateURL,
method: http.MethodPut,
reqData: "invalid_json",
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: "json.Decode: json: cannot unmarshal string into Go value of type" +
" filtering.rewriteUpdateJSON\n",
wantList: testRewrites,
}, {
name: "update_error_target",
url: updateURL,
method: http.MethodPut,
reqData: rewriteUpdateJSON{
Target: rewriteJSON{Domain: "inv.local", Answer: "inv.rewrite"},
Update: rewriteJSON{Domain: "upd.local", Answer: "upd.rewrite"},
},
wantConfMod: false,
wantStatus: http.StatusBadRequest,
wantBody: "target rule not found\n",
wantList: testRewrites,
}}
for _, tc := range testCases {
onConfModified := func() {
if !tc.wantConfMod {
panic("config modified has been fired")
}
testutil.RequireSend(testutil.PanicT{}, confModCh, struct{}{}, testTimeout)
}
t.Run(tc.name, func(t *testing.T) {
handlers := make(map[string]http.Handler)
d, err := filtering.New(&filtering.Config{
ConfigModified: onConfModified,
HTTPRegister: func(_, url string, handler http.HandlerFunc) {
handlers[url] = handler
},
Rewrites: rewriteEntriesToLegacyRewrites(testRewrites),
}, nil)
require.NoError(t, err)
t.Cleanup(d.Close)
d.RegisterFilteringHandlers()
require.NotEmpty(t, handlers)
require.Contains(t, handlers, listURL)
require.Contains(t, handlers, tc.url)
var body io.Reader
if tc.reqData != nil {
data, rErr := json.Marshal(tc.reqData)
require.NoError(t, rErr)
body = bytes.NewReader(data)
}
r := httptest.NewRequest(tc.method, tc.url, body)
w := httptest.NewRecorder()
go func() {
handlers[tc.url].ServeHTTP(w, r)
testutil.RequireSend(testutil.PanicT{}, reqCh, struct{}{}, testTimeout)
}()
if tc.wantConfMod {
testutil.RequireReceive(t, confModCh, testTimeout)
}
testutil.RequireReceive(t, reqCh, testTimeout)
assert.Equal(t, tc.wantStatus, w.Code)
respBody, err := io.ReadAll(w.Body)
require.NoError(t, err)
assert.Equal(t, []byte(tc.wantBody), respBody)
assertRewritesList(t, handlers[listURL], tc.wantList)
})
}
}
// assertRewritesList checks if rewrites list equals the list received from the
// handler by listURL.
func assertRewritesList(t *testing.T, handler http.Handler, wantList []*rewriteJSON) {
t.Helper()
r := httptest.NewRequest(http.MethodGet, listURL, nil)
w := httptest.NewRecorder()
handler.ServeHTTP(w, r)
require.Equal(t, http.StatusOK, w.Code)
var actual []*rewriteJSON
err := json.NewDecoder(w.Body).Decode(&actual)
require.NoError(t, err)
assert.Equal(t, wantList, actual)
}
// rewriteEntriesToLegacyRewrites gets legacy rewrites from json entries.
func rewriteEntriesToLegacyRewrites(entries []*rewriteJSON) (rw []*filtering.LegacyRewrite) {
for _, entry := range entries {
rw = append(rw, &filtering.LegacyRewrite{
Domain: entry.Domain,
Answer: entry.Answer,
})
}
return rw
}

View File

@@ -12,14 +12,6 @@ type blockedService struct {
// blockedServices contains raw blocked service data. // blockedServices contains raw blocked service data.
var blockedServices = []blockedService{{ var blockedServices = []blockedService{{
ID: "500px",
Name: "500px",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 5 14 L 2.5 26 L 6.800781 26 C 6.800781 26 7.699219 24.300781 10.199219 24.300781 C 12.699219 24.300781 14 26.199219 14 28.300781 C 14 30.402344 12.5 32.800781 10.199219 32.800781 C 7.898438 32.800781 6.5 30.398438 6.5 29 L 2 29 C 2 30.199219 3 36 10.199219 36 C 15.15625 36 17.417969 33.121094 18.015625 31.898438 C 19.386719 34.34375 21.992188 36 24.984375 36 C 27.253906 36 29.777344 34.808594 32.5 32.453125 C 35.222656 34.808594 37.746094 36 40.015625 36 C 44.417969 36 48 32.410156 48 28 C 48 23.589844 44.417969 20 40.015625 20 C 37.746094 20 35.222656 21.191406 32.5 23.546875 C 29.777344 21.191406 27.253906 20 24.984375 20 C 21.832031 20 19.105469 21.847656 17.8125 24.511719 C 17.113281 23.382813 15.414063 21 11.902344 21 C 8.101563 21 7.300781 22.597656 7.300781 22.597656 C 7.300781 22.597656 7.699219 21.300781 8.300781 18 L 17 18 L 17 14 Z M 24.984375 25 C 25.453125 25 26.800781 25.226563 29.230469 27.328125 L 30.011719 28 L 29.230469 28.671875 C 26.800781 30.773438 25.453125 31 24.984375 31 C 23.339844 31 22 29.652344 22 28 C 22 26.347656 23.339844 25 24.984375 25 Z M 40.015625 25 C 41.660156 25 43 26.347656 43 28 C 43 29.652344 41.660156 31 40.015625 31 C 39.546875 31 38.199219 30.773438 35.769531 28.671875 L 34.988281 28 L 35.769531 27.328125 C 38.199219 25.226563 39.546875 25 40.015625 25 Z\"/></svg>"),
Rules: []string{
"||500px.com^",
"||500px.org^",
},
}, {
ID: "9gag", ID: "9gag",
Name: "9GAG", Name: "9GAG",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 44 14 C 44 13.644531 43.8125 13.316406 43.507813 13.136719 C 40.453125 11.347656 28.46875 4.847656 25.535156 3.136719 C 25.222656 2.957031 24.839844 2.957031 24.527344 3.136719 C 21.128906 5.117188 10.089844 11.621094 7.496094 13.136719 C 7.1875 13.316406 7 13.644531 7 14 L 7 20 C 7 20.378906 7.214844 20.722656 7.550781 20.894531 C 7.660156 20.949219 18.597656 26.453125 24.5 29.867188 C 24.8125 30.046875 25.195313 30.046875 25.507813 29.863281 C 27.269531 28.828125 29.117188 27.859375 30.902344 26.921875 C 32.253906 26.214844 33.636719 25.488281 35.003906 24.722656 C 35.007813 26.820313 35.003906 29.296875 35 30.40625 L 25 35.859375 L 14.480469 30.121094 C 14.144531 29.9375 13.730469 29.964844 13.417969 30.1875 L 6.417969 35.1875 C 6.140625 35.386719 5.980469 35.714844 6.003906 36.054688 C 6.023438 36.398438 6.214844 36.707031 6.515625 36.871094 L 24.542969 46.871094 C 24.695313 46.957031 24.859375 47 25.027344 47 C 25.195313 47 25.363281 46.957031 25.515625 46.875 L 43.484375 36.875 C 43.804688 36.695313 44 36.363281 44 36 C 44 36 43.992188 21.011719 44 14 Z M 25 20 L 18 16 L 25 12 L 32 16 Z\" /></svg>"), IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 44 14 C 44 13.644531 43.8125 13.316406 43.507813 13.136719 C 40.453125 11.347656 28.46875 4.847656 25.535156 3.136719 C 25.222656 2.957031 24.839844 2.957031 24.527344 3.136719 C 21.128906 5.117188 10.089844 11.621094 7.496094 13.136719 C 7.1875 13.316406 7 13.644531 7 14 L 7 20 C 7 20.378906 7.214844 20.722656 7.550781 20.894531 C 7.660156 20.949219 18.597656 26.453125 24.5 29.867188 C 24.8125 30.046875 25.195313 30.046875 25.507813 29.863281 C 27.269531 28.828125 29.117188 27.859375 30.902344 26.921875 C 32.253906 26.214844 33.636719 25.488281 35.003906 24.722656 C 35.007813 26.820313 35.003906 29.296875 35 30.40625 L 25 35.859375 L 14.480469 30.121094 C 14.144531 29.9375 13.730469 29.964844 13.417969 30.1875 L 6.417969 35.1875 C 6.140625 35.386719 5.980469 35.714844 6.003906 36.054688 C 6.023438 36.398438 6.214844 36.707031 6.515625 36.871094 L 24.542969 46.871094 C 24.695313 46.957031 24.859375 47 25.027344 47 C 25.195313 47 25.363281 46.957031 25.515625 46.875 L 43.484375 36.875 C 43.804688 36.695313 44 36.363281 44 36 C 44 36 43.992188 21.011719 44 14 Z M 25 20 L 18 16 L 25 12 L 32 16 Z\" /></svg>"),
@@ -1188,18 +1180,6 @@ var blockedServices = []blockedService{{
"||zuckerberg.com^", "||zuckerberg.com^",
"||zuckerberg.net^", "||zuckerberg.net^",
}, },
}, {
ID: "flickr",
Name: "Flickr",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 9 4 C 6.2504839 4 4 6.2504839 4 9 L 4 41 C 4 43.749516 6.2504839 46 9 46 L 41 46 C 43.749516 46 46 43.749516 46 41 L 46 9 C 46 6.2504839 43.749516 4 41 4 L 9 4 z M 9 6 L 41 6 C 42.668484 6 44 7.3315161 44 9 L 44 41 C 44 42.668484 42.668484 44 41 44 L 9 44 C 7.3315161 44 6 42.668484 6 41 L 6 9 C 6 7.3315161 7.3315161 6 9 6 z M 16 17 C 11.59 17 8 20.59 8 25 C 8 29.41 11.59 33 16 33 C 20.41 33 24 29.41 24 25 C 24 20.59 20.41 17 16 17 z M 34 17 C 29.59 17 26 20.59 26 25 C 26 29.41 29.59 33 34 33 C 38.41 33 42 29.41 42 25 C 42 20.59 38.41 17 34 17 z\"/></svg>"),
Rules: []string{
"||flic.kr^",
"||flickr.com^",
"||flickr.net^",
"||flickrprints.com^",
"||flickrpro.com^",
"||staticflickr.com^",
},
}, { }, {
ID: "gog", ID: "gog",
Name: "GOG", Name: "GOG",
@@ -1345,13 +1325,6 @@ var blockedServices = []blockedService{{
"||kakao.com^", "||kakao.com^",
"||kgslb.com^", "||kgslb.com^",
}, },
}, {
ID: "kik",
Name: "Kik",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 3.5039062 12 C 1.9347705 11.994817 0.87857579 12.97636 0.4453125 13.849609 C 0.01204921 14.722858 0 15.564453 0 15.564453 A 1.0001 1.0001 0 0 0 0 15.59375 L 0 35 A 1.0001 1.0001 0 0 0 0.00390625 35.078125 C 0.00390625 35.078125 0.05696144 35.828363 0.5390625 36.554688 C 1.0211636 37.281011 2.0459252 38.004441 3.5019531 38.001953 C 4.8916439 38.000053 5.8837351 37.273604 6.3769531 36.578125 C 6.8701712 35.882646 6.9863281 35.166016 6.9863281 35.166016 A 1.0001 1.0001 0 0 0 7 35 L 7 31.802734 L 10.167969 36.554688 L 10.130859 36.494141 C 10.511831 37.164615 11.143097 37.525465 11.742188 37.730469 C 12.341278 37.935473 12.950104 38.001953 13.5 38.001953 C 15.411725 38.001953 17 36.431487 17 34.5 C 17 34.056649 16.90825 34.03442 16.851562 33.912109 C 16.794882 33.789799 16.730864 33.671331 16.654297 33.537109 C 16.501163 33.268666 16.298339 32.944015 16.058594 32.572266 C 15.579103 31.828767 14.950355 30.90254 14.322266 29.992188 C 13.310206 28.525308 12.655222 27.610988 12.300781 27.113281 L 14.707031 24.707031 A 1.0001 1.0001 0 0 0 14.738281 24.673828 C 14.738281 24.673828 15.354706 24.012223 15.748047 23.042969 C 16.141388 22.073714 16.298687 20.56089 15.259766 19.349609 C 14.281705 18.208994 12.842689 18.141009 11.925781 18.416016 C 11.008874 18.691022 10.371094 19.222656 10.371094 19.222656 A 1.0001 1.0001 0 0 0 10.292969 19.292969 L 6.9980469 22.587891 L 6.9921875 15.646484 A 1.0001 1.0001 0 0 0 6.9902344 15.580078 C 6.9902344 15.580078 6.9441634 14.743069 6.5058594 13.875 C 6.0675579 13.006938 5.0412971 12.005313 3.5039062 12 z M 30.503906 12 C 28.93477 11.9948 27.878577 12.97636 27.445312 13.849609 C 27.012049 14.722858 27 15.564453 27 15.564453 A 1.0001 1.0001 0 0 0 27 15.59375 L 27 35 A 1.0001 1.0001 0 0 0 27.003906 35.078125 C 27.003906 35.078125 27.056966 35.828363 27.539062 36.554688 C 28.021165 37.281011 29.045925 38.004441 30.501953 38.001953 C 31.891644 38.000053 32.883735 37.273604 33.376953 36.578125 C 33.870171 35.882646 33.986328 35.166016 33.986328 35.166016 A 1.0001 1.0001 0 0 0 34 35 L 34 31.802734 L 37.167969 36.554688 L 37.130859 36.494141 C 37.511831 37.164615 38.143096 37.525465 38.742188 37.730469 C 39.341277 37.935473 39.950104 38.001953 40.5 38.001953 C 42.411725 38.001953 44 36.431487 44 34.5 C 44 34.056649 43.908251 34.03442 43.851562 33.912109 C 43.794882 33.789799 43.730864 33.671331 43.654297 33.537109 C 43.501163 33.268666 43.298339 32.944015 43.058594 32.572266 C 42.579103 31.828767 41.950355 30.90254 41.322266 29.992188 C 40.310206 28.525308 39.655222 27.610988 39.300781 27.113281 L 41.707031 24.707031 A 1.0001 1.0001 0 0 0 41.738281 24.673828 C 41.738281 24.673828 42.354706 24.012223 42.748047 23.042969 C 43.141388 22.073714 43.298687 20.56089 42.259766 19.349609 C 41.281705 18.208994 39.842689 18.141009 38.925781 18.416016 C 38.008874 18.691022 37.371094 19.222656 37.371094 19.222656 A 1.0001 1.0001 0 0 0 37.292969 19.292969 L 33.998047 22.587891 L 33.992188 15.646484 A 1.0001 1.0001 0 0 0 33.990234 15.580078 C 33.990234 15.580078 33.944164 14.743069 33.505859 13.875 C 33.067647 13.006938 32.041297 12.005313 30.503906 12 z M 21.507812 18 C 19.85324 17.98686 18.785557 19.124468 18.382812 20.09375 C 18.181441 20.578391 18.090615 21.031738 18.044922 21.375 C 18.022072 21.546631 18.011459 21.69063 18.005859 21.796875 C 18.000252 21.90312 18 22.065333 18 21.984375 L 17.982422 34.998047 A 1.0001 1.0001 0 0 0 17.990234 35.134766 C 17.990234 35.134766 18.085674 35.862804 18.576172 36.568359 C 19.06667 37.273915 20.071581 37.997467 21.486328 38 C 22.885358 38.0026 23.885897 37.278643 24.380859 36.580078 C 24.875822 35.881513 24.986328 35.160156 24.986328 35.160156 A 1.0001 1.0001 0 0 0 25 35 L 25 21.996094 C 25 21.996094 25.02572 21.084043 24.625 20.117188 C 24.224283 19.150332 23.164841 18.013078 21.507812 18 z M 46.5 24 C 44.578848 24 43 25.578848 43 27.5 C 43 29.421152 44.578848 31 46.5 31 C 48.421152 31 50 29.421152 50 27.5 C 50 25.578848 48.421152 24 46.5 24 z M 46.5 26 C 47.340272 26 48 26.659728 48 27.5 C 48 28.340272 47.340272 29 46.5 29 C 45.659728 29 45 28.340272 45 27.5 C 45 26.659728 45.659728 26 46.5 26 z\"/></svg>"),
Rules: []string{
"||kik.com^",
},
}, { }, {
ID: "lazada", ID: "lazada",
Name: "Lazada", Name: "Lazada",
@@ -1412,7 +1385,6 @@ var blockedServices = []blockedService{{
Rules: []string{ Rules: []string{
"||aus.social^", "||aus.social^",
"||awscommunity.social^", "||awscommunity.social^",
"||climatejustice.social^",
"||cyberplace.social^", "||cyberplace.social^",
"||defcon.social^", "||defcon.social^",
"||det.social^", "||det.social^",
@@ -1470,13 +1442,13 @@ var blockedServices = []blockedService{{
"||mstdn.plus^", "||mstdn.plus^",
"||mstdn.social^", "||mstdn.social^",
"||muenchen.social^", "||muenchen.social^",
"||muenster.im^",
"||newsie.social^", "||newsie.social^",
"||noc.social^", "||noc.social^",
"||norden.social^", "||norden.social^",
"||nrw.social^", "||nrw.social^",
"||o3o.ca^", "||o3o.ca^",
"||ohai.social^", "||ohai.social^",
"||pewtix.com^",
"||piaille.fr^", "||piaille.fr^",
"||pol.social^", "||pol.social^",
"||ravenation.club^", "||ravenation.club^",
@@ -1508,6 +1480,7 @@ var blockedServices = []blockedService{{
"||union.place^", "||union.place^",
"||universeodon.com^", "||universeodon.com^",
"||urbanists.social^", "||urbanists.social^",
"||wien.rocks^",
"||wxw.moe^", "||wxw.moe^",
}, },
}, { }, {
@@ -1854,13 +1827,6 @@ var blockedServices = []blockedService{{
"||tx.me^", "||tx.me^",
"||usercontent.dev^", "||usercontent.dev^",
}, },
}, {
ID: "tidal",
Name: "Tidal",
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M 9 12 C 8.7615 12 8.5237969 12.091437 8.3417969 12.273438 L 1.2734375 19.341797 C 0.9094375 19.705797 0.9094375 20.294203 1.2734375 20.658203 L 8.3417969 27.726562 C 8.7057969 28.090563 9.2942031 28.090563 9.6582031 27.726562 L 16.726562 20.658203 C 16.908563 20.476203 17 20.2385 17 20 C 17 19.7615 16.908563 19.523797 16.726562 19.341797 L 9.6582031 12.273438 C 9.4762031 12.091437 9.2385 12 9 12 z M 17 20 C 17 20.2385 17.091438 20.476203 17.273438 20.658203 L 24.341797 27.726562 C 24.523797 27.908563 24.7615 28 25 28 C 25.2385 28 25.476203 27.908563 25.658203 27.726562 L 32.726562 20.658203 C 32.908563 20.476203 33 20.2385 33 20 C 33 19.7615 32.908563 19.523797 32.726562 19.341797 L 25.658203 12.273438 C 25.294203 11.909437 24.705797 11.909437 24.341797 12.273438 L 17.273438 19.341797 C 17.091437 19.523797 17 19.7615 17 20 z M 33 20 C 33 20.2385 33.091437 20.476203 33.273438 20.658203 L 40.341797 27.726562 C 40.705797 28.090563 41.294203 28.090563 41.658203 27.726562 L 48.726562 20.658203 C 49.090563 20.294203 49.090563 19.705797 48.726562 19.341797 L 41.658203 12.273438 C 41.294203 11.909437 40.705797 11.909437 40.341797 12.273438 L 33.273438 19.341797 C 33.091437 19.523797 33 19.7615 33 20 z M 25 28 C 24.7615 28 24.523797 28.091437 24.341797 28.273438 L 17.273438 35.341797 C 16.909437 35.705797 16.909437 36.294203 17.273438 36.658203 L 24.341797 43.726562 C 24.705797 44.090562 25.294203 44.090562 25.658203 43.726562 L 32.726562 36.658203 C 33.090563 36.294203 33.090563 35.705797 32.726562 35.341797 L 25.658203 28.273438 C 25.476203 28.091437 25.2385 28 25 28 z\"/></svg>"),
Rules: []string{
"||tidal.com^",
},
}, { }, {
ID: "tiktok", ID: "tiktok",
Name: "TikTok", Name: "TikTok",

View File

@@ -8,7 +8,6 @@ import (
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch" "github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
"github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/proxy"
"github.com/AdguardTeam/golibs/stringutil"
) )
// Client contains information about persistent clients. // Client contains information about persistent clients.
@@ -38,19 +37,6 @@ type Client struct {
IgnoreStatistics bool IgnoreStatistics bool
} }
// ShallowClone returns a deep copy of the client, except upstreamConfig,
// safeSearchConf, SafeSearch fields, because it's difficult to copy them.
func (c *Client) ShallowClone() (sh *Client) {
clone := *c
clone.IDs = stringutil.CloneSlice(c.IDs)
clone.Tags = stringutil.CloneSlice(c.Tags)
clone.BlockedServices = stringutil.CloneSlice(c.BlockedServices)
clone.Upstreams = stringutil.CloneSlice(c.Upstreams)
return &clone
}
// closeUpstreams closes the client-specific upstream config of c if any. // closeUpstreams closes the client-specific upstream config of c if any.
func (c *Client) closeUpstreams() (err error) { func (c *Client) closeUpstreams() (err error) {
if c.upstreamConfig != nil { if c.upstreamConfig != nil {

View File

@@ -378,7 +378,6 @@ func (clients *clientsContainer) clientOrArtificial(
}, true }, true
} }
// Find returns a shallow copy of the client if there is one found.
func (clients *clientsContainer) Find(id string) (c *Client, ok bool) { func (clients *clientsContainer) Find(id string) (c *Client, ok bool) {
clients.lock.Lock() clients.lock.Lock()
defer clients.lock.Unlock() defer clients.lock.Unlock()
@@ -388,18 +387,20 @@ func (clients *clientsContainer) Find(id string) (c *Client, ok bool) {
return nil, false return nil, false
} }
return c.ShallowClone(), true c.IDs = stringutil.CloneSlice(c.IDs)
c.Tags = stringutil.CloneSlice(c.Tags)
c.BlockedServices = stringutil.CloneSlice(c.BlockedServices)
c.Upstreams = stringutil.CloneSlice(c.Upstreams)
return c, true
} }
// shouldCountClient is a wrapper around Find to make it a valid client // shouldCountClient is a wrapper around Find to make it a valid client
// information finder for the statistics. If no information about the client // information finder for the statistics. If no information about the client
// is found, it returns true. // is found, it returns true.
func (clients *clientsContainer) shouldCountClient(ids []string) (y bool) { func (clients *clientsContainer) shouldCountClient(ids []string) (y bool) {
clients.lock.Lock()
defer clients.lock.Unlock()
for _, id := range ids { for _, id := range ids {
client, ok := clients.findLocked(id) client, ok := clients.Find(id)
if ok { if ok {
return !client.IgnoreStatistics return !client.IgnoreStatistics
} }
@@ -616,15 +617,6 @@ func (clients *clientsContainer) Add(c *Client) (ok bool, err error) {
} }
} }
clients.add(c)
log.Debug("clients: added %q: ID:%q [%d]", c.Name, c.IDs, len(clients.list))
return true, nil
}
// add c to the indexes. clients.lock is expected to be locked.
func (clients *clientsContainer) add(c *Client) {
// update Name index // update Name index
clients.list[c.Name] = c clients.list[c.Name] = c
@@ -632,6 +624,10 @@ func (clients *clientsContainer) add(c *Client) {
for _, id := range c.IDs { for _, id := range c.IDs {
clients.idIndex[id] = c clients.idIndex[id] = c
} }
log.Debug("clients: added %q: ID:%q [%d]", c.Name, c.IDs, len(clients.list))
return true, nil
} }
// Del removes a client. ok is false if there is no such client. // Del removes a client. ok is false if there is no such client.
@@ -649,53 +645,86 @@ func (clients *clientsContainer) Del(name string) (ok bool) {
log.Error("client container: removing client %s: %s", name, err) log.Error("client container: removing client %s: %s", name, err)
} }
clients.del(c)
return true
}
// del removes c from the indexes. clients.lock is expected to be locked.
func (clients *clientsContainer) del(c *Client) {
// update Name index // update Name index
delete(clients.list, c.Name) delete(clients.list, name)
// update ID index // update ID index
for _, id := range c.IDs { for _, id := range c.IDs {
delete(clients.idIndex, id) delete(clients.idIndex, id)
} }
return true
} }
// Update updates a client by its name. // Update updates a client by its name.
func (clients *clientsContainer) Update(prev, c *Client) (err error) { func (clients *clientsContainer) Update(name string, c *Client) (err error) {
err = clients.check(c) err = clients.check(c)
if err != nil { if err != nil {
// Don't wrap the error since it's informative enough as is.
return err return err
} }
clients.lock.Lock() clients.lock.Lock()
defer clients.lock.Unlock() defer clients.lock.Unlock()
// Check the name index. prev, ok := clients.list[name]
if !ok {
return errors.Error("client not found")
}
// First, check the name index.
if prev.Name != c.Name { if prev.Name != c.Name {
_, ok := clients.list[c.Name] _, ok = clients.list[c.Name]
if ok { if ok {
return errors.Error("client already exists") return errors.Error("client already exists")
} }
} }
// Check the ID index. // Second, update the ID index.
if !slices.Equal(prev.IDs, c.IDs) { err = clients.updateIDIndex(prev, c.IDs)
for _, id := range c.IDs { if err != nil {
// Don't wrap the error, because it's informative enough as is.
return err
}
// Update name index.
if prev.Name != c.Name {
delete(clients.list, prev.Name)
clients.list[c.Name] = prev
}
// Update upstreams cache.
err = c.closeUpstreams()
if err != nil {
return err
}
*prev = *c
return nil
}
// updateIDIndex updates the ID index data for cli using the information from
// newIDs.
func (clients *clientsContainer) updateIDIndex(cli *Client, newIDs []string) (err error) {
if slices.Equal(cli.IDs, newIDs) {
return nil
}
for _, id := range newIDs {
existing, ok := clients.idIndex[id] existing, ok := clients.idIndex[id]
if ok && existing != prev { if ok && existing != cli {
return fmt.Errorf("id %q is used by client with name %q", id, existing.Name) return fmt.Errorf("id %q is used by client with name %q", id, existing.Name)
} }
} }
// Update the IDs in the index.
for _, id := range cli.IDs {
delete(clients.idIndex, id)
} }
clients.del(prev) for _, id := range newIDs {
clients.add(c) clients.idIndex[id] = cli
}
return nil return nil
} }

View File

@@ -98,8 +98,22 @@ func TestClients(t *testing.T) {
assert.False(t, ok) assert.False(t, ok)
}) })
t.Run("update_fail_name", func(t *testing.T) {
err := clients.Update("client3", &Client{
IDs: []string{"1.2.3.0"},
Name: "client3",
})
require.Error(t, err)
err = clients.Update("client3", &Client{
IDs: []string{"1.2.3.0"},
Name: "client2",
})
assert.Error(t, err)
})
t.Run("update_fail_ip", func(t *testing.T) { t.Run("update_fail_ip", func(t *testing.T) {
err := clients.Update(&Client{Name: "client1"}, &Client{ err := clients.Update("client1", &Client{
IDs: []string{"2.2.2.2"}, IDs: []string{"2.2.2.2"},
Name: "client1", Name: "client1",
}) })
@@ -115,10 +129,7 @@ func TestClients(t *testing.T) {
cliNewIP = netip.MustParseAddr(cliNew) cliNewIP = netip.MustParseAddr(cliNew)
) )
prev, ok := clients.list["client1"] err := clients.Update("client1", &Client{
require.True(t, ok)
err := clients.Update(prev, &Client{
IDs: []string{cliNew}, IDs: []string{cliNew},
Name: "client1", Name: "client1",
}) })
@@ -127,10 +138,7 @@ func TestClients(t *testing.T) {
assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone) assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone)
assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent) assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent)
prev, ok = clients.list["client1"] err = clients.Update("client1", &Client{
require.True(t, ok)
err = clients.Update(prev, &Client{
IDs: []string{cliNew}, IDs: []string{cliNew},
Name: "client1-renamed", Name: "client1-renamed",
UseOwnSettings: true, UseOwnSettings: true,

View File

@@ -289,7 +289,7 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
return return
} }
err = clients.Update(prev, c) err = clients.Update(dj.Name, c)
if err != nil { if err != nil {
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err) aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)

View File

@@ -3,24 +3,19 @@ package querylog
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"net" "net"
"strings" "strings"
"time" "time"
"github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/AdGuardHome/internal/filtering"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"github.com/AdguardTeam/urlfilter/rules" "github.com/AdguardTeam/urlfilter/rules"
"github.com/miekg/dns" "github.com/miekg/dns"
) )
// logEntryHandler represents a handler for decoding json token to the logEntry
// struct.
type logEntryHandler func(t json.Token, ent *logEntry) error type logEntryHandler func(t json.Token, ent *logEntry) error
// logEntryHandlers is the map of log entry decode handlers for various keys.
var logEntryHandlers = map[string]logEntryHandler{ var logEntryHandlers = map[string]logEntryHandler{
"CID": func(t json.Token, ent *logEntry) error { "CID": func(t json.Token, ent *logEntry) error {
v, ok := t.(string) v, ok := t.(string)
@@ -171,7 +166,6 @@ var logEntryHandlers = map[string]logEntryHandler{
}, },
} }
// decodeResultRuleKey decodes the token of "Rules" type to logEntry struct.
func decodeResultRuleKey(key string, i int, dec *json.Decoder, ent *logEntry) { func decodeResultRuleKey(key string, i int, dec *json.Decoder, ent *logEntry) {
var vToken json.Token var vToken json.Token
switch key { switch key {
@@ -195,8 +189,6 @@ func decodeResultRuleKey(key string, i int, dec *json.Decoder, ent *logEntry) {
} }
} }
// decodeVTokenAndAddRule decodes the "Rules" toke as [filtering.ResultRule]
// and then adds the decoded object to the slice of result rules.
func decodeVTokenAndAddRule( func decodeVTokenAndAddRule(
key string, key string,
i int, i int,
@@ -221,8 +213,6 @@ func decodeVTokenAndAddRule(
return newRules, vToken return newRules, vToken
} }
// decodeResultRules parses the dec's tokens into logEntry ent interpreting it
// as a slice of the result rules.
func decodeResultRules(dec *json.Decoder, ent *logEntry) { func decodeResultRules(dec *json.Decoder, ent *logEntry) {
for { for {
delimToken, err := dec.Token() delimToken, err := dec.Token()
@@ -234,32 +224,24 @@ func decodeResultRules(dec *json.Decoder, ent *logEntry) {
return return
} }
if d, ok := delimToken.(json.Delim); !ok { if d, ok := delimToken.(json.Delim); ok {
return if d != '[' {
} else if d != '[' {
log.Debug("decodeResultRules: unexpected delim %q", d) log.Debug("decodeResultRules: unexpected delim %q", d)
} }
} else {
err = decodeResultRuleToken(dec, ent)
if err != nil {
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
log.Debug("decodeResultRules err: %s", err)
}
return return
} }
}
}
// decodeResultRuleToken decodes the tokens of "Rules" type to the logEntry ent.
func decodeResultRuleToken(dec *json.Decoder, ent *logEntry) (err error) {
i := 0 i := 0
for { for {
var keyToken json.Token var keyToken json.Token
keyToken, err = dec.Token() keyToken, err = dec.Token()
if err != nil { if err != nil {
// Don't wrap the error, because it's informative enough as is. if err != io.EOF {
return err log.Debug("decodeResultRules err: %s", err)
}
return
} }
if d, ok := keyToken.(json.Delim); ok { if d, ok := keyToken.(json.Delim); ok {
@@ -267,7 +249,7 @@ func decodeResultRuleToken(dec *json.Decoder, ent *logEntry) (err error) {
case '}': case '}':
i++ i++
case ']': case ']':
return ErrEndOfToken return
default: default:
// Go on. // Go on.
} }
@@ -277,11 +259,14 @@ func decodeResultRuleToken(dec *json.Decoder, ent *logEntry) (err error) {
key, ok := keyToken.(string) key, ok := keyToken.(string)
if !ok { if !ok {
return fmt.Errorf("keyToken is %T (%[1]v) and not string", keyToken) log.Debug("decodeResultRules: keyToken is %T (%[1]v) and not string", keyToken)
return
} }
decodeResultRuleKey(key, i, dec, ent) decodeResultRuleKey(key, i, dec, ent)
} }
}
} }
// decodeResultReverseHosts parses the dec's tokens into ent interpreting it as // decodeResultReverseHosts parses the dec's tokens into ent interpreting it as
@@ -337,8 +322,6 @@ func decodeResultReverseHosts(dec *json.Decoder, ent *logEntry) {
} }
} }
// decodeResultIPList parses the dec's tokens into logEntry ent interpreting it
// as the result IP addresses list.
func decodeResultIPList(dec *json.Decoder, ent *logEntry) { func decodeResultIPList(dec *json.Decoder, ent *logEntry) {
for { for {
itemToken, err := dec.Token() itemToken, err := dec.Token()
@@ -372,8 +355,6 @@ func decodeResultIPList(dec *json.Decoder, ent *logEntry) {
} }
} }
// decodeResultDNSRewriteResultKey decodes the token of "DNSRewriteResult" type
// to the logEntry struct.
func decodeResultDNSRewriteResultKey(key string, dec *json.Decoder, ent *logEntry) { func decodeResultDNSRewriteResultKey(key string, dec *json.Decoder, ent *logEntry) {
var err error var err error
@@ -414,29 +395,50 @@ func decodeResultDNSRewriteResultKey(key string, dec *json.Decoder, ent *logEntr
log.Debug("decodeResultDNSRewriteResultKey response err: %s", err) log.Debug("decodeResultDNSRewriteResultKey response err: %s", err)
} }
ent.parseDNSRewriteResultIPs() for rrType, rrValues := range ent.Result.DNSRewriteResult.Response {
switch rrType {
case
dns.TypeA,
dns.TypeAAAA:
for i, v := range rrValues {
s, _ := v.(string)
rrValues[i] = net.ParseIP(s)
}
default:
// Go on.
}
}
default: default:
// Go on. // Go on.
} }
} }
// decodeResultDNSRewriteResult parses the dec's tokens into logEntry ent
// interpreting it as the result DNSRewriteResult.
func decodeResultDNSRewriteResult(dec *json.Decoder, ent *logEntry) { func decodeResultDNSRewriteResult(dec *json.Decoder, ent *logEntry) {
for { for {
key, err := parseKeyToken(dec) keyToken, err := dec.Token()
if err != nil { if err != nil {
if err != io.EOF && !errors.Is(err, ErrEndOfToken) { if err != io.EOF {
log.Debug("decodeResultDNSRewriteResult: %s", err) log.Debug("decodeResultDNSRewriteResult err: %s", err)
} }
return return
} }
if key == "" { if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return
}
continue continue
} }
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResultDNSRewriteResult: keyToken is %T (%[1]v) and not string", keyToken)
return
}
decodeResultDNSRewriteResultKey(key, dec, ent) decodeResultDNSRewriteResultKey(key, dec, ent)
} }
} }
@@ -472,51 +474,34 @@ func translateResult(ent *logEntry) {
res.IPList = nil res.IPList = nil
} }
// ErrEndOfToken is an error returned by parse key token when the closing
// bracket is found.
const ErrEndOfToken errors.Error = "end of token"
// parseKeyToken parses the dec's token key.
func parseKeyToken(dec *json.Decoder) (key string, err error) {
keyToken, err := dec.Token()
if err != nil {
return "", err
}
if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return "", ErrEndOfToken
}
return "", nil
}
key, ok := keyToken.(string)
if !ok {
return "", fmt.Errorf("keyToken is %T (%[1]v) and not string", keyToken)
}
return key, nil
}
// decodeResult decodes a token of "Result" type to logEntry struct.
func decodeResult(dec *json.Decoder, ent *logEntry) { func decodeResult(dec *json.Decoder, ent *logEntry) {
defer translateResult(ent) defer translateResult(ent)
for { for {
key, err := parseKeyToken(dec) keyToken, err := dec.Token()
if err != nil { if err != nil {
if err != io.EOF && !errors.Is(err, ErrEndOfToken) { if err != io.EOF {
log.Debug("decodeResult: %s", err) log.Debug("decodeResult err: %s", err)
} }
return return
} }
if key == "" { if d, ok := keyToken.(json.Delim); ok {
if d == '}' {
return
}
continue continue
} }
key, ok := keyToken.(string)
if !ok {
log.Debug("decodeResult: keyToken is %T (%[1]v) and not string", keyToken)
return
}
decHandler, ok := resultDecHandlers[key] decHandler, ok := resultDecHandlers[key]
if ok { if ok {
decHandler(dec, ent) decHandler(dec, ent)
@@ -542,16 +527,13 @@ func decodeResult(dec *json.Decoder, ent *logEntry) {
} }
} }
// resultHandlers is the map of log entry decode handlers for various keys.
var resultHandlers = map[string]logEntryHandler{ var resultHandlers = map[string]logEntryHandler{
"IsFiltered": func(t json.Token, ent *logEntry) error { "IsFiltered": func(t json.Token, ent *logEntry) error {
v, ok := t.(bool) v, ok := t.(bool)
if !ok { if !ok {
return nil return nil
} }
ent.Result.IsFiltered = v ent.Result.IsFiltered = v
return nil return nil
}, },
"Rule": func(t json.Token, ent *logEntry) error { "Rule": func(t json.Token, ent *logEntry) error {
@@ -596,14 +578,11 @@ var resultHandlers = map[string]logEntryHandler{
if !ok { if !ok {
return nil return nil
} }
i, err := v.Int64() i, err := v.Int64()
if err != nil { if err != nil {
return err return err
} }
ent.Result.Reason = filtering.Reason(i) ent.Result.Reason = filtering.Reason(i)
return nil return nil
}, },
"ServiceName": func(t json.Token, ent *logEntry) error { "ServiceName": func(t json.Token, ent *logEntry) error {
@@ -628,7 +607,6 @@ var resultHandlers = map[string]logEntryHandler{
}, },
} }
// resultDecHandlers is the map of decode handlers for various keys.
var resultDecHandlers = map[string]func(dec *json.Decoder, ent *logEntry){ var resultDecHandlers = map[string]func(dec *json.Decoder, ent *logEntry){
"ReverseHosts": decodeResultReverseHosts, "ReverseHosts": decodeResultReverseHosts,
"IPList": decodeResultIPList, "IPList": decodeResultIPList,
@@ -636,11 +614,9 @@ var resultDecHandlers = map[string]func(dec *json.Decoder, ent *logEntry){
"DNSRewriteResult": decodeResultDNSRewriteResult, "DNSRewriteResult": decodeResultDNSRewriteResult,
} }
// decodeLogEntry decodes string str to logEntry ent.
func decodeLogEntry(ent *logEntry, str string) { func decodeLogEntry(ent *logEntry, str string) {
dec := json.NewDecoder(strings.NewReader(str)) dec := json.NewDecoder(strings.NewReader(str))
dec.UseNumber() dec.UseNumber()
for { for {
keyToken, err := dec.Token() keyToken, err := dec.Token()
if err != nil { if err != nil {

View File

@@ -68,19 +68,3 @@ func (e *logEntry) addResponse(resp *dns.Msg, isOrig bool) {
log.Error("querylog: %s", err) log.Error("querylog: %s", err)
} }
} }
// parseDNSRewriteResultIPs fills logEntry's DNSRewriteResult response records
// with the IP addresses parsed from the raw strings.
func (e *logEntry) parseDNSRewriteResultIPs() {
for rrType, rrValues := range e.Result.DNSRewriteResult.Response {
switch rrType {
case dns.TypeA, dns.TypeAAAA:
for i, v := range rrValues {
s, _ := v.(string)
rrValues[i] = net.ParseIP(s)
}
default:
// Go on.
}
}
}

View File

@@ -16,35 +16,32 @@ import (
"github.com/miekg/dns" "github.com/miekg/dns"
) )
// queryLogFileName is a name of the log file. ".gz" extension is added later const (
// during compression. queryLogFileName = "querylog.json" // .gz added during compression
const queryLogFileName = "querylog.json" )
// queryLog is a structure that writes and reads the DNS query log. // queryLog is a structure that writes and reads the DNS query log
type queryLog struct { type queryLog struct {
findClient func(ids []string) (c *Client, err error)
// confMu protects conf. // confMu protects conf.
confMu *sync.RWMutex confMu *sync.RWMutex
conf *Config conf *Config
anonymizer *aghnet.IPMut
findClient func(ids []string) (c *Client, err error)
// logFile is the path to the log file. // logFile is the path to the log file.
logFile string logFile string
// bufferLock protects buffer.
bufferLock sync.RWMutex
// buffer contains recent log entries. The entries in this buffer must not // buffer contains recent log entries. The entries in this buffer must not
// be modified. // be modified.
buffer []*logEntry buffer []*logEntry
// bufferLock protects buffer. fileFlushLock sync.Mutex // synchronize a file-flushing goroutine and main thread
bufferLock sync.RWMutex flushPending bool // don't start another goroutine while the previous one is still running
// fileFlushLock synchronizes a file-flushing goroutine and main thread.
fileFlushLock sync.Mutex
fileWriteLock sync.Mutex fileWriteLock sync.Mutex
flushPending bool anonymizer *aghnet.IPMut
} }
// ClientProto values are names of the client protocols. // ClientProto values are names of the client protocols.
@@ -158,43 +155,6 @@ func (l *queryLog) clear() {
log.Debug("querylog: cleared") log.Debug("querylog: cleared")
} }
// newLogEntry creates an instance of logEntry from parameters.
func newLogEntry(params *AddParams) (entry *logEntry) {
q := params.Question.Question[0]
entry = &logEntry{
// TODO(d.kolyshev): Export this timestamp to func params.
Time: time.Now(),
QHost: strings.ToLower(q.Name[:len(q.Name)-1]),
QType: dns.Type(q.Qtype).String(),
QClass: dns.Class(q.Qclass).String(),
ClientID: params.ClientID,
ClientProto: params.ClientProto,
Result: *params.Result,
Upstream: params.Upstream,
IP: params.ClientIP,
Elapsed: params.Elapsed,
Cached: params.Cached,
AuthenticatedData: params.AuthenticatedData,
}
if params.ReqECS != nil {
entry.ReqECS = params.ReqECS.String()
}
entry.addResponse(params.Answer, false)
entry.addResponse(params.OrigAnswer, true)
return entry
}
// Add implements the [QueryLog] interface for *queryLog.
func (l *queryLog) Add(params *AddParams) { func (l *queryLog) Add(params *AddParams) {
var isEnabled, fileIsEnabled bool var isEnabled, fileIsEnabled bool
var memSize uint32 var memSize uint32
@@ -221,7 +181,35 @@ func (l *queryLog) Add(params *AddParams) {
params.Result = &filtering.Result{} params.Result = &filtering.Result{}
} }
entry := newLogEntry(params) now := time.Now()
q := params.Question.Question[0]
entry := &logEntry{
Time: now,
QHost: strings.ToLower(q.Name[:len(q.Name)-1]),
QType: dns.Type(q.Qtype).String(),
QClass: dns.Class(q.Qclass).String(),
ClientID: params.ClientID,
ClientProto: params.ClientProto,
Result: *params.Result,
Upstream: params.Upstream,
IP: params.ClientIP,
Elapsed: params.Elapsed,
Cached: params.Cached,
AuthenticatedData: params.AuthenticatedData,
}
if params.ReqECS != nil {
entry.ReqECS = params.ReqECS.String()
}
entry.addResponse(params.Answer, false)
entry.addResponse(params.OrigAnswer, true)
needFlush := false needFlush := false
func() { func() {

View File

@@ -45,10 +45,9 @@ func TestQueryLog(t *testing.T) {
addEntry(l, "example.com", net.IPv4(1, 1, 1, 4), net.IPv4(2, 2, 2, 4)) addEntry(l, "example.com", net.IPv4(1, 1, 1, 4), net.IPv4(2, 2, 2, 4))
type tcAssertion struct { type tcAssertion struct {
host string
answer net.IP
client net.IP
num int num int
host string
answer, client net.IP
} }
testCases := []struct { testCases := []struct {

View File

@@ -12,181 +12,141 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
) )
// Timestamp not found errors.
const ( const (
// Timestamp not found errors. ErrTSNotFound errors.Error = "ts not found"
errTSNotFound errors.Error = "ts not found" ErrTSTooLate errors.Error = "ts too late"
errTSTooLate errors.Error = "ts too late" ErrTSTooEarly errors.Error = "ts too early"
errTSTooEarly errors.Error = "ts too early"
// maxEntrySize is a maximum size of the entry.
//
// TODO: Find a way to grow buffer instead of relying on this value when
// reading strings.
maxEntrySize = 16 * 1024
// bufferSize should be enough for at least this number of entries.
bufferSize = 100 * maxEntrySize
) )
// qLogFile represents a single query log file. It allows reading from the // TODO: Find a way to grow buffer instead of relying on this value when reading strings
// file in the reverse order. const maxEntrySize = 16 * 1024
// buffer should be enough for at least this number of entries
const bufferSize = 100 * maxEntrySize
// QLogFile represents a single query log file
// It allows reading from the file in the reverse order
// //
// Please note, that this is a stateful object. Internally, it contains a // Please note that this is a stateful object.
// pointer to a specific position in the file, and it reads lines in reverse // Internally, it contains a pointer to a specific position in the file,
// order starting from that position. // and it reads lines in reverse order starting from that position.
type qLogFile struct { type QLogFile struct {
// file is the query log file. file *os.File // the query log file
file *os.File position int64 // current position in the file
// buffer that we've read from the file. buffer []byte // buffer that we've read from the file
buffer []byte bufferStart int64 // start of the buffer (in the file)
bufferLen int // buffer len
// lock is a mutex to make it thread-safe. lock sync.Mutex // We use mutex to make it thread-safe
lock sync.Mutex
// position is the position in the file.
position int64
// bufferStart is the start of the buffer (in the file).
bufferStart int64
// bufferLen is the length of the buffer.
bufferLen int
} }
// newQLogFile initializes a new instance of the qLogFile. // NewQLogFile initializes a new instance of the QLogFile
func newQLogFile(path string) (qf *qLogFile, err error) { func NewQLogFile(path string) (*QLogFile, error) {
f, err := os.OpenFile(path, os.O_RDONLY, 0o644) f, err := os.OpenFile(path, os.O_RDONLY, 0o644)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &qLogFile{file: f}, nil return &QLogFile{
} file: f,
}, nil
// validateQLogLineIdx returns error if the line index is not valid to continue
// search.
func (q *qLogFile) validateQLogLineIdx(lineIdx, lastProbeLineIdx, ts, fSize int64) (err error) {
if lineIdx == lastProbeLineIdx {
if lineIdx == 0 {
return errTSTooEarly
}
// If we're testing the same line twice then most likely the scope is
// too narrow and we won't find anything anymore in any other file.
return fmt.Errorf("looking up timestamp %d in %q: %w", ts, q.file.Name(), errTSNotFound)
} else if lineIdx == fSize {
return errTSTooLate
}
return nil
} }
// seekTS performs binary search in the query log file looking for a record // seekTS performs binary search in the query log file looking for a record
// with the specified timestamp. Once the record is found, it sets "position" // with the specified timestamp. Once the record is found, it sets
// so that the next ReadNext call returned that record. // "position" so that the next ReadNext call returned that record.
// //
// The algorithm is rather simple: // The algorithm is rather simple:
// 1. It starts with the position in the middle of a file. // 1. It starts with the position in the middle of a file
// 2. Shifts back to the beginning of the line. // 2. Shifts back to the beginning of the line
// 3. Checks the log record timestamp. // 3. Checks the log record timestamp
// 4. If it is lower than the timestamp we are looking for, it shifts seek // 4. If it is lower than the timestamp we are looking for,
// position to 3/4 of the file. Otherwise, to 1/4 of the file. // it shifts seek position to 3/4 of the file. Otherwise, to 1/4 of the file.
// 5. It performs the search again, every time the search scope is narrowed // 5. It performs the search again, every time the search scope is narrowed twice.
// twice.
// //
// Returns: // Returns:
// - It returns the position of the line with the timestamp we were looking // * It returns the position of the the line with the timestamp we were looking for
// for so that when we call "ReadNext" this line was returned. // so that when we call "ReadNext" this line was returned.
// - Depth of the search (how many times we compared timestamps). // * Depth of the search (how many times we compared timestamps).
// - If we could not find it, it returns one of the errors described above. // * If we could not find it, it returns one of the errors described above.
func (q *qLogFile) seekTS(timestamp int64) (pos int64, depth int, err error) { func (q *QLogFile) seekTS(timestamp int64) (int64, int, error) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
// Empty the buffer. // Empty the buffer
q.buffer = nil q.buffer = nil
// First of all, check the file size. // First of all, check the file size
fileInfo, err := q.file.Stat() fileInfo, err := q.file.Stat()
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
// Define the search scope. // Define the search scope
start := int64(0) // start of the search interval (position in the file)
// Start of the search interval (position in the file). end := fileInfo.Size() // end of the search interval (position in the file)
start := int64(0) probe := (end - start) / 2 // probe -- approximate index of the line we'll try to check
// End of the search interval (position in the file).
end := fileInfo.Size()
// Probe is the approximate index of the line we'll try to check.
probe := (end - start) / 2
var line string var line string
// Index of the probe line in the file. var lineIdx int64 // index of the probe line in the file
var lineIdx int64
var lineEndIdx int64 var lineEndIdx int64
// Index of the last probe line. var lastProbeLineIdx int64 // index of the last probe line
var lastProbeLineIdx int64
lastProbeLineIdx = -1 lastProbeLineIdx = -1
// Count seek depth in order to detect mistakes. If depth is too large, // Count seek depth in order to detect mistakes
// we should stop the search. // If depth is too large, we should stop the search
depth := 0
for { for {
// Get the line at the specified position. // Get the line at the specified position
line, lineIdx, lineEndIdx, err = q.readProbeLine(probe) line, lineIdx, lineEndIdx, err = q.readProbeLine(probe)
if err != nil { if err != nil {
return 0, depth, err return 0, depth, err
} }
// Check if the line index if invalid. if lineIdx == lastProbeLineIdx {
err = q.validateQLogLineIdx(lineIdx, lastProbeLineIdx, timestamp, fileInfo.Size()) if lineIdx == 0 {
if err != nil { return 0, depth, ErrTSTooEarly
return 0, depth, err
} }
// Save the last found idx. // If we're testing the same line twice then most likely
// the scope is too narrow and we won't find anything
// anymore in any other file.
return 0, depth, fmt.Errorf("looking up timestamp %d in %q: %w", timestamp, q.file.Name(), ErrTSNotFound)
} else if lineIdx == fileInfo.Size() {
return 0, depth, ErrTSTooLate
}
// Save the last found idx
lastProbeLineIdx = lineIdx lastProbeLineIdx = lineIdx
// Get the timestamp from the query log record. // Get the timestamp from the query log record
ts := readQLogTimestamp(line) ts := readQLogTimestamp(line)
if ts == 0 { if ts == 0 {
return 0, depth, fmt.Errorf( return 0, depth, fmt.Errorf("looking up timestamp %d in %q: record %q has empty timestamp", timestamp, q.file.Name(), line)
"looking up timestamp %d in %q: record %q has empty timestamp",
timestamp,
q.file.Name(),
line,
)
} }
if ts == timestamp { if ts == timestamp {
// Hurray, returning the result. // Hurray, returning the result
break break
} }
// Narrow the scope and repeat the search. // Narrow the scope and repeat the search
if ts > timestamp { if ts > timestamp {
// If the timestamp we're looking for is OLDER than what we found, // If the timestamp we're looking for is OLDER than what we found
// then the line is somewhere on the LEFT side from the current // Then the line is somewhere on the LEFT side from the current probe position
// probe position.
end = lineIdx end = lineIdx
} else { } else {
// If the timestamp we're looking for is NEWER than what we found, // If the timestamp we're looking for is NEWER than what we found
// then the line is somewhere on the RIGHT side from the current // Then the line is somewhere on the RIGHT side from the current probe position
// probe position.
start = lineEndIdx start = lineEndIdx
} }
probe = start + (end-start)/2 probe = start + (end-start)/2
depth++ depth++
if depth >= 100 { if depth >= 100 {
return 0, depth, fmt.Errorf( return 0, depth, fmt.Errorf("looking up timestamp %d in %q: depth %d too high: %w", timestamp, q.file.Name(), depth, ErrTSNotFound)
"looking up timestamp %d in %q: depth %d too high: %w",
timestamp,
q.file.Name(),
depth,
errTSNotFound,
)
} }
} }
@@ -194,39 +154,37 @@ func (q *qLogFile) seekTS(timestamp int64) (pos int64, depth int, err error) {
return q.position, depth, nil return q.position, depth, nil
} }
// SeekStart changes the current position to the end of the file. Please note, // SeekStart changes the current position to the end of the file
// that we're reading query log in the reverse order and that's why log start // Please note that we're reading query log in the reverse order
// is actually the end of file. // and that's why log start is actually the end of file
// //
// Returns nil if we were able to change the current position. Returns error // Returns nil if we were able to change the current position.
// in any other case. // Returns error in any other case.
func (q *qLogFile) SeekStart() (int64, error) { func (q *QLogFile) SeekStart() (int64, error) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
// Empty the buffer. // Empty the buffer
q.buffer = nil q.buffer = nil
// First of all, check the file size. // First of all, check the file size
fileInfo, err := q.file.Stat() fileInfo, err := q.file.Stat()
if err != nil { if err != nil {
return 0, err return 0, err
} }
// Place the position to the very end of file. // Place the position to the very end of file
q.position = fileInfo.Size() - 1 q.position = fileInfo.Size() - 1
if q.position < 0 { if q.position < 0 {
q.position = 0 q.position = 0
} }
return q.position, nil return q.position, nil
} }
// ReadNext reads the next line (in the reverse order) from the file and shifts // ReadNext reads the next line (in the reverse order) from the file
// the current position left to the next (actually prev) line. // and shifts the current position left to the next (actually prev) line.
// // returns io.EOF if there's nothing to read more
// Returns io.EOF if there's nothing more to read. func (q *QLogFile) ReadNext() (string, error) {
func (q *qLogFile) ReadNext() (string, error) {
q.lock.Lock() q.lock.Lock()
defer q.lock.Unlock() defer q.lock.Unlock()
@@ -239,34 +197,35 @@ func (q *qLogFile) ReadNext() (string, error) {
return "", err return "", err
} }
// Shift position. // Shift position
if lineIdx == 0 { if lineIdx == 0 {
q.position = 0 q.position = 0
} else { } else {
// There's usually a line break before the line, so we should shift one // there's usually a line break before the line
// more char left from the line "\nline". // so we should shift one more char left from the line
// line\nline
q.position = lineIdx - 1 q.position = lineIdx - 1
} }
return line, err return line, err
} }
// Close frees the underlying resources. // Close frees the underlying resources
func (q *qLogFile) Close() error { func (q *QLogFile) Close() error {
return q.file.Close() return q.file.Close()
} }
// readNextLine reads the next line from the specified position. This line // readNextLine reads the next line from the specified position
// actually have to END on that position. // this line actually have to END on that position.
// //
// The algorithm is: // the algorithm is:
// 1. Check if we have the buffer initialized. // 1. check if we have the buffer initialized
// 2. If it is so, scan it and look for the line there. // 2. if it is, scan it and look for the line there
// 3. If we cannot find the line there, read the prev chunk into the buffer. // 3. if we cannot find the line there, read the prev chunk into the buffer
// 4. Read the line from the buffer. // 4. read the line from the buffer
func (q *qLogFile) readNextLine(position int64) (string, int64, error) { func (q *QLogFile) readNextLine(position int64) (string, int64, error) {
relativePos := position - q.bufferStart relativePos := position - q.bufferStart
if q.buffer == nil || (relativePos < maxEntrySize && q.bufferStart != 0) { if q.buffer == nil || (relativePos < maxEntrySize && q.bufferStart != 0) {
// Time to re-init the buffer. // Time to re-init the buffer
err := q.initBuffer(position) err := q.initBuffer(position)
if err != nil { if err != nil {
return "", 0, err return "", 0, err
@@ -274,7 +233,8 @@ func (q *qLogFile) readNextLine(position int64) (string, int64, error) {
relativePos = position - q.bufferStart relativePos = position - q.bufferStart
} }
// Look for the end of the prev line, this is where we'll read from. // Look for the end of the prev line
// This is where we'll read from
startLine := int64(0) startLine := int64(0)
for i := relativePos - 1; i >= 0; i-- { for i := relativePos - 1; i >= 0; i-- {
if q.buffer[i] == '\n' { if q.buffer[i] == '\n' {
@@ -285,19 +245,18 @@ func (q *qLogFile) readNextLine(position int64) (string, int64, error) {
line := string(q.buffer[startLine:relativePos]) line := string(q.buffer[startLine:relativePos])
lineIdx := q.bufferStart + startLine lineIdx := q.bufferStart + startLine
return line, lineIdx, nil return line, lineIdx, nil
} }
// initBuffer initializes the qLogFile buffer. The goal is to read a chunk of // initBuffer initializes the QLogFile buffer.
// file that includes the line with the specified position. // the goal is to read a chunk of file that includes the line with the specified position.
func (q *qLogFile) initBuffer(position int64) error { func (q *QLogFile) initBuffer(position int64) error {
q.bufferStart = int64(0) q.bufferStart = int64(0)
if position > bufferSize { if position > bufferSize {
q.bufferStart = position - bufferSize q.bufferStart = position - bufferSize
} }
// Seek to this position. // Seek to this position
_, err := q.file.Seek(q.bufferStart, io.SeekStart) _, err := q.file.Seek(q.bufferStart, io.SeekStart)
if err != nil { if err != nil {
return err return err
@@ -312,35 +271,34 @@ func (q *qLogFile) initBuffer(position int64) error {
return err return err
} }
// readProbeLine reads a line that includes the specified position. This // readProbeLine reads a line that includes the specified position
// method is supposed to be used when we use binary search in the Seek method. // this method is supposed to be used when we use binary search in the Seek method
// In the case of consecutive reads, use readNext, cause it uses better buffer. // in the case of consecutive reads, use readNext (it uses a better buffer)
func (q *qLogFile) readProbeLine(position int64) (string, int64, int64, error) { func (q *QLogFile) readProbeLine(position int64) (string, int64, int64, error) {
// First of all, we should read a buffer that will include the query log // First of all, we should read a buffer that will include the query log line
// line. In order to do this, we'll define the boundaries. // In order to do this, we'll define the boundaries
seekPosition := int64(0) seekPosition := int64(0)
// Position relative to the buffer we're going to read. relativePos := position // position relative to the buffer we're going to read
relativePos := position
if position > maxEntrySize { if position > maxEntrySize {
seekPosition = position - maxEntrySize seekPosition = position - maxEntrySize
relativePos = maxEntrySize relativePos = maxEntrySize
} }
// Seek to this position. // Seek to this position
_, err := q.file.Seek(seekPosition, io.SeekStart) _, err := q.file.Seek(seekPosition, io.SeekStart)
if err != nil { if err != nil {
return "", 0, 0, err return "", 0, 0, err
} }
// The buffer size is 2*maxEntrySize. // The buffer size is 2*maxEntrySize
buffer := make([]byte, maxEntrySize*2) buffer := make([]byte, maxEntrySize*2)
bufferLen, err := q.file.Read(buffer) bufferLen, err := q.file.Read(buffer)
if err != nil { if err != nil {
return "", 0, 0, err return "", 0, 0, err
} }
// Now start looking for the new line character starting from the // Now start looking for the new line character starting
// relativePos and going left. // from the relativePos and going left
startLine := int64(0) startLine := int64(0)
for i := relativePos - 1; i >= 0; i-- { for i := relativePos - 1; i >= 0; i-- {
if buffer[i] == '\n' { if buffer[i] == '\n' {
@@ -348,7 +306,7 @@ func (q *qLogFile) readProbeLine(position int64) (string, int64, int64, error) {
break break
} }
} }
// Looking for the end of line now. // Looking for the end of line now
endLine := int64(bufferLen) endLine := int64(bufferLen)
lineEndIdx := endLine + seekPosition lineEndIdx := endLine + seekPosition
for i := relativePos; i < int64(bufferLen); i++ { for i := relativePos; i < int64(bufferLen); i++ {
@@ -359,13 +317,13 @@ func (q *qLogFile) readProbeLine(position int64) (string, int64, int64, error) {
} }
} }
// Finally we can return the string we were looking for. // Finally we can return the string we were looking for
lineIdx := startLine + seekPosition lineIdx := startLine + seekPosition
return string(buffer[startLine:endLine]), lineIdx, lineEndIdx, nil return string(buffer[startLine:endLine]), lineIdx, lineEndIdx, nil
} }
// readJSONValue reads a JSON string in form of '"key":"value"'. prefix must // readJSONvalue reads a JSON string in form of '"key":"value"'. prefix must be
// be of the form '"key":"' to generate less garbage. // of the form '"key":"' to generate less garbage.
func readJSONValue(s, prefix string) string { func readJSONValue(s, prefix string) string {
i := strings.Index(s, prefix) i := strings.Index(s, prefix)
if i == -1 { if i == -1 {
@@ -382,7 +340,7 @@ func readJSONValue(s, prefix string) string {
return s[start:end] return s[start:end]
} }
// readQLogTimestamp reads the timestamp field from the query log line. // readQLogTimestamp reads the timestamp field from the query log line
func readQLogTimestamp(str string) int64 { func readQLogTimestamp(str string) int64 {
val := readJSONValue(str, `"T":"`) val := readJSONValue(str, `"T":"`)
if len(val) == 0 { if len(val) == 0 {
@@ -393,12 +351,10 @@ func readQLogTimestamp(str string) int64 {
log.Error("Couldn't find timestamp: %s", str) log.Error("Couldn't find timestamp: %s", str)
return 0 return 0
} }
tm, err := time.Parse(time.RFC3339Nano, val) tm, err := time.Parse(time.RFC3339Nano, val)
if err != nil { if err != nil {
log.Error("Couldn't parse timestamp: %s", val) log.Error("Couldn't parse timestamp: %s", val)
return 0 return 0
} }
return tm.UnixNano() return tm.UnixNano()
} }

View File

@@ -72,15 +72,15 @@ func prepareTestFiles(t *testing.T, filesNum, linesNum int) []string {
return files return files
} }
// newTestQLogFile creates new *qLogFile for tests and registers the required // newTestQLogFile creates new *QLogFile for tests and registers the required
// cleanup functions. // cleanup functions.
func newTestQLogFile(t *testing.T, linesNum int) (file *qLogFile) { func newTestQLogFile(t *testing.T, linesNum int) (file *QLogFile) {
t.Helper() t.Helper()
testFile := prepareTestFiles(t, 1, linesNum)[0] testFile := prepareTestFiles(t, 1, linesNum)[0]
// Create the new qLogFile instance. // Create the new QLogFile instance.
file, err := newQLogFile(testFile) file, err := NewQLogFile(testFile)
require.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, file) assert.NotNil(t, file)
@@ -240,7 +240,7 @@ func TestQLogFile_SeekTS_bad(t *testing.T) {
} }
} }
func getQLogFileLine(q *qLogFile, lineNumber int) (line string, err error) { func getQLogFileLine(q *QLogFile, lineNumber int) (line string, err error) {
if _, err = q.SeekStart(); err != nil { if _, err = q.SeekStart(); err != nil {
return line, err return line, err
} }
@@ -256,7 +256,7 @@ func getQLogFileLine(q *qLogFile, lineNumber int) (line string, err error) {
// Check adding and loading (with filtering) entries from disk and memory. // Check adding and loading (with filtering) entries from disk and memory.
func TestQLogFile(t *testing.T) { func TestQLogFile(t *testing.T) {
// Create the new qLogFile instance. // Create the new QLogFile instance.
q := newTestQLogFile(t, 2) q := newTestQLogFile(t, 2)
// Seek to the start. // Seek to the start.
@@ -285,7 +285,7 @@ func TestQLogFile(t *testing.T) {
assert.Empty(t, line) assert.Empty(t, line)
} }
func newTestQLogFileData(t *testing.T, data string) (file *qLogFile) { func NewTestQLogFileData(t *testing.T, data string) (file *QLogFile) {
f, err := os.CreateTemp(t.TempDir(), "*.txt") f, err := os.CreateTemp(t.TempDir(), "*.txt")
require.NoError(t, err) require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, f.Close) testutil.CleanupAndRequireSuccess(t, f.Close)
@@ -293,7 +293,7 @@ func newTestQLogFileData(t *testing.T, data string) (file *qLogFile) {
_, err = f.WriteString(data) _, err = f.WriteString(data)
require.NoError(t, err) require.NoError(t, err)
file, err = newQLogFile(f.Name()) file, err = NewQLogFile(f.Name())
require.NoError(t, err) require.NoError(t, err)
testutil.CleanupAndRequireSuccess(t, file.Close) testutil.CleanupAndRequireSuccess(t, file.Close)
@@ -309,9 +309,9 @@ func TestQLog_Seek(t *testing.T) {
timestamp, _ := time.Parse(time.RFC3339Nano, "2020-08-31T18:44:25.376690873+03:00") timestamp, _ := time.Parse(time.RFC3339Nano, "2020-08-31T18:44:25.376690873+03:00")
testCases := []struct { testCases := []struct {
wantErr error
name string name string
delta int delta int
wantErr error
wantDepth int wantDepth int
}{{ }{{
name: "ok", name: "ok",
@@ -321,12 +321,12 @@ func TestQLog_Seek(t *testing.T) {
}, { }, {
name: "too_late", name: "too_late",
delta: 2, delta: 2,
wantErr: errTSTooLate, wantErr: ErrTSTooLate,
wantDepth: 2, wantDepth: 2,
}, { }, {
name: "too_early", name: "too_early",
delta: -2, delta: -2,
wantErr: errTSTooEarly, wantErr: ErrTSTooEarly,
wantDepth: 1, wantDepth: 1,
}} }}
@@ -338,7 +338,7 @@ func TestQLog_Seek(t *testing.T) {
timestamp.Add(time.Second).Format(time.RFC3339Nano), timestamp.Add(time.Second).Format(time.RFC3339Nano),
) )
q := newTestQLogFileData(t, data) q := NewTestQLogFileData(t, data)
_, depth, err := q.seekTS(timestamp.Add(time.Second * time.Duration(tc.delta)).UnixNano()) _, depth, err := q.seekTS(timestamp.Add(time.Second * time.Duration(tc.delta)).UnixNano())
require.Truef(t, errors.Is(err, tc.wantErr), "%v", err) require.Truef(t, errors.Is(err, tc.wantErr), "%v", err)

View File

@@ -9,36 +9,36 @@ import (
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
) )
// qLogReader allows reading from multiple query log files in the reverse // QLogReader allows reading from multiple query log files in the reverse order.
// order.
// //
// Please note that this is a stateful object. Internally, it contains a // Please note that this is a stateful object.
// pointer to a particular query log file, and to a specific position in this // Internally, it contains a pointer to a particular query log file, and
// file, and it reads lines in reverse order starting from that position. // to a specific position in this file, and it reads lines in reverse order
type qLogReader struct { // starting from that position.
// qFiles is an array with the query log files. The order is from oldest type QLogReader struct {
// to newest. // qFiles - array with the query log files
qFiles []*qLogFile // The order is - from oldest to newest
qFiles []*QLogFile
// currentFile is the index of the current file. currentFile int // Index of the current file
currentFile int
} }
// newQLogReader initializes a qLogReader instance with the specified files. // NewQLogReader initializes a QLogReader instance
func newQLogReader(files []string) (*qLogReader, error) { // with the specified files
qFiles := make([]*qLogFile, 0) func NewQLogReader(files []string) (*QLogReader, error) {
qFiles := make([]*QLogFile, 0)
for _, f := range files { for _, f := range files {
q, err := newQLogFile(f) q, err := NewQLogFile(f)
if err != nil { if err != nil {
if errors.Is(err, os.ErrNotExist) { if errors.Is(err, os.ErrNotExist) {
continue continue
} }
// Close what we've already opened. // Close what we've already opened.
cErr := closeQFiles(qFiles) cerr := closeQFiles(qFiles)
if cErr != nil { if cerr != nil {
log.Debug("querylog: closing files: %s", cErr) log.Debug("querylog: closing files: %s", cerr)
} }
return nil, err return nil, err
@@ -47,28 +47,31 @@ func newQLogReader(files []string) (*qLogReader, error) {
qFiles = append(qFiles, q) qFiles = append(qFiles, q)
} }
return &qLogReader{qFiles: qFiles, currentFile: len(qFiles) - 1}, nil return &QLogReader{
qFiles: qFiles,
currentFile: (len(qFiles) - 1),
}, nil
} }
// seekTS performs binary search of a query log record with the specified // seekTS performs binary search of a query log record with the specified
// timestamp. If the record is found, it sets qLogReader's position to point // timestamp. If the record is found, it sets QLogReader's position to point to
// to that line, so that the next ReadNext call returned this line. // that line, so that the next ReadNext call returned this line.
func (r *qLogReader) seekTS(timestamp int64) (err error) { func (r *QLogReader) seekTS(timestamp int64) (err error) {
for i := len(r.qFiles) - 1; i >= 0; i-- { for i := len(r.qFiles) - 1; i >= 0; i-- {
q := r.qFiles[i] q := r.qFiles[i]
_, _, err = q.seekTS(timestamp) _, _, err = q.seekTS(timestamp)
if err != nil { if err != nil {
if errors.Is(err, errTSTooEarly) { if errors.Is(err, ErrTSTooEarly) {
// Look at the next file, since we've reached the end of this // Look at the next file, since we've reached the end of this
// one. If there is no next file, it's not found. // one. If there is no next file, it's not found.
err = errTSNotFound err = ErrTSNotFound
continue continue
} else if errors.Is(err, errTSTooLate) { } else if errors.Is(err, ErrTSTooLate) {
// Just seek to the start then. timestamp is probably between // Just seek to the start then. timestamp is probably between
// the end of the previous one and the start of this one. // the end of the previous one and the start of this one.
return r.SeekStart() return r.SeekStart()
} else if errors.Is(err, errTSNotFound) { } else if errors.Is(err, ErrTSNotFound) {
return err return err
} else { } else {
return fmt.Errorf("seekts: file at index %d: %w", i, err) return fmt.Errorf("seekts: file at index %d: %w", i, err)
@@ -77,7 +80,7 @@ func (r *qLogReader) seekTS(timestamp int64) (err error) {
// The search is finished, and the searched element has been found. // The search is finished, and the searched element has been found.
// Update currentFile only, position is already set properly in // Update currentFile only, position is already set properly in
// qLogFile. // QLogFile.
r.currentFile = i r.currentFile = i
return nil return nil
@@ -90,13 +93,13 @@ func (r *qLogReader) seekTS(timestamp int64) (err error) {
return nil return nil
} }
// SeekStart changes the current position to the end of the newest file. // SeekStart changes the current position to the end of the newest file
// Please note that we're reading query log in the reverse order and that's why // Please note that we're reading query log in the reverse order
// the log starts actually at the end of file. // and that's why log start is actually the end of file
// //
// Returns nil if we were able to change the current position. Returns error // Returns nil if we were able to change the current position.
// in any other cases. // Returns error in any other case.
func (r *qLogReader) SeekStart() error { func (r *QLogReader) SeekStart() error {
if len(r.qFiles) == 0 { if len(r.qFiles) == 0 {
return nil return nil
} }
@@ -107,12 +110,10 @@ func (r *qLogReader) SeekStart() error {
return err return err
} }
// ReadNext reads the next line (in the reverse order) from the query log // ReadNext reads the next line (in the reverse order) from the query log files.
// files. Then shifts the current position left to the next (actually prev) // and shifts the current position left to the next (actually prev) line (or the next file).
// line (or the next file). // returns io.EOF if there's nothing to read more.
// func (r *QLogReader) ReadNext() (string, error) {
// Returns io.EOF if there is nothing more to read.
func (r *qLogReader) ReadNext() (string, error) {
if len(r.qFiles) == 0 { if len(r.qFiles) == 0 {
return "", io.EOF return "", io.EOF
} }
@@ -121,7 +122,7 @@ func (r *qLogReader) ReadNext() (string, error) {
q := r.qFiles[r.currentFile] q := r.qFiles[r.currentFile]
line, err := q.ReadNext() line, err := q.ReadNext()
if err != nil { if err != nil {
// Shift to the older file. // Shift to the older file
r.currentFile-- r.currentFile--
if r.currentFile < 0 { if r.currentFile < 0 {
break break
@@ -129,10 +130,10 @@ func (r *qLogReader) ReadNext() (string, error) {
q = r.qFiles[r.currentFile] q = r.qFiles[r.currentFile]
// Set its position to the start right away. // Set it's position to the start right away
_, err = q.SeekStart() _, err = q.SeekStart()
// This is unexpected, return an error right away. // This is unexpected, return an error right away
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -141,17 +142,17 @@ func (r *qLogReader) ReadNext() (string, error) {
} }
} }
// Nothing to read anymore. // Nothing to read anymore
return "", io.EOF return "", io.EOF
} }
// Close closes the qLogReader. // Close closes the QLogReader
func (r *qLogReader) Close() error { func (r *QLogReader) Close() error {
return closeQFiles(r.qFiles) return closeQFiles(r.qFiles)
} }
// closeQFiles is a helper method to close multiple qLogFile instances. // closeQFiles - helper method to close multiple QLogFile instances
func closeQFiles(qFiles []*qLogFile) error { func closeQFiles(qFiles []*QLogFile) error {
var errs []error var errs []error
for _, q := range qFiles { for _, q := range qFiles {
@@ -162,7 +163,7 @@ func closeQFiles(qFiles []*qLogFile) error {
} }
if len(errs) > 0 { if len(errs) > 0 {
return errors.List("error while closing qLogReader", errs...) return errors.List("error while closing QLogReader", errs...)
} }
return nil return nil

View File

@@ -10,15 +10,15 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
// newTestQLogReader creates new *qLogReader for tests and registers the // newTestQLogReader creates new *QLogReader for tests and registers the
// required cleanup functions. // required cleanup functions.
func newTestQLogReader(t *testing.T, filesNum, linesNum int) (reader *qLogReader) { func newTestQLogReader(t *testing.T, filesNum, linesNum int) (reader *QLogReader) {
t.Helper() t.Helper()
testFiles := prepareTestFiles(t, filesNum, linesNum) testFiles := prepareTestFiles(t, filesNum, linesNum)
// Create the new qLogReader instance. // Create the new QLogReader instance.
reader, err := newQLogReader(testFiles) reader, err := NewQLogReader(testFiles)
require.NoError(t, err) require.NoError(t, err)
assert.NotNil(t, reader) assert.NotNil(t, reader)
@@ -75,9 +75,9 @@ func TestQLogReader_Seek(t *testing.T) {
r := newTestQLogReader(t, 2, 10000) r := newTestQLogReader(t, 2, 10000)
testCases := []struct { testCases := []struct {
want error
name string name string
time string time string
want error
}{{ }{{
name: "not_too_old", name: "not_too_old",
time: "2020-02-18T22:39:35.920973+03:00", time: "2020-02-18T22:39:35.920973+03:00",
@@ -97,7 +97,7 @@ func TestQLogReader_Seek(t *testing.T) {
}, { }, {
name: "non-existent_long_ago", name: "non-existent_long_ago",
time: "2000-02-19T01:23:16.920973+03:00", time: "2000-02-19T01:23:16.920973+03:00",
want: errTSNotFound, want: ErrTSNotFound,
}, { }, {
name: "non-existent_far_ahead", name: "non-existent_far_ahead",
time: "2100-02-19T01:23:16.920973+03:00", time: "2100-02-19T01:23:16.920973+03:00",
@@ -105,7 +105,7 @@ func TestQLogReader_Seek(t *testing.T) {
}, { }, {
name: "non-existent_but_could", name: "non-existent_but_could",
time: "2020-02-18T22:36:37.000000+03:00", time: "2020-02-18T22:36:37.000000+03:00",
want: errTSNotFound, want: ErrTSNotFound,
}} }}
for _, tc := range testCases { for _, tc := range testCases {
@@ -125,9 +125,9 @@ func TestQLogReader_ReadNext(t *testing.T) {
r := newTestQLogReader(t, filesNum, linesNum) r := newTestQLogReader(t, filesNum, linesNum)
testCases := []struct { testCases := []struct {
want error
name string name string
start int start int
want error
}{{ }{{
name: "ok", name: "ok",
start: 0, start: 0,

View File

@@ -1,11 +1,9 @@
package querylog package querylog
import ( import (
"fmt"
"io" "io"
"time" "time"
"github.com/AdguardTeam/golibs/errors"
"github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/log"
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
) )
@@ -136,112 +134,84 @@ func (l *queryLog) search(params *searchParams) (entries []*logEntry, oldest tim
return entries, oldest return entries, oldest
} }
// seekRecord changes the current position to the next record older than the // searchFiles looks up log records from all log files. It optionally uses the
// provided parameter. // client cache, if provided. searchFiles does not scan more than
func (r *qLogReader) seekRecord(olderThan time.Time) (err error) { // maxFileScanEntries so callers may need to call it several times to get all
if olderThan.IsZero() { // results. oldest and total are the time of the oldest processed entry and the
return r.SeekStart() // total number of processed entries, including discarded ones, correspondingly.
} func (l *queryLog) searchFiles(
params *searchParams,
err = r.seekTS(olderThan.UnixNano()) cache clientCache,
if err == nil { ) (entries []*logEntry, oldest time.Time, total int) {
// Read to the next record, because we only need the one that goes
// after it.
_, err = r.ReadNext()
}
return err
}
// setQLogReader creates a reader with the specified files and sets the
// position to the next record older than the provided parameter.
func (l *queryLog) setQLogReader(olderThan time.Time) (qr *qLogReader, err error) {
files := []string{ files := []string{
l.logFile + ".1", l.logFile + ".1",
l.logFile, l.logFile,
} }
r, err := newQLogReader(files) r, err := NewQLogReader(files)
if err != nil { if err != nil {
return nil, fmt.Errorf("opening qlog reader: %s", err) log.Error("querylog: opening qlog reader: %s", err)
return entries, oldest, 0
} }
err = r.seekRecord(olderThan) defer func() {
if err != nil { closeErr := r.Close()
defer func() { err = errors.WithDeferred(err, r.Close()) }() if closeErr != nil {
log.Debug("querylog: cannot seek to %s: %s", olderThan, err) log.Error("querylog: closing file: %s", err)
}
}()
return nil, nil if params.olderThan.IsZero() {
err = r.SeekStart()
} else {
err = r.seekTS(params.olderThan.UnixNano())
if err == nil {
// Read to the next record, because we only need the one that goes
// after it.
_, err = r.ReadNext()
}
} }
return r, nil if err != nil {
} log.Debug("querylog: cannot seek to %s: %s", params.olderThan, err)
// readEntries reads entries from the reader to totalLimit. By default, we do return entries, oldest, 0
// not scan more than maxFileScanEntries at once. The idea is to make search }
// calls faster so that the UI could handle it and show something quicker.
// This behavior can be overridden if maxFileScanEntries is set to 0. totalLimit := params.offset + params.limit
func (l *queryLog) readEntries( oldestNano := int64(0)
r *qLogReader,
params *searchParams, // By default, we do not scan more than maxFileScanEntries at once. The
cache clientCache, // idea is to make search calls faster so that the UI could handle it and
totalLimit int, // show something quicker. This behavior can be overridden if
) (entries []*logEntry, oldestNano int64, total int) { // maxFileScanEntries is set to 0.
for total < params.maxFileScanEntries || params.maxFileScanEntries <= 0 { for total < params.maxFileScanEntries || params.maxFileScanEntries <= 0 {
ent, ts, rErr := l.readNextEntry(r, params, cache) var e *logEntry
if rErr != nil { var ts int64
if rErr == io.EOF {
e, ts, err = l.readNextEntry(r, params, cache)
if err != nil {
if err == io.EOF {
oldestNano = 0 oldestNano = 0
break break
} }
log.Error("querylog: reading next entry: %s", rErr) log.Error("querylog: reading next entry: %s", err)
} }
oldestNano = ts oldestNano = ts
total++ total++
if ent == nil { if e != nil {
continue entries = append(entries, e)
}
entries = append(entries, ent)
if len(entries) == totalLimit { if len(entries) == totalLimit {
break break
} }
} }
return entries, oldestNano, total
}
// searchFiles looks up log records from all log files. It optionally uses the
// client cache, if provided. searchFiles does not scan more than
// maxFileScanEntries so callers may need to call it several times to get all
// the results. oldest and total are the time of the oldest processed entry
// and the total number of processed entries, including discarded ones,
// correspondingly.
func (l *queryLog) searchFiles(
params *searchParams,
cache clientCache,
) (entries []*logEntry, oldest time.Time, total int) {
r, err := l.setQLogReader(params.olderThan)
if err != nil {
log.Error("querylog: %s", err)
} }
if r == nil {
return entries, oldest, 0
}
defer func() {
if closeErr := r.Close(); closeErr != nil {
log.Error("querylog: closing file: %s", closeErr)
}
}()
totalLimit := params.offset + params.limit
entries, oldestNano, total := l.readEntries(r, params, cache, totalLimit)
if oldestNano != 0 { if oldestNano != 0 {
oldest = time.Unix(0, oldestNano) oldest = time.Unix(0, oldestNano)
} }
@@ -273,11 +243,11 @@ func (f quickMatchClientFinder) findClient(clientID, ip string) (c *Client) {
} }
// readNextEntry reads the next log entry and checks if it matches the search // readNextEntry reads the next log entry and checks if it matches the search
// criteria. It optionally uses the client cache, if provided. e is nil if // criteria. It optionally uses the client cache, if provided. e is nil if the
// the entry doesn't match the search criteria. ts is the timestamp of the // entry doesn't match the search criteria. ts is the timestamp of the
// processed entry. // processed entry.
func (l *queryLog) readNextEntry( func (l *queryLog) readNextEntry(
r *qLogReader, r *QLogReader,
params *searchParams, params *searchParams,
cache clientCache, cache clientCache,
) (e *logEntry, ts int64, err error) { ) (e *logEntry, ts int64, err error) {

View File

@@ -2,25 +2,18 @@ package querylog
import "time" import "time"
// searchParams represent the search query sent by the client. // searchParams represent the search query sent by the client
type searchParams struct { type searchParams struct {
// olderThen represents a parameter for entries that are older than this // searchCriteria - list of search criteria that we use to get filter results
// parameter value. If not set, disregard it and return any value.
olderThan time.Time
// searchCriteria is a list of search criteria that we use to get filter
// results.
searchCriteria []searchCriterion searchCriteria []searchCriterion
// offset for the search. // olderThen - return entries that are older than this value
offset int // if not set - disregard it and return any value
olderThan time.Time
// limit the number of records returned. offset int // offset for the search
limit int limit int // limit the number of records returned
maxFileScanEntries int // maximum log entries to scan in query log files. if 0 - no limit
// maxFileScanEntries is a maximum of log entries to scan in query log
// files. If not set, then no limit.
maxFileScanEntries int
} }
// newSearchParams - creates an empty instance of searchParams // newSearchParams - creates an empty instance of searchParams

View File

@@ -8,16 +8,16 @@ require (
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28
github.com/kisielk/errcheck v1.6.3 github.com/kisielk/errcheck v1.6.3
github.com/kyoh86/looppointer v0.2.1 github.com/kyoh86/looppointer v0.2.1
github.com/securego/gosec/v2 v2.16.0 github.com/securego/gosec/v2 v2.15.0
golang.org/x/tools v0.9.3 golang.org/x/tools v0.8.0
golang.org/x/vuln v0.1.0 golang.org/x/vuln v0.0.0-20230418010118-28ba02ac73db
honnef.co/go/tools v0.4.3 honnef.co/go/tools v0.4.3
mvdan.cc/gofumpt v0.5.0 mvdan.cc/gofumpt v0.5.0
mvdan.cc/unparam v0.0.0-20230312165513-e84e2d14e3b8 mvdan.cc/unparam v0.0.0-20230312165513-e84e2d14e3b8
) )
require ( require (
github.com/BurntSushi/toml v1.3.1 // indirect github.com/BurntSushi/toml v1.2.1 // indirect
github.com/google/go-cmp v0.5.9 // indirect github.com/google/go-cmp v0.5.9 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/uuid v1.3.0 // indirect
github.com/gookit/color v1.5.3 // indirect github.com/gookit/color v1.5.3 // indirect
@@ -25,9 +25,9 @@ require (
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // 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 v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/exp/typeparams v0.0.0-20230522175609-2e198f4a06a1 // indirect golang.org/x/exp/typeparams v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/mod v0.10.0 // indirect golang.org/x/mod v0.10.0 // indirect
golang.org/x/sync v0.2.0 // indirect golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.8.0 // indirect golang.org/x/sys v0.7.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

View File

@@ -1,19 +1,17 @@
github.com/BurntSushi/toml v1.3.1 h1:rHnDkSK+/g6DlREUK73PkmIs60pqrnuduK+JmP++JmU= github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/BurntSushi/toml v1.3.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0= github.com/golangci/misspell v0.4.0 h1:KtVB/hTK4bbL/S6bs64rYyk8adjmh1BygbBiaAiX+a0=
github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc= github.com/golangci/misspell v0.4.0/go.mod h1:W6O/bwV6lGDxUCChm2ykw9NQdd5bYd1Xkjo88UcWyJc=
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 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -31,13 +29,13 @@ github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI= github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8= github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/securego/gosec/v2 v2.16.0 h1:Pi0JKoasQQ3NnoRao/ww/N/XdynIB9NRYYZT5CyOs5U= github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw=
github.com/securego/gosec/v2 v2.16.0/go.mod h1:xvLcVZqUfo4aAQu56TNv7/Ltz6emAOQAEsrZrt7uGlI= github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8=
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
@@ -51,8 +49,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/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 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/exp/typeparams v0.0.0-20230522175609-2e198f4a06a1 h1:pnP8r+W8Fm7XJ8CWtXi4S9oJmPBTrkfYN/dNbaPj6Y4= golang.org/x/exp/typeparams v0.0.0-20230321023759-10a507213a29 h1:e7LhZmJ631l59keHP9ssC3sgSn3/oiEHKHKXDkimURY=
golang.org/x/exp/typeparams v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230321023759-10a507213a29/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
@@ -64,12 +62,12 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -79,23 +77,23 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/vuln v0.1.0 h1:9GRdj6wAIkDrsMevuolY+SXERPjQPp2P1ysYA0jpZe0= golang.org/x/vuln v0.0.0-20230418010118-28ba02ac73db h1:tLxfII6jPR3mfwEMkyOakawu+Lldo9hIA7vliXnDZYg=
golang.org/x/vuln v0.1.0/go.mod h1:/YuzZYjGbwB8y19CisAppfyw3uTZnuCz3r+qgx/QRzU= golang.org/x/vuln v0.0.0-20230418010118-28ba02ac73db/go.mod h1:64LpnL2PuSMzFYeCmJjYiRbroOUG9aCZYznINnF5PHE=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@@ -12,18 +12,6 @@
`GET /control/dhcp/interfaces` HTTP APIs is now correctly set to `GET /control/dhcp/interfaces` HTTP APIs is now correctly set to
`application/json` as opposed to `text/plain`. `application/json` as opposed to `text/plain`.
### New HTTP API 'PUT /control/rewrite/update'
* The new `PUT /control/rewrite/update` HTTP API allows rewrite rule updates.
It accepts a JSON object with the following format:
```json
{
"target": {"domain":"example.com","answer":"answer-to-update"},
"update": {"domain":"example.com","answer":"new-answer"}
}
```
## v0.107.29: API changes ## v0.107.29: API changes

View File

@@ -1061,17 +1061,6 @@
'responses': 'responses':
'200': '200':
'description': 'OK.' 'description': 'OK.'
'/rewrite/update':
'put':
'tags':
- 'rewrite'
'operationId': 'rewriteUpdate'
'summary': 'Update a Rewrite rule'
'requestBody':
'$ref': '#/components/requestBodies/RewriteUpdate'
'responses':
'200':
'description': 'OK.'
'/i18n/change_language': '/i18n/change_language':
'post': 'post':
'deprecated': true 'deprecated': true
@@ -1322,12 +1311,6 @@
'schema': 'schema':
'$ref': '#/components/schemas/RewriteEntry' '$ref': '#/components/schemas/RewriteEntry'
'required': true 'required': true
'RewriteUpdate':
'content':
'application/json':
'schema':
'$ref': '#/components/schemas/RewriteUpdate'
'required': true
'schemas': 'schemas':
'ServerStatus': 'ServerStatus':
'type': 'object' 'type': 'object'
@@ -2719,14 +2702,6 @@
'items': 'items':
'$ref': '#/components/schemas/RewriteEntry' '$ref': '#/components/schemas/RewriteEntry'
'description': 'Rewrite rules array' 'description': 'Rewrite rules array'
'RewriteUpdate':
'type': 'object'
'description': 'Rewrite rule update object'
'properties':
'target':
'$ref': '#/components/schemas/RewriteEntry'
'update':
'$ref': '#/components/schemas/RewriteEntry'
'RewriteEntry': 'RewriteEntry':
'type': 'object' 'type': 'object'
'description': 'Rewrite rule' 'description': 'Rewrite rule'

View File

@@ -35,7 +35,7 @@ set -f -u
go_version="$( "${GO:-go}" version )" go_version="$( "${GO:-go}" version )"
readonly go_version readonly go_version
go_min_version='go1.19.10' go_min_version='go1.19.8'
go_version_msg=" go_version_msg="
warning: your go version (${go_version}) is different from the recommended minimal one (${go_min_version}). warning: your go version (${go_version}) is different from the recommended minimal one (${go_min_version}).
if you have the version installed, please set the GO environment variable. if you have the version installed, please set the GO environment variable.
@@ -160,7 +160,29 @@ run_linter "$GO" vet ./...
run_linter govulncheck ./... run_linter govulncheck ./...
run_linter gocyclo --over 10 . # Apply more lax standards to the code we haven't properly refactored yet.
run_linter gocyclo --over 13 ./internal/querylog
run_linter gocyclo --over 12 ./internal/dhcpd
# Apply the normal standards to new or somewhat refactored code.
run_linter gocyclo --over 10\
./internal/aghio/\
./internal/aghnet/\
./internal/aghos/\
./internal/aghtest/\
./internal/dnsforward/\
./internal/filtering/\
./internal/home/\
./internal/stats/\
./internal/tools/\
./internal/updater/\
./internal/next/\
./internal/version/\
./scripts/blocked-services/\
./scripts/vetted-filters/\
./scripts/translations/\
./main.go\
;
run_linter ineffassign ./... run_linter ineffassign ./...