Compare commits
18 Commits
5799-allow
...
fix-templa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d49d284d4a | ||
|
|
9fda7bfd34 | ||
|
|
826b314f19 | ||
|
|
156c199bbb | ||
|
|
040596f3a0 | ||
|
|
fde082af44 | ||
|
|
329e144b4f | ||
|
|
367b319f10 | ||
|
|
d6fe0bd740 | ||
|
|
ae7bd79a6c | ||
|
|
0211424191 | ||
|
|
ea8d634f65 | ||
|
|
0fec990bcf | ||
|
|
a7680a593a | ||
|
|
cbc7985e75 | ||
|
|
941eb1dd73 | ||
|
|
fc64e0089e | ||
|
|
24b41100c3 |
197
.github/ISSUE_TEMPLATE/bug.yml
vendored
197
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -1,109 +1,142 @@
|
||||
'body':
|
||||
- 'attributes':
|
||||
'description': >
|
||||
Please make sure that the issue is not a duplicate or a question.
|
||||
If it's a duplicate, please react to the original issue with a
|
||||
thumbs up. If it's a question, please post it to the GitHub
|
||||
Discussions page.
|
||||
'label': 'Prerequisites'
|
||||
'options':
|
||||
- 'label': >
|
||||
I have checked the
|
||||
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
|
||||
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
|
||||
and found no answer
|
||||
'required': true
|
||||
- 'label': >
|
||||
I have searched other issues and found no duplicates
|
||||
'required': true
|
||||
- 'label': >
|
||||
I want to report a bug and not ask a question
|
||||
'required': true
|
||||
'description': >
|
||||
Please make sure that the issue is not a duplicate or a question.
|
||||
If it's a duplicate, please react to the original issue with a
|
||||
thumbs up. If it's a question, please post it to the GitHub
|
||||
Discussions page.
|
||||
'label': 'Prerequisites'
|
||||
'options':
|
||||
- 'label': >
|
||||
I have checked the
|
||||
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
|
||||
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
|
||||
and found no answer
|
||||
'required': true
|
||||
- 'label': >
|
||||
I have searched other issues and found no duplicates
|
||||
'required': true
|
||||
- 'label': >
|
||||
I want to report a bug and not ask a question
|
||||
'required': true
|
||||
'id': 'prerequisites'
|
||||
'type': 'checkboxes'
|
||||
- 'attributes':
|
||||
'description': 'On which operating system type does the issue occur?'
|
||||
'label': 'Operating system type'
|
||||
'options':
|
||||
- 'FreeBSD'
|
||||
- 'Linux, OpenWrt'
|
||||
- 'Linux, Other (please mention the version in the description)'
|
||||
- 'macOS (aka Darwin)'
|
||||
- 'OpenBSD'
|
||||
- 'Windows'
|
||||
- 'Other (please mention in the description)'
|
||||
'description': 'On which operating system type does the issue occur?'
|
||||
'label': 'Operating system type'
|
||||
'options':
|
||||
- 'FreeBSD'
|
||||
- 'Linux, OpenWrt'
|
||||
- 'Linux, Other (please mention the version in the description)'
|
||||
- 'macOS (aka Darwin)'
|
||||
- 'OpenBSD'
|
||||
- 'Windows'
|
||||
- 'Other (please mention in the description)'
|
||||
'id': 'os'
|
||||
'type': 'dropdown'
|
||||
'validations':
|
||||
'required': true
|
||||
'required': true
|
||||
- 'attributes':
|
||||
'description': 'On which CPU architecture does the issue occur?'
|
||||
'label': 'CPU architecture'
|
||||
'options':
|
||||
- 'AMD64'
|
||||
- 'x86'
|
||||
- '64-bit ARM'
|
||||
- 'ARMv5'
|
||||
- 'ARMv6'
|
||||
- 'ARMv7'
|
||||
- '64-bit MIPS'
|
||||
- '64-bit MIPS LE'
|
||||
- '32-bit MIPS'
|
||||
- '32-bit MIPS LE'
|
||||
- '64-bit PowerPC LE'
|
||||
- 'Other (please mention in the description)'
|
||||
'description': 'On which CPU architecture does the issue occur?'
|
||||
'label': 'CPU architecture'
|
||||
'options':
|
||||
- 'AMD64'
|
||||
- 'x86'
|
||||
- '64-bit ARM'
|
||||
- 'ARMv5'
|
||||
- 'ARMv6'
|
||||
- 'ARMv7'
|
||||
- '64-bit MIPS'
|
||||
- '64-bit MIPS LE'
|
||||
- '32-bit MIPS'
|
||||
- '32-bit MIPS LE'
|
||||
- '64-bit PowerPC LE'
|
||||
- 'Other (please mention in the description)'
|
||||
'id': 'arch'
|
||||
'type': 'dropdown'
|
||||
'validations':
|
||||
'required': true
|
||||
'required': true
|
||||
- 'attributes':
|
||||
'description': 'How did you install AdGuard Home?'
|
||||
'label': 'Installation'
|
||||
'options':
|
||||
- 'GitHub releases or script from README'
|
||||
- 'Docker'
|
||||
- 'Snapcraft'
|
||||
- 'Custom port'
|
||||
- 'Other (please mention in the description)'
|
||||
'description': 'How did you install AdGuard Home?'
|
||||
'label': 'Installation'
|
||||
'options':
|
||||
- 'GitHub releases or script from README'
|
||||
- 'Docker'
|
||||
- 'Snapcraft'
|
||||
- 'Custom port'
|
||||
- 'Other (please mention in the description)'
|
||||
'id': 'install'
|
||||
'type': 'dropdown'
|
||||
'validations':
|
||||
'required': true
|
||||
'required': true
|
||||
- 'attributes':
|
||||
'description': 'How did you setup AdGuard Home?'
|
||||
'label': 'Setup'
|
||||
'options':
|
||||
- 'On one machine'
|
||||
- 'On a router, DHCP is handled by the router'
|
||||
- 'On a router, DHCP is handled by AdGuard Home'
|
||||
- 'Other (please mention in the description)'
|
||||
'description': 'How did you setup AdGuard Home?'
|
||||
'label': 'Setup'
|
||||
'options':
|
||||
- 'On one machine'
|
||||
- 'On a router, DHCP is handled by the router'
|
||||
- 'On a router, DHCP is handled by AdGuard Home'
|
||||
- 'Other (please mention in the description)'
|
||||
'id': 'setup'
|
||||
'type': 'dropdown'
|
||||
'validations':
|
||||
'required': true
|
||||
'required': true
|
||||
- 'attributes':
|
||||
'description': 'What version of AdGuard Home are you using?'
|
||||
'label': 'AdGuard Home version'
|
||||
'description': 'What version of AdGuard Home are you using?'
|
||||
'label': 'AdGuard Home version'
|
||||
'id': 'version'
|
||||
'type': 'input'
|
||||
'validations':
|
||||
'required': true
|
||||
'required': true
|
||||
- 'attributes':
|
||||
'description': 'Please describe the bug'
|
||||
'label': 'Description'
|
||||
'value': |
|
||||
#### What did you do?
|
||||
|
||||
#### Expected result
|
||||
|
||||
#### Actual result
|
||||
|
||||
#### Screenshots (if applicable)
|
||||
|
||||
#### Additional information
|
||||
'id': 'description'
|
||||
'description': 'Please provide a set of steps to reproduce the issue'
|
||||
'label': 'Issue Details'
|
||||
'placeholder':
|
||||
'value': |
|
||||
Steps to reproduce:
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
'id': 'what-happened'
|
||||
'type': 'textarea'
|
||||
'validations':
|
||||
'required': true
|
||||
'description': 'File a bug report'
|
||||
'name': 'Bug'
|
||||
'required': true
|
||||
- 'attributes':
|
||||
'label': 'Expected Behavior'
|
||||
'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' ]
|
||||
|
||||
77
.github/ISSUE_TEMPLATE/feature.yml
vendored
77
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -1,41 +1,54 @@
|
||||
'body':
|
||||
- 'attributes':
|
||||
'description': >
|
||||
Please make sure that the issue is not a duplicate or a question.
|
||||
If it's a duplicate, please react to the original issue with a
|
||||
thumbs up. If it's a question, please post it to the GitHub
|
||||
Discussions page.
|
||||
'label': 'Prerequisites'
|
||||
'options':
|
||||
- 'label': >
|
||||
I have checked the
|
||||
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
|
||||
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
|
||||
and found no answer
|
||||
'required': true
|
||||
- 'label': >
|
||||
I have searched other issues and found no duplicates
|
||||
'required': true
|
||||
- 'label': >
|
||||
I want to request a feature or enhancement and not ask a
|
||||
question
|
||||
'required': true
|
||||
'description': >
|
||||
Please make sure that the issue is not a duplicate or a question.
|
||||
If it's a duplicate, please react to the original issue with a
|
||||
thumbs up. If it's a question, please post it to the GitHub
|
||||
Discussions page.
|
||||
'label': 'Prerequisites'
|
||||
'options':
|
||||
- 'label': >
|
||||
I have checked the
|
||||
[Wiki](https://github.com/AdguardTeam/AdGuardHome/wiki) and
|
||||
[Discussions](https://github.com/AdguardTeam/AdGuardHome/discussions)
|
||||
and found no answer
|
||||
'required': true
|
||||
- 'label': >
|
||||
I have searched other issues and found no duplicates
|
||||
'required': true
|
||||
- 'label': >
|
||||
I want to request a feature or enhancement and not ask a
|
||||
question
|
||||
'required': true
|
||||
'id': 'prerequisites'
|
||||
'type': 'checkboxes'
|
||||
- 'attributes':
|
||||
'description': 'Please describe the request'
|
||||
'label': 'Description'
|
||||
'value': |
|
||||
#### What problem are you trying to solve?
|
||||
|
||||
#### Proposed solution
|
||||
|
||||
#### Alternatives considered
|
||||
|
||||
#### Additional information
|
||||
'id': 'description'
|
||||
'description': 'What happened?'
|
||||
'placeholder': |
|
||||
Is your feature request related to a problem? Please add a clear and
|
||||
concise description of what the problem is.
|
||||
'label': 'Issue Details'
|
||||
'id': 'what-happened'
|
||||
'type': 'textarea'
|
||||
'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'
|
||||
'name': 'Feature request or enhancement'
|
||||
'labels': [ 'feature request' ]
|
||||
|
||||
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'build'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.19.8'
|
||||
'GO_VERSION': '1.19.10'
|
||||
'NODE_VERSION': '14'
|
||||
|
||||
'on':
|
||||
|
||||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
@@ -1,7 +1,7 @@
|
||||
'name': 'lint'
|
||||
|
||||
'env':
|
||||
'GO_VERSION': '1.19.8'
|
||||
'GO_VERSION': '1.19.10'
|
||||
|
||||
'on':
|
||||
'push':
|
||||
|
||||
34
.github/workflows/potential-duplicates.yml
vendored
Normal file
34
.github/workflows/potential-duplicates.yml
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
'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}}
|
||||
63
CHANGELOG.md
63
CHANGELOG.md
@@ -14,11 +14,11 @@ and this project adheres to
|
||||
<!--
|
||||
## [v0.108.0] - TBA
|
||||
|
||||
## [v0.107.30] - 2023-04-26 (APPROX.)
|
||||
## [v0.107.32] - 2023-06-28 (APPROX.)
|
||||
|
||||
See also the [v0.107.30 GitHub milestone][ms-v0.107.30].
|
||||
See also the [v0.107.32 GitHub milestone][ms-v0.107.32].
|
||||
|
||||
[ms-v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/milestone/66?closed=1
|
||||
[ms-v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/milestone/68?closed=1
|
||||
|
||||
NOTE: Add new changes BELOW THIS COMMENT.
|
||||
-->
|
||||
@@ -30,8 +30,46 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
||||
|
||||
### Fixed
|
||||
|
||||
- Private networks are now ignored from allowed/blocked clients access lists.
|
||||
These addresses are always allowed ([#5799]).
|
||||
- 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].
|
||||
|
||||
### Security
|
||||
|
||||
- Go version has been updated to prevent the possibility of exploiting the
|
||||
CVE-2023-29402, CVE-2023-29403, and CVE-2023-29404 Go vulnerabilities fixed in
|
||||
[Go 1.19.10][go-1.19.10].
|
||||
|
||||
### Fixed
|
||||
|
||||
- Unquoted IPv6 bind hosts with trailing colons erroneously considered
|
||||
unspecified addresses are now properly validated ([#5752]).
|
||||
@@ -43,13 +81,10 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
||||
- Provided bootstrap servers are now used to resolve the hostnames of plain
|
||||
UDP/TCP upstream servers.
|
||||
|
||||
[#1577]: https://github.com/AdguardTeam/AdGuardHome/issues/1577
|
||||
[#5716]: https://github.com/AdguardTeam/AdGuardHome/issues/5716
|
||||
[#5799]: https://github.com/AdguardTeam/AdGuardHome/issues/5799
|
||||
|
||||
<!--
|
||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||
-->
|
||||
[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
|
||||
|
||||
|
||||
|
||||
@@ -1974,11 +2009,13 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
|
||||
|
||||
|
||||
<!--
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.30...HEAD
|
||||
[v0.107.30]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...v0.107.30
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.32...HEAD
|
||||
[v0.107.32]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...v0.107.32
|
||||
-->
|
||||
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.29...HEAD
|
||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.31...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.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
|
||||
|
||||
@@ -466,6 +466,10 @@ bug or implementing the feature.
|
||||
Home](https://github.com/ebrianne/adguard-exporter) by
|
||||
[@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
|
||||
routers](https://forum.gl-inet.com/t/adguardhome-on-gl-routers/10664) by
|
||||
[Gl-Inet](https://gl-inet.com/).
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
# Make sure to sync any changes with the branch overrides below.
|
||||
'variables':
|
||||
'channel': 'edge'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
|
||||
'stages':
|
||||
- 'Build frontend':
|
||||
@@ -232,25 +232,24 @@
|
||||
case "$channel"
|
||||
in
|
||||
('release')
|
||||
snapchannel='candidate'
|
||||
;;
|
||||
snapchannel='candidate'
|
||||
;;
|
||||
('beta')
|
||||
snapchannel='beta'
|
||||
;;
|
||||
snapchannel='beta'
|
||||
;;
|
||||
('edge')
|
||||
snapchannel='edge'
|
||||
;;
|
||||
snapchannel='edge'
|
||||
;;
|
||||
(*)
|
||||
echo "invalid channel '$channel'"
|
||||
exit 1
|
||||
;;
|
||||
echo "invalid channel '$channel'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
env\
|
||||
SNAPCRAFT_CHANNEL="$snapchannel"\
|
||||
SNAPCRAFT_EMAIL="${bamboo.snapcraftEmail}"\
|
||||
SNAPCRAFT_MACAROON="${bamboo.snapcraftMacaroonPassword}"\
|
||||
SNAPCRAFT_UBUNTU_DISCHARGE="${bamboo.snapcraftUbuntuDischargePassword}"\
|
||||
SNAPCRAFT_STORE_CREDENTIALS="${bamboo.snapcraftMacaroonPassword}"\
|
||||
../bamboo-deploy-publisher/deploy.sh adguard-home-snap
|
||||
'final-tasks':
|
||||
- 'clean'
|
||||
@@ -280,9 +279,9 @@
|
||||
|
||||
if [ "$channel" != 'release' ] && [ "${channel}" != 'beta' ]
|
||||
then
|
||||
echo "don't publish to GitHub Releases for this channel"
|
||||
echo "don't publish to GitHub Releases for this channel"
|
||||
|
||||
exit 0
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cd ./dist/
|
||||
@@ -331,7 +330,7 @@
|
||||
# need to build a few of these.
|
||||
'variables':
|
||||
'channel': 'beta'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
# release-vX.Y.Z branches are the branches from which the actual final release
|
||||
# is built.
|
||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||
@@ -346,4 +345,4 @@
|
||||
# are the ones that actually get released.
|
||||
'variables':
|
||||
'channel': 'release'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'key': 'AHBRTSPECS'
|
||||
'name': 'AdGuard Home - Build and run tests'
|
||||
'variables':
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.3'
|
||||
'dockerGo': 'adguard/golang-ubuntu:6.7'
|
||||
|
||||
'stages':
|
||||
- 'Tests':
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
"dns_allowlists": "Белыя спісы DNS",
|
||||
"dns_blocklists_desc": "AdGuard Home будзе блакаваць дамены з чорных спісаў.",
|
||||
"dns_allowlists_desc": "Дамены з белых спісаў DNS будуць дазволены, нават калі яны знаходзяцца ў любым з чорных спісаў.",
|
||||
"custom_filtering_rules": "Карыстацкія правілы фільтрацыі",
|
||||
"custom_filtering_rules": "Карыстальніцкія правілы фільтрацыі",
|
||||
"encryption_settings": "Налады шыфравання",
|
||||
"dhcp_settings": "Налады DHCP",
|
||||
"upstream_dns": "Upstream DNS-серверы",
|
||||
@@ -247,7 +247,7 @@
|
||||
"loading_table_status": "Загрузка...",
|
||||
"page_table_footer_text": "Старонка",
|
||||
"rows_table_footer_text": "радкоў",
|
||||
"updated_custom_filtering_toast": "Занесены змены ў карыстацкія правілы",
|
||||
"updated_custom_filtering_toast": "Карыстальніцкія правілы паспяхова захаваны",
|
||||
"rule_removed_from_custom_filtering_toast": "Карыстацкае правіла выдалена: {{rule}}",
|
||||
"rule_added_to_custom_filtering_toast": "Карыстацкае правіла дададзена: {{rule}}",
|
||||
"query_log_response_status": "Статус: {{value}}",
|
||||
@@ -568,7 +568,7 @@
|
||||
"check_desc": "Праверыць фільтрацыю імя хаста",
|
||||
"check": "Праверыць",
|
||||
"form_enter_host": "Увядзіце імя хаста",
|
||||
"filtered_custom_rules": "Адфільтраваны з дапамогай карыстацкіх правілаў фільтрацыі",
|
||||
"filtered_custom_rules": "Адфільтраваны з дапамогай карыстальніцкіх правіл фільтрацыі",
|
||||
"choose_from_list": "Абраць са спіса",
|
||||
"add_custom_list": "Дадаць свой спіс",
|
||||
"host_whitelisted": "Хост занесены ў белы спіс",
|
||||
|
||||
@@ -268,6 +268,8 @@
|
||||
"blocking_mode_nxdomain": "NXDOMAIN: پاسخ با کُد NXDOMAIN",
|
||||
"blocking_mode_null_ip": "Null IP: پاسخ با آدرس آی پی صفر(0.0.0.0 برای A; :: برای AAAA)",
|
||||
"blocking_mode_custom_ip": "آی پی دستی: پاسخ با آدرس آی پی دستی تنظیم شده",
|
||||
"theme_light": "پوسته روشن",
|
||||
"theme_dark": "پوسته تیره",
|
||||
"upstream_dns_client_desc": "اگر این فیلد را خالی نگه دارید، AdGuard Home از سرور پیکربندی شده در <0> تنظیماتDNS </0> استفاده می کند.",
|
||||
"tracker_source": "منبع ردیاب",
|
||||
"source_label": "منبع",
|
||||
@@ -567,5 +569,6 @@
|
||||
"use_saved_key": "از کلید ذخیره شده قبلی استفاده کنید",
|
||||
"parental_control": "نظارت والدین",
|
||||
"safe_browsing": "وب گردی اَمن",
|
||||
"form_error_password_length": "رمزعبور باید حداقل {{value}} کاراکتر باشد."
|
||||
"form_error_password_length": "رمزعبور باید حداقل {{value}} کاراکتر باشد.",
|
||||
"protection_section_label": "حفاظت"
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
"request_details": "Pyynnön tiedot",
|
||||
"client_details": "Päätelaitteen tiedot",
|
||||
"details": "Yksityiskohdat",
|
||||
"back": "Takaisin",
|
||||
"back": "Palaa takaisin",
|
||||
"dashboard": "Tila",
|
||||
"settings": "Asetukset",
|
||||
"filters": "Suodattimet",
|
||||
@@ -146,8 +146,8 @@
|
||||
"no_servers_specified": "Palvelimia ei ole määritetty",
|
||||
"general_settings": "Yleiset asetukset",
|
||||
"dns_settings": "DNS-asetukset",
|
||||
"dns_blocklists": "DNS-estolistat",
|
||||
"dns_allowlists": "DNS-sallittujen listat",
|
||||
"dns_blocklists": "DNS-estot",
|
||||
"dns_allowlists": "DNS-sallinnat",
|
||||
"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.",
|
||||
"custom_filtering_rules": "Omat suodatussäännöt",
|
||||
@@ -627,7 +627,7 @@
|
||||
"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.",
|
||||
"filter_category_general": "Yleiset",
|
||||
"filter_category_security": "Turvallisuus",
|
||||
"filter_category_security": "Tietoturva",
|
||||
"filter_category_regional": "Alueelliset",
|
||||
"filter_category_other": "Muut",
|
||||
"filter_category_general_desc": "Listat, jotka estävät seurannan ja mainokset useimmilla laitteilla",
|
||||
|
||||
@@ -282,6 +282,8 @@
|
||||
"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",
|
||||
"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>.",
|
||||
"tracker_source": "Sporerkilde",
|
||||
"source_label": "Kilde",
|
||||
|
||||
@@ -222,7 +222,7 @@
|
||||
"all_lists_up_to_date_toast": "Wszystkie listy są już aktualne",
|
||||
"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_not_ok_toast": "Serwer \"{{key}}\": nie można go użyć, sprawdź, czy napisałeś go poprawnie",
|
||||
"dns_test_not_ok_toast": "Serwer \"{{key}}\": nie może być użyte, sprawdź, czy zapisano go poprawnie",
|
||||
"dns_test_warning_toast": "Upstream \"{{key}}\" nie odpowiada na zapytania testowe i może nie działać prawidłowo",
|
||||
"unblock": "Odblokuj",
|
||||
"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_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_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_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_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_2": "Kliknij Sieć.",
|
||||
@@ -396,7 +396,7 @@
|
||||
"encryption_issuer": "Zgłaszający",
|
||||
"encryption_hostnames": "Nazwy hostów",
|
||||
"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_expired_certificate": "Twój certyfikat SSL wygasł. Zaktualizuj <0>Ustawienia szyfrowania</0>.",
|
||||
"form_error_port_range": "Wpisz numer portu z zakresu 80-65535",
|
||||
@@ -542,7 +542,7 @@
|
||||
"password_placeholder": "Wpisz hasło",
|
||||
"sign_in": "Zaloguj się",
|
||||
"sign_out": "Wyloguj się",
|
||||
"forgot_password": "Zapomniałeś hasła?",
|
||||
"forgot_password": "Zapomniano hasła?",
|
||||
"forgot_password_desc": "Wykonaj <0>te kroki</0>, aby utworzyć nowe hasło do konta użytkownika.",
|
||||
"location": "Lokalizacja",
|
||||
"orgname": "Nazwa firmy",
|
||||
|
||||
@@ -529,7 +529,7 @@
|
||||
"ignore_domains": "Domínios ignorados (separados por nova linha)",
|
||||
"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_query": "As consultas para esses domínios não são gravadas no log de consulta",
|
||||
"ignore_domains_desc_query": "As consultas para esses domínios não são gravadas no registro de consulta",
|
||||
"interval_hours": "{{count}} hora",
|
||||
"interval_hours_plural": "{{count}} horas",
|
||||
"filters_configuration": "Configuração de filtros",
|
||||
|
||||
@@ -529,7 +529,7 @@
|
||||
"ignore_domains": "Domínios ignorados (separados por nova linha)",
|
||||
"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_query": "As consultas para estes domínios nãoaparecem no registo de consultas",
|
||||
"ignore_domains_desc_query": "As consultas para estes domínios não aparecem no registo de consultas",
|
||||
"interval_hours": "{{count}} hora",
|
||||
"interval_hours_plural": "{{count}} horas",
|
||||
"filters_configuration": "Definição dos filtros",
|
||||
|
||||
@@ -167,6 +167,7 @@
|
||||
"enabled_parental_toast": "«Батьківський контроль» увімкнено",
|
||||
"disabled_safe_search_toast": "Безпечний пошук вимкнено",
|
||||
"enabled_save_search_toast": "Безпечний пошук увімкнено",
|
||||
"updated_save_search_toast": "Налаштування Безпечного пошуку оновлено",
|
||||
"enabled_table_header": "Увімкнено",
|
||||
"name_table_header": "Назва",
|
||||
"list_url_table_header": "URL списку",
|
||||
@@ -290,6 +291,8 @@
|
||||
"rate_limit": "Обмеження швидкості",
|
||||
"edns_enable": "Увімкнути відправку EDNS Client Subnet",
|
||||
"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» означатиме необмежену кількість.",
|
||||
"blocking_ipv4_desc": "IP-адреса, яку потрібно видати для заблокованого A запиту",
|
||||
"blocking_ipv6_desc": "IP-адреса, яку потрібно видати для заблокованого АААА запиту",
|
||||
@@ -523,6 +526,10 @@
|
||||
"statistics_retention_confirm": "Ви впевнені, що хочете змінити тривалість статистики? Якщо зменшити значення інтервалу, деякі дані будуть втрачені",
|
||||
"statistics_cleared": "Статистику успішно очищено",
|
||||
"statistics_enable": "Увімкнути статистику",
|
||||
"ignore_domains": "Ігноровані домени (по одному на рядок)",
|
||||
"ignore_domains_title": "Ігноровані домени",
|
||||
"ignore_domains_desc_stats": "Запити для цих доменів в статистику не пишуться",
|
||||
"ignore_domains_desc_query": "Запити для цих доменів не записуються до журналу запитів",
|
||||
"interval_hours": "{{count}} година",
|
||||
"interval_hours_plural": "{{count}} годин(и)",
|
||||
"filters_configuration": "Конфігурація фільтрів",
|
||||
@@ -643,5 +650,29 @@
|
||||
"confirm_dns_cache_clear": "Ви впевнені, що бажаєте очистити кеш DNS?",
|
||||
"cache_cleared": "Кеш DNS успішно очищено",
|
||||
"clear_cache": "Очистити кеш",
|
||||
"protection_section_label": "Захист"
|
||||
"make_static": "Зробити статичним",
|
||||
"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": "Ігноруйте цей клієнт в статистиці"
|
||||
}
|
||||
|
||||
@@ -100,6 +100,12 @@ export default {
|
||||
"homepage": "https://github.com/DandelionSprout/adfilt",
|
||||
"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": {
|
||||
"name": "POL: Polish filters for Pi-hole",
|
||||
"categoryId": "regional",
|
||||
@@ -118,6 +124,12 @@ export default {
|
||||
"homepage": "https://github.com/bkrucarci/turk-adlist",
|
||||
"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": {
|
||||
"name": "VNM: ABPVN List",
|
||||
"categoryId": "regional",
|
||||
@@ -214,6 +226,12 @@ export default {
|
||||
"homepage": "https://github.com/durablenapkin/scamblocklist",
|
||||
"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": {
|
||||
"name": "Stalkerware Indicators List",
|
||||
"categoryId": "security",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"timeUpdated": "2023-04-06T10:46:09.881Z",
|
||||
"timeUpdated": "2023-06-01T00:12:12.660Z",
|
||||
"categories": {
|
||||
"0": "audio_video_player",
|
||||
"1": "comments",
|
||||
@@ -19526,6 +19526,13 @@
|
||||
"companyId": "qualcomm",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"qualcomm_location_service": {
|
||||
"name": "Qualcomm Location Service",
|
||||
"categoryId": 15,
|
||||
"url": "https://www.qualcomm.com/site/privacy/services",
|
||||
"companyId": "qualcomm",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"recaptcha": {
|
||||
"name": "reCAPTCHA",
|
||||
"categoryId": 8,
|
||||
@@ -19533,6 +19540,55 @@
|
||||
"companyId": "google",
|
||||
"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": {
|
||||
"name": "Sectigo Limited",
|
||||
"categoryId": 5,
|
||||
@@ -19589,6 +19645,13 @@
|
||||
"companyId": "telstra",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"ubuntu": {
|
||||
"name": "Ubuntu",
|
||||
"categoryId": 8,
|
||||
"url": "https://ubuntu.com/",
|
||||
"companyId": "ubuntu",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"unity_ads": {
|
||||
"name": "Unity Ads",
|
||||
"categoryId": 4,
|
||||
@@ -19651,6 +19714,13 @@
|
||||
"url": "https://www.3gpp.org/",
|
||||
"companyId": "3gpp",
|
||||
"source": "AdGuard"
|
||||
},
|
||||
"7plus": {
|
||||
"name": "7plus",
|
||||
"categoryId": 0,
|
||||
"url": "https://7plus.com.au/",
|
||||
"companyId": "7plus",
|
||||
"source": "AdGuard"
|
||||
}
|
||||
},
|
||||
"trackerDomains": {
|
||||
@@ -19843,8 +19913,8 @@
|
||||
"adfreestyle.pl": "adfreestyle",
|
||||
"adfront.org": "adfront",
|
||||
"adfrontiers.com": "adfrontiers",
|
||||
"adgear.com": "adgear",
|
||||
"adgrx.com": "adgear",
|
||||
"adgear.com": "samsungads",
|
||||
"adgrx.com": "samsungads",
|
||||
"adgebra.co.in": "adgebra",
|
||||
"adgenie.co.uk": "adgenie",
|
||||
"ad.adgile.com": "adgile",
|
||||
@@ -24056,6 +24126,10 @@
|
||||
"safebrowsing.g.applimg.com": "apple",
|
||||
"applvn.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",
|
||||
"azure.com": "azure",
|
||||
"trafficmanager.net": "azure",
|
||||
@@ -24063,6 +24137,21 @@
|
||||
"mobileapptracking.com": "branch",
|
||||
"bttn.io": "button",
|
||||
"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",
|
||||
"phicdn.net": "digicert_trust_seal",
|
||||
"domain.glass": "domainglass",
|
||||
@@ -24092,6 +24181,9 @@
|
||||
"qy.net": "iqiyi",
|
||||
"iqiyi.com": "iqiyi",
|
||||
"iq.com": "iqiyi",
|
||||
"ironsrc.com": "ironsource",
|
||||
"ironsrc.net": "ironsource",
|
||||
"supersonicads.com": "ironsource",
|
||||
"karambasecurity.com": "karambasecurity",
|
||||
"kik.com": "kik",
|
||||
"apikik.com": "kik",
|
||||
@@ -24121,6 +24213,23 @@
|
||||
"mozilla.com": "mozilla",
|
||||
"mozilla.net": "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",
|
||||
"netify.ai": "netify",
|
||||
"nab.com": "nab",
|
||||
@@ -24144,9 +24253,69 @@
|
||||
"oztam.com.au": "oztam",
|
||||
"plex.tv": "plex",
|
||||
"plex.direct": "plex",
|
||||
"xtracloud.net": "qualcomm",
|
||||
"qualcomm.com": "qualcomm",
|
||||
"gpsonextra.net": "qualcomm_location_service",
|
||||
"izatcloud.net": "qualcomm_location_service",
|
||||
"xtracloud.net": "qualcomm_location_service",
|
||||
"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",
|
||||
"showrss.info": "showrss",
|
||||
"similarweb.io": "similarweb",
|
||||
@@ -24171,6 +24340,13 @@
|
||||
"telstra.com.au": "telstra",
|
||||
"telstra.com": "telstra",
|
||||
"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",
|
||||
"exp-tas.com": "vscode",
|
||||
"vscode-unpkg.net": "vscode",
|
||||
@@ -24190,6 +24366,7 @@
|
||||
"yandex.kz": "yandex",
|
||||
"appmetrica.yandex.com": "yandex_appmetrica",
|
||||
"3gppnetwork.org": "3gpp",
|
||||
"3gpp.org": "3gpp"
|
||||
"3gpp.org": "3gpp",
|
||||
"swm.digital": "7plus"
|
||||
}
|
||||
}
|
||||
|
||||
28
go.mod
28
go.mod
@@ -3,7 +3,7 @@ module github.com/AdguardTeam/AdGuardHome
|
||||
go 1.19
|
||||
|
||||
require (
|
||||
github.com/AdguardTeam/dnsproxy v0.49.1
|
||||
github.com/AdguardTeam/dnsproxy v0.50.2
|
||||
github.com/AdguardTeam/golibs v0.13.2
|
||||
github.com/AdguardTeam/urlfilter v0.16.1
|
||||
github.com/NYTimes/gziphandler v1.1.1
|
||||
@@ -20,20 +20,20 @@ require (
|
||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
|
||||
github.com/kardianos/service v1.2.2
|
||||
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
|
||||
github.com/mdlayher/netlink v1.7.1
|
||||
github.com/mdlayher/packet v1.1.1
|
||||
github.com/mdlayher/netlink v1.7.2
|
||||
github.com/mdlayher/packet v1.1.2
|
||||
// TODO(a.garipov): This package is deprecated; find a new one or use our
|
||||
// own code for that. Perhaps, use gopacket.
|
||||
github.com/mdlayher/raw v0.1.0
|
||||
github.com/miekg/dns v1.1.53
|
||||
github.com/quic-go/quic-go v0.33.0
|
||||
github.com/miekg/dns v1.1.54
|
||||
github.com/quic-go/quic-go v0.35.1
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/ti-mo/netfilter v0.5.0
|
||||
go.etcd.io/bbolt v1.3.7
|
||||
golang.org/x/crypto v0.8.0
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29
|
||||
golang.org/x/net v0.9.0
|
||||
golang.org/x/sys v0.7.0
|
||||
golang.org/x/crypto v0.9.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/net v0.10.0
|
||||
golang.org/x/sys v0.8.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
howett.net/plist v1.0.0
|
||||
@@ -48,9 +48,9 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect
|
||||
github.com/mdlayher/socket v0.4.0 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect
|
||||
github.com/mdlayher/socket v0.4.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.10.0 // indirect
|
||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.17 // 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/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/text v0.9.0 // indirect
|
||||
golang.org/x/tools v0.8.0 // indirect
|
||||
golang.org/x/tools v0.9.3 // indirect
|
||||
)
|
||||
|
||||
101
go.sum
101
go.sum
@@ -1,5 +1,5 @@
|
||||
github.com/AdguardTeam/dnsproxy v0.49.1 h1:JpStBK05uCgA3ldleaNLRmIwE9V7vRg7/kVJQSdnQYg=
|
||||
github.com/AdguardTeam/dnsproxy v0.49.1/go.mod h1:Y7g7jRTd/u7+KJ/QvnGI2PCE8vnisp6EsW47/Sz0DZw=
|
||||
github.com/AdguardTeam/dnsproxy v0.50.2 h1:p1471SsMZ6SMo7T51Olw4aNluahvMwSLMorwxYV18ts=
|
||||
github.com/AdguardTeam/dnsproxy v0.50.2/go.mod h1:CQhZTkqC8X0ID6glrtyaxgqRRdiYfn1gJulC1cZ5Dn8=
|
||||
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.13.2 h1:BPASsyQKmb+b8VnvsNOHp7bKfcZl9Z+Z2UhPjOiupSc=
|
||||
@@ -31,10 +31,9 @@ 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/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2PccwOFQ=
|
||||
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/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
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-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
|
||||
@@ -45,28 +44,20 @@ 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/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.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.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/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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/pprof v0.0.0-20230406165453-00490a63f317 h1:hFhpt7CTmR3DX+b4R19ydQFtofxT0Sv3QsKNMVQYTMQ=
|
||||
github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs=
|
||||
github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA=
|
||||
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/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/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/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||
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/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q=
|
||||
github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
@@ -74,45 +65,33 @@ github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2C
|
||||
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/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/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
|
||||
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.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
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/go.mod h1:ZFUnHIVchZ9lJoWoEGUg8Q3M4U8aNNWA3CVSUTkW4og=
|
||||
github.com/mdlayher/netlink v0.0.0-20190313131330-258ea9dff42c/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
github.com/mdlayher/netlink v0.0.0-20190409211403-11939a169225/go.mod h1:eQB3mZE4aiYnlUsyGGCOpPETfdQq4Jhsgf1fk3cwQaA=
|
||||
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/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
|
||||
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
|
||||
github.com/mdlayher/packet v1.0.0/go.mod h1:eE7/ctqDhoiRhQ44ko5JZU2zxB88g+JH/6jmnjzPjOU=
|
||||
github.com/mdlayher/packet v1.1.1 h1:7Fv4OEMYqPl7//uBm04VgPpnSNi8fbBZznppgh6WMr8=
|
||||
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/packet v1.1.2 h1:3Up1NG6LZrsgDVn6X4L9Ge/iyRyxFEFD9o6Pr3Q1nQY=
|
||||
github.com/mdlayher/packet v1.1.2/go.mod h1:GEu1+n9sG5VtiRE4SydOmX5GTwyyYlteZiFU+x0kew4=
|
||||
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/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
|
||||
github.com/mdlayher/socket v0.4.0 h1:280wsy40IC9M9q1uPGcLBwXpcTQDtoGwVt+BNoITxIw=
|
||||
github.com/mdlayher/socket v0.4.0/go.mod h1:xxFqz5GRCUN3UEOm9CZqEJsAbe1C8OwSK46NlmWuVoc=
|
||||
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
|
||||
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/miekg/dns v1.1.53 h1:ZBkuHr5dxHtB1caEOlZTLPo7D3L3TWckgUUs/RHfDxw=
|
||||
github.com/miekg/dns v1.1.53/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
|
||||
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
|
||||
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/onsi/ginkgo/v2 v2.9.2 h1:BA2GMJOtfGAfagzYtrAlufIP0lq6QERkFmHLMLPwFSU=
|
||||
github.com/onsi/ginkgo/v2 v2.9.2/go.mod h1:WHcJJG2dIlcCqVfBAwUCrJxSPFb6v4azBwgxeMeDuts=
|
||||
github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E=
|
||||
github.com/onsi/ginkgo/v2 v2.10.0 h1:sfUl4qgLdvkChZrWCYndY2EAu9BRIw1YphNAzy1VNWs=
|
||||
github.com/onsi/ginkgo/v2 v2.10.0/go.mod h1:UDQOh5wbQUlMnkLfVaIUMtQ1Vus92oM+P2JX1aulgcE=
|
||||
github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU=
|
||||
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/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
@@ -129,12 +108,10 @@ 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-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/quic-go v0.33.0 h1:ItNoTDN/Fm/zBlq769lLJc8ECe9gYaW40veHCCco7y0=
|
||||
github.com/quic-go/quic-go v0.33.0/go.mod h1:YMuhaAV9/jIu0XclDXwZPAsP/2Kgr5yMYhe9oxhhOFA=
|
||||
github.com/quic-go/quic-go v0.35.1 h1:b0kzj6b/cQAf05cT0CkQubHM31wiA+xH3IBkxP62poo=
|
||||
github.com/quic-go/quic-go v0.35.1/go.mod h1:+4CVgVppm0FNjpG3UcX8Joi/frKOH7/ciD5yGcwOO1g=
|
||||
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/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.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
@@ -154,7 +131,6 @@ 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/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
|
||||
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/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
@@ -162,11 +138,10 @@ go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
|
||||
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-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
|
||||
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
|
||||
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/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
|
||||
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
@@ -174,40 +149,25 @@ 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/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-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-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-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-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-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
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.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.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-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-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-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-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-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-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -221,8 +181,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-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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
@@ -232,12 +192,11 @@ 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/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-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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
||||
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
|
||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -1,46 +1,60 @@
|
||||
# DHCP server
|
||||
# Testing DHCP Server
|
||||
|
||||
Contents:
|
||||
* [Test setup with Virtual Box](#vbox)
|
||||
* [Test setup with Virtual Box](#vbox)
|
||||
* [Quick test with DHCPTest](#dhcptest)
|
||||
|
||||
<a id="vbox"></a>
|
||||
## Test setup with Virtual Box
|
||||
## <a href="#vbox" id="vbox" name="vbox">Test setup with Virtual Box</a>
|
||||
|
||||
To set up a test environment for DHCP server you need:
|
||||
### Prerequisites
|
||||
|
||||
* Linux host machine
|
||||
* Virtual Box
|
||||
* Virtual machine (guest OS doesn't matter)
|
||||
To set up a test environment for DHCP server you will need:
|
||||
|
||||
### Configure client
|
||||
* Linux AG Home host machine (Virtual).
|
||||
* Virtual Box.
|
||||
* Virtual machine (guest OS doesn't matter).
|
||||
|
||||
1. Install Virtual Box and run the following command to create a Host-Only network:
|
||||
### Configure Virtual Box
|
||||
|
||||
$ VBoxManage hostonlyif create
|
||||
1. Install Virtual Box and run the following command to create a Host-Only
|
||||
network:
|
||||
|
||||
You can check its status by `ip a` command.
|
||||
```sh
|
||||
$ VBoxManage hostonlyif create
|
||||
```
|
||||
|
||||
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
|
||||
DHCP and the OS should ask for a IP address from our DHCP server.
|
||||
|
||||
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.
|
||||
4. To see the current IP addresses on client OS you can use `ip a` command on
|
||||
Linux or `ipconfig` on Windows.
|
||||
|
||||
4. To see the current IP address on client OS you can use `ip a` command on Linux or `ipconfig` on Windows.
|
||||
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.
|
||||
|
||||
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.
|
||||
### Configure server
|
||||
|
||||
### Configure server
|
||||
1. Edit server configuration file `AdGuardHome.yaml`, for example:
|
||||
|
||||
1. Edit server configuration file 'AdGuardHome.yaml', for example:
|
||||
|
||||
dhcp:
|
||||
```yaml
|
||||
dhcp:
|
||||
enabled: true
|
||||
interface_name: vboxnet0
|
||||
local_domain_name: lan
|
||||
dhcpv4:
|
||||
gateway_ip: 192.168.56.1
|
||||
subnet_mask: 255.255.255.0
|
||||
@@ -54,11 +68,29 @@ To set up a test environment for DHCP server you need:
|
||||
lease_duration: 86400
|
||||
ra_slaac_only: false
|
||||
ra_allow_slaac: false
|
||||
```
|
||||
|
||||
2. Start the server
|
||||
2. Start the server
|
||||
|
||||
./AdGuardHome
|
||||
```sh
|
||||
./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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build darwin
|
||||
//go:build darwin || freebsd || openbsd
|
||||
|
||||
package dhcpd
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build darwin
|
||||
//go:build darwin || freebsd || openbsd
|
||||
|
||||
package dhcpd
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build freebsd || linux || openbsd
|
||||
//go:build linux
|
||||
|
||||
package dhcpd
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build freebsd || linux || openbsd
|
||||
//go:build linux
|
||||
|
||||
package dhcpd
|
||||
|
||||
@@ -239,36 +239,16 @@ func Create(conf *ServerConfig) (s *server, err error) {
|
||||
// [aghhttp.RegisterFunc].
|
||||
s.registerHandlers()
|
||||
|
||||
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)
|
||||
v4Enabled, v6Enabled, err := s.setServers(conf)
|
||||
if err != nil {
|
||||
if v4conf.Enabled {
|
||||
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)
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.conf.Conf4 = conf.Conf4
|
||||
s.conf.Conf6 = conf.Conf6
|
||||
|
||||
if s.conf.Enabled && !v4conf.Enabled && !v6conf.Enabled {
|
||||
if s.conf.Enabled && !v4Enabled && !v6Enabled {
|
||||
return nil, fmt.Errorf("neither dhcpv4 nor dhcpv6 srv is configured")
|
||||
}
|
||||
|
||||
@@ -289,6 +269,39 @@ func Create(conf *ServerConfig) (s *server, err error) {
|
||||
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.
|
||||
func (s *server) Enabled() (ok bool) {
|
||||
return s.conf.Enabled
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghnet"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
)
|
||||
|
||||
type v4ServerConfJSON struct {
|
||||
@@ -263,6 +264,28 @@ func (s *server) handleDHCPSetConfigV6(
|
||||
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) {
|
||||
conf := &dhcpServerConfigJSON{}
|
||||
conf.Enabled = aghalg.BoolToNullBool(s.conf.Enabled)
|
||||
@@ -275,22 +298,9 @@ func (s *server) handleDHCPSetConfig(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
srv4, v4Enabled, err := s.handleDHCPSetConfigV4(conf)
|
||||
srv4, srv6, err := s.createServers(conf)
|
||||
if err != nil {
|
||||
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")
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||
|
||||
return
|
||||
}
|
||||
@@ -350,10 +360,10 @@ type netInterfaceJSON struct {
|
||||
Addrs6 []netip.Addr `json:"ipv6_addresses"`
|
||||
}
|
||||
|
||||
// handleDHCPInterfaces is the handler for the GET /control/dhcp/interfaces HTTP
|
||||
// API.
|
||||
// handleDHCPInterfaces is the handler for the GET /control/dhcp/interfaces
|
||||
// HTTP API.
|
||||
func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
resp := map[string]netInterfaceJSON{}
|
||||
resp := map[string]*netInterfaceJSON{}
|
||||
|
||||
ifaces, err := net.Interfaces()
|
||||
if err != nil {
|
||||
@@ -364,68 +374,23 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
for _, iface := range ifaces {
|
||||
if iface.Flags&net.FlagLoopback != 0 {
|
||||
// it's a loopback, skip it
|
||||
continue
|
||||
}
|
||||
if iface.Flags&net.FlagBroadcast == 0 {
|
||||
// this interface doesn't support broadcast, skip it
|
||||
// It's a loopback, skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
var addrs []net.Addr
|
||||
addrs, err = iface.Addrs()
|
||||
if err != nil {
|
||||
aghhttp.Error(
|
||||
r,
|
||||
w,
|
||||
http.StatusInternalServerError,
|
||||
"Failed to get addresses for interface %s: %s",
|
||||
iface.Name,
|
||||
err,
|
||||
)
|
||||
if iface.Flags&net.FlagBroadcast == 0 {
|
||||
// This interface doesn't support broadcast, skip it.
|
||||
continue
|
||||
}
|
||||
|
||||
jsonIface, iErr := newNetInterfaceJSON(iface)
|
||||
if iErr != nil {
|
||||
aghhttp.Error(r, w, http.StatusInternalServerError, "%s", iErr)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
jsonIface := netInterfaceJSON{
|
||||
Name: iface.Name,
|
||||
HardwareAddr: iface.HardwareAddr.String(),
|
||||
}
|
||||
|
||||
if iface.Flags != 0 {
|
||||
jsonIface.Flags = iface.Flags.String()
|
||||
}
|
||||
// we don't want link-local addresses in json, so skip them
|
||||
for _, addr := range addrs {
|
||||
ipnet, ok := addr.(*net.IPNet)
|
||||
if !ok {
|
||||
// not an IPNet, should not happen
|
||||
aghhttp.Error(
|
||||
r,
|
||||
w,
|
||||
http.StatusInternalServerError,
|
||||
"got iface.Addrs() element %[1]s that is not net.IPNet, it is %[1]T",
|
||||
addr)
|
||||
|
||||
return
|
||||
}
|
||||
// ignore link-local
|
||||
//
|
||||
// TODO(e.burkov): Try to listen DHCP on LLA as well.
|
||||
if ipnet.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
|
||||
if ip4 := ipnet.IP.To4(); ip4 != nil {
|
||||
addr := netip.AddrFrom4(*(*[4]byte)(ip4))
|
||||
jsonIface.Addrs4 = append(jsonIface.Addrs4, addr)
|
||||
} else {
|
||||
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)
|
||||
if jsonIface != nil {
|
||||
resp[iface.Name] = jsonIface
|
||||
}
|
||||
}
|
||||
@@ -433,6 +398,64 @@ func (s *server) handleDHCPInterfaces(w http.ResponseWriter, r *http.Request) {
|
||||
_ = 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,
|
||||
HardwareAddr: iface.HardwareAddr.String(),
|
||||
}
|
||||
|
||||
if iface.Flags != 0 {
|
||||
out.Flags = iface.Flags.String()
|
||||
}
|
||||
|
||||
// We don't want link-local addresses in JSON, so skip them.
|
||||
for _, addr := range addrs {
|
||||
ipNet, ok := addr.(*net.IPNet)
|
||||
if !ok {
|
||||
// Not an IPNet, should not happen.
|
||||
return nil, fmt.Errorf("got iface.Addrs() element %[1]s that is not"+
|
||||
" net.IPNet, it is %[1]T", addr)
|
||||
}
|
||||
|
||||
// Ignore link-local.
|
||||
//
|
||||
// TODO(e.burkov): Try to listen DHCP on LLA as well.
|
||||
if ipNet.IP.IsLinkLocalUnicast() {
|
||||
continue
|
||||
}
|
||||
|
||||
vAddr, iErr := netutil.IPToAddrNoMapped(ipNet.IP)
|
||||
if iErr != nil {
|
||||
// Not an IPNet, should not happen.
|
||||
return nil, fmt.Errorf("failed to convert IP address %[1]s: %w", addr, iErr)
|
||||
}
|
||||
|
||||
if vAddr.Is4() {
|
||||
out.Addrs4 = append(out.Addrs4, vAddr)
|
||||
} else {
|
||||
out.Addrs6 = append(out.Addrs6, vAddr)
|
||||
}
|
||||
}
|
||||
|
||||
if len(out.Addrs4)+len(out.Addrs6) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
out.GatewayIP = aghnet.GatewayIP(iface.Name)
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// dhcpSearchOtherResult contains information about other DHCP server for
|
||||
// specific network interface.
|
||||
type dhcpSearchOtherResult struct {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"golang.org/x/net/icmp"
|
||||
@@ -195,7 +196,7 @@ func createICMPv6RAPacket(params icmpv6RA) (data []byte, err error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
// Init - initialize RA module
|
||||
// Init initializes RA module.
|
||||
func (ra *raCtx) Init() (err error) {
|
||||
ra.stop.Store(0)
|
||||
ra.conn = nil
|
||||
@@ -203,8 +204,7 @@ func (ra *raCtx) Init() (err error) {
|
||||
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{
|
||||
managedAddressConfiguration: !ra.raSLAACOnly,
|
||||
@@ -223,18 +223,15 @@ func (ra *raCtx) Init() (err error) {
|
||||
return fmt.Errorf("creating packet: %w", err)
|
||||
}
|
||||
|
||||
success := false
|
||||
ipAndScope := ra.ipAddr.String() + "%" + ra.ifaceName
|
||||
ra.conn, err = icmp.ListenPacket("ip6:ipv6-icmp", ipAndScope)
|
||||
if err != nil {
|
||||
return fmt.Errorf("dhcpv6 ra: icmp.ListenPacket: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if !success {
|
||||
derr := ra.Close()
|
||||
if derr != nil {
|
||||
log.Error("closing context: %s", derr)
|
||||
}
|
||||
if err != nil {
|
||||
err = errors.WithDeferred(err, ra.Close())
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -269,7 +266,6 @@ func (ra *raCtx) Init() (err error) {
|
||||
log.Debug("dhcpv6 ra: loop exit")
|
||||
}()
|
||||
|
||||
success = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -342,8 +342,8 @@ func (s *v4Server) rmLease(lease *Lease) (err error) {
|
||||
// server to be configured and it's not.
|
||||
const ErrUnconfigured errors.Error = "server is unconfigured"
|
||||
|
||||
// AddStaticLease implements the DHCPServer interface for *v4Server. It is safe
|
||||
// for concurrent use.
|
||||
// AddStaticLease implements the DHCPServer interface for *v4Server. It is
|
||||
// safe for concurrent use.
|
||||
func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv4: adding static lease: %w") }()
|
||||
|
||||
@@ -354,21 +354,23 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
l.IP = l.IP.Unmap()
|
||||
|
||||
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 {
|
||||
return fmt.Errorf("can't assign the gateway IP %s to the lease", gwIP)
|
||||
return fmt.Errorf("can't assign the gateway IP %q to the lease", gwIP)
|
||||
}
|
||||
|
||||
l.IsStatic = true
|
||||
|
||||
err = netutil.ValidateMAC(l.HWAddr)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
if hostname := l.Hostname; hostname != "" {
|
||||
hostname, err = normalizeHostname(hostname)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -386,32 +388,9 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
l.Hostname = hostname
|
||||
}
|
||||
|
||||
// Perform the following actions in an anonymous function to make sure
|
||||
// that the lock gets unlocked before the notification step.
|
||||
func() {
|
||||
s.leasesLock.Lock()
|
||||
defer s.leasesLock.Unlock()
|
||||
|
||||
err = s.rmDynamicLease(l)
|
||||
if err != nil {
|
||||
err = fmt.Errorf(
|
||||
"removing dynamic leases for %s (%s): %w",
|
||||
l.IP,
|
||||
l.HWAddr,
|
||||
err,
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
err = s.addLease(l)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
|
||||
|
||||
return
|
||||
}
|
||||
}()
|
||||
err = s.updateStaticLease(l)
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -421,6 +400,25 @@ func (s *v4Server) AddStaticLease(l *Lease) (err error) {
|
||||
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()
|
||||
defer s.leasesLock.Unlock()
|
||||
|
||||
err = s.rmDynamicLease(l)
|
||||
if err != nil {
|
||||
return fmt.Errorf("removing dynamic leases for %s (%s): %w", l.IP, l.HWAddr, err)
|
||||
}
|
||||
|
||||
err = s.addLease(l)
|
||||
if err != nil {
|
||||
return fmt.Errorf("adding static lease for %s (%s): %w", l.IP, l.HWAddr, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemoveStaticLease removes a static lease. It is safe for concurrent use.
|
||||
func (s *v4Server) RemoveStaticLease(l *Lease) (err error) {
|
||||
defer func() { err = errors.Annotate(err, "dhcpv4: %w") }()
|
||||
@@ -894,24 +892,9 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||
reqIP = req.ClientIPAddr
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
oldLease := s.findLeaseForIP(reqIP, mac)
|
||||
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
|
||||
}
|
||||
@@ -925,7 +908,7 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||
if err != nil {
|
||||
return fmt.Errorf("allocating new lease for %s: %w", mac, err)
|
||||
} 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.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
||||
@@ -941,15 +924,32 @@ func (s *v4Server) handleDecline(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||
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)
|
||||
|
||||
resp.YourIPAddr = net.IP(newLease.IP.AsSlice())
|
||||
log.Info("dhcpv4: changed IP from %s to %s for %s", reqIP, newLease.IP, mac)
|
||||
|
||||
resp.YourIPAddr = newLease.IP.AsSlice()
|
||||
resp.UpdateOption(dhcpv4.OptMessageType(dhcpv4.MessageTypeAck))
|
||||
|
||||
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.
|
||||
func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||
mac := req.ClientHWAddr
|
||||
@@ -995,11 +995,80 @@ func (s *v4Server) handleRelease(req, resp *dhcpv4.DHCPv4) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Find a lease associated with MAC and prepare response
|
||||
// Return 1: OK
|
||||
// Return 0: error; reply with Nak
|
||||
// Return -1: error; don't reply
|
||||
func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
|
||||
// messageHandler describes a DHCPv4 message handler function.
|
||||
type messageHandler func(s *v4Server, req, resp *dhcpv4.DHCPv4) (rCode int, l *Lease, err error)
|
||||
|
||||
// messageHandlers is a map of handlers for various messages with message types
|
||||
// keys.
|
||||
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
|
||||
|
||||
// Include server's identifier option since any reply should contain it.
|
||||
@@ -1007,47 +1076,26 @@ func (s *v4Server) handle(req, resp *dhcpv4.DHCPv4) int {
|
||||
// See https://datatracker.ietf.org/doc/html/rfc2131#page-29.
|
||||
resp.UpdateOption(dhcpv4.OptServerIdentifier(s.conf.dnsIPAddrs[0].AsSlice()))
|
||||
|
||||
// TODO(a.garipov): Refactor this into handlers.
|
||||
var l *Lease
|
||||
switch mt := req.MessageType(); mt {
|
||||
case dhcpv4.MessageTypeDiscover:
|
||||
l, err = s.handleDiscover(req, resp)
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: handling discover: %s", err)
|
||||
handler := messageHandlers[req.MessageType()]
|
||||
if handler == nil {
|
||||
s.updateOptions(req, resp)
|
||||
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if l == nil {
|
||||
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)
|
||||
rCode, l, err := handler(s, req, resp)
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: %s", err)
|
||||
|
||||
return 0
|
||||
}
|
||||
case dhcpv4.MessageTypeRelease:
|
||||
err = s.handleRelease(req, resp)
|
||||
if err != nil {
|
||||
log.Error("dhcpv4: handling release: %s", err)
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
if rCode != 1 {
|
||||
return rCode
|
||||
}
|
||||
|
||||
if l != nil {
|
||||
resp.YourIPAddr = net.IP(l.IP.AsSlice())
|
||||
resp.YourIPAddr = l.IP.AsSlice()
|
||||
}
|
||||
|
||||
s.updateOptions(req, resp)
|
||||
@@ -1162,23 +1210,8 @@ func (s *v4Server) Start() (err error) {
|
||||
// No available IP addresses which may appear later.
|
||||
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...))
|
||||
}
|
||||
|
||||
for _, ip := range dnsIPAddrs {
|
||||
ip = ip.To4()
|
||||
if ip == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
s.conf.dnsIPAddrs = append(s.conf.dnsIPAddrs, netip.AddrFrom4(*(*[4]byte)(ip)))
|
||||
}
|
||||
s.configureDNSIPAddrs(dnsIPAddrs)
|
||||
|
||||
var c net.PacketConn
|
||||
if c, err = s.newDHCPConn(iface); err != nil {
|
||||
@@ -1199,10 +1232,10 @@ func (s *v4Server) Start() (err error) {
|
||||
log.Info("dhcpv4: listening")
|
||||
|
||||
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")
|
||||
} else if serr != nil {
|
||||
log.Error("dhcpv4: srv.Serve: %s", serr)
|
||||
} else if sErr != nil {
|
||||
log.Error("dhcpv4: srv.Serve: %s", sErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -1213,6 +1246,28 @@ func (s *v4Server) Start() (err error) {
|
||||
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
|
||||
func (s *v4Server) Stop() (err error) {
|
||||
if s.srv == nil {
|
||||
|
||||
@@ -227,7 +227,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
|
||||
},
|
||||
name: "with_gateway_ip",
|
||||
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{
|
||||
Hostname: "ip6.local",
|
||||
@@ -236,7 +236,7 @@ func TestV4Server_AddRemove_static(t *testing.T) {
|
||||
},
|
||||
name: "ipv6",
|
||||
wantErrMsg: `dhcpv4: adding static lease: ` +
|
||||
`invalid ip "ffff::1", only ipv4 is supported`,
|
||||
`invalid IP "ffff::1": only IPv4 is supported`,
|
||||
}, {
|
||||
lease: &Lease{
|
||||
Hostname: "bad-mac.local",
|
||||
|
||||
@@ -586,9 +586,31 @@ func (s *v6Server) packetHandler(conn net.PacketConn, peer net.Addr, req dhcpv6.
|
||||
}
|
||||
}
|
||||
|
||||
// initialize RA module
|
||||
func (s *v6Server) initRA(iface *net.Interface) error {
|
||||
// choose the source IP address - should be link-local-unicast
|
||||
// configureDNSIPAddrs updates v6Server configuration with the slice of DNS IP
|
||||
// addresses of provided interface iface. Initializes RA module.
|
||||
func (s *v6Server) configureDNSIPAddrs(iface *net.Interface) (ok bool, err error) {
|
||||
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]
|
||||
for _, ip := range s.conf.dnsIPAddrs {
|
||||
if ip.IsLinkLocalUnicast() {
|
||||
@@ -604,6 +626,7 @@ func (s *v6Server) initRA(iface *net.Interface) error {
|
||||
s.ra.ifaceName = s.conf.InterfaceName
|
||||
s.ra.iface = iface
|
||||
s.ra.packetSendPeriod = 1 * time.Second
|
||||
|
||||
return s.ra.Init()
|
||||
}
|
||||
|
||||
@@ -623,37 +646,24 @@ func (s *v6Server) Start() (err error) {
|
||||
|
||||
log.Debug("dhcpv6: starting...")
|
||||
|
||||
dnsIPAddrs, err := aghnet.IfaceDNSIPAddrs(
|
||||
iface,
|
||||
aghnet.IPVersion6,
|
||||
defaultMaxAttempts,
|
||||
defaultBackoff,
|
||||
)
|
||||
ok, err := s.configureDNSIPAddrs(iface)
|
||||
if err != nil {
|
||||
return fmt.Errorf("interface %s: %w", ifaceName, err)
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dnsIPAddrs) == 0 {
|
||||
if !ok {
|
||||
// No available IP addresses which may appear later.
|
||||
return nil
|
||||
}
|
||||
|
||||
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
|
||||
// Don't initialize DHCPv6 server if we must force the clients to use SLAAC.
|
||||
if s.conf.RASLAACOnly {
|
||||
log.Debug("not starting dhcpv6 server due to ra_slaac_only=true")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Debug("dhcpv6: listening...")
|
||||
|
||||
err = netutil.ValidateMAC(iface.HardwareAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("validating interface %s: %w", iface.Name, err)
|
||||
@@ -665,20 +675,18 @@ func (s *v6Server) Start() (err error) {
|
||||
Time: dhcpv6.GetTime(),
|
||||
}
|
||||
|
||||
laddr := &net.UDPAddr{
|
||||
IP: net.ParseIP("::"),
|
||||
Port: dhcpv6.DefaultServerPort,
|
||||
}
|
||||
s.srv, err = server6.NewServer(iface.Name, laddr, s.packetHandler, server6.WithDebugLogger())
|
||||
s.srv, err = server6.NewServer(iface.Name, nil, s.packetHandler, server6.WithDebugLogger())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("dhcpv6: listening...")
|
||||
|
||||
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")
|
||||
} else if serr != nil {
|
||||
log.Error("dhcpv6: srv.Serve: %s", serr)
|
||||
} else if sErr != nil {
|
||||
log.Error("dhcpv6: srv.Serve: %s", sErr)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghalg"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
"github.com/AdguardTeam/urlfilter"
|
||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||
@@ -31,9 +30,6 @@ type accessManager struct {
|
||||
|
||||
blockedHostsEng *urlfilter.DNSEngine
|
||||
|
||||
// privateNets is the set of IP networks those are ignored by the manager.
|
||||
privateNets netutil.SubnetSet
|
||||
|
||||
// TODO(a.garipov): Create a type for a set of IP networks.
|
||||
allowedNets []netip.Prefix
|
||||
blockedNets []netip.Prefix
|
||||
@@ -68,20 +64,13 @@ func processAccessClients(
|
||||
}
|
||||
|
||||
// newAccessCtx creates a new accessCtx.
|
||||
func newAccessCtx(
|
||||
allowed []string,
|
||||
blocked []string,
|
||||
blockedHosts []string,
|
||||
privateNets netutil.SubnetSet,
|
||||
) (a *accessManager, err error) {
|
||||
func newAccessCtx(allowed, blocked, blockedHosts []string) (a *accessManager, err error) {
|
||||
a = &accessManager{
|
||||
allowedIPs: map[netip.Addr]unit{},
|
||||
blockedIPs: map[netip.Addr]unit{},
|
||||
|
||||
allowedClientIDs: stringutil.NewSet(),
|
||||
blockedClientIDs: stringutil.NewSet(),
|
||||
|
||||
privateNets: privateNets,
|
||||
}
|
||||
|
||||
err = processAccessClients(allowed, a.allowedIPs, &a.allowedNets, a.allowedClientIDs)
|
||||
@@ -149,13 +138,9 @@ func (a *accessManager) isBlockedHost(host string, qt rules.RRType) (ok bool) {
|
||||
return ok
|
||||
}
|
||||
|
||||
// isBlockedIP returns the status of the IP address blocking as well as the
|
||||
// rule that blocked it. Addresses from private nets are always allowed.
|
||||
// isBlockedIP returns the status of the IP address blocking as well as the rule
|
||||
// that blocked it.
|
||||
func (a *accessManager) isBlockedIP(ip netip.Addr) (blocked bool, rule string) {
|
||||
if a.privateNets != nil && a.privateNets.Contains(ip.AsSlice()) {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
blocked = true
|
||||
ips := a.blockedIPs
|
||||
ipnets := a.blockedNets
|
||||
@@ -256,7 +241,7 @@ func (s *Server) handleAccessSet(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
var a *accessManager
|
||||
a, err = newAccessCtx(list.AllowedClients, list.DisallowedClients, list.BlockedHosts, nil)
|
||||
a, err = newAccessCtx(list.AllowedClients, list.DisallowedClients, list.BlockedHosts)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "creating access ctx: %s", err)
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/AdguardTeam/golibs/netutil"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -15,12 +14,12 @@ func TestIsBlockedClientID(t *testing.T) {
|
||||
clientID := "client-1"
|
||||
clients := []string{clientID}
|
||||
|
||||
a, err := newAccessCtx(clients, nil, nil, nil)
|
||||
a, err := newAccessCtx(clients, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.False(t, a.isBlockedClientID(clientID))
|
||||
|
||||
a, err = newAccessCtx(nil, clients, nil, nil)
|
||||
a, err = newAccessCtx(nil, clients, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.True(t, a.isBlockedClientID(clientID))
|
||||
@@ -32,7 +31,7 @@ func TestIsBlockedHost(t *testing.T) {
|
||||
"*.host.com",
|
||||
"||host3.com^",
|
||||
"||*^$dnstype=HTTPS",
|
||||
}, nil)
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
@@ -104,104 +103,58 @@ func TestIsBlockedHost(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAccessManager_IsBlockedIP_allow(t *testing.T) {
|
||||
func TestIsBlockedIP(t *testing.T) {
|
||||
clients := []string{
|
||||
"1.2.3.4",
|
||||
"5.6.7.8/24",
|
||||
}
|
||||
|
||||
privateNets := netutil.SubnetSetFunc(netutil.IsLocallyServed)
|
||||
allowCtx, err := newAccessCtx(clients, nil, nil, privateNets)
|
||||
allowCtx, err := newAccessCtx(clients, nil, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
blockCtx, err := newAccessCtx(nil, clients, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
ip netip.Addr
|
||||
want assert.BoolAssertionFunc
|
||||
name string
|
||||
wantRule string
|
||||
ip netip.Addr
|
||||
name string
|
||||
wantRule string
|
||||
wantBlocked bool
|
||||
}{{
|
||||
ip: netip.MustParseAddr("1.2.3.4"),
|
||||
name: "match_ip",
|
||||
wantRule: "1.2.3.4",
|
||||
want: assert.False,
|
||||
ip: netip.MustParseAddr("1.2.3.4"),
|
||||
name: "match_ip",
|
||||
wantRule: "1.2.3.4",
|
||||
wantBlocked: true,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("5.6.7.100"),
|
||||
name: "match_cidr",
|
||||
wantRule: "5.6.7.8/24",
|
||||
want: assert.False,
|
||||
ip: netip.MustParseAddr("5.6.7.100"),
|
||||
name: "match_cidr",
|
||||
wantRule: "5.6.7.8/24",
|
||||
wantBlocked: true,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("9.2.3.4"),
|
||||
name: "no_match_ip",
|
||||
wantRule: "",
|
||||
want: assert.True,
|
||||
ip: netip.MustParseAddr("9.2.3.4"),
|
||||
name: "no_match_ip",
|
||||
wantRule: "",
|
||||
wantBlocked: false,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("9.6.7.100"),
|
||||
name: "no_match_cidr",
|
||||
wantRule: "",
|
||||
want: assert.True,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("127.0.0.1"),
|
||||
name: "locally_served_ip",
|
||||
wantRule: "",
|
||||
want: assert.False,
|
||||
ip: netip.MustParseAddr("9.6.7.100"),
|
||||
name: "no_match_cidr",
|
||||
wantRule: "",
|
||||
wantBlocked: false,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Run("allow", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
blocked, rule := allowCtx.isBlockedIP(tc.ip)
|
||||
tc.want(t, blocked)
|
||||
assert.Equal(t, !tc.wantBlocked, blocked)
|
||||
assert.Equal(t, tc.wantRule, rule)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
func TestAccessManager_IsBlockedIP_block(t *testing.T) {
|
||||
clients := []string{
|
||||
"1.2.3.4",
|
||||
"5.6.7.8/24",
|
||||
}
|
||||
|
||||
privateNets := netutil.SubnetSetFunc(netutil.IsLocallyServed)
|
||||
blockCtx, err := newAccessCtx(nil, clients, nil, privateNets)
|
||||
require.NoError(t, err)
|
||||
|
||||
testCases := []struct {
|
||||
ip netip.Addr
|
||||
want assert.BoolAssertionFunc
|
||||
name string
|
||||
wantRule string
|
||||
}{{
|
||||
ip: netip.MustParseAddr("1.2.3.4"),
|
||||
name: "match_ip",
|
||||
wantRule: "1.2.3.4",
|
||||
want: assert.True,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("5.6.7.100"),
|
||||
name: "match_cidr",
|
||||
wantRule: "5.6.7.8/24",
|
||||
want: assert.True,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("9.2.3.4"),
|
||||
name: "no_match_ip",
|
||||
wantRule: "",
|
||||
want: assert.False,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("9.6.7.100"),
|
||||
name: "no_match_cidr",
|
||||
wantRule: "",
|
||||
want: assert.False,
|
||||
}, {
|
||||
ip: netip.MustParseAddr("127.0.0.1"),
|
||||
name: "locally_served_ip",
|
||||
wantRule: "",
|
||||
want: assert.False,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Run("block", func(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
blocked, rule := blockCtx.isBlockedIP(tc.ip)
|
||||
tc.want(t, blocked)
|
||||
assert.Equal(t, tc.wantBlocked, blocked)
|
||||
assert.Equal(t, tc.wantRule, rule)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -509,7 +509,6 @@ func (s *Server) Prepare(conf *ServerConfig) (err error) {
|
||||
s.conf.AllowedClients,
|
||||
s.conf.DisallowedClients,
|
||||
s.conf.BlockedHosts,
|
||||
s.privateNets,
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("preparing access: %w", err)
|
||||
@@ -701,12 +700,10 @@ func (s *Server) IsBlockedClient(ip netip.Addr, clientID string) (blocked bool,
|
||||
blockedByIP := false
|
||||
if ip != (netip.Addr{}) {
|
||||
blockedByIP, rule = s.access.isBlockedIP(ip)
|
||||
log.Debug("by ip %v", blockedByIP)
|
||||
}
|
||||
|
||||
allowlistMode := s.access.allowlistMode()
|
||||
blockedByClientID := s.access.isBlockedClientID(clientID)
|
||||
log.Debug("by client id %v", blockedByClientID)
|
||||
|
||||
// Allow if at least one of the checks allows in allowlist mode, but block
|
||||
// if at least one of the checks blocks in blocklist mode.
|
||||
|
||||
@@ -487,7 +487,8 @@ func TestServer_handleTestUpstreaDNS(t *testing.T) {
|
||||
},
|
||||
wantResp: map[string]any{
|
||||
badUps: `upstream "` + badUps + `" fails to exchange: ` +
|
||||
`couldn't communicate with upstream: dns: id mismatch`,
|
||||
`couldn't communicate with upstream: exchanging with ` +
|
||||
badUps + ` over tcp: dns: id mismatch`,
|
||||
},
|
||||
name: "broken",
|
||||
}, {
|
||||
@@ -497,7 +498,8 @@ func TestServer_handleTestUpstreaDNS(t *testing.T) {
|
||||
wantResp: map[string]any{
|
||||
goodUps: "OK",
|
||||
badUps: `upstream "` + badUps + `" fails to exchange: ` +
|
||||
`couldn't communicate with upstream: dns: id mismatch`,
|
||||
`couldn't communicate with upstream: exchanging with ` +
|
||||
badUps + ` over tcp: dns: id mismatch`,
|
||||
},
|
||||
name: "both",
|
||||
}}
|
||||
|
||||
@@ -12,6 +12,14 @@ type blockedService struct {
|
||||
|
||||
// blockedServices contains raw blocked service data.
|
||||
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",
|
||||
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>"),
|
||||
@@ -1180,6 +1188,18 @@ var blockedServices = []blockedService{{
|
||||
"||zuckerberg.com^",
|
||||
"||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",
|
||||
Name: "GOG",
|
||||
@@ -1325,6 +1345,13 @@ var blockedServices = []blockedService{{
|
||||
"||kakao.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",
|
||||
Name: "Lazada",
|
||||
@@ -1385,6 +1412,7 @@ var blockedServices = []blockedService{{
|
||||
Rules: []string{
|
||||
"||aus.social^",
|
||||
"||awscommunity.social^",
|
||||
"||climatejustice.social^",
|
||||
"||cyberplace.social^",
|
||||
"||defcon.social^",
|
||||
"||det.social^",
|
||||
@@ -1442,13 +1470,13 @@ var blockedServices = []blockedService{{
|
||||
"||mstdn.plus^",
|
||||
"||mstdn.social^",
|
||||
"||muenchen.social^",
|
||||
"||muenster.im^",
|
||||
"||newsie.social^",
|
||||
"||noc.social^",
|
||||
"||norden.social^",
|
||||
"||nrw.social^",
|
||||
"||o3o.ca^",
|
||||
"||ohai.social^",
|
||||
"||pewtix.com^",
|
||||
"||piaille.fr^",
|
||||
"||pol.social^",
|
||||
"||ravenation.club^",
|
||||
@@ -1480,7 +1508,6 @@ var blockedServices = []blockedService{{
|
||||
"||union.place^",
|
||||
"||universeodon.com^",
|
||||
"||urbanists.social^",
|
||||
"||wien.rocks^",
|
||||
"||wxw.moe^",
|
||||
},
|
||||
}, {
|
||||
@@ -1827,6 +1854,13 @@ var blockedServices = []blockedService{{
|
||||
"||tx.me^",
|
||||
"||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",
|
||||
Name: "TikTok",
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||
"github.com/AdguardTeam/golibs/stringutil"
|
||||
)
|
||||
|
||||
// Client contains information about persistent clients.
|
||||
@@ -37,6 +38,19 @@ type Client struct {
|
||||
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.
|
||||
func (c *Client) closeUpstreams() (err error) {
|
||||
if c.upstreamConfig != nil {
|
||||
|
||||
@@ -378,6 +378,7 @@ func (clients *clientsContainer) clientOrArtificial(
|
||||
}, true
|
||||
}
|
||||
|
||||
// Find returns a shallow copy of the client if there is one found.
|
||||
func (clients *clientsContainer) Find(id string) (c *Client, ok bool) {
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
@@ -387,20 +388,18 @@ func (clients *clientsContainer) Find(id string) (c *Client, ok bool) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
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
|
||||
return c.ShallowClone(), true
|
||||
}
|
||||
|
||||
// shouldCountClient is a wrapper around Find to make it a valid client
|
||||
// information finder for the statistics. If no information about the client
|
||||
// is found, it returns true.
|
||||
func (clients *clientsContainer) shouldCountClient(ids []string) (y bool) {
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
for _, id := range ids {
|
||||
client, ok := clients.Find(id)
|
||||
client, ok := clients.findLocked(id)
|
||||
if ok {
|
||||
return !client.IgnoreStatistics
|
||||
}
|
||||
@@ -617,6 +616,15 @@ 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
|
||||
clients.list[c.Name] = c
|
||||
|
||||
@@ -624,10 +632,6 @@ func (clients *clientsContainer) Add(c *Client) (ok bool, err error) {
|
||||
for _, id := range c.IDs {
|
||||
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.
|
||||
@@ -645,86 +649,53 @@ func (clients *clientsContainer) Del(name string) (ok bool) {
|
||||
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
|
||||
delete(clients.list, name)
|
||||
delete(clients.list, c.Name)
|
||||
|
||||
// update ID index
|
||||
for _, id := range c.IDs {
|
||||
delete(clients.idIndex, id)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Update updates a client by its name.
|
||||
func (clients *clientsContainer) Update(name string, c *Client) (err error) {
|
||||
func (clients *clientsContainer) Update(prev, c *Client) (err error) {
|
||||
err = clients.check(c)
|
||||
if err != nil {
|
||||
// Don't wrap the error since it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
clients.lock.Lock()
|
||||
defer clients.lock.Unlock()
|
||||
|
||||
prev, ok := clients.list[name]
|
||||
if !ok {
|
||||
return errors.Error("client not found")
|
||||
}
|
||||
|
||||
// First, check the name index.
|
||||
// Check the name index.
|
||||
if prev.Name != c.Name {
|
||||
_, ok = clients.list[c.Name]
|
||||
_, ok := clients.list[c.Name]
|
||||
if ok {
|
||||
return errors.Error("client already exists")
|
||||
}
|
||||
}
|
||||
|
||||
// Second, update the ID index.
|
||||
err = clients.updateIDIndex(prev, 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]
|
||||
if ok && existing != cli {
|
||||
return fmt.Errorf("id %q is used by client with name %q", id, existing.Name)
|
||||
// Check the ID index.
|
||||
if !slices.Equal(prev.IDs, c.IDs) {
|
||||
for _, id := range c.IDs {
|
||||
existing, ok := clients.idIndex[id]
|
||||
if ok && existing != prev {
|
||||
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)
|
||||
}
|
||||
|
||||
for _, id := range newIDs {
|
||||
clients.idIndex[id] = cli
|
||||
}
|
||||
clients.del(prev)
|
||||
clients.add(c)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -98,22 +98,8 @@ func TestClients(t *testing.T) {
|
||||
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) {
|
||||
err := clients.Update("client1", &Client{
|
||||
err := clients.Update(&Client{Name: "client1"}, &Client{
|
||||
IDs: []string{"2.2.2.2"},
|
||||
Name: "client1",
|
||||
})
|
||||
@@ -129,7 +115,10 @@ func TestClients(t *testing.T) {
|
||||
cliNewIP = netip.MustParseAddr(cliNew)
|
||||
)
|
||||
|
||||
err := clients.Update("client1", &Client{
|
||||
prev, ok := clients.list["client1"]
|
||||
require.True(t, ok)
|
||||
|
||||
err := clients.Update(prev, &Client{
|
||||
IDs: []string{cliNew},
|
||||
Name: "client1",
|
||||
})
|
||||
@@ -138,7 +127,10 @@ func TestClients(t *testing.T) {
|
||||
assert.Equal(t, clients.clientSource(cliOldIP), ClientSourceNone)
|
||||
assert.Equal(t, clients.clientSource(cliNewIP), ClientSourcePersistent)
|
||||
|
||||
err = clients.Update("client1", &Client{
|
||||
prev, ok = clients.list["client1"]
|
||||
require.True(t, ok)
|
||||
|
||||
err = clients.Update(prev, &Client{
|
||||
IDs: []string{cliNew},
|
||||
Name: "client1-renamed",
|
||||
UseOwnSettings: true,
|
||||
|
||||
@@ -289,7 +289,7 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
|
||||
return
|
||||
}
|
||||
|
||||
err = clients.Update(dj.Name, c)
|
||||
err = clients.Update(prev, c)
|
||||
if err != nil {
|
||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||
|
||||
|
||||
@@ -3,19 +3,24 @@ package querylog
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"github.com/AdguardTeam/urlfilter/rules"
|
||||
"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
|
||||
|
||||
// logEntryHandlers is the map of log entry decode handlers for various keys.
|
||||
var logEntryHandlers = map[string]logEntryHandler{
|
||||
"CID": func(t json.Token, ent *logEntry) error {
|
||||
v, ok := t.(string)
|
||||
@@ -166,6 +171,7 @@ 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) {
|
||||
var vToken json.Token
|
||||
switch key {
|
||||
@@ -189,6 +195,8 @@ 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(
|
||||
key string,
|
||||
i int,
|
||||
@@ -213,6 +221,8 @@ func decodeVTokenAndAddRule(
|
||||
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) {
|
||||
for {
|
||||
delimToken, err := dec.Token()
|
||||
@@ -224,48 +234,53 @@ func decodeResultRules(dec *json.Decoder, ent *logEntry) {
|
||||
return
|
||||
}
|
||||
|
||||
if d, ok := delimToken.(json.Delim); ok {
|
||||
if d != '[' {
|
||||
log.Debug("decodeResultRules: unexpected delim %q", d)
|
||||
if d, ok := delimToken.(json.Delim); !ok {
|
||||
return
|
||||
} else if d != '[' {
|
||||
log.Debug("decodeResultRules: unexpected delim %q", d)
|
||||
}
|
||||
|
||||
err = decodeResultRuleToken(dec, ent)
|
||||
if err != nil {
|
||||
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
|
||||
log.Debug("decodeResultRules err: %s", err)
|
||||
}
|
||||
} else {
|
||||
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i := 0
|
||||
for {
|
||||
var keyToken json.Token
|
||||
keyToken, err = dec.Token()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Debug("decodeResultRules err: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if d, ok := keyToken.(json.Delim); ok {
|
||||
switch d {
|
||||
case '}':
|
||||
i++
|
||||
case ']':
|
||||
return
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
log.Debug("decodeResultRules: keyToken is %T (%[1]v) and not string", keyToken)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
decodeResultRuleKey(key, i, dec, ent)
|
||||
// decodeResultRuleToken decodes the tokens of "Rules" type to the logEntry ent.
|
||||
func decodeResultRuleToken(dec *json.Decoder, ent *logEntry) (err error) {
|
||||
i := 0
|
||||
for {
|
||||
var keyToken json.Token
|
||||
keyToken, err = dec.Token()
|
||||
if err != nil {
|
||||
// Don't wrap the error, because it's informative enough as is.
|
||||
return err
|
||||
}
|
||||
|
||||
if d, ok := keyToken.(json.Delim); ok {
|
||||
switch d {
|
||||
case '}':
|
||||
i++
|
||||
case ']':
|
||||
return ErrEndOfToken
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("keyToken is %T (%[1]v) and not string", keyToken)
|
||||
}
|
||||
|
||||
decodeResultRuleKey(key, i, dec, ent)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,6 +337,8 @@ 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) {
|
||||
for {
|
||||
itemToken, err := dec.Token()
|
||||
@@ -355,6 +372,8 @@ 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) {
|
||||
var err error
|
||||
|
||||
@@ -395,50 +414,29 @@ func decodeResultDNSRewriteResultKey(key string, dec *json.Decoder, ent *logEntr
|
||||
log.Debug("decodeResultDNSRewriteResultKey response err: %s", err)
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
ent.parseDNSRewriteResultIPs()
|
||||
default:
|
||||
// Go on.
|
||||
}
|
||||
}
|
||||
|
||||
// decodeResultDNSRewriteResult parses the dec's tokens into logEntry ent
|
||||
// interpreting it as the result DNSRewriteResult.
|
||||
func decodeResultDNSRewriteResult(dec *json.Decoder, ent *logEntry) {
|
||||
for {
|
||||
keyToken, err := dec.Token()
|
||||
key, err := parseKeyToken(dec)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Debug("decodeResultDNSRewriteResult err: %s", err)
|
||||
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
|
||||
log.Debug("decodeResultDNSRewriteResult: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if d, ok := keyToken.(json.Delim); ok {
|
||||
if d == '}' {
|
||||
return
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
log.Debug("decodeResultDNSRewriteResult: keyToken is %T (%[1]v) and not string", keyToken)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
decodeResultDNSRewriteResultKey(key, dec, ent)
|
||||
}
|
||||
}
|
||||
@@ -474,34 +472,51 @@ func translateResult(ent *logEntry) {
|
||||
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) {
|
||||
defer translateResult(ent)
|
||||
|
||||
for {
|
||||
keyToken, err := dec.Token()
|
||||
key, err := parseKeyToken(dec)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Debug("decodeResult err: %s", err)
|
||||
if err != io.EOF && !errors.Is(err, ErrEndOfToken) {
|
||||
log.Debug("decodeResult: %s", err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
if d, ok := keyToken.(json.Delim); ok {
|
||||
if d == '}' {
|
||||
return
|
||||
}
|
||||
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
key, ok := keyToken.(string)
|
||||
if !ok {
|
||||
log.Debug("decodeResult: keyToken is %T (%[1]v) and not string", keyToken)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
decHandler, ok := resultDecHandlers[key]
|
||||
if ok {
|
||||
decHandler(dec, ent)
|
||||
@@ -527,13 +542,16 @@ func decodeResult(dec *json.Decoder, ent *logEntry) {
|
||||
}
|
||||
}
|
||||
|
||||
// resultHandlers is the map of log entry decode handlers for various keys.
|
||||
var resultHandlers = map[string]logEntryHandler{
|
||||
"IsFiltered": func(t json.Token, ent *logEntry) error {
|
||||
v, ok := t.(bool)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
ent.Result.IsFiltered = v
|
||||
|
||||
return nil
|
||||
},
|
||||
"Rule": func(t json.Token, ent *logEntry) error {
|
||||
@@ -578,11 +596,14 @@ var resultHandlers = map[string]logEntryHandler{
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
i, err := v.Int64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ent.Result.Reason = filtering.Reason(i)
|
||||
|
||||
return nil
|
||||
},
|
||||
"ServiceName": func(t json.Token, ent *logEntry) error {
|
||||
@@ -607,6 +628,7 @@ 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){
|
||||
"ReverseHosts": decodeResultReverseHosts,
|
||||
"IPList": decodeResultIPList,
|
||||
@@ -614,9 +636,11 @@ var resultDecHandlers = map[string]func(dec *json.Decoder, ent *logEntry){
|
||||
"DNSRewriteResult": decodeResultDNSRewriteResult,
|
||||
}
|
||||
|
||||
// decodeLogEntry decodes string str to logEntry ent.
|
||||
func decodeLogEntry(ent *logEntry, str string) {
|
||||
dec := json.NewDecoder(strings.NewReader(str))
|
||||
dec.UseNumber()
|
||||
|
||||
for {
|
||||
keyToken, err := dec.Token()
|
||||
if err != nil {
|
||||
|
||||
@@ -68,3 +68,19 @@ func (e *logEntry) addResponse(resp *dns.Msg, isOrig bool) {
|
||||
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.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,32 +16,35 @@ import (
|
||||
"github.com/miekg/dns"
|
||||
)
|
||||
|
||||
const (
|
||||
queryLogFileName = "querylog.json" // .gz added during compression
|
||||
)
|
||||
// queryLogFileName is a name of the log file. ".gz" extension is added later
|
||||
// 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 {
|
||||
findClient func(ids []string) (c *Client, err error)
|
||||
|
||||
// confMu protects conf.
|
||||
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 string
|
||||
|
||||
// bufferLock protects buffer.
|
||||
bufferLock sync.RWMutex
|
||||
// buffer contains recent log entries. The entries in this buffer must not
|
||||
// be modified.
|
||||
buffer []*logEntry
|
||||
|
||||
fileFlushLock sync.Mutex // synchronize a file-flushing goroutine and main thread
|
||||
flushPending bool // don't start another goroutine while the previous one is still running
|
||||
// bufferLock protects buffer.
|
||||
bufferLock sync.RWMutex
|
||||
|
||||
// fileFlushLock synchronizes a file-flushing goroutine and main thread.
|
||||
fileFlushLock sync.Mutex
|
||||
fileWriteLock sync.Mutex
|
||||
|
||||
anonymizer *aghnet.IPMut
|
||||
flushPending bool
|
||||
}
|
||||
|
||||
// ClientProto values are names of the client protocols.
|
||||
@@ -155,6 +158,43 @@ func (l *queryLog) clear() {
|
||||
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) {
|
||||
var isEnabled, fileIsEnabled bool
|
||||
var memSize uint32
|
||||
@@ -181,35 +221,7 @@ func (l *queryLog) Add(params *AddParams) {
|
||||
params.Result = &filtering.Result{}
|
||||
}
|
||||
|
||||
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)
|
||||
entry := newLogEntry(params)
|
||||
|
||||
needFlush := false
|
||||
func() {
|
||||
|
||||
@@ -45,9 +45,10 @@ func TestQueryLog(t *testing.T) {
|
||||
addEntry(l, "example.com", net.IPv4(1, 1, 1, 4), net.IPv4(2, 2, 2, 4))
|
||||
|
||||
type tcAssertion struct {
|
||||
num int
|
||||
host string
|
||||
answer, client net.IP
|
||||
host string
|
||||
answer net.IP
|
||||
client net.IP
|
||||
num int
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
|
||||
@@ -12,141 +12,181 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// Timestamp not found errors.
|
||||
const (
|
||||
ErrTSNotFound errors.Error = "ts not found"
|
||||
ErrTSTooLate errors.Error = "ts too late"
|
||||
ErrTSTooEarly errors.Error = "ts too early"
|
||||
// Timestamp not found errors.
|
||||
errTSNotFound errors.Error = "ts not found"
|
||||
errTSTooLate errors.Error = "ts too late"
|
||||
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
|
||||
)
|
||||
|
||||
// TODO: Find a way to grow buffer instead of relying on this value when reading strings
|
||||
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
|
||||
// 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 pointer to a specific position in the file,
|
||||
// and it reads lines in reverse order starting from that position.
|
||||
type QLogFile struct {
|
||||
file *os.File // the query log file
|
||||
position int64 // current position in the file
|
||||
// Please note, that this is a stateful object. Internally, it contains a
|
||||
// pointer to a specific position in the file, and it reads lines in reverse
|
||||
// order starting from that position.
|
||||
type qLogFile struct {
|
||||
// file is the query log file.
|
||||
file *os.File
|
||||
|
||||
buffer []byte // buffer that we've read from the file
|
||||
bufferStart int64 // start of the buffer (in the file)
|
||||
bufferLen int // buffer len
|
||||
// buffer that we've read from the file.
|
||||
buffer []byte
|
||||
|
||||
lock sync.Mutex // We use mutex to make it thread-safe
|
||||
// lock is a 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
|
||||
func NewQLogFile(path string) (*QLogFile, error) {
|
||||
// newQLogFile initializes a new instance of the qLogFile.
|
||||
func newQLogFile(path string) (qf *qLogFile, err error) {
|
||||
f, err := os.OpenFile(path, os.O_RDONLY, 0o644)
|
||||
if err != nil {
|
||||
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
|
||||
// with the specified timestamp. Once the record is found, it sets
|
||||
// "position" so that the next ReadNext call returned that record.
|
||||
// with the specified timestamp. Once the record is found, it sets "position"
|
||||
// so that the next ReadNext call returned that record.
|
||||
//
|
||||
// The algorithm is rather simple:
|
||||
// 1. It starts with the position in the middle of a file
|
||||
// 2. Shifts back to the beginning of the line
|
||||
// 3. Checks the log record timestamp
|
||||
// 4. If it is lower than the timestamp we are looking for,
|
||||
// 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 twice.
|
||||
// 1. It starts with the position in the middle of a file.
|
||||
// 2. Shifts back to the beginning of the line.
|
||||
// 3. Checks the log record timestamp.
|
||||
// 4. If it is lower than the timestamp we are looking for, 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
|
||||
// twice.
|
||||
//
|
||||
// Returns:
|
||||
// * It returns the position of the the line with the timestamp we were looking for
|
||||
// so that when we call "ReadNext" this line was returned.
|
||||
// * Depth of the search (how many times we compared timestamps).
|
||||
// * If we could not find it, it returns one of the errors described above.
|
||||
func (q *QLogFile) seekTS(timestamp int64) (int64, int, error) {
|
||||
// - It returns the position of the line with the timestamp we were looking
|
||||
// for so that when we call "ReadNext" this line was returned.
|
||||
// - Depth of the search (how many times we compared timestamps).
|
||||
// - 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) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
// Empty the buffer
|
||||
// Empty the buffer.
|
||||
q.buffer = nil
|
||||
|
||||
// First of all, check the file size
|
||||
// First of all, check the file size.
|
||||
fileInfo, err := q.file.Stat()
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// Define the search scope
|
||||
start := int64(0) // start of the search interval (position in the file)
|
||||
end := fileInfo.Size() // end of the search interval (position in the file)
|
||||
probe := (end - start) / 2 // probe -- approximate index of the line we'll try to check
|
||||
// Define the search scope.
|
||||
|
||||
// Start of the search interval (position in the file).
|
||||
start := int64(0)
|
||||
// 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 lineIdx int64 // index of the probe line in the file
|
||||
// Index of the probe line in the file.
|
||||
var lineIdx int64
|
||||
var lineEndIdx int64
|
||||
var lastProbeLineIdx int64 // index of the last probe line
|
||||
// Index of the last probe line.
|
||||
var lastProbeLineIdx int64
|
||||
lastProbeLineIdx = -1
|
||||
|
||||
// Count seek depth in order to detect mistakes
|
||||
// If depth is too large, we should stop the search
|
||||
depth := 0
|
||||
|
||||
// Count seek depth in order to detect mistakes. If depth is too large,
|
||||
// we should stop the search.
|
||||
for {
|
||||
// Get the line at the specified position
|
||||
// Get the line at the specified position.
|
||||
line, lineIdx, lineEndIdx, err = q.readProbeLine(probe)
|
||||
if err != nil {
|
||||
return 0, depth, err
|
||||
}
|
||||
|
||||
if lineIdx == lastProbeLineIdx {
|
||||
if lineIdx == 0 {
|
||||
return 0, depth, 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 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
|
||||
// Check if the line index if invalid.
|
||||
err = q.validateQLogLineIdx(lineIdx, lastProbeLineIdx, timestamp, fileInfo.Size())
|
||||
if err != nil {
|
||||
return 0, depth, err
|
||||
}
|
||||
|
||||
// Save the last found idx
|
||||
// Save the last found idx.
|
||||
lastProbeLineIdx = lineIdx
|
||||
|
||||
// Get the timestamp from the query log record
|
||||
// Get the timestamp from the query log record.
|
||||
ts := readQLogTimestamp(line)
|
||||
if ts == 0 {
|
||||
return 0, depth, fmt.Errorf("looking up timestamp %d in %q: record %q has empty timestamp", timestamp, q.file.Name(), line)
|
||||
return 0, depth, fmt.Errorf(
|
||||
"looking up timestamp %d in %q: record %q has empty timestamp",
|
||||
timestamp,
|
||||
q.file.Name(),
|
||||
line,
|
||||
)
|
||||
}
|
||||
|
||||
if ts == timestamp {
|
||||
// Hurray, returning the result
|
||||
// Hurray, returning the result.
|
||||
break
|
||||
}
|
||||
|
||||
// Narrow the scope and repeat the search
|
||||
// Narrow the scope and repeat the search.
|
||||
if ts > timestamp {
|
||||
// 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 probe position
|
||||
// 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
|
||||
// probe position.
|
||||
end = lineIdx
|
||||
} else {
|
||||
// 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 probe position
|
||||
// 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
|
||||
// probe position.
|
||||
start = lineEndIdx
|
||||
}
|
||||
probe = start + (end-start)/2
|
||||
|
||||
depth++
|
||||
if depth >= 100 {
|
||||
return 0, depth, fmt.Errorf("looking up timestamp %d in %q: depth %d too high: %w", timestamp, q.file.Name(), depth, ErrTSNotFound)
|
||||
return 0, depth, fmt.Errorf(
|
||||
"looking up timestamp %d in %q: depth %d too high: %w",
|
||||
timestamp,
|
||||
q.file.Name(),
|
||||
depth,
|
||||
errTSNotFound,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,37 +194,39 @@ func (q *QLogFile) seekTS(timestamp int64) (int64, int, error) {
|
||||
return q.position, depth, nil
|
||||
}
|
||||
|
||||
// SeekStart changes the current position to the end of the file
|
||||
// Please note that we're reading query log in the reverse order
|
||||
// and that's why log start is actually the end of file
|
||||
// SeekStart changes the current position to the end of the file. Please note,
|
||||
// that we're reading query log in the reverse order 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 in any other case.
|
||||
func (q *QLogFile) SeekStart() (int64, error) {
|
||||
// Returns nil if we were able to change the current position. Returns error
|
||||
// in any other case.
|
||||
func (q *qLogFile) SeekStart() (int64, error) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
// Empty the buffer
|
||||
// Empty the buffer.
|
||||
q.buffer = nil
|
||||
|
||||
// First of all, check the file size
|
||||
// First of all, check the file size.
|
||||
fileInfo, err := q.file.Stat()
|
||||
if err != nil {
|
||||
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
|
||||
if q.position < 0 {
|
||||
q.position = 0
|
||||
}
|
||||
|
||||
return q.position, nil
|
||||
}
|
||||
|
||||
// ReadNext reads the next line (in the reverse order) from the file
|
||||
// and shifts the current position left to the next (actually prev) line.
|
||||
// returns io.EOF if there's nothing to read more
|
||||
func (q *QLogFile) ReadNext() (string, error) {
|
||||
// ReadNext reads the next line (in the reverse order) from the file and shifts
|
||||
// the current position left to the next (actually prev) line.
|
||||
//
|
||||
// Returns io.EOF if there's nothing more to read.
|
||||
func (q *qLogFile) ReadNext() (string, error) {
|
||||
q.lock.Lock()
|
||||
defer q.lock.Unlock()
|
||||
|
||||
@@ -197,35 +239,34 @@ func (q *QLogFile) ReadNext() (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Shift position
|
||||
// Shift position.
|
||||
if lineIdx == 0 {
|
||||
q.position = 0
|
||||
} else {
|
||||
// there's usually a line break before the line
|
||||
// so we should shift one more char left from the line
|
||||
// line\nline
|
||||
// There's usually a line break before the line, so we should shift one
|
||||
// more char left from the line "\nline".
|
||||
q.position = lineIdx - 1
|
||||
}
|
||||
return line, err
|
||||
}
|
||||
|
||||
// Close frees the underlying resources
|
||||
func (q *QLogFile) Close() error {
|
||||
// Close frees the underlying resources.
|
||||
func (q *qLogFile) Close() error {
|
||||
return q.file.Close()
|
||||
}
|
||||
|
||||
// readNextLine reads the next line from the specified position
|
||||
// this line actually have to END on that position.
|
||||
// readNextLine reads the next line from the specified position. This line
|
||||
// actually have to END on that position.
|
||||
//
|
||||
// the algorithm is:
|
||||
// 1. check if we have the buffer initialized
|
||||
// 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
|
||||
// 4. read the line from the buffer
|
||||
func (q *QLogFile) readNextLine(position int64) (string, int64, error) {
|
||||
// The algorithm is:
|
||||
// 1. Check if we have the buffer initialized.
|
||||
// 2. If it is so, scan it and look for the line there.
|
||||
// 3. If we cannot find the line there, read the prev chunk into the buffer.
|
||||
// 4. Read the line from the buffer.
|
||||
func (q *qLogFile) readNextLine(position int64) (string, int64, error) {
|
||||
relativePos := position - q.bufferStart
|
||||
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)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
@@ -233,8 +274,7 @@ func (q *QLogFile) readNextLine(position int64) (string, int64, error) {
|
||||
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)
|
||||
for i := relativePos - 1; i >= 0; i-- {
|
||||
if q.buffer[i] == '\n' {
|
||||
@@ -245,18 +285,19 @@ func (q *QLogFile) readNextLine(position int64) (string, int64, error) {
|
||||
|
||||
line := string(q.buffer[startLine:relativePos])
|
||||
lineIdx := q.bufferStart + startLine
|
||||
|
||||
return line, lineIdx, nil
|
||||
}
|
||||
|
||||
// initBuffer initializes the QLogFile buffer.
|
||||
// the goal is to read a chunk of file that includes the line with the specified position.
|
||||
func (q *QLogFile) initBuffer(position int64) error {
|
||||
// initBuffer initializes the qLogFile buffer. The goal is to read a chunk of
|
||||
// file that includes the line with the specified position.
|
||||
func (q *qLogFile) initBuffer(position int64) error {
|
||||
q.bufferStart = int64(0)
|
||||
if position > bufferSize {
|
||||
q.bufferStart = position - bufferSize
|
||||
}
|
||||
|
||||
// Seek to this position
|
||||
// Seek to this position.
|
||||
_, err := q.file.Seek(q.bufferStart, io.SeekStart)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -271,34 +312,35 @@ func (q *QLogFile) initBuffer(position int64) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// readProbeLine reads a line that includes the specified position
|
||||
// this method is supposed to be used when we use binary search in the Seek method
|
||||
// in the case of consecutive reads, use readNext (it uses a better buffer)
|
||||
func (q *QLogFile) readProbeLine(position int64) (string, int64, int64, error) {
|
||||
// First of all, we should read a buffer that will include the query log line
|
||||
// In order to do this, we'll define the boundaries
|
||||
// readProbeLine reads a line that includes the specified position. 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.
|
||||
func (q *qLogFile) readProbeLine(position int64) (string, int64, int64, error) {
|
||||
// First of all, we should read a buffer that will include the query log
|
||||
// line. In order to do this, we'll define the boundaries.
|
||||
seekPosition := int64(0)
|
||||
relativePos := position // position relative to the buffer we're going to read
|
||||
// Position relative to the buffer we're going to read.
|
||||
relativePos := position
|
||||
if position > maxEntrySize {
|
||||
seekPosition = position - maxEntrySize
|
||||
relativePos = maxEntrySize
|
||||
}
|
||||
|
||||
// Seek to this position
|
||||
// Seek to this position.
|
||||
_, err := q.file.Seek(seekPosition, io.SeekStart)
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
}
|
||||
|
||||
// The buffer size is 2*maxEntrySize
|
||||
// The buffer size is 2*maxEntrySize.
|
||||
buffer := make([]byte, maxEntrySize*2)
|
||||
bufferLen, err := q.file.Read(buffer)
|
||||
if err != nil {
|
||||
return "", 0, 0, err
|
||||
}
|
||||
|
||||
// Now start looking for the new line character starting
|
||||
// from the relativePos and going left
|
||||
// Now start looking for the new line character starting from the
|
||||
// relativePos and going left.
|
||||
startLine := int64(0)
|
||||
for i := relativePos - 1; i >= 0; i-- {
|
||||
if buffer[i] == '\n' {
|
||||
@@ -306,7 +348,7 @@ func (q *QLogFile) readProbeLine(position int64) (string, int64, int64, error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
// Looking for the end of line now
|
||||
// Looking for the end of line now.
|
||||
endLine := int64(bufferLen)
|
||||
lineEndIdx := endLine + seekPosition
|
||||
for i := relativePos; i < int64(bufferLen); i++ {
|
||||
@@ -317,13 +359,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
|
||||
return string(buffer[startLine:endLine]), lineIdx, lineEndIdx, nil
|
||||
}
|
||||
|
||||
// readJSONvalue reads a JSON string in form of '"key":"value"'. prefix must be
|
||||
// of the form '"key":"' to generate less garbage.
|
||||
// readJSONValue reads a JSON string in form of '"key":"value"'. prefix must
|
||||
// be of the form '"key":"' to generate less garbage.
|
||||
func readJSONValue(s, prefix string) string {
|
||||
i := strings.Index(s, prefix)
|
||||
if i == -1 {
|
||||
@@ -340,7 +382,7 @@ func readJSONValue(s, prefix string) string {
|
||||
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 {
|
||||
val := readJSONValue(str, `"T":"`)
|
||||
if len(val) == 0 {
|
||||
@@ -351,10 +393,12 @@ func readQLogTimestamp(str string) int64 {
|
||||
log.Error("Couldn't find timestamp: %s", str)
|
||||
return 0
|
||||
}
|
||||
|
||||
tm, err := time.Parse(time.RFC3339Nano, val)
|
||||
if err != nil {
|
||||
log.Error("Couldn't parse timestamp: %s", val)
|
||||
return 0
|
||||
}
|
||||
|
||||
return tm.UnixNano()
|
||||
}
|
||||
|
||||
@@ -72,15 +72,15 @@ func prepareTestFiles(t *testing.T, filesNum, linesNum int) []string {
|
||||
return files
|
||||
}
|
||||
|
||||
// newTestQLogFile creates new *QLogFile for tests and registers the required
|
||||
// newTestQLogFile creates new *qLogFile for tests and registers the required
|
||||
// cleanup functions.
|
||||
func newTestQLogFile(t *testing.T, linesNum int) (file *QLogFile) {
|
||||
func newTestQLogFile(t *testing.T, linesNum int) (file *qLogFile) {
|
||||
t.Helper()
|
||||
|
||||
testFile := prepareTestFiles(t, 1, linesNum)[0]
|
||||
|
||||
// Create the new QLogFile instance.
|
||||
file, err := NewQLogFile(testFile)
|
||||
// Create the new qLogFile instance.
|
||||
file, err := newQLogFile(testFile)
|
||||
require.NoError(t, err)
|
||||
|
||||
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 {
|
||||
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.
|
||||
func TestQLogFile(t *testing.T) {
|
||||
// Create the new QLogFile instance.
|
||||
// Create the new qLogFile instance.
|
||||
q := newTestQLogFile(t, 2)
|
||||
|
||||
// Seek to the start.
|
||||
@@ -285,7 +285,7 @@ func TestQLogFile(t *testing.T) {
|
||||
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")
|
||||
require.NoError(t, err)
|
||||
testutil.CleanupAndRequireSuccess(t, f.Close)
|
||||
@@ -293,7 +293,7 @@ func NewTestQLogFileData(t *testing.T, data string) (file *QLogFile) {
|
||||
_, err = f.WriteString(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
file, err = NewQLogFile(f.Name())
|
||||
file, err = newQLogFile(f.Name())
|
||||
require.NoError(t, err)
|
||||
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")
|
||||
|
||||
testCases := []struct {
|
||||
wantErr error
|
||||
name string
|
||||
delta int
|
||||
wantErr error
|
||||
wantDepth int
|
||||
}{{
|
||||
name: "ok",
|
||||
@@ -321,12 +321,12 @@ func TestQLog_Seek(t *testing.T) {
|
||||
}, {
|
||||
name: "too_late",
|
||||
delta: 2,
|
||||
wantErr: ErrTSTooLate,
|
||||
wantErr: errTSTooLate,
|
||||
wantDepth: 2,
|
||||
}, {
|
||||
name: "too_early",
|
||||
delta: -2,
|
||||
wantErr: ErrTSTooEarly,
|
||||
wantErr: errTSTooEarly,
|
||||
wantDepth: 1,
|
||||
}}
|
||||
|
||||
@@ -338,7 +338,7 @@ func TestQLog_Seek(t *testing.T) {
|
||||
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())
|
||||
require.Truef(t, errors.Is(err, tc.wantErr), "%v", err)
|
||||
|
||||
@@ -9,36 +9,36 @@ import (
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
)
|
||||
|
||||
// QLogReader allows reading from multiple query log files in the reverse order.
|
||||
// qLogReader allows reading from multiple query log files in the reverse
|
||||
// order.
|
||||
//
|
||||
// Please note that this is a stateful object.
|
||||
// Internally, it contains a pointer to a particular query log file, and
|
||||
// to a specific position in this file, and it reads lines in reverse order
|
||||
// starting from that position.
|
||||
type QLogReader struct {
|
||||
// qFiles - array with the query log files
|
||||
// The order is - from oldest to newest
|
||||
qFiles []*QLogFile
|
||||
// Please note that this is a stateful object. Internally, it contains a
|
||||
// pointer to a particular query log file, and to a specific position in this
|
||||
// file, and it reads lines in reverse order starting from that position.
|
||||
type qLogReader struct {
|
||||
// qFiles is an array with the query log files. The order is from oldest
|
||||
// to newest.
|
||||
qFiles []*qLogFile
|
||||
|
||||
currentFile int // Index of the current file
|
||||
// currentFile is the index of the current file.
|
||||
currentFile int
|
||||
}
|
||||
|
||||
// NewQLogReader initializes a QLogReader instance
|
||||
// with the specified files
|
||||
func NewQLogReader(files []string) (*QLogReader, error) {
|
||||
qFiles := make([]*QLogFile, 0)
|
||||
// newQLogReader initializes a qLogReader instance with the specified files.
|
||||
func newQLogReader(files []string) (*qLogReader, error) {
|
||||
qFiles := make([]*qLogFile, 0)
|
||||
|
||||
for _, f := range files {
|
||||
q, err := NewQLogFile(f)
|
||||
q, err := newQLogFile(f)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Close what we've already opened.
|
||||
cerr := closeQFiles(qFiles)
|
||||
if cerr != nil {
|
||||
log.Debug("querylog: closing files: %s", cerr)
|
||||
cErr := closeQFiles(qFiles)
|
||||
if cErr != nil {
|
||||
log.Debug("querylog: closing files: %s", cErr)
|
||||
}
|
||||
|
||||
return nil, err
|
||||
@@ -47,31 +47,28 @@ func NewQLogReader(files []string) (*QLogReader, error) {
|
||||
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
|
||||
// timestamp. If the record is found, it sets QLogReader's position to point to
|
||||
// that line, so that the next ReadNext call returned this line.
|
||||
func (r *QLogReader) seekTS(timestamp int64) (err error) {
|
||||
// timestamp. If the record is found, it sets qLogReader's position to point
|
||||
// to that line, so that the next ReadNext call returned this line.
|
||||
func (r *qLogReader) seekTS(timestamp int64) (err error) {
|
||||
for i := len(r.qFiles) - 1; i >= 0; i-- {
|
||||
q := r.qFiles[i]
|
||||
_, _, err = q.seekTS(timestamp)
|
||||
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
|
||||
// one. If there is no next file, it's not found.
|
||||
err = ErrTSNotFound
|
||||
err = errTSNotFound
|
||||
|
||||
continue
|
||||
} else if errors.Is(err, ErrTSTooLate) {
|
||||
} else if errors.Is(err, errTSTooLate) {
|
||||
// Just seek to the start then. timestamp is probably between
|
||||
// the end of the previous one and the start of this one.
|
||||
return r.SeekStart()
|
||||
} else if errors.Is(err, ErrTSNotFound) {
|
||||
} else if errors.Is(err, errTSNotFound) {
|
||||
return err
|
||||
} else {
|
||||
return fmt.Errorf("seekts: file at index %d: %w", i, err)
|
||||
@@ -80,7 +77,7 @@ func (r *QLogReader) seekTS(timestamp int64) (err error) {
|
||||
|
||||
// The search is finished, and the searched element has been found.
|
||||
// Update currentFile only, position is already set properly in
|
||||
// QLogFile.
|
||||
// qLogFile.
|
||||
r.currentFile = i
|
||||
|
||||
return nil
|
||||
@@ -93,13 +90,13 @@ func (r *QLogReader) seekTS(timestamp int64) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 log start is actually the end of 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
|
||||
// the log starts actually at the end of file.
|
||||
//
|
||||
// Returns nil if we were able to change the current position.
|
||||
// Returns error in any other case.
|
||||
func (r *QLogReader) SeekStart() error {
|
||||
// Returns nil if we were able to change the current position. Returns error
|
||||
// in any other cases.
|
||||
func (r *qLogReader) SeekStart() error {
|
||||
if len(r.qFiles) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -110,10 +107,12 @@ func (r *QLogReader) SeekStart() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadNext reads the next line (in the reverse order) from the query log files.
|
||||
// and shifts the current position left to the next (actually prev) line (or the next file).
|
||||
// returns io.EOF if there's nothing to read more.
|
||||
func (r *QLogReader) ReadNext() (string, error) {
|
||||
// ReadNext reads the next line (in the reverse order) from the query log
|
||||
// files. Then shifts the current position left to the next (actually prev)
|
||||
// line (or the next file).
|
||||
//
|
||||
// Returns io.EOF if there is nothing more to read.
|
||||
func (r *qLogReader) ReadNext() (string, error) {
|
||||
if len(r.qFiles) == 0 {
|
||||
return "", io.EOF
|
||||
}
|
||||
@@ -122,7 +121,7 @@ func (r *QLogReader) ReadNext() (string, error) {
|
||||
q := r.qFiles[r.currentFile]
|
||||
line, err := q.ReadNext()
|
||||
if err != nil {
|
||||
// Shift to the older file
|
||||
// Shift to the older file.
|
||||
r.currentFile--
|
||||
if r.currentFile < 0 {
|
||||
break
|
||||
@@ -130,10 +129,10 @@ func (r *QLogReader) ReadNext() (string, error) {
|
||||
|
||||
q = r.qFiles[r.currentFile]
|
||||
|
||||
// Set it's position to the start right away
|
||||
// Set its position to the start right away.
|
||||
_, err = q.SeekStart()
|
||||
|
||||
// This is unexpected, return an error right away
|
||||
// This is unexpected, return an error right away.
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
@@ -142,17 +141,17 @@ func (r *QLogReader) ReadNext() (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing to read anymore
|
||||
// Nothing to read anymore.
|
||||
return "", io.EOF
|
||||
}
|
||||
|
||||
// Close closes the QLogReader
|
||||
func (r *QLogReader) Close() error {
|
||||
// Close closes the qLogReader.
|
||||
func (r *qLogReader) Close() error {
|
||||
return closeQFiles(r.qFiles)
|
||||
}
|
||||
|
||||
// closeQFiles - helper method to close multiple QLogFile instances
|
||||
func closeQFiles(qFiles []*QLogFile) error {
|
||||
// closeQFiles is a helper method to close multiple qLogFile instances.
|
||||
func closeQFiles(qFiles []*qLogFile) error {
|
||||
var errs []error
|
||||
|
||||
for _, q := range qFiles {
|
||||
@@ -163,7 +162,7 @@ func closeQFiles(qFiles []*QLogFile) error {
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errors.List("error while closing QLogReader", errs...)
|
||||
return errors.List("error while closing qLogReader", errs...)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -10,15 +10,15 @@ import (
|
||||
"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.
|
||||
func newTestQLogReader(t *testing.T, filesNum, linesNum int) (reader *QLogReader) {
|
||||
func newTestQLogReader(t *testing.T, filesNum, linesNum int) (reader *qLogReader) {
|
||||
t.Helper()
|
||||
|
||||
testFiles := prepareTestFiles(t, filesNum, linesNum)
|
||||
|
||||
// Create the new QLogReader instance.
|
||||
reader, err := NewQLogReader(testFiles)
|
||||
// Create the new qLogReader instance.
|
||||
reader, err := newQLogReader(testFiles)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.NotNil(t, reader)
|
||||
@@ -75,9 +75,9 @@ func TestQLogReader_Seek(t *testing.T) {
|
||||
r := newTestQLogReader(t, 2, 10000)
|
||||
|
||||
testCases := []struct {
|
||||
want error
|
||||
name string
|
||||
time string
|
||||
want error
|
||||
}{{
|
||||
name: "not_too_old",
|
||||
time: "2020-02-18T22:39:35.920973+03:00",
|
||||
@@ -97,7 +97,7 @@ func TestQLogReader_Seek(t *testing.T) {
|
||||
}, {
|
||||
name: "non-existent_long_ago",
|
||||
time: "2000-02-19T01:23:16.920973+03:00",
|
||||
want: ErrTSNotFound,
|
||||
want: errTSNotFound,
|
||||
}, {
|
||||
name: "non-existent_far_ahead",
|
||||
time: "2100-02-19T01:23:16.920973+03:00",
|
||||
@@ -105,7 +105,7 @@ func TestQLogReader_Seek(t *testing.T) {
|
||||
}, {
|
||||
name: "non-existent_but_could",
|
||||
time: "2020-02-18T22:36:37.000000+03:00",
|
||||
want: ErrTSNotFound,
|
||||
want: errTSNotFound,
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
@@ -125,9 +125,9 @@ func TestQLogReader_ReadNext(t *testing.T) {
|
||||
r := newTestQLogReader(t, filesNum, linesNum)
|
||||
|
||||
testCases := []struct {
|
||||
want error
|
||||
name string
|
||||
start int
|
||||
want error
|
||||
}{{
|
||||
name: "ok",
|
||||
start: 0,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package querylog
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/AdguardTeam/golibs/errors"
|
||||
"github.com/AdguardTeam/golibs/log"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
@@ -134,84 +136,112 @@ func (l *queryLog) search(params *searchParams) (entries []*logEntry, oldest tim
|
||||
return entries, oldest
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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) {
|
||||
// seekRecord changes the current position to the next record older than the
|
||||
// provided parameter.
|
||||
func (r *qLogReader) seekRecord(olderThan time.Time) (err error) {
|
||||
if olderThan.IsZero() {
|
||||
return r.SeekStart()
|
||||
}
|
||||
|
||||
err = r.seekTS(olderThan.UnixNano())
|
||||
if err == nil {
|
||||
// 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{
|
||||
l.logFile + ".1",
|
||||
l.logFile,
|
||||
}
|
||||
|
||||
r, err := NewQLogReader(files)
|
||||
r, err := newQLogReader(files)
|
||||
if err != nil {
|
||||
log.Error("querylog: opening qlog reader: %s", err)
|
||||
|
||||
return entries, oldest, 0
|
||||
}
|
||||
|
||||
defer func() {
|
||||
closeErr := r.Close()
|
||||
if closeErr != nil {
|
||||
log.Error("querylog: closing file: %s", err)
|
||||
}
|
||||
}()
|
||||
|
||||
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 nil, fmt.Errorf("opening qlog reader: %s", err)
|
||||
}
|
||||
|
||||
err = r.seekRecord(olderThan)
|
||||
if err != nil {
|
||||
log.Debug("querylog: cannot seek to %s: %s", params.olderThan, err)
|
||||
defer func() { err = errors.WithDeferred(err, r.Close()) }()
|
||||
log.Debug("querylog: cannot seek to %s: %s", olderThan, err)
|
||||
|
||||
return entries, oldest, 0
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
totalLimit := params.offset + params.limit
|
||||
oldestNano := int64(0)
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// By default, we do 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.
|
||||
// readEntries reads entries from the reader to totalLimit. By default, we do
|
||||
// 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.
|
||||
func (l *queryLog) readEntries(
|
||||
r *qLogReader,
|
||||
params *searchParams,
|
||||
cache clientCache,
|
||||
totalLimit int,
|
||||
) (entries []*logEntry, oldestNano int64, total int) {
|
||||
for total < params.maxFileScanEntries || params.maxFileScanEntries <= 0 {
|
||||
var e *logEntry
|
||||
var ts int64
|
||||
|
||||
e, ts, err = l.readNextEntry(r, params, cache)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
ent, ts, rErr := l.readNextEntry(r, params, cache)
|
||||
if rErr != nil {
|
||||
if rErr == io.EOF {
|
||||
oldestNano = 0
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
log.Error("querylog: reading next entry: %s", err)
|
||||
log.Error("querylog: reading next entry: %s", rErr)
|
||||
}
|
||||
|
||||
oldestNano = ts
|
||||
total++
|
||||
|
||||
if e != nil {
|
||||
entries = append(entries, e)
|
||||
if len(entries) == totalLimit {
|
||||
break
|
||||
}
|
||||
if ent == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
entries = append(entries, ent)
|
||||
if len(entries) == totalLimit {
|
||||
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 {
|
||||
oldest = time.Unix(0, oldestNano)
|
||||
}
|
||||
@@ -243,11 +273,11 @@ func (f quickMatchClientFinder) findClient(clientID, ip string) (c *Client) {
|
||||
}
|
||||
|
||||
// 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 the
|
||||
// entry doesn't match the search criteria. ts is the timestamp of the
|
||||
// criteria. It optionally uses the client cache, if provided. e is nil if
|
||||
// the entry doesn't match the search criteria. ts is the timestamp of the
|
||||
// processed entry.
|
||||
func (l *queryLog) readNextEntry(
|
||||
r *QLogReader,
|
||||
r *qLogReader,
|
||||
params *searchParams,
|
||||
cache clientCache,
|
||||
) (e *logEntry, ts int64, err error) {
|
||||
|
||||
@@ -2,18 +2,25 @@ package querylog
|
||||
|
||||
import "time"
|
||||
|
||||
// searchParams represent the search query sent by the client
|
||||
// searchParams represent the search query sent by the client.
|
||||
type searchParams struct {
|
||||
// searchCriteria - list of search criteria that we use to get filter results
|
||||
searchCriteria []searchCriterion
|
||||
|
||||
// olderThen - return entries that are older than this value
|
||||
// if not set - disregard it and return any value
|
||||
// olderThen represents a parameter for entries that are older than this
|
||||
// parameter value. If not set, disregard it and return any value.
|
||||
olderThan time.Time
|
||||
|
||||
offset int // offset for the search
|
||||
limit int // limit the number of records returned
|
||||
maxFileScanEntries int // maximum log entries to scan in query log files. if 0 - no limit
|
||||
// searchCriteria is a list of search criteria that we use to get filter
|
||||
// results.
|
||||
searchCriteria []searchCriterion
|
||||
|
||||
// offset for the search.
|
||||
offset int
|
||||
|
||||
// limit the number of records returned.
|
||||
limit int
|
||||
|
||||
// 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
|
||||
|
||||
@@ -8,16 +8,16 @@ require (
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28
|
||||
github.com/kisielk/errcheck v1.6.3
|
||||
github.com/kyoh86/looppointer v0.2.1
|
||||
github.com/securego/gosec/v2 v2.15.0
|
||||
golang.org/x/tools v0.8.0
|
||||
golang.org/x/vuln v0.0.0-20230418010118-28ba02ac73db
|
||||
github.com/securego/gosec/v2 v2.16.0
|
||||
golang.org/x/tools v0.9.3
|
||||
golang.org/x/vuln v0.1.0
|
||||
honnef.co/go/tools v0.4.3
|
||||
mvdan.cc/gofumpt v0.5.0
|
||||
mvdan.cc/unparam v0.0.0-20230312165513-e84e2d14e3b8
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.2.1 // indirect
|
||||
github.com/BurntSushi/toml v1.3.1 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/google/uuid v1.3.0 // 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/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20230321023759-10a507213a29 // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20230522175609-2e198f4a06a1 // indirect
|
||||
golang.org/x/mod v0.10.0 // indirect
|
||||
golang.org/x/sync v0.1.0 // indirect
|
||||
golang.org/x/sys v0.7.0 // indirect
|
||||
golang.org/x/sync v0.2.0 // indirect
|
||||
golang.org/x/sys v0.8.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.3.1 h1:rHnDkSK+/g6DlREUK73PkmIs60pqrnuduK+JmP++JmU=
|
||||
github.com/BurntSushi/toml v1.3.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
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.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
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/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
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/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-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
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/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -29,13 +31,13 @@ github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU=
|
||||
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/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
|
||||
github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI=
|
||||
github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q=
|
||||
github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE=
|
||||
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
|
||||
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/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
|
||||
github.com/securego/gosec/v2 v2.15.0 h1:v4Ym7FF58/jlykYmmhZ7mTm7FQvN/setNm++0fgIAtw=
|
||||
github.com/securego/gosec/v2 v2.15.0/go.mod h1:VOjTrZOkUtSDt2QLSJmQBMWnvwiQPEjg0l+5juIqGk8=
|
||||
github.com/securego/gosec/v2 v2.16.0 h1:Pi0JKoasQQ3NnoRao/ww/N/XdynIB9NRYYZT5CyOs5U=
|
||||
github.com/securego/gosec/v2 v2.16.0/go.mod h1:xvLcVZqUfo4aAQu56TNv7/Ltz6emAOQAEsrZrt7uGlI=
|
||||
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/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
@@ -49,8 +51,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug=
|
||||
golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230321023759-10a507213a29 h1:e7LhZmJ631l59keHP9ssC3sgSn3/oiEHKHKXDkimURY=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230321023759-10a507213a29/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230522175609-2e198f4a06a1 h1:pnP8r+W8Fm7XJ8CWtXi4S9oJmPBTrkfYN/dNbaPj6Y4=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
@@ -62,12 +64,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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
|
||||
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
|
||||
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-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
||||
golang.org/x/sync v0.2.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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -77,23 +79,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-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.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
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.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
|
||||
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-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||
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.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
|
||||
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
|
||||
golang.org/x/vuln v0.0.0-20230418010118-28ba02ac73db h1:tLxfII6jPR3mfwEMkyOakawu+Lldo9hIA7vliXnDZYg=
|
||||
golang.org/x/vuln v0.0.0-20230418010118-28ba02ac73db/go.mod h1:64LpnL2PuSMzFYeCmJjYiRbroOUG9aCZYznINnF5PHE=
|
||||
golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM=
|
||||
golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
||||
golang.org/x/vuln v0.1.0 h1:9GRdj6wAIkDrsMevuolY+SXERPjQPp2P1ysYA0jpZe0=
|
||||
golang.org/x/vuln v0.1.0/go.mod h1:/YuzZYjGbwB8y19CisAppfyw3uTZnuCz3r+qgx/QRzU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -35,7 +35,7 @@ set -f -u
|
||||
go_version="$( "${GO:-go}" version )"
|
||||
readonly go_version
|
||||
|
||||
go_min_version='go1.19.8'
|
||||
go_min_version='go1.19.10'
|
||||
go_version_msg="
|
||||
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.
|
||||
@@ -160,29 +160,7 @@ run_linter "$GO" vet ./...
|
||||
|
||||
run_linter govulncheck ./...
|
||||
|
||||
# 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 gocyclo --over 10 .
|
||||
|
||||
run_linter ineffassign ./...
|
||||
|
||||
|
||||
Reference in New Issue
Block a user