Merge branch 'master' into 4299-querylog-stats-api
This commit is contained in:
commit
56acdfde5b
|
@ -1,7 +1,7 @@
|
||||||
'name': 'build'
|
'name': 'build'
|
||||||
|
|
||||||
'env':
|
'env':
|
||||||
'GO_VERSION': '1.19.6'
|
'GO_VERSION': '1.19.7'
|
||||||
'NODE_VERSION': '14'
|
'NODE_VERSION': '14'
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'name': 'lint'
|
'name': 'lint'
|
||||||
|
|
||||||
'env':
|
'env':
|
||||||
'GO_VERSION': '1.19.6'
|
'GO_VERSION': '1.19.7'
|
||||||
|
|
||||||
'on':
|
'on':
|
||||||
'push':
|
'push':
|
||||||
|
|
136
CHANGELOG.md
136
CHANGELOG.md
|
@ -14,11 +14,11 @@ and this project adheres to
|
||||||
<!--
|
<!--
|
||||||
## [v0.108.0] - TBA
|
## [v0.108.0] - TBA
|
||||||
|
|
||||||
## [v0.107.26] - 2023-03-09 (APPROX.)
|
## [v0.107.27] - 2023-03-29 (APPROX.)
|
||||||
|
|
||||||
See also the [v0.107.26 GitHub milestone][ms-v0.107.26].
|
See also the [v0.107.27 GitHub milestone][ms-v0.107.27].
|
||||||
|
|
||||||
[ms-v0.107.26]: https://github.com/AdguardTeam/AdGuardHome/milestone/62?closed=1
|
[ms-v0.107.27]: https://github.com/AdguardTeam/AdGuardHome/milestone/63?closed=1
|
||||||
|
|
||||||
NOTE: Add new changes BELOW THIS COMMENT.
|
NOTE: Add new changes BELOW THIS COMMENT.
|
||||||
-->
|
-->
|
||||||
|
@ -31,19 +31,19 @@ NOTE: Add new changes BELOW THIS COMMENT.
|
||||||
- Two new HTTP APIs, `PUT /control/querylog/config/update` and `GET
|
- Two new HTTP APIs, `PUT /control/querylog/config/update` and `GET
|
||||||
control/querylog/config`, which can be used to set and receive the statistics
|
control/querylog/config`, which can be used to set and receive the statistics
|
||||||
configuration. See openapi/openapi.yaml for the full description.
|
configuration. See openapi/openapi.yaml for the full description.
|
||||||
- The ability to set custom IP for EDNS Client Subnet by using the new
|
- The ability to manage safesearch for each service by using the new
|
||||||
`dns.edns_client_subnet.use_custom` and `dns.edns_client_subnet.custom_ip`
|
`safe_search` field ([#1163]).
|
||||||
fields ([#1472]). The UI changes are coming in the upcoming releases.
|
|
||||||
- The ability to use `dnstype` rules in the disallowed domains list ([#5468]).
|
|
||||||
This allows dropping requests based on their question types.
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
- ARPA domain names containing a subnet within private networks now also
|
||||||
|
considered private, behaving closer to [RFC 6761][rfc6761] ([#5567]).
|
||||||
|
|
||||||
#### Configuration Changes
|
#### Configuration Changes
|
||||||
|
|
||||||
In this release, the schema version has changed from 16 to 18.
|
In this release, the schema version has changed from 17 to 20.
|
||||||
|
|
||||||
- Property `statistics.interval`, which in schema versions 17 and earlier used
|
- Property `statistics.interval`, which in schema versions 19 and earlier used
|
||||||
to be an integer number of days, is now a string with a human-readable
|
to be an integer number of days, is now a string with a human-readable
|
||||||
duration:
|
duration:
|
||||||
|
|
||||||
|
@ -60,7 +60,87 @@ In this release, the schema version has changed from 16 to 18.
|
||||||
```
|
```
|
||||||
|
|
||||||
To rollback this change, convert the property back into days and change the
|
To rollback this change, convert the property back into days and change the
|
||||||
|
`schema_version` back to `19`.
|
||||||
|
- The `dns.safesearch_enabled` field has been replaced with `safe_search`
|
||||||
|
object containing per-service settings.
|
||||||
|
- The `clients.persistent.safesearch_enabled` field has been replaced with
|
||||||
|
`safe_search` object containing per-service settings.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# BEFORE:
|
||||||
|
'safesearch_enabled': true
|
||||||
|
|
||||||
|
# AFTER:
|
||||||
|
'safe_search':
|
||||||
|
'enabled': true
|
||||||
|
'bing': true
|
||||||
|
'duckduckgo': true
|
||||||
|
'google': true
|
||||||
|
'pixabay': true
|
||||||
|
'yandex': true
|
||||||
|
'youtube': true
|
||||||
|
```
|
||||||
|
|
||||||
|
To rollback this change, move the value of `dns.safe_search.enabled` into the
|
||||||
|
`dns.safesearch_enabled`, then remove `dns.safe_search` field. Do the same
|
||||||
|
client's specific `clients.persistent.safesearch` and then change the
|
||||||
`schema_version` back to `17`.
|
`schema_version` back to `17`.
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
- The `GET /control/stats_info` HTTP API; use the new `GET
|
||||||
|
/control/stats/config` API instead.
|
||||||
|
|
||||||
|
**NOTE:** If interval is custom then it will be equal to `90` days for
|
||||||
|
compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`.
|
||||||
|
- The `POST /control/stats_config` HTTP API; use the new `PUT
|
||||||
|
/control/stats/config/update` API instead.
|
||||||
|
- The `GET /control/querylog_info` HTTP API; use the new `GET
|
||||||
|
/control/querylog/config` API instead.
|
||||||
|
|
||||||
|
**NOTE:** If interval is custom then it will be equal to `90` days for
|
||||||
|
compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`.
|
||||||
|
- The `POST /control/querylog_config` HTTP API; use the new `PUT
|
||||||
|
/control/querylog/config/update` API instead.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Panic caused by empty top-level domain name label in `/etc/hosts` files
|
||||||
|
([#5584]).
|
||||||
|
|
||||||
|
[#1163]: https://github.com/AdguardTeam/AdGuardHome/issues/1163
|
||||||
|
[#5567]: https://github.com/AdguardTeam/AdGuardHome/issues/5567
|
||||||
|
[#5584]: https://github.com/AdguardTeam/AdGuardHome/issues/5584
|
||||||
|
|
||||||
|
[rfc6761]: https://www.rfc-editor.org/rfc/rfc6761
|
||||||
|
|
||||||
|
<!--
|
||||||
|
NOTE: Add new changes ABOVE THIS COMMENT.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [v0.107.26] - 2023-03-09
|
||||||
|
|
||||||
|
See also the [v0.107.26 GitHub milestone][ms-v0.107.26].
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Go version has been updated to prevent the possibility of exploiting the
|
||||||
|
CVE-2023-24532 Go vulnerability fixed in [Go 1.19.7][go-1.19.7].
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- The ability to set custom IP for EDNS Client Subnet by using the new
|
||||||
|
`dns.edns_client_subnet.use_custom` and `dns.edns_client_subnet.custom_ip`
|
||||||
|
fields ([#1472]). The UI changes are coming in the upcoming releases.
|
||||||
|
- The ability to use `dnstype` rules in the disallowed domains list ([#5468]).
|
||||||
|
This allows dropping requests based on their question types.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
#### Configuration Changes
|
||||||
|
|
||||||
- Property `edns_client_subnet`, which in schema versions 16 and earlier used
|
- Property `edns_client_subnet`, which in schema versions 16 and earlier used
|
||||||
to be a part of the `dns` object, is now part of the `dns.edns_client_subnet`
|
to be a part of the `dns` object, is now part of the `dns.edns_client_subnet`
|
||||||
object:
|
object:
|
||||||
|
@ -86,25 +166,9 @@ In this release, the schema version has changed from 16 to 18.
|
||||||
`dns.edns_client_subnet.custom_ip`, and change the `schema_version` back to
|
`dns.edns_client_subnet.custom_ip`, and change the `schema_version` back to
|
||||||
`16`.
|
`16`.
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
|
|
||||||
- The `GET /control/stats_info` HTTP API; use the new `GET
|
|
||||||
/control/stats/config` API instead.
|
|
||||||
|
|
||||||
**NOTE:** If interval is custom then it will be equal to `90` days for
|
|
||||||
compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`.
|
|
||||||
- The `POST /control/stats_config` HTTP API; use the new `PUT
|
|
||||||
/control/stats/config/update` API instead.
|
|
||||||
- The `GET /control/querylog_info` HTTP API; use the new `GET
|
|
||||||
/control/querylog/config` API instead.
|
|
||||||
|
|
||||||
**NOTE:** If interval is custom then it will be equal to `90` days for
|
|
||||||
compatibility reasons. See openapi/openapi.yaml and `openapi/CHANGELOG.md`.
|
|
||||||
- The `POST /control/querylog_config` HTTP API; use the new `PUT
|
|
||||||
/control/querylog/config/update` API instead.
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Obsolete value of the Interface MTU DHCP option is now omitted ([#5281]).
|
||||||
- Various dark theme bugs ([#5439], [#5441], [#5442], [#5515]).
|
- Various dark theme bugs ([#5439], [#5441], [#5442], [#5515]).
|
||||||
- Automatic update on MIPS64 and little-endian 32-bit MIPS architectures
|
- Automatic update on MIPS64 and little-endian 32-bit MIPS architectures
|
||||||
([#5270], [#5373]).
|
([#5270], [#5373]).
|
||||||
|
@ -115,6 +179,7 @@ In this release, the schema version has changed from 16 to 18.
|
||||||
[#1472]: https://github.com/AdguardTeam/AdGuardHome/issues/1472
|
[#1472]: https://github.com/AdguardTeam/AdGuardHome/issues/1472
|
||||||
[#4884]: https://github.com/AdguardTeam/AdGuardHome/issues/4884
|
[#4884]: https://github.com/AdguardTeam/AdGuardHome/issues/4884
|
||||||
[#5270]: https://github.com/AdguardTeam/AdGuardHome/issues/5270
|
[#5270]: https://github.com/AdguardTeam/AdGuardHome/issues/5270
|
||||||
|
[#5281]: https://github.com/AdguardTeam/AdGuardHome/issues/5281
|
||||||
[#5373]: https://github.com/AdguardTeam/AdGuardHome/issues/5373
|
[#5373]: https://github.com/AdguardTeam/AdGuardHome/issues/5373
|
||||||
[#5431]: https://github.com/AdguardTeam/AdGuardHome/issues/5431
|
[#5431]: https://github.com/AdguardTeam/AdGuardHome/issues/5431
|
||||||
[#5439]: https://github.com/AdguardTeam/AdGuardHome/issues/5439
|
[#5439]: https://github.com/AdguardTeam/AdGuardHome/issues/5439
|
||||||
|
@ -123,11 +188,9 @@ In this release, the schema version has changed from 16 to 18.
|
||||||
[#5468]: https://github.com/AdguardTeam/AdGuardHome/issues/5468
|
[#5468]: https://github.com/AdguardTeam/AdGuardHome/issues/5468
|
||||||
[#5515]: https://github.com/AdguardTeam/AdGuardHome/issues/5515
|
[#5515]: https://github.com/AdguardTeam/AdGuardHome/issues/5515
|
||||||
|
|
||||||
[rfc3696]: https://datatracker.ietf.org/doc/html/rfc3696
|
[go-1.19.7]: https://groups.google.com/g/golang-announce/c/3-TpUx48iQY
|
||||||
|
[ms-v0.107.26]: https://github.com/AdguardTeam/AdGuardHome/milestone/62?closed=1
|
||||||
<!--
|
[rfc3696]: https://datatracker.ietf.org/doc/html/rfc3696
|
||||||
NOTE: Add new changes ABOVE THIS COMMENT.
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1786,11 +1849,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...HEAD
|
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.27...HEAD
|
||||||
[v0.107.26]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.25...v0.107.26
|
[v0.107.27]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...v0.107.27
|
||||||
-->
|
-->
|
||||||
|
|
||||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.25...HEAD
|
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.26...HEAD
|
||||||
|
[v0.107.26]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.25...v0.107.26
|
||||||
[v0.107.25]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.24...v0.107.25
|
[v0.107.25]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.24...v0.107.25
|
||||||
[v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...v0.107.24
|
[v0.107.24]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.23...v0.107.24
|
||||||
[v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.22...v0.107.23
|
[v0.107.23]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.22...v0.107.23
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
# Make sure to sync any changes with the branch overrides below.
|
# Make sure to sync any changes with the branch overrides below.
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'edge'
|
'channel': 'edge'
|
||||||
'dockerGo': 'adguard/golang-ubuntu:6.1'
|
'dockerGo': 'adguard/golang-ubuntu:6.2'
|
||||||
|
|
||||||
'stages':
|
'stages':
|
||||||
- 'Build frontend':
|
- 'Build frontend':
|
||||||
|
@ -331,7 +331,7 @@
|
||||||
# need to build a few of these.
|
# need to build a few of these.
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'beta'
|
'channel': 'beta'
|
||||||
'dockerGo': 'adguard/golang-ubuntu:6.1'
|
'dockerGo': 'adguard/golang-ubuntu:6.2'
|
||||||
# release-vX.Y.Z branches are the branches from which the actual final release
|
# release-vX.Y.Z branches are the branches from which the actual final release
|
||||||
# is built.
|
# is built.
|
||||||
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
- '^release-v[0-9]+\.[0-9]+\.[0-9]+':
|
||||||
|
@ -346,4 +346,4 @@
|
||||||
# are the ones that actually get released.
|
# are the ones that actually get released.
|
||||||
'variables':
|
'variables':
|
||||||
'channel': 'release'
|
'channel': 'release'
|
||||||
'dockerGo': 'adguard/golang-ubuntu:6.1'
|
'dockerGo': 'adguard/golang-ubuntu:6.2'
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
'key': 'AHBRTSPECS'
|
'key': 'AHBRTSPECS'
|
||||||
'name': 'AdGuard Home - Build and run tests'
|
'name': 'AdGuard Home - Build and run tests'
|
||||||
'variables':
|
'variables':
|
||||||
'dockerGo': 'adguard/golang-ubuntu:6.1'
|
'dockerGo': 'adguard/golang-ubuntu:6.2'
|
||||||
|
|
||||||
'stages':
|
'stages':
|
||||||
- 'Tests':
|
- 'Tests':
|
||||||
|
|
|
@ -297,7 +297,7 @@
|
||||||
"blocking_mode_refused": "REFUSED: Vastaa REFUSED-koodilla",
|
"blocking_mode_refused": "REFUSED: Vastaa REFUSED-koodilla",
|
||||||
"blocking_mode_nxdomain": "NXDOMAIN: Vastaa NXDOMAIN-koodilla",
|
"blocking_mode_nxdomain": "NXDOMAIN: Vastaa NXDOMAIN-koodilla",
|
||||||
"blocking_mode_null_ip": "Tyhjä IP: Vastaa IP-nollaosoitteella (0.0.0.0 korvaa A; :: korvaa AAAA)",
|
"blocking_mode_null_ip": "Tyhjä IP: Vastaa IP-nollaosoitteella (0.0.0.0 korvaa A; :: korvaa AAAA)",
|
||||||
"blocking_mode_custom_ip": "Mukautettu IP: Vastaa itse määritetyllä IP-osoitteella",
|
"blocking_mode_custom_ip": "Mukautettu IP: Vastaa manuaalisesti määritetyllä IP-osoitteella",
|
||||||
"theme_auto": "Automaattinen",
|
"theme_auto": "Automaattinen",
|
||||||
"theme_light": "Vaalea",
|
"theme_light": "Vaalea",
|
||||||
"theme_dark": "Tumma",
|
"theme_dark": "Tumma",
|
||||||
|
|
|
@ -281,6 +281,7 @@
|
||||||
"blocking_mode_nxdomain": "NXDOMAIN: Svar med NXDOMAIN-koden",
|
"blocking_mode_nxdomain": "NXDOMAIN: Svar med NXDOMAIN-koden",
|
||||||
"blocking_mode_null_ip": "Null IP: Svar med en 0-IP-adresse (0.0.0.0 for A; :: for AAAA)",
|
"blocking_mode_null_ip": "Null IP: Svar med en 0-IP-adresse (0.0.0.0 for A; :: for AAAA)",
|
||||||
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt valgt IP-adresse",
|
"blocking_mode_custom_ip": "Tilpasset IP: Svar med en manuelt valgt IP-adresse",
|
||||||
|
"theme_auto": "Auto",
|
||||||
"upstream_dns_client_desc": "Hvis dette feltet holdes tomt, vil AdGuard Home bruke tjenerne som er satt opp i <0>DNS-innstillingene</0>.",
|
"upstream_dns_client_desc": "Hvis dette feltet holdes tomt, vil AdGuard Home bruke tjenerne som er satt opp i <0>DNS-innstillingene</0>.",
|
||||||
"tracker_source": "Sporerkilde",
|
"tracker_source": "Sporerkilde",
|
||||||
"source_label": "Kilde",
|
"source_label": "Kilde",
|
||||||
|
|
|
@ -60,7 +60,7 @@ const Dashboard = ({
|
||||||
title={t('refresh_btn')}
|
title={t('refresh_btn')}
|
||||||
onClick={() => getAllStats()}
|
onClick={() => getAllStats()}
|
||||||
>
|
>
|
||||||
<svg className="icons">
|
<svg className="icons icon12">
|
||||||
<use xlinkHref="#refresh" />
|
<use xlinkHref="#refresh" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>;
|
</button>;
|
||||||
|
|
|
@ -100,7 +100,7 @@ class Table extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<svg className="icons">
|
<svg className="icons icon12">
|
||||||
<use xlinkHref="#edit" />
|
<use xlinkHref="#edit" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
@ -110,7 +110,7 @@ class Table extends Component {
|
||||||
onClick={() => handleDelete(url)}
|
onClick={() => handleDelete(url)}
|
||||||
title={t('delete_table_action')}
|
title={t('delete_table_action')}
|
||||||
>
|
>
|
||||||
<svg className="icons">
|
<svg className="icons icon12">
|
||||||
<use xlinkHref="#delete" />
|
<use xlinkHref="#delete" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -162,7 +162,7 @@ const ClientCell = ({
|
||||||
{content && (
|
{content && (
|
||||||
<button className={buttonArrowClass} disabled={processingRules}>
|
<button className={buttonArrowClass} disabled={processingRules}>
|
||||||
<IconTooltip
|
<IconTooltip
|
||||||
className="h-100"
|
className="icon24"
|
||||||
tooltipClass="button-action--arrow-option-container"
|
tooltipClass="button-action--arrow-option-container"
|
||||||
xlinkHref="chevron-down"
|
xlinkHref="chevron-down"
|
||||||
triggerClass="button-action--icon"
|
triggerClass="button-action--icon"
|
||||||
|
|
|
@ -129,7 +129,6 @@ const Form = (props) => {
|
||||||
|
|
||||||
const onInputClear = async () => {
|
const onInputClear = async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setDebouncedSearch(DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
|
||||||
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
change(FORM_NAMES.search, DEFAULT_LOGS_FILTER[FORM_NAMES.search]);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
};
|
};
|
||||||
|
|
|
@ -106,6 +106,16 @@
|
||||||
max-height: 100% !important;
|
max-height: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon24 {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon12 {
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.cursor--pointer {
|
.cursor--pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
@ -311,7 +321,6 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button-action:active {
|
.button-action:active {
|
||||||
|
|
|
@ -290,7 +290,7 @@ const ClientsTable = ({
|
||||||
disabled={processingUpdating}
|
disabled={processingUpdating}
|
||||||
title={t('edit_table_action')}
|
title={t('edit_table_action')}
|
||||||
>
|
>
|
||||||
<svg className="icons">
|
<svg className="icons icon12">
|
||||||
<use xlinkHref="#edit" />
|
<use xlinkHref="#edit" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
@ -301,7 +301,7 @@ const ClientsTable = ({
|
||||||
disabled={processingDeleting}
|
disabled={processingDeleting}
|
||||||
title={t('delete_table_action')}
|
title={t('delete_table_action')}
|
||||||
>
|
>
|
||||||
<svg className="icons">
|
<svg className="icons icon12">
|
||||||
<use xlinkHref="#delete" />
|
<use xlinkHref="#delete" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -54,6 +54,12 @@
|
||||||
color: #495057;
|
color: #495057;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.service__icon svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
fill: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
.service--global .service__icon {
|
.service--global .service__icon {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,10 +86,10 @@ const Icons = () => (
|
||||||
d="m19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1 -2.83 0l-.06-.06a1.65 1.65 0 0 0 -1.82-.33 1.65 1.65 0 0 0 -1 1.51v.17a2 2 0 0 1 -2 2 2 2 0 0 1 -2-2v-.09a1.65 1.65 0 0 0 -1.08-1.51 1.65 1.65 0 0 0 -1.82.33l-.06.06a2 2 0 0 1 -2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0 -1.51-1h-.17a2 2 0 0 1 -2-2 2 2 0 0 1 2-2h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0 -.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33h.08a1.65 1.65 0 0 0 1-1.51v-.17a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0 -.33 1.82v.08a1.65 1.65 0 0 0 1.51 1h.17a2 2 0 0 1 2 2 2 2 0 0 1 -2 2h-.09a1.65 1.65 0 0 0 -1.51 1z" />
|
d="m19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1 -2.83 0l-.06-.06a1.65 1.65 0 0 0 -1.82-.33 1.65 1.65 0 0 0 -1 1.51v.17a2 2 0 0 1 -2 2 2 2 0 0 1 -2-2v-.09a1.65 1.65 0 0 0 -1.08-1.51 1.65 1.65 0 0 0 -1.82.33l-.06.06a2 2 0 0 1 -2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0 -1.51-1h-.17a2 2 0 0 1 -2-2 2 2 0 0 1 2-2h.09a1.65 1.65 0 0 0 1.51-1.08 1.65 1.65 0 0 0 -.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33h.08a1.65 1.65 0 0 0 1-1.51v-.17a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0 -.33 1.82v.08a1.65 1.65 0 0 0 1.51 1h.17a2 2 0 0 1 2 2 2 2 0 0 1 -2 2h-.09a1.65 1.65 0 0 0 -1.51 1z" />
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
<symbol id="refresh" viewBox="0 0 24 24" stroke="currentColor" fill="none"
|
<symbol id="refresh" viewBox="0 0 24 24" stroke="currentColor" fill="none" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
|
||||||
strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
|
<polyline points="23 4 23 10 17 10"></polyline>
|
||||||
<path d="M23 4v6h-6M1 20v-6h6" />
|
<polyline points="1 20 1 14 7 14"></polyline>
|
||||||
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" />
|
<path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
<symbol id="dns_privacy" viewBox="0 0 30 30" stroke="none" fill="currentColor"
|
<symbol id="dns_privacy" viewBox="0 0 30 30" stroke="none" fill="currentColor"
|
||||||
|
@ -198,7 +198,7 @@ const Icons = () => (
|
||||||
</svg>
|
</svg>
|
||||||
</symbol>
|
</symbol>
|
||||||
|
|
||||||
<symbol id="chevron-down" viewBox="0 0 24 24">
|
<symbol id="chevron-down" width="24" height="24" viewBox="0 0 24 24">
|
||||||
<g fill="none" fillRule="evenodd">
|
<g fill="none" fillRule="evenodd">
|
||||||
<path d="M0 0h24v24H0z" fill="#878787" fillOpacity=".01" />
|
<path d="M0 0h24v24H0z" fill="#878787" fillOpacity=".01" />
|
||||||
<path stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"
|
<path stroke="currentColor" strokeWidth="1.5" strokeLinecap="round"
|
||||||
|
|
|
@ -39,7 +39,7 @@ const Version = () => {
|
||||||
disabled={processingVersion}
|
disabled={processingVersion}
|
||||||
title={t('check_updates_now')}
|
title={t('check_updates_now')}
|
||||||
>
|
>
|
||||||
<svg className="icons">
|
<svg className="icons icon12">
|
||||||
<use xlinkHref="#refresh" />
|
<use xlinkHref="#refresh" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>}
|
</button>}
|
||||||
|
|
|
@ -235,7 +235,7 @@ export default {
|
||||||
"urlhaus_filter_online": {
|
"urlhaus_filter_online": {
|
||||||
"name": "Malicious URL Blocklist (URLHaus)",
|
"name": "Malicious URL Blocklist (URLHaus)",
|
||||||
"categoryId": "security",
|
"categoryId": "security",
|
||||||
"homepage": "https://gitlab.com/malware-filter/urlhaus-filter",
|
"homepage": "https://urlhaus.abuse.ch/",
|
||||||
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt"
|
"source": "https://adguardteam.github.io/HostlistsRegistry/assets/filter_11.txt"
|
||||||
},
|
},
|
||||||
"windowsspyblocker_hosts_spy_rules": {
|
"windowsspyblocker_hosts_spy_rules": {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"timeUpdated": "2023-03-01T10:05:51.445Z",
|
"timeUpdated": "2023-03-08T00:09:48.692Z",
|
||||||
"categories": {
|
"categories": {
|
||||||
"0": "audio_video_player",
|
"0": "audio_video_player",
|
||||||
"1": "comments",
|
"1": "comments",
|
||||||
|
@ -19316,6 +19316,13 @@
|
||||||
"companyId": null,
|
"companyId": null,
|
||||||
"source": "AdGuard"
|
"source": "AdGuard"
|
||||||
},
|
},
|
||||||
|
"edgio": {
|
||||||
|
"name": "Edgio",
|
||||||
|
"categoryId": 9,
|
||||||
|
"url": "https://edg.io/",
|
||||||
|
"companyId": "edgio",
|
||||||
|
"source": "AdGuard"
|
||||||
|
},
|
||||||
"element": {
|
"element": {
|
||||||
"name": "Element",
|
"name": "Element",
|
||||||
"categoryId": 7,
|
"categoryId": 7,
|
||||||
|
@ -19372,6 +19379,13 @@
|
||||||
"companyId": "lets_encrypt",
|
"companyId": "lets_encrypt",
|
||||||
"source": "AdGuard"
|
"source": "AdGuard"
|
||||||
},
|
},
|
||||||
|
"lgtv": {
|
||||||
|
"name": "LG TV",
|
||||||
|
"categoryId": 8,
|
||||||
|
"url": "https://www.lg.com/",
|
||||||
|
"companyId": "lgcorp",
|
||||||
|
"source": "AdGuard"
|
||||||
|
},
|
||||||
"matrix": {
|
"matrix": {
|
||||||
"name": "Matrix",
|
"name": "Matrix",
|
||||||
"categoryId": 5,
|
"categoryId": 5,
|
||||||
|
@ -19407,6 +19421,13 @@
|
||||||
"companyId": "mozilla",
|
"companyId": "mozilla",
|
||||||
"source": "AdGuard"
|
"source": "AdGuard"
|
||||||
},
|
},
|
||||||
|
"nab": {
|
||||||
|
"name": "National Australia Bank",
|
||||||
|
"categoryId": 8,
|
||||||
|
"url": "https://www.nab.com.au/",
|
||||||
|
"companyId": "nab",
|
||||||
|
"source": "AdGuard"
|
||||||
|
},
|
||||||
"notion": {
|
"notion": {
|
||||||
"name": "Notion",
|
"name": "Notion",
|
||||||
"categoryId": 8,
|
"categoryId": 8,
|
||||||
|
@ -19456,6 +19477,13 @@
|
||||||
"companyId": "qualcomm",
|
"companyId": "qualcomm",
|
||||||
"source": "AdGuard"
|
"source": "AdGuard"
|
||||||
},
|
},
|
||||||
|
"recaptcha": {
|
||||||
|
"name": "reCAPTCHA",
|
||||||
|
"categoryId": 8,
|
||||||
|
"url": "https://www.google.com/recaptcha/about/",
|
||||||
|
"companyId": "google",
|
||||||
|
"source": "AdGuard"
|
||||||
|
},
|
||||||
"sectigo": {
|
"sectigo": {
|
||||||
"name": "Sectigo Limited",
|
"name": "Sectigo Limited",
|
||||||
"categoryId": 5,
|
"categoryId": 5,
|
||||||
|
@ -23893,6 +23921,11 @@
|
||||||
"adguardvpn.com": "adguard_vpn",
|
"adguardvpn.com": "adguard_vpn",
|
||||||
"adguard-vpn.com": "adguard_vpn",
|
"adguard-vpn.com": "adguard_vpn",
|
||||||
"adguard-vpn.online": "adguard_vpn",
|
"adguard-vpn.online": "adguard_vpn",
|
||||||
|
"adjust.net.in": "adjust",
|
||||||
|
"adj.st": "adjust",
|
||||||
|
"adjust.io": "adjust",
|
||||||
|
"adjust.world": "adjust",
|
||||||
|
"apptrace.com": "adjust",
|
||||||
"akadns.net": "akamai_technologies",
|
"akadns.net": "akamai_technologies",
|
||||||
"akamaiedge.net": "akamai_technologies",
|
"akamaiedge.net": "akamai_technologies",
|
||||||
"akaquill.net": "akamai_technologies",
|
"akaquill.net": "akamai_technologies",
|
||||||
|
@ -23907,6 +23940,21 @@
|
||||||
"aliyun.com": "alibaba_cloud",
|
"aliyun.com": "alibaba_cloud",
|
||||||
"ucweb.com": "alibaba_ucbrowser",
|
"ucweb.com": "alibaba_ucbrowser",
|
||||||
"alipayobjects.com": "alipay.com",
|
"alipayobjects.com": "alipay.com",
|
||||||
|
"amazoncrl.com": "amazon",
|
||||||
|
"aamazoncognito.com": "amazon",
|
||||||
|
"amazonbrowserapp.es": "amazon",
|
||||||
|
"amazonbrowserapp.co.uk": "amazon",
|
||||||
|
"amazon.sa": "amazon",
|
||||||
|
"amazon.nl": "amazon",
|
||||||
|
"amazon.in": "amazon",
|
||||||
|
"amazon.com.mx": "amazon",
|
||||||
|
"amazon.com.au": "amazon",
|
||||||
|
"amazon-corp.com": "amazon",
|
||||||
|
"a2z.com": "amazon",
|
||||||
|
"amazontrust.com": "amazon_cdn",
|
||||||
|
"associates-amazon.com": "amazon_cdn",
|
||||||
|
"amazonpay.in": "amazon_payments",
|
||||||
|
"amazonvideo.com": "amazon_video",
|
||||||
"taobao.com": "taobao",
|
"taobao.com": "taobao",
|
||||||
"appcenter.ms": "appcenter",
|
"appcenter.ms": "appcenter",
|
||||||
"iadsdk.apple.com": "apple_ads",
|
"iadsdk.apple.com": "apple_ads",
|
||||||
|
@ -23926,6 +23974,8 @@
|
||||||
"apple-livephotoskit.com": "apple",
|
"apple-livephotoskit.com": "apple",
|
||||||
"safebrowsing.apple": "apple",
|
"safebrowsing.apple": "apple",
|
||||||
"safebrowsing.g.applimg.com": "apple",
|
"safebrowsing.g.applimg.com": "apple",
|
||||||
|
"applvn.com": "applovin",
|
||||||
|
"applovin.com": "applovin",
|
||||||
"blob.core.windows.net": "azure_blob_storage",
|
"blob.core.windows.net": "azure_blob_storage",
|
||||||
"azure.com": "azure",
|
"azure.com": "azure",
|
||||||
"trafficmanager.net": "azure",
|
"trafficmanager.net": "azure",
|
||||||
|
@ -23935,12 +23985,21 @@
|
||||||
"cloudflare-dns.com": "cloudflare",
|
"cloudflare-dns.com": "cloudflare",
|
||||||
"crashlytics.com": "crashlytics",
|
"crashlytics.com": "crashlytics",
|
||||||
"phicdn.net": "digicert_trust_seal",
|
"phicdn.net": "digicert_trust_seal",
|
||||||
|
"alphacdn.net": "edgio",
|
||||||
|
"edg.io": "edgio",
|
||||||
|
"edgecast.com": "edgio",
|
||||||
|
"edgecastcdn.net": "edgio",
|
||||||
|
"edgecastdns.net": "edgio",
|
||||||
|
"sigmacdn.net": "edgio",
|
||||||
"element.io": "element",
|
"element.io": "element",
|
||||||
"riot.im": "element",
|
"riot.im": "element",
|
||||||
"app-measurement.com": "firebase",
|
"app-measurement.com": "firebase",
|
||||||
"flipboard.com": "flipboard",
|
"flipboard.com": "flipboard",
|
||||||
"flurry.com": "flurry",
|
"flurry.com": "flurry",
|
||||||
|
"ghcr.io": "github",
|
||||||
|
"github.dev": "github",
|
||||||
"gmail.com": "gmail",
|
"gmail.com": "gmail",
|
||||||
|
"googlehosted.com": "google_appspot",
|
||||||
"gvt1.com": "google_servers",
|
"gvt1.com": "google_servers",
|
||||||
"gvt2.com": "google_servers",
|
"gvt2.com": "google_servers",
|
||||||
"gvt3.com": "google_servers",
|
"gvt3.com": "google_servers",
|
||||||
|
@ -23949,10 +24008,15 @@
|
||||||
"kik.com": "kik",
|
"kik.com": "kik",
|
||||||
"apikik.com": "kik",
|
"apikik.com": "kik",
|
||||||
"kik-live.com": "kik",
|
"kik-live.com": "kik",
|
||||||
"letsencrypt.org": "lets_encrypt",
|
|
||||||
"slatic.net": "lazada",
|
"slatic.net": "lazada",
|
||||||
"lencr.org": "lets_encrypt",
|
"lencr.org": "lets_encrypt",
|
||||||
"edgecastcdn.net": "markmonitor",
|
"letsencrypt.org": "lets_encrypt",
|
||||||
|
"lgsmartad.com": "lgtv",
|
||||||
|
"lgtvcommon.com": "lgtv",
|
||||||
|
"lgtvsdp.com": "lgtv",
|
||||||
|
"lge.com": "lgtv",
|
||||||
|
"lg.com": "lgtv",
|
||||||
|
"markmonitor.com": "markmonitor",
|
||||||
"matrix.org": "matrix",
|
"matrix.org": "matrix",
|
||||||
"medialab.la": "medialab",
|
"medialab.la": "medialab",
|
||||||
"media-lab.ai": "medialab",
|
"media-lab.ai": "medialab",
|
||||||
|
@ -23968,6 +24032,13 @@
|
||||||
"mozilla.net": "mozilla",
|
"mozilla.net": "mozilla",
|
||||||
"mozilla.org": "mozilla",
|
"mozilla.org": "mozilla",
|
||||||
"nflximg.com": "netflix",
|
"nflximg.com": "netflix",
|
||||||
|
"nab.com": "nab",
|
||||||
|
"nab.com.au": "nab",
|
||||||
|
"nab.net": "nab",
|
||||||
|
"nabgroup.com": "nab",
|
||||||
|
"national.com.au": "nab",
|
||||||
|
"nationalaustraliabank.com.au": "nab",
|
||||||
|
"nationalbank.com.au": "nab",
|
||||||
"notion.so": "notion",
|
"notion.so": "notion",
|
||||||
"ntp.org": "ntppool",
|
"ntp.org": "ntppool",
|
||||||
"ntppool.org": "ntppool",
|
"ntppool.org": "ntppool",
|
||||||
|
@ -23984,6 +24055,7 @@
|
||||||
"plex.direct": "plex",
|
"plex.direct": "plex",
|
||||||
"xtracloud.net": "qualcomm",
|
"xtracloud.net": "qualcomm",
|
||||||
"qualcomm.com": "qualcomm",
|
"qualcomm.com": "qualcomm",
|
||||||
|
"recaptcha.net": "recaptcha",
|
||||||
"sectigo.com": "sectigo",
|
"sectigo.com": "sectigo",
|
||||||
"showrss.info": "showrss",
|
"showrss.info": "showrss",
|
||||||
"similarweb.io": "similarweb",
|
"similarweb.io": "similarweb",
|
||||||
|
|
16
go.mod
16
go.mod
|
@ -5,7 +5,7 @@ go 1.19
|
||||||
require (
|
require (
|
||||||
// TODO(a.garipov): Use v0.48.0 when it's released.
|
// TODO(a.garipov): Use v0.48.0 when it's released.
|
||||||
github.com/AdguardTeam/dnsproxy v0.48.0
|
github.com/AdguardTeam/dnsproxy v0.48.0
|
||||||
github.com/AdguardTeam/golibs v0.12.0
|
github.com/AdguardTeam/golibs v0.13.0
|
||||||
github.com/AdguardTeam/urlfilter v0.16.1
|
github.com/AdguardTeam/urlfilter v0.16.1
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.2.5
|
github.com/ameshkov/dnscrypt/v2 v2.2.5
|
||||||
|
@ -26,13 +26,13 @@ require (
|
||||||
github.com/mdlayher/raw v0.1.0
|
github.com/mdlayher/raw v0.1.0
|
||||||
github.com/miekg/dns v1.1.50
|
github.com/miekg/dns v1.1.50
|
||||||
github.com/quic-go/quic-go v0.32.0
|
github.com/quic-go/quic-go v0.32.0
|
||||||
github.com/stretchr/testify v1.8.1
|
github.com/stretchr/testify v1.8.2
|
||||||
github.com/ti-mo/netfilter v0.5.0
|
github.com/ti-mo/netfilter v0.5.0
|
||||||
go.etcd.io/bbolt v1.3.7
|
go.etcd.io/bbolt v1.3.7
|
||||||
golang.org/x/crypto v0.6.0
|
golang.org/x/crypto v0.6.0
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb
|
golang.org/x/exp v0.0.0-20230306221820-f0f767cdffd6
|
||||||
golang.org/x/net v0.7.0
|
golang.org/x/net v0.8.0
|
||||||
golang.org/x/sys v0.5.0
|
golang.org/x/sys v0.6.0
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
howett.net/plist v1.0.0
|
howett.net/plist v1.0.0
|
||||||
|
@ -63,6 +63,10 @@ require (
|
||||||
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
|
||||||
golang.org/x/mod v0.8.0 // indirect
|
golang.org/x/mod v0.8.0 // indirect
|
||||||
golang.org/x/sync v0.1.0 // indirect
|
golang.org/x/sync v0.1.0 // indirect
|
||||||
golang.org/x/text v0.7.0 // indirect
|
golang.org/x/text v0.8.0 // indirect
|
||||||
golang.org/x/tools v0.6.0 // indirect
|
golang.org/x/tools v0.6.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(a.garipov): Remove this and update github.com/ameshkov/dnscrypt when
|
||||||
|
// it's released.
|
||||||
|
replace github.com/ameshkov/dnscrypt/v2 => github.com/ainar-g/dnscrypt/v2 v2.0.1-0.20230315131826-cdb2bf61bda8
|
||||||
|
|
28
go.sum
28
go.sum
|
@ -2,8 +2,8 @@ github.com/AdguardTeam/dnsproxy v0.48.0 h1:sGViYy2pV0cEp2zCsxPjFd9rlgD0+yELpIeLk
|
||||||
github.com/AdguardTeam/dnsproxy v0.48.0/go.mod h1:9OHoeaVod+moWwrLjHF95RQnFWGi/6B1tfKsxWc/yGE=
|
github.com/AdguardTeam/dnsproxy v0.48.0/go.mod h1:9OHoeaVod+moWwrLjHF95RQnFWGi/6B1tfKsxWc/yGE=
|
||||||
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
github.com/AdguardTeam/golibs v0.4.0/go.mod h1:skKsDKIBB7kkFflLJBpfGX+G8QFTx0WKUzB6TIgtUj4=
|
||||||
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
github.com/AdguardTeam/golibs v0.10.4/go.mod h1:rSfQRGHIdgfxriDDNgNJ7HmE5zRoURq8R+VdR81Zuzw=
|
||||||
github.com/AdguardTeam/golibs v0.12.0 h1:z4Q3Mz0pHJ2Zag4B0RBaIXEUue1TPOKkbRiYkwC4r7I=
|
github.com/AdguardTeam/golibs v0.13.0 h1:hVBeNQXT/BgcjKz/4FMpFGvEYqXiXDJG+b5XpGCUOLk=
|
||||||
github.com/AdguardTeam/golibs v0.12.0/go.mod h1:87bN2x4VsTritptE3XZg9l8T6gznWsIxHBcQ1DeRIXA=
|
github.com/AdguardTeam/golibs v0.13.0/go.mod h1:rIglKDHdLvFT1UbhumBLHO9S4cvWS9MEyT1njommI/Y=
|
||||||
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
github.com/AdguardTeam/gomitmproxy v0.2.0/go.mod h1:Qdv0Mktnzer5zpdpi5rAwixNJzW2FN91LjKJCkVbYGU=
|
||||||
github.com/AdguardTeam/urlfilter v0.16.1 h1:ZPi0rjqo8cQf2FVdzo6cqumNoHZx2KPXj2yZa1A5BBw=
|
github.com/AdguardTeam/urlfilter v0.16.1 h1:ZPi0rjqo8cQf2FVdzo6cqumNoHZx2KPXj2yZa1A5BBw=
|
||||||
github.com/AdguardTeam/urlfilter v0.16.1/go.mod h1:46YZDOV1+qtdRDuhZKVPSSp7JWWes0KayqHrKAFBdEI=
|
github.com/AdguardTeam/urlfilter v0.16.1/go.mod h1:46YZDOV1+qtdRDuhZKVPSSp7JWWes0KayqHrKAFBdEI=
|
||||||
|
@ -15,8 +15,8 @@ github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmH
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
|
||||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635 h1:52m0LGchQBBVqJRyYYufQuIbVqRawmubW3OFGqK1ekw=
|
||||||
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
github.com/aead/poly1305 v0.0.0-20180717145839-3fee0db0b635/go.mod h1:lmLxL+FV291OopO93Bwf9fQLQeLyt33VJRUg5VJ30us=
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.2.5 h1:Ju1gQeez+6XLtk/b/k3RoJ2t+Ls+BSItLTZjZeedneY=
|
github.com/ainar-g/dnscrypt/v2 v2.0.1-0.20230315131826-cdb2bf61bda8 h1:jc3/aOQ01Yy3vxB/uqcuUi4F+6E/FKWaTCHq5J1ef2o=
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.2.5/go.mod h1:Cu5GgMvCR10BeXgACiGDwXyOpfMktsSIidml1XBp6uM=
|
github.com/ainar-g/dnscrypt/v2 v2.0.1-0.20230315131826-cdb2bf61bda8/go.mod h1:qPWhwz6FdSmuK7W4sMyvogrez4MWdtzosdqlr0Rg3ow=
|
||||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||||
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
github.com/ameshkov/dnsstamps v1.0.3/go.mod h1:Ii3eUu73dx4Vw5O4wjzmT5+lkCwovjzaEZZ4gKyIH5A=
|
||||||
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG++IsjHiI2p4bxALD1Y2nQKGMR5zDQM=
|
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 h1:0b2vaepXIfMsG++IsjHiI2p4bxALD1Y2nQKGMR5zDQM=
|
||||||
|
@ -145,8 +145,8 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
|
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU=
|
github.com/ti-mo/netfilter v0.2.0/go.mod h1:8GbBGsY/8fxtyIdfwy29JiluNcPK4K7wIT+x42ipqUU=
|
||||||
github.com/ti-mo/netfilter v0.5.0 h1:MZmsUw5bFRecOb0AeyjOPxTHg4UxYzyEs0Ek/6Lxoy8=
|
github.com/ti-mo/netfilter v0.5.0 h1:MZmsUw5bFRecOb0AeyjOPxTHg4UxYzyEs0Ek/6Lxoy8=
|
||||||
github.com/ti-mo/netfilter v0.5.0/go.mod h1:nt+8B9hx/QpqHr7Hazq+2qMCCA8u2OTkyc/7+U9ARz8=
|
github.com/ti-mo/netfilter v0.5.0/go.mod h1:nt+8B9hx/QpqHr7Hazq+2qMCCA8u2OTkyc/7+U9ARz8=
|
||||||
|
@ -165,8 +165,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
|
||||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb h1:PaBZQdo+iSDyHT053FjUCgZQ/9uqVwPOcl7KSWhKn6w=
|
golang.org/x/exp v0.0.0-20230306221820-f0f767cdffd6 h1:3p+wVC0x0TCIPgd3LCQlpgVlEtjziEC5v42w7+B8t8M=
|
||||||
golang.org/x/exp v0.0.0-20230213192124-5e25df0256eb/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
golang.org/x/exp v0.0.0-20230306221820-f0f767cdffd6/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
|
@ -188,8 +188,8 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210929193557-e81a3d93ecf6/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
|
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||||
|
@ -222,16 +222,16 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
|
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
|
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
|
|
|
@ -608,6 +608,11 @@ func TestUniqueRules_ParseLine(t *testing.T) {
|
||||||
line: ``,
|
line: ``,
|
||||||
wantIP: netip.Addr{},
|
wantIP: netip.Addr{},
|
||||||
wantHosts: nil,
|
wantHosts: nil,
|
||||||
|
}, {
|
||||||
|
name: "bad_hosts",
|
||||||
|
line: ipStr + ` bad..host bad._tld empty.tld. ok.host`,
|
||||||
|
wantIP: ip,
|
||||||
|
wantHosts: []string{"ok.host"},
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
|
@ -263,15 +263,12 @@ func (s *v4Server) prepareOptions() {
|
||||||
|
|
||||||
// IP-Layer Per Interface
|
// IP-Layer Per Interface
|
||||||
|
|
||||||
// Since nearly all networks in the Internet currently support an MTU of
|
// Don't set the Interface MTU because client may choose the value on
|
||||||
// 576 or greater, we strongly recommend the use of 576 for datagrams
|
// their own since it's listed in the [Host Requirements RFC]. It also
|
||||||
// sent to non-local networks.
|
// seems the values listed there sometimes appear obsolete, see
|
||||||
|
// https://github.com/AdguardTeam/AdGuardHome/issues/5281.
|
||||||
//
|
//
|
||||||
// See https://datatracker.ietf.org/doc/html/rfc1122#section-3.3.3.
|
// [Host Requirements RFC]: https://datatracker.ietf.org/doc/html/rfc1122#section-3.3.3.
|
||||||
dhcpv4.Option{
|
|
||||||
Code: dhcpv4.OptionInterfaceMTU,
|
|
||||||
Value: dhcpv4.Uint16(576),
|
|
||||||
},
|
|
||||||
|
|
||||||
// Set the All Subnets Are Local Option to false since commonly the
|
// Set the All Subnets Are Local Option to false since commonly the
|
||||||
// connected hosts aren't expected to be multihomed.
|
// connected hosts aren't expected to be multihomed.
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -37,6 +38,8 @@ type dnsContext struct {
|
||||||
// was parsed successfully and belongs to one of the locally served IP
|
// was parsed successfully and belongs to one of the locally served IP
|
||||||
// ranges. It is also filled with unmapped version of the address if it's
|
// ranges. It is also filled with unmapped version of the address if it's
|
||||||
// within DNS64 prefixes.
|
// within DNS64 prefixes.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Use netip.Addr when we switch to netip more fully.
|
||||||
unreversedReqIP net.IP
|
unreversedReqIP net.IP
|
||||||
|
|
||||||
// err is the error returned from a processing function.
|
// err is the error returned from a processing function.
|
||||||
|
@ -442,6 +445,88 @@ func (s *Server) processDHCPHosts(dctx *dnsContext) (rc resultCode) {
|
||||||
return resultCodeSuccess
|
return resultCodeSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// indexFirstV4Label returns the index at which the reversed IPv4 address
|
||||||
|
// starts, assuiming the domain is pre-validated ARPA domain having in-addr and
|
||||||
|
// arpa labels removed.
|
||||||
|
func indexFirstV4Label(domain string) (idx int) {
|
||||||
|
idx = len(domain)
|
||||||
|
for labelsNum := 0; labelsNum < net.IPv4len && idx > 0; labelsNum++ {
|
||||||
|
curIdx := strings.LastIndexByte(domain[:idx-1], '.') + 1
|
||||||
|
_, parseErr := strconv.ParseUint(domain[curIdx:idx-1], 10, 8)
|
||||||
|
if parseErr != nil {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = curIdx
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexFirstV6Label returns the index at which the reversed IPv6 address
|
||||||
|
// starts, assuiming the domain is pre-validated ARPA domain having ip6 and arpa
|
||||||
|
// labels removed.
|
||||||
|
func indexFirstV6Label(domain string) (idx int) {
|
||||||
|
idx = len(domain)
|
||||||
|
for labelsNum := 0; labelsNum < net.IPv6len*2 && idx > 0; labelsNum++ {
|
||||||
|
curIdx := idx - len("a.")
|
||||||
|
if curIdx > 1 && domain[curIdx-1] != '.' {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
nibble := domain[curIdx]
|
||||||
|
if (nibble < '0' || nibble > '9') && (nibble < 'a' || nibble > 'f') {
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = curIdx
|
||||||
|
}
|
||||||
|
|
||||||
|
return idx
|
||||||
|
}
|
||||||
|
|
||||||
|
// extractARPASubnet tries to convert a reversed ARPA address being a part of
|
||||||
|
// domain to an IP network. domain must be an FQDN.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Move to golibs.
|
||||||
|
func extractARPASubnet(domain string) (pref netip.Prefix, err error) {
|
||||||
|
err = netutil.ValidateDomainName(strings.TrimSuffix(domain, "."))
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return netip.Prefix{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
v4Suffix = "in-addr.arpa."
|
||||||
|
v6Suffix = "ip6.arpa."
|
||||||
|
)
|
||||||
|
|
||||||
|
domain = strings.ToLower(domain)
|
||||||
|
|
||||||
|
var idx int
|
||||||
|
switch {
|
||||||
|
case strings.HasSuffix(domain, v4Suffix):
|
||||||
|
idx = indexFirstV4Label(domain[:len(domain)-len(v4Suffix)])
|
||||||
|
case strings.HasSuffix(domain, v6Suffix):
|
||||||
|
idx = indexFirstV6Label(domain[:len(domain)-len(v6Suffix)])
|
||||||
|
default:
|
||||||
|
return netip.Prefix{}, &netutil.AddrError{
|
||||||
|
Err: netutil.ErrNotAReversedSubnet,
|
||||||
|
Kind: netutil.AddrKindARPA,
|
||||||
|
Addr: domain,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var subnet *net.IPNet
|
||||||
|
subnet, err = netutil.SubnetFromReversedAddr(domain[idx:])
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error since it's informative enough as is.
|
||||||
|
return netip.Prefix{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return netutil.IPNetToPrefixNoMapped(subnet)
|
||||||
|
}
|
||||||
|
|
||||||
// processRestrictLocal responds with NXDOMAIN to PTR requests for IP addresses
|
// processRestrictLocal responds with NXDOMAIN to PTR requests for IP addresses
|
||||||
// in locally served network from external clients.
|
// in locally served network from external clients.
|
||||||
func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
|
func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
|
||||||
|
@ -453,34 +538,29 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
|
||||||
return resultCodeSuccess
|
return resultCodeSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
ip, err := netutil.IPFromReversedAddr(q.Name)
|
subnet, err := extractARPASubnet(q.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debug("dnsforward: parsing reversed addr: %s", err)
|
if errors.Is(err, netutil.ErrNotAReversedSubnet) {
|
||||||
|
log.Debug("dnsforward: request is not for arpa domain")
|
||||||
|
|
||||||
// DNS-Based Service Discovery uses PTR records having not an ARPA
|
return resultCodeSuccess
|
||||||
// format of the domain name in question. Those shouldn't be
|
|
||||||
// invalidated. See http://www.dns-sd.org/ServerStaticSetup.html and
|
|
||||||
// RFC 2782.
|
|
||||||
name := strings.TrimSuffix(q.Name, ".")
|
|
||||||
if err = netutil.ValidateSRVDomainName(name); err != nil {
|
|
||||||
log.Debug("dnsforward: validating service domain: %s", err)
|
|
||||||
|
|
||||||
return resultCodeError
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("dnsforward: request is not for arpa domain")
|
log.Debug("dnsforward: parsing reversed addr: %s", err)
|
||||||
|
|
||||||
return resultCodeSuccess
|
return resultCodeError
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restrict an access to local addresses for external clients. We also
|
// Restrict an access to local addresses for external clients. We also
|
||||||
// assume that all the DHCP leases we give are locally served or at least
|
// assume that all the DHCP leases we give are locally served or at least
|
||||||
// shouldn't be accessible externally.
|
// shouldn't be accessible externally.
|
||||||
if !s.privateNets.Contains(ip) {
|
subnetAddr := subnet.Addr()
|
||||||
|
addrData := subnetAddr.AsSlice()
|
||||||
|
if !s.privateNets.Contains(addrData) {
|
||||||
return resultCodeSuccess
|
return resultCodeSuccess
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("dnsforward: addr %s is from locally served network", ip)
|
log.Debug("dnsforward: addr %s is from locally served network", subnetAddr)
|
||||||
|
|
||||||
if !dctx.isLocalClient {
|
if !dctx.isLocalClient {
|
||||||
log.Debug("dnsforward: %q requests an internal ip", pctx.Addr)
|
log.Debug("dnsforward: %q requests an internal ip", pctx.Addr)
|
||||||
|
@ -491,7 +571,7 @@ func (s *Server) processRestrictLocal(dctx *dnsContext) (rc resultCode) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do not perform unreversing ever again.
|
// Do not perform unreversing ever again.
|
||||||
dctx.unreversedReqIP = ip
|
dctx.unreversedReqIP = addrData
|
||||||
|
|
||||||
// There is no need to filter request from external addresses since this
|
// There is no need to filter request from external addresses since this
|
||||||
// code is only executed when the request is for locally served ARPA
|
// code is only executed when the request is for locally served ARPA
|
||||||
|
|
|
@ -605,3 +605,129 @@ func TestIPStringFromAddr(t *testing.T) {
|
||||||
assert.Empty(t, ipStringFromAddr(nil))
|
assert.Empty(t, ipStringFromAddr(nil))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(e.burkov): Add fuzzing when moving to golibs.
|
||||||
|
func TestExtractARPASubnet(t *testing.T) {
|
||||||
|
const (
|
||||||
|
v4Suf = `in-addr.arpa.`
|
||||||
|
v4Part = `2.1.` + v4Suf
|
||||||
|
v4Whole = `4.3.` + v4Part
|
||||||
|
|
||||||
|
v6Suf = `ip6.arpa.`
|
||||||
|
v6Part = `4.3.2.1.0.0.0.0.0.0.0.0.0.0.0.0.` + v6Suf
|
||||||
|
v6Whole = `f.e.d.c.0.0.0.0.0.0.0.0.0.0.0.0.` + v6Part
|
||||||
|
)
|
||||||
|
|
||||||
|
v4Pref := netip.MustParsePrefix("1.2.3.4/32")
|
||||||
|
v4PrefPart := netip.MustParsePrefix("1.2.0.0/16")
|
||||||
|
v6Pref := netip.MustParsePrefix("::1234:0:0:0:cdef/128")
|
||||||
|
v6PrefPart := netip.MustParsePrefix("0:0:0:1234::/64")
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
want netip.Prefix
|
||||||
|
name string
|
||||||
|
domain string
|
||||||
|
wantErr string
|
||||||
|
}{{
|
||||||
|
want: netip.Prefix{},
|
||||||
|
name: "not_an_arpa",
|
||||||
|
domain: "some.domain.name.",
|
||||||
|
wantErr: `bad arpa domain name "some.domain.name.": ` +
|
||||||
|
`not a reversed ip network`,
|
||||||
|
}, {
|
||||||
|
want: netip.Prefix{},
|
||||||
|
name: "bad_domain_name",
|
||||||
|
domain: "abc.123.",
|
||||||
|
wantErr: `bad domain name "abc.123": ` +
|
||||||
|
`bad top-level domain name label "123": all octets are numeric`,
|
||||||
|
}, {
|
||||||
|
want: v4Pref,
|
||||||
|
name: "whole_v4",
|
||||||
|
domain: v4Whole,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v4PrefPart,
|
||||||
|
name: "partial_v4",
|
||||||
|
domain: v4Part,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v4Pref,
|
||||||
|
name: "whole_v4_within_domain",
|
||||||
|
domain: "a." + v4Whole,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v4Pref,
|
||||||
|
name: "whole_v4_additional_label",
|
||||||
|
domain: "5." + v4Whole,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v4PrefPart,
|
||||||
|
name: "partial_v4_within_domain",
|
||||||
|
domain: "a." + v4Part,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v4PrefPart,
|
||||||
|
name: "overflow_v4",
|
||||||
|
domain: "256." + v4Part,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v4PrefPart,
|
||||||
|
name: "overflow_v4_within_domain",
|
||||||
|
domain: "a.256." + v4Part,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: netip.Prefix{},
|
||||||
|
name: "empty_v4",
|
||||||
|
domain: v4Suf,
|
||||||
|
wantErr: `bad arpa domain name "in-addr.arpa": ` +
|
||||||
|
`not a reversed ip network`,
|
||||||
|
}, {
|
||||||
|
want: netip.Prefix{},
|
||||||
|
name: "empty_v4_within_domain",
|
||||||
|
domain: "a." + v4Suf,
|
||||||
|
wantErr: `bad arpa domain name "in-addr.arpa": ` +
|
||||||
|
`not a reversed ip network`,
|
||||||
|
}, {
|
||||||
|
want: v6Pref,
|
||||||
|
name: "whole_v6",
|
||||||
|
domain: v6Whole,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v6PrefPart,
|
||||||
|
name: "partial_v6",
|
||||||
|
domain: v6Part,
|
||||||
|
}, {
|
||||||
|
want: v6Pref,
|
||||||
|
name: "whole_v6_within_domain",
|
||||||
|
domain: "g." + v6Whole,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v6Pref,
|
||||||
|
name: "whole_v6_additional_label",
|
||||||
|
domain: "1." + v6Whole,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: v6PrefPart,
|
||||||
|
name: "partial_v6_within_domain",
|
||||||
|
domain: "label." + v6Part,
|
||||||
|
wantErr: "",
|
||||||
|
}, {
|
||||||
|
want: netip.Prefix{},
|
||||||
|
name: "empty_v6",
|
||||||
|
domain: v6Suf,
|
||||||
|
wantErr: `bad arpa domain name "ip6.arpa": not a reversed ip network`,
|
||||||
|
}, {
|
||||||
|
want: netip.Prefix{},
|
||||||
|
name: "empty_v6_within_domain",
|
||||||
|
domain: "g." + v6Suf,
|
||||||
|
wantErr: `bad arpa domain name "ip6.arpa": not a reversed ip network`,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
subnet, err := extractARPASubnet(tc.domain)
|
||||||
|
testutil.AssertErrorMsg(t, tc.wantErr, err)
|
||||||
|
assert.Equal(t, tc.want, subnet)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
|
@ -412,7 +413,7 @@ func TestServerRace(t *testing.T) {
|
||||||
filterConf := &filtering.Config{
|
filterConf := &filtering.Config{
|
||||||
SafeBrowsingEnabled: true,
|
SafeBrowsingEnabled: true,
|
||||||
SafeBrowsingCacheSize: 1000,
|
SafeBrowsingCacheSize: 1000,
|
||||||
SafeSearchEnabled: true,
|
SafeSearchConf: filtering.SafeSearchConfig{Enabled: true},
|
||||||
SafeSearchCacheSize: 1000,
|
SafeSearchCacheSize: 1000,
|
||||||
ParentalCacheSize: 1000,
|
ParentalCacheSize: 1000,
|
||||||
CacheTime: 30,
|
CacheTime: 30,
|
||||||
|
@ -440,12 +441,26 @@ func TestServerRace(t *testing.T) {
|
||||||
|
|
||||||
func TestSafeSearch(t *testing.T) {
|
func TestSafeSearch(t *testing.T) {
|
||||||
resolver := &aghtest.TestResolver{}
|
resolver := &aghtest.TestResolver{}
|
||||||
|
safeSearchConf := filtering.SafeSearchConfig{
|
||||||
|
Enabled: true,
|
||||||
|
Google: true,
|
||||||
|
Yandex: true,
|
||||||
|
CustomResolver: resolver,
|
||||||
|
}
|
||||||
|
|
||||||
filterConf := &filtering.Config{
|
filterConf := &filtering.Config{
|
||||||
SafeSearchEnabled: true,
|
SafeSearchConf: safeSearchConf,
|
||||||
SafeSearchCacheSize: 1000,
|
SafeSearchCacheSize: 1000,
|
||||||
CacheTime: 30,
|
CacheTime: 30,
|
||||||
CustomResolver: resolver,
|
|
||||||
}
|
}
|
||||||
|
safeSearch, err := safesearch.NewDefaultSafeSearch(
|
||||||
|
safeSearchConf,
|
||||||
|
filterConf.SafeSearchCacheSize,
|
||||||
|
time.Minute*time.Duration(filterConf.CacheTime),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
filterConf.SafeSearch = safeSearch
|
||||||
forwardConf := ServerConfig{
|
forwardConf := ServerConfig{
|
||||||
UDPListenAddrs: []*net.UDPAddr{{}},
|
UDPListenAddrs: []*net.UDPAddr{{}},
|
||||||
TCPListenAddrs: []*net.TCPAddr{{}},
|
TCPListenAddrs: []*net.TCPAddr{{}},
|
||||||
|
@ -498,7 +513,8 @@ func TestSafeSearch(t *testing.T) {
|
||||||
t.Run(tc.host, func(t *testing.T) {
|
t.Run(tc.host, func(t *testing.T) {
|
||||||
req := createTestMessage(tc.host)
|
req := createTestMessage(tc.host)
|
||||||
|
|
||||||
reply, _, err := client.Exchange(req, addr)
|
var reply *dns.Msg
|
||||||
|
reply, _, err = client.Exchange(req, addr)
|
||||||
require.NoErrorf(t, err, "couldn't talk to server %s: %s", addr, err)
|
require.NoErrorf(t, err, "couldn't talk to server %s: %s", addr, err)
|
||||||
assertResponse(t, reply, tc.want)
|
assertResponse(t, reply, tc.want)
|
||||||
})
|
})
|
||||||
|
|
|
@ -388,15 +388,15 @@ func ValidateUpstreamsPrivate(upstreams []string, privateNets netutil.SubnetSet)
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
for _, domain := range keys {
|
for _, domain := range keys {
|
||||||
var subnet *net.IPNet
|
var subnet netip.Prefix
|
||||||
subnet, err = netutil.SubnetFromReversedAddr(domain)
|
subnet, err = extractARPASubnet(domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if !privateNets.Contains(subnet.IP) {
|
if !privateNets.Contains(subnet.Addr().AsSlice()) {
|
||||||
errs = append(
|
errs = append(
|
||||||
errs,
|
errs,
|
||||||
fmt.Errorf("arpa domain %q should point to a locally-served network", domain),
|
fmt.Errorf("arpa domain %q should point to a locally-served network", domain),
|
||||||
|
|
|
@ -57,7 +57,7 @@ func TestDNSForwardHTTP_handleGetConfig(t *testing.T) {
|
||||||
filterConf := &filtering.Config{
|
filterConf := &filtering.Config{
|
||||||
SafeBrowsingEnabled: true,
|
SafeBrowsingEnabled: true,
|
||||||
SafeBrowsingCacheSize: 1000,
|
SafeBrowsingCacheSize: 1000,
|
||||||
SafeSearchEnabled: true,
|
SafeSearchConf: filtering.SafeSearchConfig{Enabled: true},
|
||||||
SafeSearchCacheSize: 1000,
|
SafeSearchCacheSize: 1000,
|
||||||
ParentalCacheSize: 1000,
|
ParentalCacheSize: 1000,
|
||||||
CacheTime: 30,
|
CacheTime: 30,
|
||||||
|
@ -133,7 +133,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
||||||
filterConf := &filtering.Config{
|
filterConf := &filtering.Config{
|
||||||
SafeBrowsingEnabled: true,
|
SafeBrowsingEnabled: true,
|
||||||
SafeBrowsingCacheSize: 1000,
|
SafeBrowsingCacheSize: 1000,
|
||||||
SafeSearchEnabled: true,
|
SafeSearchConf: filtering.SafeSearchConfig{Enabled: true},
|
||||||
SafeSearchCacheSize: 1000,
|
SafeSearchCacheSize: 1000,
|
||||||
ParentalCacheSize: 1000,
|
ParentalCacheSize: 1000,
|
||||||
CacheTime: 30,
|
CacheTime: 30,
|
||||||
|
@ -212,7 +212,7 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
name: "local_ptr_upstreams_bad",
|
name: "local_ptr_upstreams_bad",
|
||||||
wantSet: `validating private upstream servers: checking domain-specific upstreams: ` +
|
wantSet: `validating private upstream servers: checking domain-specific upstreams: ` +
|
||||||
`bad arpa domain name "non.arpa": not a reversed ip network`,
|
`bad arpa domain name "non.arpa.": not a reversed ip network`,
|
||||||
}, {
|
}, {
|
||||||
name: "local_ptr_upstreams_null",
|
name: "local_ptr_upstreams_null",
|
||||||
wantSet: "",
|
wantSet: "",
|
||||||
|
@ -373,7 +373,7 @@ func TestValidateUpstreamsPrivate(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
name: "not_arpa_subnet",
|
name: "not_arpa_subnet",
|
||||||
wantErr: `checking domain-specific upstreams: ` +
|
wantErr: `checking domain-specific upstreams: ` +
|
||||||
`bad arpa domain name "hello.world": not a reversed ip network`,
|
`bad arpa domain name "hello.world.": not a reversed ip network`,
|
||||||
u: "[/hello.world/]#",
|
u: "[/hello.world/]#",
|
||||||
}, {
|
}, {
|
||||||
name: "non-private_arpa_address",
|
name: "non-private_arpa_address",
|
||||||
|
@ -389,8 +389,12 @@ func TestValidateUpstreamsPrivate(t *testing.T) {
|
||||||
name: "several_bad",
|
name: "several_bad",
|
||||||
wantErr: `checking domain-specific upstreams: 2 errors: ` +
|
wantErr: `checking domain-specific upstreams: 2 errors: ` +
|
||||||
`"arpa domain \"1.2.3.4.in-addr.arpa.\" should point to a locally-served network", ` +
|
`"arpa domain \"1.2.3.4.in-addr.arpa.\" should point to a locally-served network", ` +
|
||||||
`"bad arpa domain name \"non.arpa\": not a reversed ip network"`,
|
`"bad arpa domain name \"non.arpa.\": not a reversed ip network"`,
|
||||||
u: "[/non.arpa/1.2.3.4.in-addr.arpa/127.in-addr.arpa/]#",
|
u: "[/non.arpa/1.2.3.4.in-addr.arpa/127.in-addr.arpa/]#",
|
||||||
|
}, {
|
||||||
|
name: "partial_good",
|
||||||
|
wantErr: "",
|
||||||
|
u: "[/a.1.2.3.10.in-addr.arpa/a.10.in-addr.arpa/]#",
|
||||||
}}
|
}}
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
|
|
|
@ -63,6 +63,9 @@ type Settings struct {
|
||||||
SafeSearchEnabled bool
|
SafeSearchEnabled bool
|
||||||
SafeBrowsingEnabled bool
|
SafeBrowsingEnabled bool
|
||||||
ParentalEnabled bool
|
ParentalEnabled bool
|
||||||
|
|
||||||
|
// ClientSafeSearch is a client configured safe search.
|
||||||
|
ClientSafeSearch SafeSearch
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolver is the interface for net.Resolver to simplify testing.
|
// Resolver is the interface for net.Resolver to simplify testing.
|
||||||
|
@ -83,13 +86,16 @@ type Config struct {
|
||||||
FiltersUpdateIntervalHours uint32 `yaml:"filters_update_interval"` // time period to update filters (in hours)
|
FiltersUpdateIntervalHours uint32 `yaml:"filters_update_interval"` // time period to update filters (in hours)
|
||||||
|
|
||||||
ParentalEnabled bool `yaml:"parental_enabled"`
|
ParentalEnabled bool `yaml:"parental_enabled"`
|
||||||
SafeSearchEnabled bool `yaml:"safesearch_enabled"`
|
|
||||||
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
|
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
|
||||||
|
|
||||||
SafeBrowsingCacheSize uint `yaml:"safebrowsing_cache_size"` // (in bytes)
|
SafeBrowsingCacheSize uint `yaml:"safebrowsing_cache_size"` // (in bytes)
|
||||||
SafeSearchCacheSize uint `yaml:"safesearch_cache_size"` // (in bytes)
|
SafeSearchCacheSize uint `yaml:"safesearch_cache_size"` // (in bytes)
|
||||||
ParentalCacheSize uint `yaml:"parental_cache_size"` // (in bytes)
|
ParentalCacheSize uint `yaml:"parental_cache_size"` // (in bytes)
|
||||||
CacheTime uint `yaml:"cache_time"` // Element's TTL (in minutes)
|
// TODO(a.garipov): Use timeutil.Duration
|
||||||
|
CacheTime uint `yaml:"cache_time"` // Element's TTL (in minutes)
|
||||||
|
|
||||||
|
SafeSearchConf SafeSearchConfig `yaml:"safe_search"`
|
||||||
|
SafeSearch SafeSearch `yaml:"-"`
|
||||||
|
|
||||||
Rewrites []*LegacyRewrite `yaml:"rewrites"`
|
Rewrites []*LegacyRewrite `yaml:"rewrites"`
|
||||||
|
|
||||||
|
@ -107,9 +113,6 @@ type Config struct {
|
||||||
// Register an HTTP handler
|
// Register an HTTP handler
|
||||||
HTTPRegister aghhttp.RegisterFunc `yaml:"-"`
|
HTTPRegister aghhttp.RegisterFunc `yaml:"-"`
|
||||||
|
|
||||||
// CustomResolver is the resolver used by DNSFilter.
|
|
||||||
CustomResolver Resolver `yaml:"-"`
|
|
||||||
|
|
||||||
// HTTPClient is the client to use for updating the remote filters.
|
// HTTPClient is the client to use for updating the remote filters.
|
||||||
HTTPClient *http.Client `yaml:"-"`
|
HTTPClient *http.Client `yaml:"-"`
|
||||||
|
|
||||||
|
@ -172,7 +175,6 @@ type DNSFilter struct {
|
||||||
|
|
||||||
safebrowsingCache cache.Cache
|
safebrowsingCache cache.Cache
|
||||||
parentalCache cache.Cache
|
parentalCache cache.Cache
|
||||||
safeSearchCache cache.Cache
|
|
||||||
|
|
||||||
Config // for direct access by library users, even a = assignment
|
Config // for direct access by library users, even a = assignment
|
||||||
// confLock protects Config.
|
// confLock protects Config.
|
||||||
|
@ -182,11 +184,6 @@ type DNSFilter struct {
|
||||||
filtersInitializerChan chan filtersInitializerParams
|
filtersInitializerChan chan filtersInitializerParams
|
||||||
filtersInitializerLock sync.Mutex
|
filtersInitializerLock sync.Mutex
|
||||||
|
|
||||||
// resolver only looks up the IP address of the host while safe search.
|
|
||||||
//
|
|
||||||
// TODO(e.burkov): Use upstream that configured in dnsforward instead.
|
|
||||||
resolver Resolver
|
|
||||||
|
|
||||||
refreshLock *sync.Mutex
|
refreshLock *sync.Mutex
|
||||||
|
|
||||||
// filterTitleRegexp is the regular expression to retrieve a name of a
|
// filterTitleRegexp is the regular expression to retrieve a name of a
|
||||||
|
@ -195,6 +192,7 @@ type DNSFilter struct {
|
||||||
// TODO(e.burkov): Don't use regexp for such a simple text processing task.
|
// TODO(e.burkov): Don't use regexp for such a simple text processing task.
|
||||||
filterTitleRegexp *regexp.Regexp
|
filterTitleRegexp *regexp.Regexp
|
||||||
|
|
||||||
|
safeSearch SafeSearch
|
||||||
hostCheckers []hostChecker
|
hostCheckers []hostChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +296,7 @@ func (d *DNSFilter) GetConfig() (s Settings) {
|
||||||
|
|
||||||
return Settings{
|
return Settings{
|
||||||
FilteringEnabled: atomic.LoadUint32(&d.Config.enabled) != 0,
|
FilteringEnabled: atomic.LoadUint32(&d.Config.enabled) != 0,
|
||||||
SafeSearchEnabled: d.Config.SafeSearchEnabled,
|
SafeSearchEnabled: d.Config.SafeSearchConf.Enabled,
|
||||||
SafeBrowsingEnabled: d.Config.SafeBrowsingEnabled,
|
SafeBrowsingEnabled: d.Config.SafeBrowsingEnabled,
|
||||||
ParentalEnabled: d.Config.ParentalEnabled,
|
ParentalEnabled: d.Config.ParentalEnabled,
|
||||||
}
|
}
|
||||||
|
@ -942,7 +940,6 @@ func InitModule() {
|
||||||
// be non-nil.
|
// be non-nil.
|
||||||
func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||||
d = &DNSFilter{
|
d = &DNSFilter{
|
||||||
resolver: net.DefaultResolver,
|
|
||||||
refreshLock: &sync.Mutex{},
|
refreshLock: &sync.Mutex{},
|
||||||
filterTitleRegexp: regexp.MustCompile(`^! Title: +(.*)$`),
|
filterTitleRegexp: regexp.MustCompile(`^! Title: +(.*)$`),
|
||||||
}
|
}
|
||||||
|
@ -951,18 +948,12 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||||
EnableLRU: true,
|
EnableLRU: true,
|
||||||
MaxSize: c.SafeBrowsingCacheSize,
|
MaxSize: c.SafeBrowsingCacheSize,
|
||||||
})
|
})
|
||||||
d.safeSearchCache = cache.New(cache.Config{
|
|
||||||
EnableLRU: true,
|
|
||||||
MaxSize: c.SafeSearchCacheSize,
|
|
||||||
})
|
|
||||||
d.parentalCache = cache.New(cache.Config{
|
d.parentalCache = cache.New(cache.Config{
|
||||||
EnableLRU: true,
|
EnableLRU: true,
|
||||||
MaxSize: c.ParentalCacheSize,
|
MaxSize: c.ParentalCacheSize,
|
||||||
})
|
})
|
||||||
|
|
||||||
if r := c.CustomResolver; r != nil {
|
d.safeSearch = c.SafeSearch
|
||||||
d.resolver = r
|
|
||||||
}
|
|
||||||
|
|
||||||
d.hostCheckers = []hostChecker{{
|
d.hostCheckers = []hostChecker{{
|
||||||
check: d.matchSysHosts,
|
check: d.matchSysHosts,
|
||||||
|
|
|
@ -2,10 +2,8 @@ package filtering
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||||
|
@ -33,7 +31,6 @@ func purgeCaches(d *DNSFilter) {
|
||||||
for _, c := range []cache.Cache{
|
for _, c := range []cache.Cache{
|
||||||
d.safebrowsingCache,
|
d.safebrowsingCache,
|
||||||
d.parentalCache,
|
d.parentalCache,
|
||||||
d.safeSearchCache,
|
|
||||||
} {
|
} {
|
||||||
if c != nil {
|
if c != nil {
|
||||||
c.Clear()
|
c.Clear()
|
||||||
|
@ -51,7 +48,7 @@ func newForTest(t testing.TB, c *Config, filters []Filter) (f *DNSFilter, setts
|
||||||
c.ParentalCacheSize = 10000
|
c.ParentalCacheSize = 10000
|
||||||
c.SafeSearchCacheSize = 1000
|
c.SafeSearchCacheSize = 1000
|
||||||
c.CacheTime = 30
|
c.CacheTime = 30
|
||||||
setts.SafeSearchEnabled = c.SafeSearchEnabled
|
setts.SafeSearchEnabled = c.SafeSearchConf.Enabled
|
||||||
setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled
|
setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled
|
||||||
setts.ParentalEnabled = c.ParentalEnabled
|
setts.ParentalEnabled = c.ParentalEnabled
|
||||||
} else {
|
} else {
|
||||||
|
@ -216,164 +213,6 @@ func TestParallelSB(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safe Search.
|
|
||||||
|
|
||||||
func TestSafeSearch(t *testing.T) {
|
|
||||||
d, _ := newForTest(t, &Config{SafeSearchEnabled: true}, nil)
|
|
||||||
t.Cleanup(d.Close)
|
|
||||||
val, ok := d.SafeSearchDomain("www.google.com")
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
assert.Equal(t, "forcesafesearch.google.com", val)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckHostSafeSearchYandex(t *testing.T) {
|
|
||||||
d, setts := newForTest(t, &Config{
|
|
||||||
SafeSearchEnabled: true,
|
|
||||||
}, nil)
|
|
||||||
t.Cleanup(d.Close)
|
|
||||||
|
|
||||||
yandexIP := net.IPv4(213, 180, 193, 56)
|
|
||||||
|
|
||||||
// Check host for each domain.
|
|
||||||
for _, host := range []string{
|
|
||||||
"yAndeX.ru",
|
|
||||||
"YANdex.COM",
|
|
||||||
"yandex.ua",
|
|
||||||
"yandex.by",
|
|
||||||
"yandex.kz",
|
|
||||||
"www.yandex.com",
|
|
||||||
} {
|
|
||||||
t.Run(strings.ToLower(host), func(t *testing.T) {
|
|
||||||
res, err := d.CheckHost(host, dns.TypeA, setts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.True(t, res.IsFiltered)
|
|
||||||
|
|
||||||
require.Len(t, res.Rules, 1)
|
|
||||||
|
|
||||||
assert.Equal(t, yandexIP, res.Rules[0].IP)
|
|
||||||
assert.EqualValues(t, SafeSearchListID, res.Rules[0].FilterListID)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCheckHostSafeSearchGoogle(t *testing.T) {
|
|
||||||
resolver := &aghtest.TestResolver{}
|
|
||||||
d, setts := newForTest(t, &Config{
|
|
||||||
SafeSearchEnabled: true,
|
|
||||||
CustomResolver: resolver,
|
|
||||||
}, nil)
|
|
||||||
t.Cleanup(d.Close)
|
|
||||||
|
|
||||||
ip, _ := resolver.HostToIPs("forcesafesearch.google.com")
|
|
||||||
|
|
||||||
// Check host for each domain.
|
|
||||||
for _, host := range []string{
|
|
||||||
"www.google.com",
|
|
||||||
"www.google.im",
|
|
||||||
"www.google.co.in",
|
|
||||||
"www.google.iq",
|
|
||||||
"www.google.is",
|
|
||||||
"www.google.it",
|
|
||||||
"www.google.je",
|
|
||||||
} {
|
|
||||||
t.Run(host, func(t *testing.T) {
|
|
||||||
res, err := d.CheckHost(host, dns.TypeA, setts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.True(t, res.IsFiltered)
|
|
||||||
|
|
||||||
require.Len(t, res.Rules, 1)
|
|
||||||
|
|
||||||
assert.Equal(t, ip, res.Rules[0].IP)
|
|
||||||
assert.EqualValues(t, SafeSearchListID, res.Rules[0].FilterListID)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSafeSearchCacheYandex(t *testing.T) {
|
|
||||||
d, setts := newForTest(t, nil, nil)
|
|
||||||
t.Cleanup(d.Close)
|
|
||||||
const domain = "yandex.ru"
|
|
||||||
|
|
||||||
// Check host with disabled safesearch.
|
|
||||||
res, err := d.CheckHost(domain, dns.TypeA, setts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.False(t, res.IsFiltered)
|
|
||||||
|
|
||||||
require.Empty(t, res.Rules)
|
|
||||||
|
|
||||||
yandexIP := net.IPv4(213, 180, 193, 56)
|
|
||||||
|
|
||||||
d, setts = newForTest(t, &Config{SafeSearchEnabled: true}, nil)
|
|
||||||
t.Cleanup(d.Close)
|
|
||||||
|
|
||||||
res, err = d.CheckHost(domain, dns.TypeA, setts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
// For yandex we already know valid IP.
|
|
||||||
require.Len(t, res.Rules, 1)
|
|
||||||
assert.Equal(t, res.Rules[0].IP, yandexIP)
|
|
||||||
|
|
||||||
// Check cache.
|
|
||||||
cachedValue, isFound := getCachedResult(d.safeSearchCache, domain)
|
|
||||||
require.True(t, isFound)
|
|
||||||
require.Len(t, cachedValue.Rules, 1)
|
|
||||||
|
|
||||||
assert.Equal(t, cachedValue.Rules[0].IP, yandexIP)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSafeSearchCacheGoogle(t *testing.T) {
|
|
||||||
resolver := &aghtest.TestResolver{}
|
|
||||||
d, setts := newForTest(t, &Config{
|
|
||||||
CustomResolver: resolver,
|
|
||||||
}, nil)
|
|
||||||
t.Cleanup(d.Close)
|
|
||||||
|
|
||||||
const domain = "www.google.ru"
|
|
||||||
res, err := d.CheckHost(domain, dns.TypeA, setts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.False(t, res.IsFiltered)
|
|
||||||
|
|
||||||
require.Empty(t, res.Rules)
|
|
||||||
|
|
||||||
d, setts = newForTest(t, &Config{SafeSearchEnabled: true}, nil)
|
|
||||||
t.Cleanup(d.Close)
|
|
||||||
d.resolver = resolver
|
|
||||||
|
|
||||||
// Lookup for safesearch domain.
|
|
||||||
safeDomain, ok := d.SafeSearchDomain(domain)
|
|
||||||
require.True(t, ok)
|
|
||||||
|
|
||||||
ips, err := resolver.LookupIP(context.Background(), "ip", safeDomain)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var ip net.IP
|
|
||||||
for _, foundIP := range ips {
|
|
||||||
if foundIP.To4() != nil {
|
|
||||||
ip = foundIP
|
|
||||||
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err = d.CheckHost(domain, dns.TypeA, setts)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Len(t, res.Rules, 1)
|
|
||||||
|
|
||||||
assert.True(t, res.Rules[0].IP.Equal(ip))
|
|
||||||
|
|
||||||
// Check cache.
|
|
||||||
cachedValue, isFound := getCachedResult(d.safeSearchCache, domain)
|
|
||||||
require.True(t, isFound)
|
|
||||||
require.Len(t, cachedValue.Rules, 1)
|
|
||||||
|
|
||||||
assert.True(t, cachedValue.Rules[0].IP.Equal(ip))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parental.
|
// Parental.
|
||||||
|
|
||||||
func TestParentalControl(t *testing.T) {
|
func TestParentalControl(t *testing.T) {
|
||||||
|
@ -854,27 +693,3 @@ func BenchmarkSafeBrowsingParallel(b *testing.B) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func BenchmarkSafeSearch(b *testing.B) {
|
|
||||||
d, _ := newForTest(b, &Config{SafeSearchEnabled: true}, nil)
|
|
||||||
b.Cleanup(d.Close)
|
|
||||||
for n := 0; n < b.N; n++ {
|
|
||||||
val, ok := d.SafeSearchDomain("www.google.com")
|
|
||||||
require.True(b, ok)
|
|
||||||
|
|
||||||
assert.Equal(b, "forcesafesearch.google.com", val, "Expected safesearch for google.com to be forcesafesearch.google.com")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkSafeSearchParallel(b *testing.B) {
|
|
||||||
d, _ := newForTest(b, &Config{SafeSearchEnabled: true}, nil)
|
|
||||||
b.Cleanup(d.Close)
|
|
||||||
b.RunParallel(func(pb *testing.PB) {
|
|
||||||
for pb.Next() {
|
|
||||||
val, ok := d.SafeSearchDomain("www.google.com")
|
|
||||||
require.True(b, ok)
|
|
||||||
|
|
||||||
assert.Equal(b, "forcesafesearch.google.com", val, "Expected safesearch for google.com to be forcesafesearch.google.com")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,74 +1,40 @@
|
||||||
package filtering
|
package filtering
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"github.com/AdguardTeam/urlfilter/rules"
|
||||||
"context"
|
"github.com/miekg/dns"
|
||||||
"encoding/binary"
|
|
||||||
"encoding/gob"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"net/http"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
|
||||||
"github.com/AdguardTeam/golibs/cache"
|
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
// SafeSearch interface describes a service for search engines hosts rewrites.
|
||||||
expire byte[4]
|
type SafeSearch interface {
|
||||||
res Result
|
// SearchHost returns a replacement address for the search engine host.
|
||||||
*/
|
SearchHost(host string, qtype uint16) (res *rules.DNSRewrite)
|
||||||
func (d *DNSFilter) setCacheResult(cache cache.Cache, host string, res Result) int {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
|
|
||||||
expire := uint(time.Now().Unix()) + d.Config.CacheTime*60
|
// CheckHost checks host with safe search engine.
|
||||||
exp := make([]byte, 4)
|
CheckHost(host string, qtype uint16) (res Result, err error)
|
||||||
binary.BigEndian.PutUint32(exp, uint32(expire))
|
|
||||||
_, _ = buf.Write(exp)
|
|
||||||
|
|
||||||
enc := gob.NewEncoder(&buf)
|
|
||||||
err := enc.Encode(res)
|
|
||||||
if err != nil {
|
|
||||||
log.Error("gob.Encode(): %s", err)
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
val := buf.Bytes()
|
|
||||||
_ = cache.Set([]byte(host), val)
|
|
||||||
return len(val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getCachedResult(cache cache.Cache, host string) (Result, bool) {
|
// SafeSearchConfig is a struct with safe search related settings.
|
||||||
data := cache.Get([]byte(host))
|
type SafeSearchConfig struct {
|
||||||
if data == nil {
|
// CustomResolver is the resolver used by safe search.
|
||||||
return Result{}, false
|
CustomResolver Resolver `yaml:"-"`
|
||||||
}
|
|
||||||
|
|
||||||
exp := int(binary.BigEndian.Uint32(data[:4]))
|
// Enabled indicates if safe search is enabled entirely.
|
||||||
if exp <= int(time.Now().Unix()) {
|
Enabled bool `yaml:"enabled" json:"enabled"`
|
||||||
cache.Del([]byte(host))
|
|
||||||
return Result{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
var buf bytes.Buffer
|
// Services flags. Each flag indicates if the corresponding service is
|
||||||
buf.Write(data[4:])
|
// enabled or disabled.
|
||||||
dec := gob.NewDecoder(&buf)
|
|
||||||
r := Result{}
|
|
||||||
err := dec.Decode(&r)
|
|
||||||
if err != nil {
|
|
||||||
log.Debug("gob.Decode(): %s", err)
|
|
||||||
return Result{}, false
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, true
|
Bing bool `yaml:"bing" json:"bing"`
|
||||||
}
|
DuckDuckGo bool `yaml:"duckduckgo" json:"duckduckgo"`
|
||||||
|
Google bool `yaml:"google" json:"google"`
|
||||||
// SafeSearchDomain returns replacement address for search engine
|
Pixabay bool `yaml:"pixabay" json:"pixabay"`
|
||||||
func (d *DNSFilter) SafeSearchDomain(host string) (string, bool) {
|
Yandex bool `yaml:"yandex" json:"yandex"`
|
||||||
val, ok := safeSearchDomains[host]
|
YouTube bool `yaml:"youtube" json:"youtube"`
|
||||||
return val, ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkSafeSearch checks host with safe search engine. Matches
|
||||||
|
// [hostChecker.check].
|
||||||
func (d *DNSFilter) checkSafeSearch(
|
func (d *DNSFilter) checkSafeSearch(
|
||||||
host string,
|
host string,
|
||||||
_ uint16,
|
_ uint16,
|
||||||
|
@ -78,295 +44,14 @@ func (d *DNSFilter) checkSafeSearch(
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if log.GetLevel() >= log.DEBUG {
|
if d.safeSearch == nil {
|
||||||
timer := log.StartTimer()
|
|
||||||
defer timer.LogElapsed("SafeSearch: lookup for %s", host)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check cache. Return cached result if it was found
|
|
||||||
cachedValue, isFound := getCachedResult(d.safeSearchCache, host)
|
|
||||||
if isFound {
|
|
||||||
// atomic.AddUint64(&gctx.stats.Safesearch.CacheHits, 1)
|
|
||||||
log.Tracef("SafeSearch: found in cache: %s", host)
|
|
||||||
return cachedValue, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
safeHost, ok := d.SafeSearchDomain(host)
|
|
||||||
if !ok {
|
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res = Result{
|
clientSafeSearch := setts.ClientSafeSearch
|
||||||
Rules: []*ResultRule{{
|
if clientSafeSearch != nil {
|
||||||
FilterListID: SafeSearchListID,
|
return clientSafeSearch.CheckHost(host, dns.TypeA)
|
||||||
}},
|
|
||||||
Reason: FilteredSafeSearch,
|
|
||||||
IsFiltered: true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ip := net.ParseIP(safeHost); ip != nil {
|
return d.safeSearch.CheckHost(host, dns.TypeA)
|
||||||
res.Rules[0].IP = ip
|
|
||||||
valLen := d.setCacheResult(d.safeSearchCache, host, res)
|
|
||||||
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, valLen)
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ips, err := d.resolver.LookupIP(context.Background(), "ip", safeHost)
|
|
||||||
if err != nil {
|
|
||||||
log.Tracef("SafeSearchDomain for %s was found but failed to lookup for %s cause %s", host, safeHost, err)
|
|
||||||
return Result{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, ip := range ips {
|
|
||||||
if ip = ip.To4(); ip == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
res.Rules[0].IP = ip
|
|
||||||
|
|
||||||
l := d.setCacheResult(d.safeSearchCache, host, res)
|
|
||||||
log.Debug("SafeSearch: stored in cache: %s (%d bytes)", host, l)
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result{}, fmt.Errorf("no ipv4 addresses in safe search response for %s", safeHost)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
|
|
||||||
setProtectedBool(&d.confLock, &d.Config.SafeSearchEnabled, true)
|
|
||||||
d.Config.ConfigModified()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
|
|
||||||
setProtectedBool(&d.confLock, &d.Config.SafeSearchEnabled, false)
|
|
||||||
d.Config.ConfigModified()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
|
||||||
resp := &struct {
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
}{
|
|
||||||
Enabled: protectedBool(&d.confLock, &d.Config.SafeSearchEnabled),
|
|
||||||
}
|
|
||||||
|
|
||||||
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
|
||||||
}
|
|
||||||
|
|
||||||
var safeSearchDomains = map[string]string{
|
|
||||||
"yandex.com": "213.180.193.56",
|
|
||||||
"yandex.ru": "213.180.193.56",
|
|
||||||
"yandex.ua": "213.180.193.56",
|
|
||||||
"yandex.by": "213.180.193.56",
|
|
||||||
"yandex.kz": "213.180.193.56",
|
|
||||||
"www.yandex.com": "213.180.193.56",
|
|
||||||
"www.yandex.ru": "213.180.193.56",
|
|
||||||
"www.yandex.ua": "213.180.193.56",
|
|
||||||
"www.yandex.by": "213.180.193.56",
|
|
||||||
"www.yandex.kz": "213.180.193.56",
|
|
||||||
|
|
||||||
"www.bing.com": "strict.bing.com",
|
|
||||||
|
|
||||||
"duckduckgo.com": "safe.duckduckgo.com",
|
|
||||||
"www.duckduckgo.com": "safe.duckduckgo.com",
|
|
||||||
"start.duckduckgo.com": "safe.duckduckgo.com",
|
|
||||||
|
|
||||||
"www.google.com": "forcesafesearch.google.com",
|
|
||||||
"www.google.ad": "forcesafesearch.google.com",
|
|
||||||
"www.google.ae": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.af": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ag": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ai": "forcesafesearch.google.com",
|
|
||||||
"www.google.al": "forcesafesearch.google.com",
|
|
||||||
"www.google.am": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.ao": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ar": "forcesafesearch.google.com",
|
|
||||||
"www.google.as": "forcesafesearch.google.com",
|
|
||||||
"www.google.at": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.au": "forcesafesearch.google.com",
|
|
||||||
"www.google.az": "forcesafesearch.google.com",
|
|
||||||
"www.google.ba": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.bd": "forcesafesearch.google.com",
|
|
||||||
"www.google.be": "forcesafesearch.google.com",
|
|
||||||
"www.google.bf": "forcesafesearch.google.com",
|
|
||||||
"www.google.bg": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.bh": "forcesafesearch.google.com",
|
|
||||||
"www.google.bi": "forcesafesearch.google.com",
|
|
||||||
"www.google.bj": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.bn": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.bo": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.br": "forcesafesearch.google.com",
|
|
||||||
"www.google.bs": "forcesafesearch.google.com",
|
|
||||||
"www.google.bt": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.bw": "forcesafesearch.google.com",
|
|
||||||
"www.google.by": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.bz": "forcesafesearch.google.com",
|
|
||||||
"www.google.ca": "forcesafesearch.google.com",
|
|
||||||
"www.google.cd": "forcesafesearch.google.com",
|
|
||||||
"www.google.cf": "forcesafesearch.google.com",
|
|
||||||
"www.google.cg": "forcesafesearch.google.com",
|
|
||||||
"www.google.ch": "forcesafesearch.google.com",
|
|
||||||
"www.google.ci": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.ck": "forcesafesearch.google.com",
|
|
||||||
"www.google.cl": "forcesafesearch.google.com",
|
|
||||||
"www.google.cm": "forcesafesearch.google.com",
|
|
||||||
"www.google.cn": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.co": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.cr": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.cu": "forcesafesearch.google.com",
|
|
||||||
"www.google.cv": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.cy": "forcesafesearch.google.com",
|
|
||||||
"www.google.cz": "forcesafesearch.google.com",
|
|
||||||
"www.google.de": "forcesafesearch.google.com",
|
|
||||||
"www.google.dj": "forcesafesearch.google.com",
|
|
||||||
"www.google.dk": "forcesafesearch.google.com",
|
|
||||||
"www.google.dm": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.do": "forcesafesearch.google.com",
|
|
||||||
"www.google.dz": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ec": "forcesafesearch.google.com",
|
|
||||||
"www.google.ee": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.eg": "forcesafesearch.google.com",
|
|
||||||
"www.google.es": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.et": "forcesafesearch.google.com",
|
|
||||||
"www.google.fi": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.fj": "forcesafesearch.google.com",
|
|
||||||
"www.google.fm": "forcesafesearch.google.com",
|
|
||||||
"www.google.fr": "forcesafesearch.google.com",
|
|
||||||
"www.google.ga": "forcesafesearch.google.com",
|
|
||||||
"www.google.ge": "forcesafesearch.google.com",
|
|
||||||
"www.google.gg": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.gh": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.gi": "forcesafesearch.google.com",
|
|
||||||
"www.google.gl": "forcesafesearch.google.com",
|
|
||||||
"www.google.gm": "forcesafesearch.google.com",
|
|
||||||
"www.google.gp": "forcesafesearch.google.com",
|
|
||||||
"www.google.gr": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.gt": "forcesafesearch.google.com",
|
|
||||||
"www.google.gy": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.hk": "forcesafesearch.google.com",
|
|
||||||
"www.google.hn": "forcesafesearch.google.com",
|
|
||||||
"www.google.hr": "forcesafesearch.google.com",
|
|
||||||
"www.google.ht": "forcesafesearch.google.com",
|
|
||||||
"www.google.hu": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.id": "forcesafesearch.google.com",
|
|
||||||
"www.google.ie": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.il": "forcesafesearch.google.com",
|
|
||||||
"www.google.im": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.in": "forcesafesearch.google.com",
|
|
||||||
"www.google.iq": "forcesafesearch.google.com",
|
|
||||||
"www.google.is": "forcesafesearch.google.com",
|
|
||||||
"www.google.it": "forcesafesearch.google.com",
|
|
||||||
"www.google.je": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.jm": "forcesafesearch.google.com",
|
|
||||||
"www.google.jo": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.jp": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.ke": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.kh": "forcesafesearch.google.com",
|
|
||||||
"www.google.ki": "forcesafesearch.google.com",
|
|
||||||
"www.google.kg": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.kr": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.kw": "forcesafesearch.google.com",
|
|
||||||
"www.google.kz": "forcesafesearch.google.com",
|
|
||||||
"www.google.la": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.lb": "forcesafesearch.google.com",
|
|
||||||
"www.google.li": "forcesafesearch.google.com",
|
|
||||||
"www.google.lk": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.ls": "forcesafesearch.google.com",
|
|
||||||
"www.google.lt": "forcesafesearch.google.com",
|
|
||||||
"www.google.lu": "forcesafesearch.google.com",
|
|
||||||
"www.google.lv": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ly": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.ma": "forcesafesearch.google.com",
|
|
||||||
"www.google.md": "forcesafesearch.google.com",
|
|
||||||
"www.google.me": "forcesafesearch.google.com",
|
|
||||||
"www.google.mg": "forcesafesearch.google.com",
|
|
||||||
"www.google.mk": "forcesafesearch.google.com",
|
|
||||||
"www.google.ml": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.mm": "forcesafesearch.google.com",
|
|
||||||
"www.google.mn": "forcesafesearch.google.com",
|
|
||||||
"www.google.ms": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.mt": "forcesafesearch.google.com",
|
|
||||||
"www.google.mu": "forcesafesearch.google.com",
|
|
||||||
"www.google.mv": "forcesafesearch.google.com",
|
|
||||||
"www.google.mw": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.mx": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.my": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.mz": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.na": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.nf": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ng": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ni": "forcesafesearch.google.com",
|
|
||||||
"www.google.ne": "forcesafesearch.google.com",
|
|
||||||
"www.google.nl": "forcesafesearch.google.com",
|
|
||||||
"www.google.no": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.np": "forcesafesearch.google.com",
|
|
||||||
"www.google.nr": "forcesafesearch.google.com",
|
|
||||||
"www.google.nu": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.nz": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.om": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.pa": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.pe": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.pg": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ph": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.pk": "forcesafesearch.google.com",
|
|
||||||
"www.google.pl": "forcesafesearch.google.com",
|
|
||||||
"www.google.pn": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.pr": "forcesafesearch.google.com",
|
|
||||||
"www.google.ps": "forcesafesearch.google.com",
|
|
||||||
"www.google.pt": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.py": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.qa": "forcesafesearch.google.com",
|
|
||||||
"www.google.ro": "forcesafesearch.google.com",
|
|
||||||
"www.google.ru": "forcesafesearch.google.com",
|
|
||||||
"www.google.rw": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.sa": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.sb": "forcesafesearch.google.com",
|
|
||||||
"www.google.sc": "forcesafesearch.google.com",
|
|
||||||
"www.google.se": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.sg": "forcesafesearch.google.com",
|
|
||||||
"www.google.sh": "forcesafesearch.google.com",
|
|
||||||
"www.google.si": "forcesafesearch.google.com",
|
|
||||||
"www.google.sk": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.sl": "forcesafesearch.google.com",
|
|
||||||
"www.google.sn": "forcesafesearch.google.com",
|
|
||||||
"www.google.so": "forcesafesearch.google.com",
|
|
||||||
"www.google.sm": "forcesafesearch.google.com",
|
|
||||||
"www.google.sr": "forcesafesearch.google.com",
|
|
||||||
"www.google.st": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.sv": "forcesafesearch.google.com",
|
|
||||||
"www.google.td": "forcesafesearch.google.com",
|
|
||||||
"www.google.tg": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.th": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.tj": "forcesafesearch.google.com",
|
|
||||||
"www.google.tk": "forcesafesearch.google.com",
|
|
||||||
"www.google.tl": "forcesafesearch.google.com",
|
|
||||||
"www.google.tm": "forcesafesearch.google.com",
|
|
||||||
"www.google.tn": "forcesafesearch.google.com",
|
|
||||||
"www.google.to": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.tr": "forcesafesearch.google.com",
|
|
||||||
"www.google.tt": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.tw": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.tz": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.ua": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.ug": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.uk": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.uy": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.uz": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.vc": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.ve": "forcesafesearch.google.com",
|
|
||||||
"www.google.vg": "forcesafesearch.google.com",
|
|
||||||
"www.google.co.vi": "forcesafesearch.google.com",
|
|
||||||
"www.google.com.vn": "forcesafesearch.google.com",
|
|
||||||
"www.google.vu": "forcesafesearch.google.com",
|
|
||||||
"www.google.ws": "forcesafesearch.google.com",
|
|
||||||
"www.google.rs": "forcesafesearch.google.com",
|
|
||||||
|
|
||||||
"www.youtube.com": "restrictmoderate.youtube.com",
|
|
||||||
"m.youtube.com": "restrictmoderate.youtube.com",
|
|
||||||
"youtubei.googleapis.com": "restrictmoderate.youtube.com",
|
|
||||||
"youtube.googleapis.com": "restrictmoderate.youtube.com",
|
|
||||||
"www.youtube-nocookie.com": "restrictmoderate.youtube.com",
|
|
||||||
|
|
||||||
"pixabay.com": "safesearch.pixabay.com",
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package safesearch
|
||||||
|
|
||||||
|
import _ "embed"
|
||||||
|
|
||||||
|
//go:embed rules/bing.txt
|
||||||
|
var bing string
|
||||||
|
|
||||||
|
//go:embed rules/google.txt
|
||||||
|
var google string
|
||||||
|
|
||||||
|
//go:embed rules/pixabay.txt
|
||||||
|
var pixabay string
|
||||||
|
|
||||||
|
//go:embed rules/duckduckgo.txt
|
||||||
|
var duckduckgo string
|
||||||
|
|
||||||
|
//go:embed rules/yandex.txt
|
||||||
|
var yandex string
|
||||||
|
|
||||||
|
//go:embed rules/youtube.txt
|
||||||
|
var youtube string
|
||||||
|
|
||||||
|
// safeSearchRules is a map with rules texts grouped by search providers.
|
||||||
|
// Source rules downloaded from:
|
||||||
|
// https://adguardteam.github.io/HostlistsRegistry/assets/engines_safe_search.txt,
|
||||||
|
// https://adguardteam.github.io/HostlistsRegistry/assets/youtube_safe_search.txt.
|
||||||
|
var safeSearchRules = map[Service]string{
|
||||||
|
Bing: bing,
|
||||||
|
DuckDuckGo: duckduckgo,
|
||||||
|
Google: google,
|
||||||
|
Pixabay: pixabay,
|
||||||
|
Yandex: yandex,
|
||||||
|
YouTube: youtube,
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
|www.bing.com^$dnsrewrite=NOERROR;CNAME;strict.bing.com
|
|
@ -0,0 +1,3 @@
|
||||||
|
|duckduckgo.com^$dnsrewrite=NOERROR;CNAME;safe.duckduckgo.com
|
||||||
|
|start.duckduckgo.com^$dnsrewrite=NOERROR;CNAME;safe.duckduckgo.com
|
||||||
|
|www.duckduckgo.com^$dnsrewrite=NOERROR;CNAME;safe.duckduckgo.com
|
|
@ -0,0 +1,191 @@
|
||||||
|
|www.google.ad^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ae^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.al^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.am^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.as^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.at^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.az^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ba^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.be^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.bf^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.bg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.bi^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.bj^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.bs^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.bt^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.by^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ca^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cat^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cd^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cf^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ch^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ci^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cl^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.ao^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.bw^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.ck^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.cr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.id^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.il^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.in^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.jp^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.ke^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.kr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.ls^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.ma^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.mz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.nz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.th^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.tz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.ug^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.uk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.uz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.ve^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.co.vi^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.af^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ag^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ai^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ar^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.au^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.bd^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.bh^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.bn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.bo^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.br^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.bz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.co^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.cu^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.cy^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.do^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ec^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.eg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.et^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.fj^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.gh^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.gi^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.gt^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.hk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.jm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.kh^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.kw^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.lb^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ly^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.mm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.mt^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.mx^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.my^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.na^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.nf^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ng^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ni^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.np^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.om^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.pa^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.pe^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.pg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ph^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.pk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.pr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.py^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.qa^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.sa^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.sb^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.sg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.sl^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.sv^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.tj^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.tr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.tw^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.ua^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.uy^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.vc^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com.vn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.com^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cv^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.cz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.de^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.dj^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.dk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.dm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.dz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ee^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.es^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.fi^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.fm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.fr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ga^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ge^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.gg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.gl^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.gm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.gp^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.gr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.gy^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.hn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.hr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ht^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.hu^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ie^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.im^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.iq^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.is^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.it^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.je^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.jo^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.kg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ki^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.kz^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.la^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.li^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.lk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.lt^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.lu^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.lv^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.md^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.me^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.mg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.mk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ml^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.mn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ms^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.mu^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.mv^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.mw^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ne^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.nl^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.no^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.nr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.nu^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.pl^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.pn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ps^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.pt^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ro^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.rs^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ru^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.rw^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.sc^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.se^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.sh^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.si^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.sk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.sm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.sn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.so^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.sr^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.st^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.td^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.tg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.tk^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.tl^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.tm^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.tn^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.to^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.tt^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.vg^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.vu^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
||||||
|
|www.google.ws^$dnsrewrite=NOERROR;CNAME;forcesafesearch.google.com
|
|
@ -0,0 +1 @@
|
||||||
|
|pixabay.com^$dnsrewrite=NOERROR;CNAME;safesearch.pixabay.com
|
|
@ -0,0 +1,52 @@
|
||||||
|
|www.xn--d1acpjx3f.xn--p1ai^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.ya.ru^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.az^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.by^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.co.il^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.com.am^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.com.ge^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.com.ru^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.com.tr^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.com^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.de^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.ee^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.eu^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.fi^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.fr^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.kz^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.lt^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.lv^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.md^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.net^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.org^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.pl^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.ru^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.tj^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.tm^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|www.yandex.uz^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|xn--d1acpjx3f.xn--p1ai^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|ya.ru^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.az^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.by^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.co.il^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.com.am^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.com.ge^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.com.ru^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.com.tr^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.com^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.de^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.ee^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.eu^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.fi^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.fr^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.kz^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.lt^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.lv^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.md^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.net^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.org^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.pl^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.ru^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.tj^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.tm^$dnsrewrite=NOERROR;A;213.180.193.56
|
||||||
|
|yandex.uz^$dnsrewrite=NOERROR;A;213.180.193.56
|
|
@ -0,0 +1,5 @@
|
||||||
|
|www.youtube.com^$dnsrewrite=NOERROR;CNAME;restrictmoderate.youtube.com
|
||||||
|
|m.youtube.com^$dnsrewrite=NOERROR;CNAME;restrictmoderate.youtube.com
|
||||||
|
|youtubei.googleapis.com^$dnsrewrite=NOERROR;CNAME;restrictmoderate.youtube.com
|
||||||
|
|youtube.googleapis.com^$dnsrewrite=NOERROR;CNAME;restrictmoderate.youtube.com
|
||||||
|
|www.youtube-nocookie.com^$dnsrewrite=NOERROR;CNAME;restrictmoderate.youtube.com
|
|
@ -0,0 +1,269 @@
|
||||||
|
// Package safesearch implements safesearch host matching.
|
||||||
|
package safesearch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/golibs/cache"
|
||||||
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/urlfilter"
|
||||||
|
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||||
|
"github.com/AdguardTeam/urlfilter/rules"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Service is a enum with service names used as search providers.
|
||||||
|
type Service string
|
||||||
|
|
||||||
|
// Service enum members.
|
||||||
|
const (
|
||||||
|
Bing Service = "bing"
|
||||||
|
DuckDuckGo Service = "duckduckgo"
|
||||||
|
Google Service = "google"
|
||||||
|
Pixabay Service = "pixabay"
|
||||||
|
Yandex Service = "yandex"
|
||||||
|
YouTube Service = "youtube"
|
||||||
|
)
|
||||||
|
|
||||||
|
// isServiceProtected returns true if the service safe search is active.
|
||||||
|
func isServiceProtected(s filtering.SafeSearchConfig, service Service) (ok bool) {
|
||||||
|
switch service {
|
||||||
|
case Bing:
|
||||||
|
return s.Bing
|
||||||
|
case DuckDuckGo:
|
||||||
|
return s.DuckDuckGo
|
||||||
|
case Google:
|
||||||
|
return s.Google
|
||||||
|
case Pixabay:
|
||||||
|
return s.Pixabay
|
||||||
|
case Yandex:
|
||||||
|
return s.Yandex
|
||||||
|
case YouTube:
|
||||||
|
return s.YouTube
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("safesearch: invalid sources: not found service %q", service))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultSafeSearch is the default safesearch struct.
|
||||||
|
type DefaultSafeSearch struct {
|
||||||
|
engine *urlfilter.DNSEngine
|
||||||
|
safeSearchCache cache.Cache
|
||||||
|
resolver filtering.Resolver
|
||||||
|
cacheTime time.Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDefaultSafeSearch returns new safesearch struct. CacheTime is an element
|
||||||
|
// TTL (in minutes).
|
||||||
|
func NewDefaultSafeSearch(
|
||||||
|
conf filtering.SafeSearchConfig,
|
||||||
|
cacheSize uint,
|
||||||
|
cacheTime time.Duration,
|
||||||
|
) (ss *DefaultSafeSearch, err error) {
|
||||||
|
engine, err := newEngine(filtering.SafeSearchListID, conf)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resolver filtering.Resolver = net.DefaultResolver
|
||||||
|
if conf.CustomResolver != nil {
|
||||||
|
resolver = conf.CustomResolver
|
||||||
|
}
|
||||||
|
|
||||||
|
return &DefaultSafeSearch{
|
||||||
|
engine: engine,
|
||||||
|
safeSearchCache: cache.New(cache.Config{
|
||||||
|
EnableLRU: true,
|
||||||
|
MaxSize: cacheSize,
|
||||||
|
}),
|
||||||
|
cacheTime: cacheTime,
|
||||||
|
resolver: resolver,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// newEngine creates new engine for provided safe search configuration.
|
||||||
|
func newEngine(listID int, conf filtering.SafeSearchConfig) (engine *urlfilter.DNSEngine, err error) {
|
||||||
|
var sb strings.Builder
|
||||||
|
for service, serviceRules := range safeSearchRules {
|
||||||
|
if isServiceProtected(conf, service) {
|
||||||
|
sb.WriteString(serviceRules)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strList := &filterlist.StringRuleList{
|
||||||
|
ID: listID,
|
||||||
|
RulesText: sb.String(),
|
||||||
|
IgnoreCosmetic: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
rs, err := filterlist.NewRuleStorage([]filterlist.RuleList{strList})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creating rule storage: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
engine = urlfilter.NewDNSEngine(rs)
|
||||||
|
log.Info("safesearch: filter %d: reset %d rules", listID, engine.RulesCount)
|
||||||
|
|
||||||
|
return engine, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// type check
|
||||||
|
var _ filtering.SafeSearch = (*DefaultSafeSearch)(nil)
|
||||||
|
|
||||||
|
// SearchHost implements the [filtering.SafeSearch] interface for *DefaultSafeSearch.
|
||||||
|
func (ss *DefaultSafeSearch) SearchHost(host string, qtype uint16) (res *rules.DNSRewrite) {
|
||||||
|
r, _ := ss.engine.MatchRequest(&urlfilter.DNSRequest{
|
||||||
|
Hostname: strings.ToLower(host),
|
||||||
|
DNSType: qtype,
|
||||||
|
})
|
||||||
|
|
||||||
|
rewritesRules := r.DNSRewrites()
|
||||||
|
if len(rewritesRules) > 0 {
|
||||||
|
return rewritesRules[0].DNSRewrite
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckHost implements the [filtering.SafeSearch] interface for
|
||||||
|
// *DefaultSafeSearch.
|
||||||
|
func (ss *DefaultSafeSearch) CheckHost(
|
||||||
|
host string,
|
||||||
|
qtype uint16,
|
||||||
|
) (res filtering.Result, err error) {
|
||||||
|
if log.GetLevel() >= log.DEBUG {
|
||||||
|
timer := log.StartTimer()
|
||||||
|
defer timer.LogElapsed("safesearch: lookup for %s", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check cache. Return cached result if it was found
|
||||||
|
cachedValue, isFound := ss.getCachedResult(host)
|
||||||
|
if isFound {
|
||||||
|
log.Debug("safesearch: found in cache: %s", host)
|
||||||
|
|
||||||
|
return cachedValue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rewrite := ss.SearchHost(host, qtype)
|
||||||
|
if rewrite == nil {
|
||||||
|
return filtering.Result{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dRes, err := ss.newResult(rewrite, qtype)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("safesearch: failed to lookup addresses for %s: %s", host, err)
|
||||||
|
|
||||||
|
return filtering.Result{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if dRes != nil {
|
||||||
|
res = *dRes
|
||||||
|
ss.setCacheResult(host, res)
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtering.Result{}, fmt.Errorf("no ipv4 addresses in safe search response for %s", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newResult creates Result object from rewrite rule.
|
||||||
|
func (ss *DefaultSafeSearch) newResult(
|
||||||
|
rewrite *rules.DNSRewrite,
|
||||||
|
qtype uint16,
|
||||||
|
) (res *filtering.Result, err error) {
|
||||||
|
res = &filtering.Result{
|
||||||
|
Rules: []*filtering.ResultRule{{
|
||||||
|
FilterListID: filtering.SafeSearchListID,
|
||||||
|
}},
|
||||||
|
Reason: filtering.FilteredSafeSearch,
|
||||||
|
IsFiltered: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
if rewrite.RRType == qtype && (qtype == dns.TypeA || qtype == dns.TypeAAAA) {
|
||||||
|
ip, ok := rewrite.Value.(net.IP)
|
||||||
|
if !ok || ip == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Rules[0].IP = ip
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if rewrite.NewCNAME == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ips, err := ss.resolver.LookupIP(context.Background(), "ip", rewrite.NewCNAME)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip = ip.To4(); ip == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Rules[0].IP = ip
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCacheResult stores data in cache for host.
|
||||||
|
func (ss *DefaultSafeSearch) setCacheResult(host string, res filtering.Result) {
|
||||||
|
expire := uint32(time.Now().Add(ss.cacheTime).Unix())
|
||||||
|
exp := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(exp, expire)
|
||||||
|
buf := bytes.NewBuffer(exp)
|
||||||
|
|
||||||
|
err := gob.NewEncoder(buf).Encode(res)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("safesearch: cache encoding: %s", err)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val := buf.Bytes()
|
||||||
|
_ = ss.safeSearchCache.Set([]byte(host), val)
|
||||||
|
|
||||||
|
log.Debug("safesearch: stored in cache: %s (%d bytes)", host, len(val))
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCachedResult returns stored data from cache for host.
|
||||||
|
func (ss *DefaultSafeSearch) getCachedResult(host string) (res filtering.Result, ok bool) {
|
||||||
|
res = filtering.Result{}
|
||||||
|
|
||||||
|
data := ss.safeSearchCache.Get([]byte(host))
|
||||||
|
if data == nil {
|
||||||
|
return res, false
|
||||||
|
}
|
||||||
|
|
||||||
|
exp := binary.BigEndian.Uint32(data[:4])
|
||||||
|
if exp <= uint32(time.Now().Unix()) {
|
||||||
|
ss.safeSearchCache.Del([]byte(host))
|
||||||
|
|
||||||
|
return res, false
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(data[4:])
|
||||||
|
|
||||||
|
err := gob.NewDecoder(buf).Decode(&res)
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("safesearch: cache decoding: %s", err)
|
||||||
|
|
||||||
|
return filtering.Result{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, true
|
||||||
|
}
|
|
@ -0,0 +1,202 @@
|
||||||
|
package safesearch
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghtest"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/urlfilter/rules"
|
||||||
|
"github.com/miekg/dns"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
safeSearchCacheSize = 5000
|
||||||
|
cacheTime = 30 * time.Minute
|
||||||
|
)
|
||||||
|
|
||||||
|
var defaultSafeSearchConf = filtering.SafeSearchConfig{
|
||||||
|
Enabled: true,
|
||||||
|
Bing: true,
|
||||||
|
DuckDuckGo: true,
|
||||||
|
Google: true,
|
||||||
|
Pixabay: true,
|
||||||
|
Yandex: true,
|
||||||
|
YouTube: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var yandexIP = net.IPv4(213, 180, 193, 56)
|
||||||
|
|
||||||
|
func newForTest(t testing.TB, ssConf filtering.SafeSearchConfig) (ss *DefaultSafeSearch) {
|
||||||
|
ss, err := NewDefaultSafeSearch(ssConf, safeSearchCacheSize, cacheTime)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return ss
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSafeSearch(t *testing.T) {
|
||||||
|
ss := newForTest(t, defaultSafeSearchConf)
|
||||||
|
val := ss.SearchHost("www.google.com", dns.TypeA)
|
||||||
|
|
||||||
|
assert.Equal(t, &rules.DNSRewrite{NewCNAME: "forcesafesearch.google.com"}, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckHostSafeSearchYandex(t *testing.T) {
|
||||||
|
ss := newForTest(t, defaultSafeSearchConf)
|
||||||
|
|
||||||
|
// Check host for each domain.
|
||||||
|
for _, host := range []string{
|
||||||
|
"yandex.ru",
|
||||||
|
"yAndeX.ru",
|
||||||
|
"YANdex.COM",
|
||||||
|
"yandex.by",
|
||||||
|
"yandex.kz",
|
||||||
|
"www.yandex.com",
|
||||||
|
} {
|
||||||
|
res, err := ss.CheckHost(host, dns.TypeA)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.True(t, res.IsFiltered)
|
||||||
|
|
||||||
|
require.Len(t, res.Rules, 1)
|
||||||
|
|
||||||
|
assert.Equal(t, yandexIP, res.Rules[0].IP)
|
||||||
|
assert.EqualValues(t, filtering.SafeSearchListID, res.Rules[0].FilterListID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCheckHostSafeSearchGoogle(t *testing.T) {
|
||||||
|
resolver := &aghtest.TestResolver{}
|
||||||
|
ip, _ := resolver.HostToIPs("forcesafesearch.google.com")
|
||||||
|
|
||||||
|
ss := newForTest(t, defaultSafeSearchConf)
|
||||||
|
ss.resolver = resolver
|
||||||
|
|
||||||
|
// Check host for each domain.
|
||||||
|
for _, host := range []string{
|
||||||
|
"www.google.com",
|
||||||
|
"www.google.im",
|
||||||
|
"www.google.co.in",
|
||||||
|
"www.google.iq",
|
||||||
|
"www.google.is",
|
||||||
|
"www.google.it",
|
||||||
|
"www.google.je",
|
||||||
|
} {
|
||||||
|
t.Run(host, func(t *testing.T) {
|
||||||
|
res, err := ss.CheckHost(host, dns.TypeA)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.True(t, res.IsFiltered)
|
||||||
|
|
||||||
|
require.Len(t, res.Rules, 1)
|
||||||
|
|
||||||
|
assert.Equal(t, ip, res.Rules[0].IP)
|
||||||
|
assert.EqualValues(t, filtering.SafeSearchListID, res.Rules[0].FilterListID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSafeSearchCacheYandex(t *testing.T) {
|
||||||
|
const domain = "yandex.ru"
|
||||||
|
|
||||||
|
ss := newForTest(t, filtering.SafeSearchConfig{Enabled: false})
|
||||||
|
|
||||||
|
// Check host with disabled safesearch.
|
||||||
|
res, err := ss.CheckHost(domain, dns.TypeA)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.False(t, res.IsFiltered)
|
||||||
|
assert.Empty(t, res.Rules)
|
||||||
|
|
||||||
|
ss = newForTest(t, defaultSafeSearchConf)
|
||||||
|
res, err = ss.CheckHost(domain, dns.TypeA)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// For yandex we already know valid IP.
|
||||||
|
require.Len(t, res.Rules, 1)
|
||||||
|
|
||||||
|
assert.Equal(t, res.Rules[0].IP, yandexIP)
|
||||||
|
|
||||||
|
// Check cache.
|
||||||
|
cachedValue, isFound := ss.getCachedResult(domain)
|
||||||
|
require.True(t, isFound)
|
||||||
|
require.Len(t, cachedValue.Rules, 1)
|
||||||
|
|
||||||
|
assert.Equal(t, cachedValue.Rules[0].IP, yandexIP)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSafeSearchCacheGoogle(t *testing.T) {
|
||||||
|
const domain = "www.google.ru"
|
||||||
|
|
||||||
|
ss := newForTest(t, filtering.SafeSearchConfig{Enabled: false})
|
||||||
|
|
||||||
|
res, err := ss.CheckHost(domain, dns.TypeA)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.False(t, res.IsFiltered)
|
||||||
|
assert.Empty(t, res.Rules)
|
||||||
|
|
||||||
|
resolver := &aghtest.TestResolver{}
|
||||||
|
ss = newForTest(t, defaultSafeSearchConf)
|
||||||
|
ss.resolver = resolver
|
||||||
|
|
||||||
|
// Lookup for safesearch domain.
|
||||||
|
rewrite := ss.SearchHost(domain, dns.TypeA)
|
||||||
|
|
||||||
|
ips, err := resolver.LookupIP(context.Background(), "ip", rewrite.NewCNAME)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var foundIP net.IP
|
||||||
|
for _, ip := range ips {
|
||||||
|
if ip.To4() != nil {
|
||||||
|
foundIP = ip
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err = ss.CheckHost(domain, dns.TypeA)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, res.Rules, 1)
|
||||||
|
|
||||||
|
assert.True(t, res.Rules[0].IP.Equal(foundIP))
|
||||||
|
|
||||||
|
// Check cache.
|
||||||
|
cachedValue, isFound := ss.getCachedResult(domain)
|
||||||
|
require.True(t, isFound)
|
||||||
|
require.Len(t, cachedValue.Rules, 1)
|
||||||
|
|
||||||
|
assert.True(t, cachedValue.Rules[0].IP.Equal(foundIP))
|
||||||
|
}
|
||||||
|
|
||||||
|
const googleHost = "www.google.com"
|
||||||
|
|
||||||
|
var dnsRewriteSink *rules.DNSRewrite
|
||||||
|
|
||||||
|
func BenchmarkSafeSearch(b *testing.B) {
|
||||||
|
ss := newForTest(b, defaultSafeSearchConf)
|
||||||
|
|
||||||
|
for n := 0; n < b.N; n++ {
|
||||||
|
dnsRewriteSink = ss.SearchHost(googleHost, dns.TypeA)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(b, "forcesafesearch.google.com", dnsRewriteSink.NewCNAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
var dnsRewriteParallelSink *rules.DNSRewrite
|
||||||
|
|
||||||
|
func BenchmarkSafeSearch_parallel(b *testing.B) {
|
||||||
|
ss := newForTest(b, defaultSafeSearchConf)
|
||||||
|
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
for pb.Next() {
|
||||||
|
dnsRewriteParallelSink = ss.SearchHost(googleHost, dns.TypeA)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.Equal(b, "forcesafesearch.google.com", dnsRewriteParallelSink.NewCNAME)
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package filtering
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(d.kolyshev): Replace handlers below with the new API.
|
||||||
|
|
||||||
|
func (d *DNSFilter) handleSafeSearchEnable(w http.ResponseWriter, r *http.Request) {
|
||||||
|
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, true)
|
||||||
|
d.Config.ConfigModified()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSFilter) handleSafeSearchDisable(w http.ResponseWriter, r *http.Request) {
|
||||||
|
setProtectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled, false)
|
||||||
|
d.Config.ConfigModified()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *DNSFilter) handleSafeSearchStatus(w http.ResponseWriter, r *http.Request) {
|
||||||
|
resp := &struct {
|
||||||
|
Enabled bool `json:"enabled"`
|
||||||
|
}{
|
||||||
|
Enabled: protectedBool(&d.confLock, &d.Config.SafeSearchConf.Enabled),
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = aghhttp.WriteJSONResponse(w, r, resp)
|
||||||
|
}
|
|
@ -1370,6 +1370,7 @@ var blockedServices = []blockedService{{
|
||||||
"||mastodon.au^",
|
"||mastodon.au^",
|
||||||
"||mastodon.bida.im^",
|
"||mastodon.bida.im^",
|
||||||
"||mastodon.com.tr^",
|
"||mastodon.com.tr^",
|
||||||
|
"||mastodon.eus^",
|
||||||
"||mastodon.green^",
|
"||mastodon.green^",
|
||||||
"||mastodon.ie^",
|
"||mastodon.ie^",
|
||||||
"||mastodon.iriseden.eu^",
|
"||mastodon.iriseden.eu^",
|
||||||
|
@ -1397,7 +1398,6 @@ var blockedServices = []blockedService{{
|
||||||
"||mindly.social^",
|
"||mindly.social^",
|
||||||
"||mstdn.ca^",
|
"||mstdn.ca^",
|
||||||
"||mstdn.jp^",
|
"||mstdn.jp^",
|
||||||
"||mstdn.party^",
|
|
||||||
"||mstdn.social^",
|
"||mstdn.social^",
|
||||||
"||muenchen.social^",
|
"||muenchen.social^",
|
||||||
"||newsie.social^",
|
"||newsie.social^",
|
||||||
|
@ -1435,11 +1435,11 @@ var blockedServices = []blockedService{{
|
||||||
"||toot.wales^",
|
"||toot.wales^",
|
||||||
"||troet.cafe^",
|
"||troet.cafe^",
|
||||||
"||twingyeo.kr^",
|
"||twingyeo.kr^",
|
||||||
"||uiuxdev.social^",
|
|
||||||
"||union.place^",
|
"||union.place^",
|
||||||
"||universeodon.com^",
|
"||universeodon.com^",
|
||||||
"||urbanists.social^",
|
"||urbanists.social^",
|
||||||
"||vocalodon.net^",
|
"||vocalodon.net^",
|
||||||
|
"||wien.rocks^",
|
||||||
"||wxw.moe^",
|
"||wxw.moe^",
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
|
@ -1636,6 +1636,14 @@ var blockedServices = []blockedService{{
|
||||||
"||snapchat.com^",
|
"||snapchat.com^",
|
||||||
"||snapkit.co",
|
"||snapkit.co",
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
ID: "soundcloud",
|
||||||
|
Name: "SoundCloud",
|
||||||
|
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 24 24\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"><path d=\"M19 17.75c2.07 0 3.75-1.68 3.75-3.75 0-2.07-1.68-3.75-3.75-3.75-.173 0-.344.012-.511.035-.73-2.337-2.913-4.035-5.489-4.035-.818 0-1.596.171-2.301.48-.273.119-.449.389-.449.687l0 9.583c0 .414.336.75.75.75l8 0zM7.25 8l0 9c0 .414.336.75.75.75.414 0 .75-.336.75-.75l0-9c0-.414-.336-.75-.75-.75-.414 0-.75.336-.75.75zM4.25 10l0 7c0 .414.336.75.75.75.414 0 .75-.336.75-.75l0-7c0-.414-.336-.75-.75-.75-.414 0-.75.336-.75.75zM1.25 12l0 5c0 .414.336.75.75.75.414 0 .75-.336.75-.75l0-5c0-.414-.336-.75-.75-.75-.414 0-.75.336-.75.75z\"/></svg>"),
|
||||||
|
Rules: []string{
|
||||||
|
"||sndcdn.com^",
|
||||||
|
"||soundcloud.com^",
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
ID: "spotify",
|
ID: "spotify",
|
||||||
Name: "Spotify",
|
Name: "Spotify",
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding"
|
"encoding"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,6 +16,9 @@ type Client struct {
|
||||||
// these upstream must be used.
|
// these upstream must be used.
|
||||||
upstreamConfig *proxy.UpstreamConfig
|
upstreamConfig *proxy.UpstreamConfig
|
||||||
|
|
||||||
|
safeSearchConf filtering.SafeSearchConfig
|
||||||
|
SafeSearch filtering.SafeSearch
|
||||||
|
|
||||||
Name string
|
Name string
|
||||||
|
|
||||||
IDs []string
|
IDs []string
|
||||||
|
@ -24,7 +28,6 @@ type Client struct {
|
||||||
|
|
||||||
UseOwnSettings bool
|
UseOwnSettings bool
|
||||||
FilteringEnabled bool
|
FilteringEnabled bool
|
||||||
SafeSearchEnabled bool
|
|
||||||
SafeBrowsingEnabled bool
|
SafeBrowsingEnabled bool
|
||||||
ParentalEnabled bool
|
ParentalEnabled bool
|
||||||
UseOwnBlockedServices bool
|
UseOwnBlockedServices bool
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||||
|
@ -69,6 +70,7 @@ func (clients *clientsContainer) Init(
|
||||||
dhcpServer dhcpd.Interface,
|
dhcpServer dhcpd.Interface,
|
||||||
etcHosts *aghnet.HostsContainer,
|
etcHosts *aghnet.HostsContainer,
|
||||||
arpdb aghnet.ARPDB,
|
arpdb aghnet.ARPDB,
|
||||||
|
filteringConf *filtering.Config,
|
||||||
) {
|
) {
|
||||||
if clients.list != nil {
|
if clients.list != nil {
|
||||||
log.Fatal("clients.list != nil")
|
log.Fatal("clients.list != nil")
|
||||||
|
@ -82,7 +84,7 @@ func (clients *clientsContainer) Init(
|
||||||
clients.dhcpServer = dhcpServer
|
clients.dhcpServer = dhcpServer
|
||||||
clients.etcHosts = etcHosts
|
clients.etcHosts = etcHosts
|
||||||
clients.arpdb = arpdb
|
clients.arpdb = arpdb
|
||||||
clients.addFromConfig(objects)
|
clients.addFromConfig(objects, filteringConf)
|
||||||
|
|
||||||
if clients.testing {
|
if clients.testing {
|
||||||
return
|
return
|
||||||
|
@ -133,6 +135,8 @@ func (clients *clientsContainer) reloadARP() {
|
||||||
|
|
||||||
// clientObject is the YAML representation of a persistent client.
|
// clientObject is the YAML representation of a persistent client.
|
||||||
type clientObject struct {
|
type clientObject struct {
|
||||||
|
SafeSearchConf filtering.SafeSearchConfig `yaml:"safe_search"`
|
||||||
|
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
|
|
||||||
Tags []string `yaml:"tags"`
|
Tags []string `yaml:"tags"`
|
||||||
|
@ -143,14 +147,13 @@ type clientObject struct {
|
||||||
UseGlobalSettings bool `yaml:"use_global_settings"`
|
UseGlobalSettings bool `yaml:"use_global_settings"`
|
||||||
FilteringEnabled bool `yaml:"filtering_enabled"`
|
FilteringEnabled bool `yaml:"filtering_enabled"`
|
||||||
ParentalEnabled bool `yaml:"parental_enabled"`
|
ParentalEnabled bool `yaml:"parental_enabled"`
|
||||||
SafeSearchEnabled bool `yaml:"safesearch_enabled"`
|
|
||||||
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
|
SafeBrowsingEnabled bool `yaml:"safebrowsing_enabled"`
|
||||||
UseGlobalBlockedServices bool `yaml:"use_global_blocked_services"`
|
UseGlobalBlockedServices bool `yaml:"use_global_blocked_services"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// addFromConfig initializes the clients container with objects from the
|
// addFromConfig initializes the clients container with objects from the
|
||||||
// configuration file.
|
// configuration file.
|
||||||
func (clients *clientsContainer) addFromConfig(objects []*clientObject) {
|
func (clients *clientsContainer) addFromConfig(objects []*clientObject, filteringConf *filtering.Config) {
|
||||||
for _, o := range objects {
|
for _, o := range objects {
|
||||||
cli := &Client{
|
cli := &Client{
|
||||||
Name: o.Name,
|
Name: o.Name,
|
||||||
|
@ -161,11 +164,28 @@ func (clients *clientsContainer) addFromConfig(objects []*clientObject) {
|
||||||
UseOwnSettings: !o.UseGlobalSettings,
|
UseOwnSettings: !o.UseGlobalSettings,
|
||||||
FilteringEnabled: o.FilteringEnabled,
|
FilteringEnabled: o.FilteringEnabled,
|
||||||
ParentalEnabled: o.ParentalEnabled,
|
ParentalEnabled: o.ParentalEnabled,
|
||||||
SafeSearchEnabled: o.SafeSearchEnabled,
|
safeSearchConf: o.SafeSearchConf,
|
||||||
SafeBrowsingEnabled: o.SafeBrowsingEnabled,
|
SafeBrowsingEnabled: o.SafeBrowsingEnabled,
|
||||||
UseOwnBlockedServices: !o.UseGlobalBlockedServices,
|
UseOwnBlockedServices: !o.UseGlobalBlockedServices,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if o.SafeSearchConf.Enabled {
|
||||||
|
o.SafeSearchConf.CustomResolver = safeSearchResolver{}
|
||||||
|
|
||||||
|
ss, err := safesearch.NewDefaultSafeSearch(
|
||||||
|
o.SafeSearchConf,
|
||||||
|
filteringConf.SafeSearchCacheSize,
|
||||||
|
time.Minute*time.Duration(filteringConf.CacheTime),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Error("clients: init client safesearch %s: %s", cli.Name, err)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cli.SafeSearch = ss
|
||||||
|
}
|
||||||
|
|
||||||
for _, s := range o.BlockedServices {
|
for _, s := range o.BlockedServices {
|
||||||
if filtering.BlockedSvcKnown(s) {
|
if filtering.BlockedSvcKnown(s) {
|
||||||
cli.BlockedServices = append(cli.BlockedServices, s)
|
cli.BlockedServices = append(cli.BlockedServices, s)
|
||||||
|
@ -210,7 +230,7 @@ func (clients *clientsContainer) forConfig() (objs []*clientObject) {
|
||||||
UseGlobalSettings: !cli.UseOwnSettings,
|
UseGlobalSettings: !cli.UseOwnSettings,
|
||||||
FilteringEnabled: cli.FilteringEnabled,
|
FilteringEnabled: cli.FilteringEnabled,
|
||||||
ParentalEnabled: cli.ParentalEnabled,
|
ParentalEnabled: cli.ParentalEnabled,
|
||||||
SafeSearchEnabled: cli.SafeSearchEnabled,
|
SafeSearchConf: cli.safeSearchConf,
|
||||||
SafeBrowsingEnabled: cli.SafeBrowsingEnabled,
|
SafeBrowsingEnabled: cli.SafeBrowsingEnabled,
|
||||||
UseGlobalBlockedServices: !cli.UseOwnBlockedServices,
|
UseGlobalBlockedServices: !cli.UseOwnBlockedServices,
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ func TestClients(t *testing.T) {
|
||||||
clients := clientsContainer{}
|
clients := clientsContainer{}
|
||||||
clients.testing = true
|
clients.testing = true
|
||||||
|
|
||||||
clients.Init(nil, nil, nil, nil)
|
clients.Init(nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
t.Run("add_success", func(t *testing.T) {
|
t.Run("add_success", func(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
|
@ -201,7 +201,7 @@ func TestClientsWHOIS(t *testing.T) {
|
||||||
clients := clientsContainer{
|
clients := clientsContainer{
|
||||||
testing: true,
|
testing: true,
|
||||||
}
|
}
|
||||||
clients.Init(nil, nil, nil, nil)
|
clients.Init(nil, nil, nil, nil, nil)
|
||||||
whois := &RuntimeClientWHOISInfo{
|
whois := &RuntimeClientWHOISInfo{
|
||||||
Country: "AU",
|
Country: "AU",
|
||||||
Orgname: "Example Org",
|
Orgname: "Example Org",
|
||||||
|
@ -250,7 +250,7 @@ func TestClientsAddExisting(t *testing.T) {
|
||||||
clients := clientsContainer{
|
clients := clientsContainer{
|
||||||
testing: true,
|
testing: true,
|
||||||
}
|
}
|
||||||
clients.Init(nil, nil, nil, nil)
|
clients.Init(nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
t.Run("simple", func(t *testing.T) {
|
t.Run("simple", func(t *testing.T) {
|
||||||
ip := netip.MustParseAddr("1.1.1.1")
|
ip := netip.MustParseAddr("1.1.1.1")
|
||||||
|
@ -328,7 +328,7 @@ func TestClientsCustomUpstream(t *testing.T) {
|
||||||
clients := clientsContainer{
|
clients := clientsContainer{
|
||||||
testing: true,
|
testing: true,
|
||||||
}
|
}
|
||||||
clients.Init(nil, nil, nil, nil)
|
clients.Init(nil, nil, nil, nil, nil)
|
||||||
|
|
||||||
// Add client with upstreams.
|
// Add client with upstreams.
|
||||||
ok, err := clients.Add(&Client{
|
ok, err := clients.Add(&Client{
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
)
|
)
|
||||||
|
|
||||||
// clientJSON is a common structure used by several handlers to deal with
|
// clientJSON is a common structure used by several handlers to deal with
|
||||||
|
@ -35,9 +36,10 @@ type clientJSON struct {
|
||||||
Tags []string `json:"tags"`
|
Tags []string `json:"tags"`
|
||||||
Upstreams []string `json:"upstreams"`
|
Upstreams []string `json:"upstreams"`
|
||||||
|
|
||||||
FilteringEnabled bool `json:"filtering_enabled"`
|
FilteringEnabled bool `json:"filtering_enabled"`
|
||||||
ParentalEnabled bool `json:"parental_enabled"`
|
ParentalEnabled bool `json:"parental_enabled"`
|
||||||
SafeBrowsingEnabled bool `json:"safebrowsing_enabled"`
|
SafeBrowsingEnabled bool `json:"safebrowsing_enabled"`
|
||||||
|
// Deprecated: use safeSearchConf.
|
||||||
SafeSearchEnabled bool `json:"safesearch_enabled"`
|
SafeSearchEnabled bool `json:"safesearch_enabled"`
|
||||||
UseGlobalBlockedServices bool `json:"use_global_blocked_services"`
|
UseGlobalBlockedServices bool `json:"use_global_blocked_services"`
|
||||||
UseGlobalSettings bool `json:"use_global_settings"`
|
UseGlobalSettings bool `json:"use_global_settings"`
|
||||||
|
@ -88,6 +90,20 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
|
||||||
|
|
||||||
// Convert JSON object to Client object
|
// Convert JSON object to Client object
|
||||||
func jsonToClient(cj clientJSON) (c *Client) {
|
func jsonToClient(cj clientJSON) (c *Client) {
|
||||||
|
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
||||||
|
// [clientJSON.SafeSearchEnabled] field.
|
||||||
|
safeSearchConf := filtering.SafeSearchConfig{Enabled: cj.SafeSearchEnabled}
|
||||||
|
|
||||||
|
// Set default service flags for enabled safesearch.
|
||||||
|
if safeSearchConf.Enabled {
|
||||||
|
safeSearchConf.Bing = true
|
||||||
|
safeSearchConf.DuckDuckGo = true
|
||||||
|
safeSearchConf.Google = true
|
||||||
|
safeSearchConf.Pixabay = true
|
||||||
|
safeSearchConf.Yandex = true
|
||||||
|
safeSearchConf.YouTube = true
|
||||||
|
}
|
||||||
|
|
||||||
return &Client{
|
return &Client{
|
||||||
Name: cj.Name,
|
Name: cj.Name,
|
||||||
IDs: cj.IDs,
|
IDs: cj.IDs,
|
||||||
|
@ -95,7 +111,7 @@ func jsonToClient(cj clientJSON) (c *Client) {
|
||||||
UseOwnSettings: !cj.UseGlobalSettings,
|
UseOwnSettings: !cj.UseGlobalSettings,
|
||||||
FilteringEnabled: cj.FilteringEnabled,
|
FilteringEnabled: cj.FilteringEnabled,
|
||||||
ParentalEnabled: cj.ParentalEnabled,
|
ParentalEnabled: cj.ParentalEnabled,
|
||||||
SafeSearchEnabled: cj.SafeSearchEnabled,
|
safeSearchConf: safeSearchConf,
|
||||||
SafeBrowsingEnabled: cj.SafeBrowsingEnabled,
|
SafeBrowsingEnabled: cj.SafeBrowsingEnabled,
|
||||||
|
|
||||||
UseOwnBlockedServices: !cj.UseGlobalBlockedServices,
|
UseOwnBlockedServices: !cj.UseGlobalBlockedServices,
|
||||||
|
@ -107,6 +123,11 @@ func jsonToClient(cj clientJSON) (c *Client) {
|
||||||
|
|
||||||
// Convert Client object to JSON
|
// Convert Client object to JSON
|
||||||
func clientToJSON(c *Client) (cj *clientJSON) {
|
func clientToJSON(c *Client) (cj *clientJSON) {
|
||||||
|
// TODO(d.kolyshev): Remove after cleaning the deprecated
|
||||||
|
// [clientJSON.SafeSearchEnabled] field.
|
||||||
|
cloneVal := c.safeSearchConf
|
||||||
|
safeSearchConf := &cloneVal
|
||||||
|
|
||||||
return &clientJSON{
|
return &clientJSON{
|
||||||
Name: c.Name,
|
Name: c.Name,
|
||||||
IDs: c.IDs,
|
IDs: c.IDs,
|
||||||
|
@ -114,7 +135,7 @@ func clientToJSON(c *Client) (cj *clientJSON) {
|
||||||
UseGlobalSettings: !c.UseOwnSettings,
|
UseGlobalSettings: !c.UseOwnSettings,
|
||||||
FilteringEnabled: c.FilteringEnabled,
|
FilteringEnabled: c.FilteringEnabled,
|
||||||
ParentalEnabled: c.ParentalEnabled,
|
ParentalEnabled: c.ParentalEnabled,
|
||||||
SafeSearchEnabled: c.SafeSearchEnabled,
|
SafeSearchEnabled: safeSearchConf.Enabled,
|
||||||
SafeBrowsingEnabled: c.SafeBrowsingEnabled,
|
SafeBrowsingEnabled: c.SafeBrowsingEnabled,
|
||||||
|
|
||||||
UseGlobalBlockedServices: !c.UseOwnBlockedServices,
|
UseGlobalBlockedServices: !c.UseOwnBlockedServices,
|
||||||
|
|
|
@ -283,6 +283,12 @@ var config = &configuration{
|
||||||
TrustedProxies: []string{"127.0.0.0/8", "::1/128"},
|
TrustedProxies: []string{"127.0.0.0/8", "::1/128"},
|
||||||
CacheSize: 4 * 1024 * 1024,
|
CacheSize: 4 * 1024 * 1024,
|
||||||
|
|
||||||
|
EDNSClientSubnet: &dnsforward.EDNSClientSubnet{
|
||||||
|
CustomIP: "",
|
||||||
|
Enabled: false,
|
||||||
|
UseCustom: false,
|
||||||
|
},
|
||||||
|
|
||||||
// set default maximum concurrent queries to 300
|
// set default maximum concurrent queries to 300
|
||||||
// we introduced a default limit due to this:
|
// we introduced a default limit due to this:
|
||||||
// https://github.com/AdguardTeam/AdGuardHome/issues/2015#issuecomment-674041912
|
// https://github.com/AdguardTeam/AdGuardHome/issues/2015#issuecomment-674041912
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package home
|
package home
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
@ -427,7 +428,8 @@ func applyAdditionalFiltering(clientIP net.IP, clientID string, setts *filtering
|
||||||
}
|
}
|
||||||
|
|
||||||
setts.FilteringEnabled = c.FilteringEnabled
|
setts.FilteringEnabled = c.FilteringEnabled
|
||||||
setts.SafeSearchEnabled = c.SafeSearchEnabled
|
setts.SafeSearchEnabled = c.safeSearchConf.Enabled
|
||||||
|
setts.ClientSafeSearch = c.SafeSearch
|
||||||
setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled
|
setts.SafeBrowsingEnabled = c.SafeBrowsingEnabled
|
||||||
setts.ParentalEnabled = c.ParentalEnabled
|
setts.ParentalEnabled = c.ParentalEnabled
|
||||||
}
|
}
|
||||||
|
@ -533,3 +535,29 @@ func closeDNSServer() {
|
||||||
|
|
||||||
log.Debug("all dns modules are closed")
|
log.Debug("all dns modules are closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// safeSearchResolver is a [filtering.Resolver] implementation used for safe
|
||||||
|
// search.
|
||||||
|
type safeSearchResolver struct{}
|
||||||
|
|
||||||
|
// type check
|
||||||
|
var _ filtering.Resolver = safeSearchResolver{}
|
||||||
|
|
||||||
|
// LookupIP implements [filtering.Resolver] interface for safeSearchResolver.
|
||||||
|
// It returns the slice of net.IP with IPv4 and IPv6 instances.
|
||||||
|
func (r safeSearchResolver) LookupIP(_ context.Context, _, host string) (ips []net.IP, err error) {
|
||||||
|
addrs, err := Context.dnsServer.Resolve(host)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(addrs) == 0 {
|
||||||
|
return nil, fmt.Errorf("couldn't lookup host: %s", host)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, a := range addrs {
|
||||||
|
ips = append(ips, a.IP)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ips, nil
|
||||||
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpd"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
"github.com/AdguardTeam/AdGuardHome/internal/dnsforward"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
"github.com/AdguardTeam/AdGuardHome/internal/querylog"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
"github.com/AdguardTeam/AdGuardHome/internal/stats"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/updater"
|
"github.com/AdguardTeam/AdGuardHome/internal/updater"
|
||||||
|
@ -298,6 +299,16 @@ func setupConfig(opts options) (err error) {
|
||||||
config.DNS.DnsfilterConf.UserRules = slices.Clone(config.UserRules)
|
config.DNS.DnsfilterConf.UserRules = slices.Clone(config.UserRules)
|
||||||
config.DNS.DnsfilterConf.HTTPClient = Context.client
|
config.DNS.DnsfilterConf.HTTPClient = Context.client
|
||||||
|
|
||||||
|
config.DNS.DnsfilterConf.SafeSearchConf.CustomResolver = safeSearchResolver{}
|
||||||
|
config.DNS.DnsfilterConf.SafeSearch, err = safesearch.NewDefaultSafeSearch(
|
||||||
|
config.DNS.DnsfilterConf.SafeSearchConf,
|
||||||
|
config.DNS.DnsfilterConf.SafeSearchCacheSize,
|
||||||
|
time.Minute*time.Duration(config.DNS.DnsfilterConf.CacheTime),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("initializing safesearch: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
config.DHCP.WorkDir = Context.workDir
|
config.DHCP.WorkDir = Context.workDir
|
||||||
config.DHCP.HTTPRegister = httpRegister
|
config.DHCP.HTTPRegister = httpRegister
|
||||||
config.DHCP.ConfigModified = onConfigModified
|
config.DHCP.ConfigModified = onConfigModified
|
||||||
|
@ -328,33 +339,16 @@ func setupConfig(opts options) (err error) {
|
||||||
arpdb = aghnet.NewARPDB()
|
arpdb = aghnet.NewARPDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
Context.clients.Init(config.Clients.Persistent, Context.dhcpServer, Context.etcHosts, arpdb)
|
Context.clients.Init(config.Clients.Persistent, Context.dhcpServer, Context.etcHosts, arpdb, config.DNS.DnsfilterConf)
|
||||||
|
|
||||||
if opts.bindPort != 0 {
|
if opts.bindPort != 0 {
|
||||||
tcpPorts := aghalg.UniqChecker[tcpPort]{}
|
|
||||||
addPorts(tcpPorts, tcpPort(opts.bindPort))
|
|
||||||
|
|
||||||
udpPorts := aghalg.UniqChecker[udpPort]{}
|
|
||||||
addPorts(udpPorts, udpPort(config.DNS.Port))
|
|
||||||
|
|
||||||
if config.TLS.Enabled {
|
|
||||||
addPorts(
|
|
||||||
tcpPorts,
|
|
||||||
tcpPort(config.TLS.PortHTTPS),
|
|
||||||
tcpPort(config.TLS.PortDNSOverTLS),
|
|
||||||
tcpPort(config.TLS.PortDNSCrypt),
|
|
||||||
)
|
|
||||||
|
|
||||||
addPorts(udpPorts, udpPort(config.TLS.PortDNSOverQUIC))
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = tcpPorts.Validate(); err != nil {
|
|
||||||
return fmt.Errorf("validating tcp ports: %w", err)
|
|
||||||
} else if err = udpPorts.Validate(); err != nil {
|
|
||||||
return fmt.Errorf("validating udp ports: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
config.BindPort = opts.bindPort
|
config.BindPort = opts.bindPort
|
||||||
|
|
||||||
|
err = checkPorts()
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// override bind host/port from the console
|
// override bind host/port from the console
|
||||||
|
@ -368,6 +362,34 @@ func setupConfig(opts options) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkPorts is a helper for ports validation in config.
|
||||||
|
func checkPorts() (err error) {
|
||||||
|
tcpPorts := aghalg.UniqChecker[tcpPort]{}
|
||||||
|
addPorts(tcpPorts, tcpPort(config.BindPort))
|
||||||
|
|
||||||
|
udpPorts := aghalg.UniqChecker[udpPort]{}
|
||||||
|
addPorts(udpPorts, udpPort(config.DNS.Port))
|
||||||
|
|
||||||
|
if config.TLS.Enabled {
|
||||||
|
addPorts(
|
||||||
|
tcpPorts,
|
||||||
|
tcpPort(config.TLS.PortHTTPS),
|
||||||
|
tcpPort(config.TLS.PortDNSOverTLS),
|
||||||
|
tcpPort(config.TLS.PortDNSCrypt),
|
||||||
|
)
|
||||||
|
|
||||||
|
addPorts(udpPorts, udpPort(config.TLS.PortDNSOverQUIC))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = tcpPorts.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("validating tcp ports: %w", err)
|
||||||
|
} else if err = udpPorts.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("validating udp ports: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func initWeb(opts options, clientBuildFS fs.FS) (web *Web, err error) {
|
func initWeb(opts options, clientBuildFS fs.FS) (web *Web, err error) {
|
||||||
var clientFS fs.FS
|
var clientFS fs.FS
|
||||||
if opts.localFrontend {
|
if opts.localFrontend {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// currentSchemaVersion is the current schema version.
|
// currentSchemaVersion is the current schema version.
|
||||||
const currentSchemaVersion = 17
|
const currentSchemaVersion = 19
|
||||||
|
|
||||||
// These aliases are provided for convenience.
|
// These aliases are provided for convenience.
|
||||||
type (
|
type (
|
||||||
|
@ -91,6 +91,8 @@ func upgradeConfigSchema(oldVersion int, diskConf yobj) (err error) {
|
||||||
upgradeSchema15to16,
|
upgradeSchema15to16,
|
||||||
upgradeSchema16to17,
|
upgradeSchema16to17,
|
||||||
upgradeSchema17to18,
|
upgradeSchema17to18,
|
||||||
|
upgradeSchema18to19,
|
||||||
|
upgradeSchema19to20,
|
||||||
}
|
}
|
||||||
|
|
||||||
n := 0
|
n := 0
|
||||||
|
@ -947,15 +949,134 @@ func upgradeSchema16to17(diskConf yobj) (err error) {
|
||||||
// upgradeSchema17to18 performs the following changes:
|
// upgradeSchema17to18 performs the following changes:
|
||||||
//
|
//
|
||||||
// # BEFORE:
|
// # BEFORE:
|
||||||
|
// 'dns':
|
||||||
|
// 'safesearch_enabled': true
|
||||||
|
//
|
||||||
|
// # AFTER:
|
||||||
|
// 'dns':
|
||||||
|
// 'safe_search':
|
||||||
|
// 'enabled': true
|
||||||
|
// 'bing': true
|
||||||
|
// 'duckduckgo': true
|
||||||
|
// 'google': true
|
||||||
|
// 'pixabay': true
|
||||||
|
// 'yandex': true
|
||||||
|
// 'youtube': true
|
||||||
|
func upgradeSchema17to18(diskConf yobj) (err error) {
|
||||||
|
log.Printf("Upgrade yaml: 17 to 18")
|
||||||
|
diskConf["schema_version"] = 18
|
||||||
|
|
||||||
|
dnsVal, ok := diskConf["dns"]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dns, ok := dnsVal.(yobj)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type of dns: %T", dnsVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
safeSearch := yobj{
|
||||||
|
"enabled": true,
|
||||||
|
"bing": true,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": true,
|
||||||
|
"pixabay": true,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeSearchKey = "safesearch_enabled"
|
||||||
|
|
||||||
|
v, has := dns[safeSearchKey]
|
||||||
|
if has {
|
||||||
|
safeSearch["enabled"] = v
|
||||||
|
}
|
||||||
|
delete(dns, safeSearchKey)
|
||||||
|
|
||||||
|
dns["safe_search"] = safeSearch
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgradeSchema18to19 performs the following changes:
|
||||||
|
//
|
||||||
|
// # BEFORE:
|
||||||
|
// 'clients':
|
||||||
|
// 'persistent':
|
||||||
|
// - 'name': 'client-name'
|
||||||
|
// 'safesearch_enabled': true
|
||||||
|
//
|
||||||
|
// # AFTER:
|
||||||
|
// 'clients':
|
||||||
|
// 'persistent':
|
||||||
|
// - 'name': 'client-name'
|
||||||
|
// 'safe_search':
|
||||||
|
// 'enabled': true
|
||||||
|
// 'bing': true
|
||||||
|
// 'duckduckgo': true
|
||||||
|
// 'google': true
|
||||||
|
// 'pixabay': true
|
||||||
|
// 'yandex': true
|
||||||
|
// 'youtube': true
|
||||||
|
func upgradeSchema18to19(diskConf yobj) (err error) {
|
||||||
|
log.Printf("Upgrade yaml: 18 to 19")
|
||||||
|
diskConf["schema_version"] = 19
|
||||||
|
|
||||||
|
clientsVal, ok := diskConf["clients"]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
clients, ok := clientsVal.(yobj)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unexpected type of clients: %T", clientsVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
persistent, ok := clients["persistent"].([]yobj)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const safeSearchKey = "safesearch_enabled"
|
||||||
|
|
||||||
|
for i := range persistent {
|
||||||
|
c := persistent[i]
|
||||||
|
|
||||||
|
safeSearch := yobj{
|
||||||
|
"enabled": true,
|
||||||
|
"bing": true,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": true,
|
||||||
|
"pixabay": true,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": true,
|
||||||
|
}
|
||||||
|
|
||||||
|
v, has := c[safeSearchKey]
|
||||||
|
if has {
|
||||||
|
safeSearch["enabled"] = v
|
||||||
|
}
|
||||||
|
delete(c, safeSearchKey)
|
||||||
|
|
||||||
|
c["safe_search"] = safeSearch
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// upgradeSchema19to20 performs the following changes:
|
||||||
|
//
|
||||||
|
// # BEFORE:
|
||||||
// 'statistics':
|
// 'statistics':
|
||||||
// 'interval': 1
|
// 'interval': 1
|
||||||
//
|
//
|
||||||
// # AFTER:
|
// # AFTER:
|
||||||
// 'statistics':
|
// 'statistics':
|
||||||
// 'interval': 24h
|
// 'interval': 24h
|
||||||
func upgradeSchema17to18(diskConf yobj) (err error) {
|
func upgradeSchema19to20(diskConf yobj) (err error) {
|
||||||
log.Printf("Upgrade yaml: 17 to 18")
|
log.Printf("Upgrade yaml: 19 to 20")
|
||||||
diskConf["schema_version"] = 18
|
diskConf["schema_version"] = 20
|
||||||
|
|
||||||
statsVal, ok := diskConf["statistics"]
|
statsVal, ok := diskConf["statistics"]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|
|
@ -810,6 +810,149 @@ func TestUpgradeSchema16to17(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpgradeSchema17to18(t *testing.T) {
|
func TestUpgradeSchema17to18(t *testing.T) {
|
||||||
|
const newSchemaVer = 18
|
||||||
|
|
||||||
|
defaultWantObj := yobj{
|
||||||
|
"dns": yobj{
|
||||||
|
"safe_search": yobj{
|
||||||
|
"enabled": true,
|
||||||
|
"bing": true,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": true,
|
||||||
|
"pixabay": true,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"schema_version": newSchemaVer,
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
in yobj
|
||||||
|
want yobj
|
||||||
|
name string
|
||||||
|
}{{
|
||||||
|
in: yobj{"dns": yobj{}},
|
||||||
|
want: defaultWantObj,
|
||||||
|
name: "default_values",
|
||||||
|
}, {
|
||||||
|
in: yobj{"dns": yobj{"safesearch_enabled": true}},
|
||||||
|
want: defaultWantObj,
|
||||||
|
name: "enabled",
|
||||||
|
}, {
|
||||||
|
in: yobj{"dns": yobj{"safesearch_enabled": false}},
|
||||||
|
want: yobj{
|
||||||
|
"dns": yobj{
|
||||||
|
"safe_search": map[string]any{
|
||||||
|
"enabled": false,
|
||||||
|
"bing": true,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": true,
|
||||||
|
"pixabay": true,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"schema_version": newSchemaVer,
|
||||||
|
},
|
||||||
|
name: "disabled",
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
err := upgradeSchema17to18(tc.in)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.want, tc.in)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpgradeSchema18to19(t *testing.T) {
|
||||||
|
const newSchemaVer = 19
|
||||||
|
|
||||||
|
defaultWantObj := yobj{
|
||||||
|
"clients": yobj{
|
||||||
|
"persistent": []yobj{{
|
||||||
|
"name": "localhost",
|
||||||
|
"safe_search": yobj{
|
||||||
|
"enabled": true,
|
||||||
|
"bing": true,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": true,
|
||||||
|
"pixabay": true,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": true,
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
"schema_version": newSchemaVer,
|
||||||
|
}
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
in yobj
|
||||||
|
want yobj
|
||||||
|
name string
|
||||||
|
}{{
|
||||||
|
in: yobj{
|
||||||
|
"clients": yobj{},
|
||||||
|
},
|
||||||
|
want: yobj{
|
||||||
|
"clients": yobj{},
|
||||||
|
"schema_version": newSchemaVer,
|
||||||
|
},
|
||||||
|
name: "no_clients",
|
||||||
|
}, {
|
||||||
|
in: yobj{
|
||||||
|
"clients": yobj{
|
||||||
|
"persistent": []yobj{{"name": "localhost"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: defaultWantObj,
|
||||||
|
name: "default_values",
|
||||||
|
}, {
|
||||||
|
in: yobj{
|
||||||
|
"clients": yobj{
|
||||||
|
"persistent": []yobj{{"name": "localhost", "safesearch_enabled": true}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: defaultWantObj,
|
||||||
|
name: "enabled",
|
||||||
|
}, {
|
||||||
|
in: yobj{
|
||||||
|
"clients": yobj{
|
||||||
|
"persistent": []yobj{{"name": "localhost", "safesearch_enabled": false}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: yobj{
|
||||||
|
"clients": yobj{"persistent": []yobj{{
|
||||||
|
"name": "localhost",
|
||||||
|
"safe_search": yobj{
|
||||||
|
"enabled": false,
|
||||||
|
"bing": true,
|
||||||
|
"duckduckgo": true,
|
||||||
|
"google": true,
|
||||||
|
"pixabay": true,
|
||||||
|
"yandex": true,
|
||||||
|
"youtube": true,
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
"schema_version": newSchemaVer,
|
||||||
|
},
|
||||||
|
name: "disabled",
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
err := upgradeSchema18to19(tc.in)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, tc.want, tc.in)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpgradeSchema19to20(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
ivl any
|
ivl any
|
||||||
want any
|
want any
|
||||||
|
@ -835,7 +978,7 @@ func TestUpgradeSchema17to18(t *testing.T) {
|
||||||
"schema_version": 17,
|
"schema_version": 17,
|
||||||
}
|
}
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
err := upgradeSchema17to18(conf)
|
err := upgradeSchema19to20(conf)
|
||||||
|
|
||||||
if tc.wantErr != "" {
|
if tc.wantErr != "" {
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
|
@ -846,7 +989,7 @@ func TestUpgradeSchema17to18(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, conf["schema_version"], 18)
|
require.Equal(t, conf["schema_version"], 20)
|
||||||
|
|
||||||
statsVal, ok := conf["statistics"]
|
statsVal, ok := conf["statistics"]
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
|
@ -864,13 +1007,13 @@ func TestUpgradeSchema17to18(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("no_stats", func(t *testing.T) {
|
t.Run("no_stats", func(t *testing.T) {
|
||||||
err := upgradeSchema17to18(yobj{})
|
err := upgradeSchema19to20(yobj{})
|
||||||
|
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("bad_stats", func(t *testing.T) {
|
t.Run("bad_stats", func(t *testing.T) {
|
||||||
err := upgradeSchema17to18(yobj{
|
err := upgradeSchema19to20(yobj{
|
||||||
"statistics": 0,
|
"statistics": 0,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -882,7 +1025,7 @@ func TestUpgradeSchema17to18(t *testing.T) {
|
||||||
"statistics": yobj{},
|
"statistics": yobj{},
|
||||||
}
|
}
|
||||||
|
|
||||||
err := upgradeSchema17to18(conf)
|
err := upgradeSchema19to20(conf)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
statsVal, ok := conf["statistics"]
|
statsVal, ok := conf["statistics"]
|
||||||
|
|
|
@ -35,7 +35,7 @@ set -f -u
|
||||||
go_version="$( "${GO:-go}" version )"
|
go_version="$( "${GO:-go}" version )"
|
||||||
readonly go_version
|
readonly go_version
|
||||||
|
|
||||||
go_min_version='go1.19.6'
|
go_min_version='go1.19.7'
|
||||||
go_version_msg="
|
go_version_msg="
|
||||||
warning: your go version (${go_version}) is different from the recommended minimal one (${go_min_version}).
|
warning: your go version (${go_version}) is different from the recommended minimal one (${go_min_version}).
|
||||||
if you have the version installed, please set the GO environment variable.
|
if you have the version installed, please set the GO environment variable.
|
||||||
|
|
Loading…
Reference in New Issue