all: sync with master
This commit is contained in:
parent
2aaf8ab3c1
commit
6affa96490
|
@ -95,7 +95,7 @@
|
||||||
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
|
'key': "${{ runner.os }}-node-${{ hashFiles('client/package-lock.json') }}"
|
||||||
'restore-keys': '${{ runner.os }}-node-'
|
'restore-keys': '${{ runner.os }}-node-'
|
||||||
- 'name': 'Set up Snapcraft'
|
- 'name': 'Set up Snapcraft'
|
||||||
'run': 'sudo apt-get -yq --no-install-suggests --no-install-recommends install snapcraft'
|
'run': 'sudo snap install snapcraft --classic'
|
||||||
- 'name': 'Set up QEMU'
|
- 'name': 'Set up QEMU'
|
||||||
'uses': 'docker/setup-qemu-action@v1'
|
'uses': 'docker/setup-qemu-action@v1'
|
||||||
- 'name': 'Set up Docker Buildx'
|
- 'name': 'Set up Docker Buildx'
|
||||||
|
|
44
CHANGELOG.md
44
CHANGELOG.md
|
@ -16,13 +16,11 @@ TODO(a.garipov): Use the common markdown formatting tools.
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
## [v0.108.0] - TBA
|
## [v0.107.55] - 2024-11-09 (APPROX.)
|
||||||
|
|
||||||
## [v0.107.54] - 2024-10-03 (APPROX.)
|
See also the [v0.107.55 GitHub milestone][ms-v0.107.55].
|
||||||
|
|
||||||
See also the [v0.107.54 GitHub milestone][ms-v0.107.54].
|
[ms-v0.107.55]: https://github.com/AdguardTeam/AdGuardHome/milestone/90?closed=1
|
||||||
|
|
||||||
[ms-v0.107.54]: https://github.com/AdguardTeam/AdGuardHome/milestone/89?closed=1
|
|
||||||
|
|
||||||
NOTE: Add new changes BELOW THIS COMMENT.
|
NOTE: Add new changes BELOW THIS COMMENT.
|
||||||
-->
|
-->
|
||||||
|
@ -33,6 +31,35 @@ NOTE: Add new changes ABOVE THIS COMMENT.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## [v0.107.54] - 2024-10-30
|
||||||
|
|
||||||
|
See also the [v0.107.54 GitHub milestone][ms-v0.107.54].
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- Incorrect handling of sensitive files permissions on Windows ([#7314]).
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved filtering performance ([#6818]).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Repetitive statistics log messages ([#7338]).
|
||||||
|
- Custom client cache ([#7250]).
|
||||||
|
- Missing runtime clients with information from the system hosts file on first
|
||||||
|
AdGuard Home start ([#7315]).
|
||||||
|
|
||||||
|
[#6818]: https://github.com/AdguardTeam/AdGuardHome/issues/6818
|
||||||
|
[#7250]: https://github.com/AdguardTeam/AdGuardHome/issues/7250
|
||||||
|
[#7314]: https://github.com/AdguardTeam/AdGuardHome/issues/7314
|
||||||
|
[#7315]: https://github.com/AdguardTeam/AdGuardHome/issues/7315
|
||||||
|
[#7338]: https://github.com/AdguardTeam/AdGuardHome/issues/7338
|
||||||
|
|
||||||
|
[ms-v0.107.54]: https://github.com/AdguardTeam/AdGuardHome/milestone/89?closed=1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## [v0.107.53] - 2024-10-03
|
## [v0.107.53] - 2024-10-03
|
||||||
|
|
||||||
See also the [v0.107.53 GitHub milestone][ms-v0.107.53].
|
See also the [v0.107.53 GitHub milestone][ms-v0.107.53].
|
||||||
|
@ -3144,11 +3171,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2].
|
||||||
|
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.54...HEAD
|
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.55...HEAD
|
||||||
[v0.107.54]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.53...v0.107.54
|
[v0.107.55]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.54...v0.107.55
|
||||||
-->
|
-->
|
||||||
|
|
||||||
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.53...HEAD
|
[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.54...HEAD
|
||||||
|
[v0.107.54]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.53...v0.107.54
|
||||||
[v0.107.53]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.52...v0.107.53
|
[v0.107.53]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.52...v0.107.53
|
||||||
[v0.107.52]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.51...v0.107.52
|
[v0.107.52]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.51...v0.107.52
|
||||||
[v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...v0.107.51
|
[v0.107.51]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.50...v0.107.51
|
||||||
|
|
|
@ -641,7 +641,7 @@
|
||||||
"show_processed_responses": "Verarbeitet",
|
"show_processed_responses": "Verarbeitet",
|
||||||
"blocked_safebrowsing": "Gesperrt durch Internetsicherheit",
|
"blocked_safebrowsing": "Gesperrt durch Internetsicherheit",
|
||||||
"blocked_adult_websites": "Gesperrt durch Kindersicherung",
|
"blocked_adult_websites": "Gesperrt durch Kindersicherung",
|
||||||
"blocked_threats": "Bedrohungen blockiert",
|
"blocked_threats": "Gesperrte Bedrohungen",
|
||||||
"allowed": "Zugelassen",
|
"allowed": "Zugelassen",
|
||||||
"filtered": "Gefiltert",
|
"filtered": "Gefiltert",
|
||||||
"rewritten": "Umgeschrieben",
|
"rewritten": "Umgeschrieben",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"timeUpdated": "2024-09-30T10:04:46.112Z",
|
"timeUpdated": "2024-10-28T10:04:59.054Z",
|
||||||
"categories": {
|
"categories": {
|
||||||
"0": "audio_video_player",
|
"0": "audio_video_player",
|
||||||
"1": "comments",
|
"1": "comments",
|
||||||
|
@ -21872,6 +21872,7 @@
|
||||||
"fastly-insights.com": "fastly_insights",
|
"fastly-insights.com": "fastly_insights",
|
||||||
"fastly.net": "fastlylb.net",
|
"fastly.net": "fastlylb.net",
|
||||||
"fastlylb.net": "fastlylb.net",
|
"fastlylb.net": "fastlylb.net",
|
||||||
|
"fastly-edge.com": "fastlylb.net",
|
||||||
"fastly-masque.net": "fastlylb.net",
|
"fastly-masque.net": "fastlylb.net",
|
||||||
"fastpic.ru": "fastpic.ru",
|
"fastpic.ru": "fastpic.ru",
|
||||||
"fmpub.net": "federated_media",
|
"fmpub.net": "federated_media",
|
||||||
|
|
40
go.mod
40
go.mod
|
@ -3,9 +3,10 @@ module github.com/AdguardTeam/AdGuardHome
|
||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/AdguardTeam/dnsproxy v0.73.2
|
// TODO(a.garipov): Update when v0.73.3 is released.
|
||||||
github.com/AdguardTeam/golibs v0.27.0
|
github.com/AdguardTeam/dnsproxy v0.73.3-0.20241004151328-c7c7b977a2a3
|
||||||
github.com/AdguardTeam/urlfilter v0.19.0
|
github.com/AdguardTeam/golibs v0.30.0
|
||||||
|
github.com/AdguardTeam/urlfilter v0.20.0
|
||||||
github.com/NYTimes/gziphandler v1.1.1
|
github.com/NYTimes/gziphandler v1.1.1
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.3.0
|
github.com/ameshkov/dnscrypt/v2 v2.3.0
|
||||||
github.com/bluele/gcache v0.0.2
|
github.com/bluele/gcache v0.0.2
|
||||||
|
@ -18,7 +19,7 @@ require (
|
||||||
github.com/google/gopacket v1.1.19
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/google/renameio/v2 v2.0.0
|
github.com/google/renameio/v2 v2.0.0
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49
|
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475
|
||||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
|
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86
|
||||||
github.com/kardianos/service v1.2.2
|
github.com/kardianos/service v1.2.2
|
||||||
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
|
github.com/mdlayher/ethernet v0.0.0-20220221185849-529eae5b6118
|
||||||
|
@ -27,15 +28,15 @@ require (
|
||||||
// TODO(a.garipov): This package is deprecated; find a new one or use our
|
// TODO(a.garipov): This package is deprecated; find a new one or use our
|
||||||
// own code for that. Perhaps, use gopacket.
|
// own code for that. Perhaps, use gopacket.
|
||||||
github.com/mdlayher/raw v0.1.0
|
github.com/mdlayher/raw v0.1.0
|
||||||
github.com/miekg/dns v1.1.61
|
github.com/miekg/dns v1.1.62
|
||||||
github.com/quic-go/quic-go v0.47.0
|
github.com/quic-go/quic-go v0.48.1
|
||||||
github.com/stretchr/testify v1.9.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/ti-mo/netfilter v0.5.2
|
github.com/ti-mo/netfilter v0.5.2
|
||||||
go.etcd.io/bbolt v1.3.10
|
go.etcd.io/bbolt v1.3.11
|
||||||
golang.org/x/crypto v0.26.0
|
golang.org/x/crypto v0.28.0
|
||||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
|
||||||
golang.org/x/net v0.28.0
|
golang.org/x/net v0.30.0
|
||||||
golang.org/x/sys v0.24.0
|
golang.org/x/sys v0.26.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.1
|
howett.net/plist v1.0.1
|
||||||
|
@ -48,22 +49,19 @@ require (
|
||||||
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
|
github.com/beefsack/go-rate v0.0.0-20220214233405-116f4ca011a0 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
||||||
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 // indirect
|
github.com/google/pprof v0.0.0-20241029010322-833c56d90c8e // indirect
|
||||||
github.com/mdlayher/socket v0.5.1 // indirect
|
github.com/mdlayher/socket v0.5.1 // indirect
|
||||||
github.com/onsi/ginkgo/v2 v2.17.3 // indirect
|
github.com/onsi/ginkgo/v2 v2.20.2 // indirect
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
github.com/quic-go/qpack v0.5.1 // indirect
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 // indirect
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
go.uber.org/mock v0.5.0 // indirect
|
||||||
golang.org/x/mod v0.20.0 // indirect
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/text v0.17.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
golang.org/x/tools v0.24.0 // indirect
|
golang.org/x/tools v0.26.0 // indirect
|
||||||
gonum.org/v1/gonum v0.15.0 // indirect
|
gonum.org/v1/gonum v0.15.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(a.garipov): Remove once https://github.com/quic-go/quic-go/pull/4685 is merged.
|
|
||||||
replace github.com/quic-go/quic-go => github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd
|
|
||||||
|
|
84
go.sum
84
go.sum
|
@ -1,17 +1,15 @@
|
||||||
github.com/AdguardTeam/dnsproxy v0.73.2 h1:O6wRXzHsnWL5TkhYcuLWCShVFF0X5RFI6qUmq1ZFVsQ=
|
github.com/AdguardTeam/dnsproxy v0.73.3-0.20241004151328-c7c7b977a2a3 h1:IGXwBjdKDzUm007QzZyxSllMnkbdXe7K79x7JWcBW/E=
|
||||||
github.com/AdguardTeam/dnsproxy v0.73.2/go.mod h1:zD5WfTctbRvYYk8PS39h6/OT84NTu6QxKbAiBN5PUcI=
|
github.com/AdguardTeam/dnsproxy v0.73.3-0.20241004151328-c7c7b977a2a3/go.mod h1:356iHROxo+SOdBVifp1MXEh6qHyydtzGCcsQMfx+ZVs=
|
||||||
github.com/AdguardTeam/golibs v0.27.0 h1:YxCFK6HBGp/ZXp3bv5uei+oLH12UfIYB8u2rh1B6nnU=
|
github.com/AdguardTeam/golibs v0.30.0 h1:3pTdW1B9GZgqARrA5BvmYlAaEG1zAHI/ReikCDxrhiE=
|
||||||
github.com/AdguardTeam/golibs v0.27.0/go.mod h1:iWdjXPCwmK2g2FKIb/OwEPnovSXeMqRhI8FWLxF5oxE=
|
github.com/AdguardTeam/golibs v0.30.0/go.mod h1:vjw1OVZG6BYyoqGRY88U4LCJLOMfhBFhU0UJBdaSAuQ=
|
||||||
github.com/AdguardTeam/urlfilter v0.19.0 h1:q7eH13+yNETlpD/VD3u5rLQOripcUdEktqZFy+KiQLk=
|
github.com/AdguardTeam/urlfilter v0.20.0 h1:X32qiuVCVd8WDYCEsbdZKfXMzwdVqrdulamtUi4rmzs=
|
||||||
github.com/AdguardTeam/urlfilter v0.19.0/go.mod h1:+N54ZvxqXYLnXuvpaUhK2exDQW+djZBRSb6F6j0rkBY=
|
github.com/AdguardTeam/urlfilter v0.20.0/go.mod h1:gjrywLTxfJh6JOkwi9SU+frhP7kVVEZ5exFGkR99qpk=
|
||||||
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
|
||||||
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
|
||||||
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
|
||||||
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/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd h1:mw4LqrCiv3vcKuCxBRg7kA17xfHKM+9hZgFWmyhe/AY=
|
|
||||||
github.com/ainar-g/quic-go v0.0.0-20240930125330-446bd86056fd/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
|
github.com/ameshkov/dnscrypt/v2 v2.3.0 h1:pDXDF7eFa6Lw+04C0hoMh8kCAQM8NwUdFEllSP2zNLs=
|
||||||
github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE=
|
github.com/ameshkov/dnscrypt/v2 v2.3.0/go.mod h1:N5hDwgx2cNb4Ay7AhvOSKst+eUiOZ/vbKRO9qMpQttE=
|
||||||
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
github.com/ameshkov/dnsstamps v1.0.3 h1:Srzik+J9mivH1alRACTbys2xOxs0lRH9qnTA7Y1OYVo=
|
||||||
|
@ -31,8 +29,8 @@ github.com/dimfeld/httptreemux/v5 v5.5.0 h1:p8jkiMrCuZ0CmhwYLcbNbl7DDo21fozhKHQ2
|
||||||
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
|
github.com/dimfeld/httptreemux/v5 v5.5.0/go.mod h1:QeEylH57C0v3VO0tkKraVz9oD3Uu93CKPnTLbsidvSw=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||||
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
|
github.com/go-ping/ping v1.1.0 h1:3MCGhVX4fyEUuhsfwPrsEdQw6xspHkv5zHsiSoDFZYw=
|
||||||
|
@ -46,8 +44,8 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
|
||||||
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
|
||||||
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30 h1:r6YdmbD41tGHeCWDyHF691LWtL7D1iSTyJaKejTWwVU=
|
github.com/google/pprof v0.0.0-20241029010322-833c56d90c8e h1:v7R0PZoC2p1KWQmv1+GqCXQe59Ab1TkDF8Y9Lg2W6m4=
|
||||||
github.com/google/pprof v0.0.0-20240521024322-9665fa269a30/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
github.com/google/pprof v0.0.0-20241029010322-833c56d90c8e/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||||
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
|
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
|
||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
@ -55,8 +53,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8CrSJVmaolDVOxTfS9kc36uB6H40kdbQq8=
|
||||||
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49 h1:/OuvSMGT9+xnyZ+7MZQ1zdngaCCAdPoSw8B/uurZ7pg=
|
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475 h1:hxST5pwMBEOWmxpkX20w9oZG+hXdhKmAIPQ3NGGAxas=
|
||||||
github.com/insomniacslk/dhcp v0.0.0-20240419123447-f1cffa2c0c49/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
|
github.com/insomniacslk/dhcp v0.0.0-20240829085014-a3a4c1f04475/go.mod h1:KclMyHxX06VrVr0DJmeFSUb1ankt7xTfoOA35pCkoic=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
|
||||||
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
github.com/josharian/native v1.1.1-0.20230202152459-5c7d0dd6ab86 h1:elKwZS1OcdQ0WwEDBeqxKwb7WB62QX8bvZ/FJnVXIfk=
|
||||||
|
@ -80,14 +78,14 @@ github.com/mdlayher/raw v0.1.0/go.mod h1:yXnxvs6c0XoF/aK52/H5PjsVHmWBCFfZUfoh/Y5
|
||||||
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
|
github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E=
|
||||||
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
|
||||||
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
|
||||||
github.com/miekg/dns v1.1.61 h1:nLxbwF3XxhwVSm8g9Dghm9MHPaUZuqhPiGL+675ZmEs=
|
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
|
||||||
github.com/miekg/dns v1.1.61/go.mod h1:mnAarhS3nWaW+NVP2wTkYVIZyHNJ098SJZUki3eykwQ=
|
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.3 h1:oJcvKpIb7/8uLpDDtnQuf18xVnwKp8DTD7DQ6gTd/MU=
|
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||||
github.com/onsi/ginkgo/v2 v2.17.3/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc=
|
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||||
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
|
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||||
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
|
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
|
||||||
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
|
||||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||||
|
@ -101,6 +99,8 @@ github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF
|
||||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
||||||
|
github.com/quic-go/quic-go v0.48.1 h1:y/8xmfWI9qmGTc+lBr4jKRUWLGSlSigv847ULJ4hYXA=
|
||||||
|
github.com/quic-go/quic-go v0.48.1/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
||||||
|
@ -122,28 +122,28 @@ github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701 h1:pyC9PaHYZFgEKFdlp3G8
|
||||||
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
github.com/u-root/uio v0.0.0-20240224005618-d2acac8f3701/go.mod h1:P3a5rG4X7tI17Nn3aOIAYr5HbIMukwXG0urG0WuL8OA=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||||
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0=
|
||||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I=
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU=
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||||
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||||
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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
|
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||||
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
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.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||||
|
@ -158,25 +158,25 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/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-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.4.1-0.20230131160137-e7d7f63158de/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
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-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
|
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||||
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
|
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
gonum.org/v1/gonum v0.15.0 h1:2lYxjRbTYyxkJxlhC+LvJIx3SsANPdRybu1tGj9/OrQ=
|
gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
|
||||||
gonum.org/v1/gonum v0.15.0/go.mod h1:xzZVBJBtS+Mz4q0Yl2LJTk+OxOg4jiXZ7qBoM0uISGo=
|
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
|
||||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
|
||||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package aghos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO(e.burkov): Add platform-independent tests.
|
||||||
|
|
||||||
|
// Chmod is an extension for [os.Chmod] that properly handles Windows access
|
||||||
|
// rights.
|
||||||
|
func Chmod(name string, perm fs.FileMode) (err error) {
|
||||||
|
return chmod(name, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mkdir is an extension for [os.Mkdir] that properly handles Windows access
|
||||||
|
// rights.
|
||||||
|
func Mkdir(name string, perm fs.FileMode) (err error) {
|
||||||
|
return mkdir(name, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MkdirAll is an extension for [os.MkdirAll] that properly handles Windows
|
||||||
|
// access rights.
|
||||||
|
func MkdirAll(path string, perm fs.FileMode) (err error) {
|
||||||
|
return mkdirAll(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFile is an extension for [os.WriteFile] that properly handles Windows
|
||||||
|
// access rights.
|
||||||
|
func WriteFile(filename string, data []byte, perm fs.FileMode) (err error) {
|
||||||
|
return writeFile(filename, data, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpenFile is an extension for [os.OpenFile] that properly handles Windows
|
||||||
|
// access rights.
|
||||||
|
func OpenFile(name string, flag int, perm fs.FileMode) (file *os.File, err error) {
|
||||||
|
return openFile(name, flag, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stat is an extension for [os.Stat] that properly handles Windows access
|
||||||
|
// rights.
|
||||||
|
//
|
||||||
|
// Note that on Windows the "other" permission bits combines the access rights
|
||||||
|
// of any trustee that is neither the owner nor the owning group for the file.
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Inspect the behavior for the World (everyone) well-known
|
||||||
|
// SID and, perhaps, use it.
|
||||||
|
func Stat(name string) (fi fs.FileInfo, err error) {
|
||||||
|
return stat(name)
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
//go:build unix
|
||||||
|
|
||||||
|
package aghos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/google/renameio/v2/maybe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// chmod is a Unix implementation of [Chmod].
|
||||||
|
func chmod(name string, perm fs.FileMode) (err error) {
|
||||||
|
return os.Chmod(name, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mkdir is a Unix implementation of [Mkdir].
|
||||||
|
func mkdir(name string, perm fs.FileMode) (err error) {
|
||||||
|
return os.Mkdir(name, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mkdirAll is a Unix implementation of [MkdirAll].
|
||||||
|
func mkdirAll(path string, perm fs.FileMode) (err error) {
|
||||||
|
return os.MkdirAll(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeFile is a Unix implementation of [WriteFile].
|
||||||
|
func writeFile(filename string, data []byte, perm fs.FileMode) (err error) {
|
||||||
|
return maybe.WriteFile(filename, data, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// openFile is a Unix implementation of [OpenFile].
|
||||||
|
func openFile(name string, flag int, perm fs.FileMode) (file *os.File, err error) {
|
||||||
|
// #nosec G304 -- This function simply wraps the [os.OpenFile] function, so
|
||||||
|
// the security concerns should be addressed to the [OpenFile] calls.
|
||||||
|
return os.OpenFile(name, flag, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// stat is a Unix implementation of [Stat].
|
||||||
|
func stat(name string) (fi os.FileInfo, err error) {
|
||||||
|
return os.Stat(name)
|
||||||
|
}
|
|
@ -0,0 +1,392 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package aghos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
// fileInfo is a Windows implementation of [fs.FileInfo], that contains the
|
||||||
|
// filemode converted from the security descriptor.
|
||||||
|
type fileInfo struct {
|
||||||
|
// fs.FileInfo is embedded to provide the default implementations and data
|
||||||
|
// successfully retrieved by [os.Stat].
|
||||||
|
fs.FileInfo
|
||||||
|
|
||||||
|
// mode is the file mode converted from the security descriptor.
|
||||||
|
mode fs.FileMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// type check
|
||||||
|
var _ fs.FileInfo = (*fileInfo)(nil)
|
||||||
|
|
||||||
|
// Mode implements [fs.FileInfo.Mode] for [*fileInfo].
|
||||||
|
func (fi *fileInfo) Mode() (mode fs.FileMode) { return fi.mode }
|
||||||
|
|
||||||
|
// stat is a Windows implementation of [Stat].
|
||||||
|
func stat(name string) (fi os.FileInfo, err error) {
|
||||||
|
absName, err := filepath.Abs(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("computing absolute path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fi, err = os.Stat(absName)
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error, since it's informative enough as is.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
dacl, owner, group, err := retrieveDACL(absName)
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error, since it's informative enough as is.
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ownerMask, groupMask, otherMask windows.ACCESS_MASK
|
||||||
|
for i := range uint32(dacl.AceCount) {
|
||||||
|
var ace *windows.ACCESS_ALLOWED_ACE
|
||||||
|
err = windows.GetAce(dacl, i, &ace)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("getting access control entry at index %d: %w", i, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entrySid := (*windows.SID)(unsafe.Pointer(&ace.SidStart))
|
||||||
|
switch {
|
||||||
|
case entrySid.Equals(owner):
|
||||||
|
ownerMask |= ace.Mask
|
||||||
|
case entrySid.Equals(group):
|
||||||
|
groupMask |= ace.Mask
|
||||||
|
default:
|
||||||
|
otherMask |= ace.Mask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mode := fi.Mode()
|
||||||
|
perm := masksToPerm(ownerMask, groupMask, otherMask, mode.IsDir())
|
||||||
|
|
||||||
|
return &fileInfo{
|
||||||
|
FileInfo: fi,
|
||||||
|
// Use the file mode from the security descriptor, but use the
|
||||||
|
// calculated permission bits.
|
||||||
|
mode: perm | mode&^fs.FileMode(0o777),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// retrieveDACL retrieves the discretionary access control list, owner, and
|
||||||
|
// group from the security descriptor of the file with the specified absolute
|
||||||
|
// name.
|
||||||
|
func retrieveDACL(absName string) (dacl *windows.ACL, owner, group *windows.SID, err error) {
|
||||||
|
// desiredSecInfo defines the parts of a security descriptor to retrieve.
|
||||||
|
const desiredSecInfo windows.SECURITY_INFORMATION = windows.OWNER_SECURITY_INFORMATION |
|
||||||
|
windows.GROUP_SECURITY_INFORMATION |
|
||||||
|
windows.DACL_SECURITY_INFORMATION |
|
||||||
|
windows.PROTECTED_DACL_SECURITY_INFORMATION |
|
||||||
|
windows.UNPROTECTED_DACL_SECURITY_INFORMATION
|
||||||
|
|
||||||
|
sd, err := windows.GetNamedSecurityInfo(absName, windows.SE_FILE_OBJECT, desiredSecInfo)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("getting security descriptor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dacl, _, err = sd.DACL()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("getting discretionary access control list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
owner, _, err = sd.Owner()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("getting owner sid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
group, _, err = sd.Group()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, fmt.Errorf("getting group sid: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return dacl, owner, group, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// chmod is a Windows implementation of [Chmod].
|
||||||
|
func chmod(name string, perm fs.FileMode) (err error) {
|
||||||
|
fi, err := os.Stat(name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("getting file info: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := make([]windows.EXPLICIT_ACCESS, 0, 3)
|
||||||
|
creatorMask, groupMask, worldMask := permToMasks(perm, fi.IsDir())
|
||||||
|
|
||||||
|
sidMasks := []struct {
|
||||||
|
Key windows.WELL_KNOWN_SID_TYPE
|
||||||
|
Value windows.ACCESS_MASK
|
||||||
|
}{{
|
||||||
|
Key: windows.WinCreatorOwnerSid,
|
||||||
|
Value: creatorMask,
|
||||||
|
}, {
|
||||||
|
Key: windows.WinCreatorGroupSid,
|
||||||
|
Value: groupMask,
|
||||||
|
}, {
|
||||||
|
Key: windows.WinWorldSid,
|
||||||
|
Value: worldMask,
|
||||||
|
}}
|
||||||
|
|
||||||
|
var errs []error
|
||||||
|
for _, sidMask := range sidMasks {
|
||||||
|
if sidMask.Value == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var trustee windows.TRUSTEE
|
||||||
|
trustee, err = newWellKnownTrustee(sidMask.Key)
|
||||||
|
if err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = append(entries, windows.EXPLICIT_ACCESS{
|
||||||
|
AccessPermissions: sidMask.Value,
|
||||||
|
AccessMode: windows.GRANT_ACCESS,
|
||||||
|
Inheritance: windows.NO_INHERITANCE,
|
||||||
|
Trustee: trustee,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = errors.Join(errs...); err != nil {
|
||||||
|
return fmt.Errorf("creating access control entries: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
acl, err := windows.ACLFromEntries(entries, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating access control list: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// secInfo defines the parts of a security descriptor to set.
|
||||||
|
const secInfo windows.SECURITY_INFORMATION = windows.DACL_SECURITY_INFORMATION |
|
||||||
|
windows.PROTECTED_DACL_SECURITY_INFORMATION
|
||||||
|
|
||||||
|
err = windows.SetNamedSecurityInfo(name, windows.SE_FILE_OBJECT, secInfo, nil, nil, acl, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("setting security descriptor: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// mkdir is a Windows implementation of [Mkdir].
|
||||||
|
//
|
||||||
|
// TODO(e.burkov): Consider using [windows.CreateDirectory] instead of
|
||||||
|
// [os.Mkdir] to reduce the number of syscalls.
|
||||||
|
func mkdir(name string, perm os.FileMode) (err error) {
|
||||||
|
name, err = filepath.Abs(name)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("computing absolute path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Mkdir(name, perm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if err != nil {
|
||||||
|
err = errors.WithDeferred(err, os.Remove(name))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return chmod(name, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mkdirAll is a Windows implementation of [MkdirAll].
|
||||||
|
func mkdirAll(path string, perm os.FileMode) (err error) {
|
||||||
|
parent, _ := filepath.Split(path)
|
||||||
|
|
||||||
|
if parent != "" {
|
||||||
|
err = os.MkdirAll(parent, perm)
|
||||||
|
if err != nil && !errors.Is(err, os.ErrExist) {
|
||||||
|
return fmt.Errorf("creating parent directories: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = mkdir(path, perm)
|
||||||
|
if errors.Is(err, os.ErrExist) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeFile is a Windows implementation of [WriteFile].
|
||||||
|
func writeFile(filename string, data []byte, perm os.FileMode) (err error) {
|
||||||
|
file, err := openFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, perm)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("opening file: %w", err)
|
||||||
|
}
|
||||||
|
defer func() { err = errors.WithDeferred(err, file.Close()) }()
|
||||||
|
|
||||||
|
_, err = file.Write(data)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("writing data: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// openFile is a Windows implementation of [OpenFile].
|
||||||
|
func openFile(name string, flag int, perm os.FileMode) (file *os.File, err error) {
|
||||||
|
// Only change permissions if the file not yet exists, but should be
|
||||||
|
// created.
|
||||||
|
if flag&os.O_CREATE == 0 {
|
||||||
|
return os.OpenFile(name, flag, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = stat(name)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
defer func() { err = errors.WithDeferred(err, chmod(name, perm)) }()
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("getting file info: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.OpenFile(name, flag, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
// newWellKnownTrustee returns a trustee for a well-known SID.
|
||||||
|
func newWellKnownTrustee(stype windows.WELL_KNOWN_SID_TYPE) (t windows.TRUSTEE, err error) {
|
||||||
|
sid, err := windows.CreateWellKnownSid(stype)
|
||||||
|
if err != nil {
|
||||||
|
return windows.TRUSTEE{}, fmt.Errorf("creating sid for type %d: %w", stype, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return windows.TRUSTEE{
|
||||||
|
TrusteeForm: windows.TRUSTEE_IS_SID,
|
||||||
|
TrusteeValue: windows.TrusteeValueFromSID(sid),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UNIX file mode permission bits.
|
||||||
|
const (
|
||||||
|
permRead = 0b100
|
||||||
|
permWrite = 0b010
|
||||||
|
permExecute = 0b001
|
||||||
|
)
|
||||||
|
|
||||||
|
// Windows access masks for appropriate UNIX file mode permission bits and
|
||||||
|
// file types.
|
||||||
|
const (
|
||||||
|
fileReadRights windows.ACCESS_MASK = windows.READ_CONTROL |
|
||||||
|
windows.FILE_READ_DATA |
|
||||||
|
windows.FILE_READ_ATTRIBUTES |
|
||||||
|
windows.FILE_READ_EA |
|
||||||
|
windows.SYNCHRONIZE |
|
||||||
|
windows.ACCESS_SYSTEM_SECURITY
|
||||||
|
|
||||||
|
fileWriteRights windows.ACCESS_MASK = windows.WRITE_DAC |
|
||||||
|
windows.WRITE_OWNER |
|
||||||
|
windows.FILE_WRITE_DATA |
|
||||||
|
windows.FILE_WRITE_ATTRIBUTES |
|
||||||
|
windows.FILE_WRITE_EA |
|
||||||
|
windows.DELETE |
|
||||||
|
windows.FILE_APPEND_DATA |
|
||||||
|
windows.SYNCHRONIZE |
|
||||||
|
windows.ACCESS_SYSTEM_SECURITY
|
||||||
|
|
||||||
|
fileExecuteRights windows.ACCESS_MASK = windows.FILE_EXECUTE
|
||||||
|
|
||||||
|
dirReadRights windows.ACCESS_MASK = windows.READ_CONTROL |
|
||||||
|
windows.FILE_LIST_DIRECTORY |
|
||||||
|
windows.FILE_READ_EA |
|
||||||
|
windows.FILE_READ_ATTRIBUTES<<1 |
|
||||||
|
windows.SYNCHRONIZE |
|
||||||
|
windows.ACCESS_SYSTEM_SECURITY
|
||||||
|
|
||||||
|
dirWriteRights windows.ACCESS_MASK = windows.WRITE_DAC |
|
||||||
|
windows.WRITE_OWNER |
|
||||||
|
windows.DELETE |
|
||||||
|
windows.FILE_WRITE_DATA |
|
||||||
|
windows.FILE_APPEND_DATA |
|
||||||
|
windows.FILE_WRITE_EA |
|
||||||
|
windows.FILE_WRITE_ATTRIBUTES<<1 |
|
||||||
|
windows.SYNCHRONIZE |
|
||||||
|
windows.ACCESS_SYSTEM_SECURITY
|
||||||
|
|
||||||
|
dirExecuteRights windows.ACCESS_MASK = windows.FILE_TRAVERSE
|
||||||
|
)
|
||||||
|
|
||||||
|
// permToMasks converts a UNIX file mode permissions to the corresponding
|
||||||
|
// Windows access masks. The [isDir] argument is used to set specific access
|
||||||
|
// bits for directories.
|
||||||
|
func permToMasks(fm os.FileMode, isDir bool) (owner, group, world windows.ACCESS_MASK) {
|
||||||
|
mask := fm.Perm()
|
||||||
|
|
||||||
|
owner = permToMask(byte((mask>>6)&0b111), isDir)
|
||||||
|
group = permToMask(byte((mask>>3)&0b111), isDir)
|
||||||
|
world = permToMask(byte(mask&0b111), isDir)
|
||||||
|
|
||||||
|
return owner, group, world
|
||||||
|
}
|
||||||
|
|
||||||
|
// permToMask converts a UNIX file mode permission bits within p byte to the
|
||||||
|
// corresponding Windows access mask. The [isDir] argument is used to set
|
||||||
|
// specific access bits for directories.
|
||||||
|
func permToMask(p byte, isDir bool) (mask windows.ACCESS_MASK) {
|
||||||
|
readRights, writeRights, executeRights := fileReadRights, fileWriteRights, fileExecuteRights
|
||||||
|
if isDir {
|
||||||
|
readRights, writeRights, executeRights = dirReadRights, dirWriteRights, dirExecuteRights
|
||||||
|
}
|
||||||
|
|
||||||
|
if p&permRead != 0 {
|
||||||
|
mask |= readRights
|
||||||
|
}
|
||||||
|
if p&permWrite != 0 {
|
||||||
|
mask |= writeRights
|
||||||
|
}
|
||||||
|
if p&permExecute != 0 {
|
||||||
|
mask |= executeRights
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask
|
||||||
|
}
|
||||||
|
|
||||||
|
// masksToPerm converts Windows access masks to the corresponding UNIX file
|
||||||
|
// mode permission bits.
|
||||||
|
func masksToPerm(u, g, o windows.ACCESS_MASK, isDir bool) (perm fs.FileMode) {
|
||||||
|
perm |= fs.FileMode(maskToPerm(u, isDir)) << 6
|
||||||
|
perm |= fs.FileMode(maskToPerm(g, isDir)) << 3
|
||||||
|
perm |= fs.FileMode(maskToPerm(o, isDir))
|
||||||
|
|
||||||
|
return perm
|
||||||
|
}
|
||||||
|
|
||||||
|
// maskToPerm converts a Windows access mask to the corresponding UNIX file
|
||||||
|
// mode permission bits.
|
||||||
|
func maskToPerm(mask windows.ACCESS_MASK, isDir bool) (perm byte) {
|
||||||
|
readMask, writeMask, executeMask := fileReadRights, fileWriteRights, fileExecuteRights
|
||||||
|
if isDir {
|
||||||
|
readMask, writeMask, executeMask = dirReadRights, dirWriteRights, dirExecuteRights
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove common bits to avoid false positive detection of unset rights.
|
||||||
|
readMask ^= windows.SYNCHRONIZE | windows.ACCESS_SYSTEM_SECURITY
|
||||||
|
writeMask ^= windows.SYNCHRONIZE | windows.ACCESS_SYSTEM_SECURITY
|
||||||
|
|
||||||
|
if mask&readMask != 0 {
|
||||||
|
perm |= permRead
|
||||||
|
}
|
||||||
|
if mask&writeMask != 0 {
|
||||||
|
perm |= permWrite
|
||||||
|
}
|
||||||
|
if mask&executeMask != 0 {
|
||||||
|
perm |= permExecute
|
||||||
|
}
|
||||||
|
|
||||||
|
return perm
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
//go:build windows
|
||||||
|
|
||||||
|
package aghos
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/fs"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPermToMasks(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
perm fs.FileMode
|
||||||
|
wantUser windows.ACCESS_MASK
|
||||||
|
wantGroup windows.ACCESS_MASK
|
||||||
|
wantOther windows.ACCESS_MASK
|
||||||
|
isDir bool
|
||||||
|
}{{
|
||||||
|
name: "all",
|
||||||
|
perm: 0b111_111_111,
|
||||||
|
wantUser: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||||
|
wantGroup: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||||
|
wantOther: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||||
|
isDir: false,
|
||||||
|
}, {
|
||||||
|
name: "user_write",
|
||||||
|
perm: 0b010_000_000,
|
||||||
|
wantUser: fileWriteRights,
|
||||||
|
wantGroup: 0,
|
||||||
|
wantOther: 0,
|
||||||
|
isDir: false,
|
||||||
|
}, {
|
||||||
|
name: "group_read",
|
||||||
|
perm: 0b000_100_000,
|
||||||
|
wantUser: 0,
|
||||||
|
wantGroup: fileReadRights,
|
||||||
|
wantOther: 0,
|
||||||
|
isDir: false,
|
||||||
|
}, {
|
||||||
|
name: "all_dir",
|
||||||
|
perm: 0b111_111_111,
|
||||||
|
wantUser: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||||
|
wantGroup: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||||
|
wantOther: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||||
|
isDir: true,
|
||||||
|
}, {
|
||||||
|
name: "user_write_dir",
|
||||||
|
perm: 0b010_000_000,
|
||||||
|
wantUser: dirWriteRights,
|
||||||
|
wantGroup: 0,
|
||||||
|
wantOther: 0,
|
||||||
|
isDir: true,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
user, group, other := permToMasks(tc.perm, tc.isDir)
|
||||||
|
assert.Equal(t, tc.wantUser, user)
|
||||||
|
assert.Equal(t, tc.wantGroup, group)
|
||||||
|
assert.Equal(t, tc.wantOther, other)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMasksToPerm(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
user windows.ACCESS_MASK
|
||||||
|
group windows.ACCESS_MASK
|
||||||
|
other windows.ACCESS_MASK
|
||||||
|
wantPerm fs.FileMode
|
||||||
|
isDir bool
|
||||||
|
}{{
|
||||||
|
name: "all",
|
||||||
|
user: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||||
|
group: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||||
|
other: fileReadRights | fileWriteRights | fileExecuteRights,
|
||||||
|
wantPerm: 0b111_111_111,
|
||||||
|
isDir: false,
|
||||||
|
}, {
|
||||||
|
name: "user_write",
|
||||||
|
user: fileWriteRights,
|
||||||
|
group: 0,
|
||||||
|
other: 0,
|
||||||
|
wantPerm: 0b010_000_000,
|
||||||
|
isDir: false,
|
||||||
|
}, {
|
||||||
|
name: "group_read",
|
||||||
|
user: 0,
|
||||||
|
group: fileReadRights,
|
||||||
|
other: 0,
|
||||||
|
wantPerm: 0b000_100_000,
|
||||||
|
isDir: false,
|
||||||
|
}, {
|
||||||
|
name: "no_access",
|
||||||
|
user: 0,
|
||||||
|
group: 0,
|
||||||
|
other: 0,
|
||||||
|
wantPerm: 0,
|
||||||
|
isDir: false,
|
||||||
|
}, {
|
||||||
|
name: "all_dir",
|
||||||
|
user: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||||
|
group: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||||
|
other: dirReadRights | dirWriteRights | dirExecuteRights,
|
||||||
|
wantPerm: 0b111_111_111,
|
||||||
|
isDir: true,
|
||||||
|
}, {
|
||||||
|
name: "user_write_dir",
|
||||||
|
user: dirWriteRights,
|
||||||
|
group: 0,
|
||||||
|
other: 0,
|
||||||
|
wantPerm: 0b010_000_000,
|
||||||
|
isDir: true,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// Don't call [fs.FileMode.Perm] since the result is expected to
|
||||||
|
// contain only the permission bits.
|
||||||
|
assert.Equal(t, tc.wantPerm, masksToPerm(tc.user, tc.group, tc.other, tc.isDir))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,7 +63,7 @@ func newPendingFile(filePath string, mode fs.FileMode) (f PendingFile, err error
|
||||||
return nil, fmt.Errorf("opening pending file: %w", err)
|
return nil, fmt.Errorf("opening pending file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = file.Chmod(mode)
|
err = aghos.Chmod(file.Name(), mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("preparing pending file: %w", err)
|
return nil, fmt.Errorf("preparing pending file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,14 +89,14 @@ func (s *ServiceWithConfig[ConfigType]) Config() (c ConfigType) {
|
||||||
// AddressProcessor is a fake [client.AddressProcessor] implementation for
|
// AddressProcessor is a fake [client.AddressProcessor] implementation for
|
||||||
// tests.
|
// tests.
|
||||||
type AddressProcessor struct {
|
type AddressProcessor struct {
|
||||||
OnProcess func(ip netip.Addr)
|
OnProcess func(ctx context.Context, ip netip.Addr)
|
||||||
OnClose func() (err error)
|
OnClose func() (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process implements the [client.AddressProcessor] interface for
|
// Process implements the [client.AddressProcessor] interface for
|
||||||
// *AddressProcessor.
|
// *AddressProcessor.
|
||||||
func (p *AddressProcessor) Process(ip netip.Addr) {
|
func (p *AddressProcessor) Process(ctx context.Context, ip netip.Addr) {
|
||||||
p.OnProcess(ip)
|
p.OnProcess(ctx, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close implements the [client.AddressProcessor] interface for
|
// Close implements the [client.AddressProcessor] interface for
|
||||||
|
@ -107,13 +107,18 @@ func (p *AddressProcessor) Close() (err error) {
|
||||||
|
|
||||||
// AddressUpdater is a fake [client.AddressUpdater] implementation for tests.
|
// AddressUpdater is a fake [client.AddressUpdater] implementation for tests.
|
||||||
type AddressUpdater struct {
|
type AddressUpdater struct {
|
||||||
OnUpdateAddress func(ip netip.Addr, host string, info *whois.Info)
|
OnUpdateAddress func(ctx context.Context, ip netip.Addr, host string, info *whois.Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateAddress implements the [client.AddressUpdater] interface for
|
// UpdateAddress implements the [client.AddressUpdater] interface for
|
||||||
// *AddressUpdater.
|
// *AddressUpdater.
|
||||||
func (p *AddressUpdater) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
func (p *AddressUpdater) UpdateAddress(
|
||||||
p.OnUpdateAddress(ip, host, info)
|
ctx context.Context,
|
||||||
|
ip netip.Addr,
|
||||||
|
host string,
|
||||||
|
info *whois.Info,
|
||||||
|
) {
|
||||||
|
p.OnUpdateAddress(ctx, ip, host, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Package dnsforward
|
// Package dnsforward
|
||||||
|
|
|
@ -11,7 +11,6 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
"github.com/AdguardTeam/AdGuardHome/internal/rdns"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
|
||||||
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
)
|
)
|
||||||
|
@ -22,7 +21,7 @@ const ErrClosed errors.Error = "use of closed address processor"
|
||||||
|
|
||||||
// AddressProcessor is the interface for types that can process clients.
|
// AddressProcessor is the interface for types that can process clients.
|
||||||
type AddressProcessor interface {
|
type AddressProcessor interface {
|
||||||
Process(ip netip.Addr)
|
Process(ctx context.Context, ip netip.Addr)
|
||||||
Close() (err error)
|
Close() (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,7 +32,7 @@ type EmptyAddrProc struct{}
|
||||||
var _ AddressProcessor = EmptyAddrProc{}
|
var _ AddressProcessor = EmptyAddrProc{}
|
||||||
|
|
||||||
// Process implements the [AddressProcessor] interface for EmptyAddrProc.
|
// Process implements the [AddressProcessor] interface for EmptyAddrProc.
|
||||||
func (EmptyAddrProc) Process(_ netip.Addr) {}
|
func (EmptyAddrProc) Process(_ context.Context, _ netip.Addr) {}
|
||||||
|
|
||||||
// Close implements the [AddressProcessor] interface for EmptyAddrProc.
|
// Close implements the [AddressProcessor] interface for EmptyAddrProc.
|
||||||
func (EmptyAddrProc) Close() (_ error) { return nil }
|
func (EmptyAddrProc) Close() (_ error) { return nil }
|
||||||
|
@ -90,12 +89,15 @@ type DefaultAddrProcConfig struct {
|
||||||
type AddressUpdater interface {
|
type AddressUpdater interface {
|
||||||
// UpdateAddress updates information about an IP address, setting host (if
|
// UpdateAddress updates information about an IP address, setting host (if
|
||||||
// not empty) and WHOIS information (if not nil).
|
// not empty) and WHOIS information (if not nil).
|
||||||
UpdateAddress(ip netip.Addr, host string, info *whois.Info)
|
UpdateAddress(ctx context.Context, ip netip.Addr, host string, info *whois.Info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// DefaultAddrProc processes incoming client addresses with rDNS and WHOIS, if
|
// DefaultAddrProc processes incoming client addresses with rDNS and WHOIS, if
|
||||||
// configured, and updates that information in a client storage.
|
// configured, and updates that information in a client storage.
|
||||||
type DefaultAddrProc struct {
|
type DefaultAddrProc struct {
|
||||||
|
// logger is used to log the operation of address processor.
|
||||||
|
logger *slog.Logger
|
||||||
|
|
||||||
// clientIPsMu serializes closure of clientIPs and access to isClosed.
|
// clientIPsMu serializes closure of clientIPs and access to isClosed.
|
||||||
clientIPsMu *sync.Mutex
|
clientIPsMu *sync.Mutex
|
||||||
|
|
||||||
|
@ -142,6 +144,7 @@ const (
|
||||||
// not be nil.
|
// not be nil.
|
||||||
func NewDefaultAddrProc(c *DefaultAddrProcConfig) (p *DefaultAddrProc) {
|
func NewDefaultAddrProc(c *DefaultAddrProcConfig) (p *DefaultAddrProc) {
|
||||||
p = &DefaultAddrProc{
|
p = &DefaultAddrProc{
|
||||||
|
logger: c.BaseLogger.With(slogutil.KeyPrefix, "addrproc"),
|
||||||
clientIPsMu: &sync.Mutex{},
|
clientIPsMu: &sync.Mutex{},
|
||||||
clientIPs: make(chan netip.Addr, defaultQueueSize),
|
clientIPs: make(chan netip.Addr, defaultQueueSize),
|
||||||
rdns: &rdns.Empty{},
|
rdns: &rdns.Empty{},
|
||||||
|
@ -164,10 +167,13 @@ func NewDefaultAddrProc(c *DefaultAddrProcConfig) (p *DefaultAddrProc) {
|
||||||
p.whois = newWHOIS(c.BaseLogger.With(slogutil.KeyPrefix, "whois"), c.DialContext)
|
p.whois = newWHOIS(c.BaseLogger.With(slogutil.KeyPrefix, "whois"), c.DialContext)
|
||||||
}
|
}
|
||||||
|
|
||||||
go p.process(c.CatchPanics)
|
// TODO(s.chzhen): Pass context.
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
|
go p.process(ctx, c.CatchPanics)
|
||||||
|
|
||||||
for _, ip := range c.InitialAddresses {
|
for _, ip := range c.InitialAddresses {
|
||||||
p.Process(ip)
|
p.Process(ctx, ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
@ -210,7 +216,7 @@ func newWHOIS(logger *slog.Logger, dialFunc aghnet.DialContextFunc) (w whois.Int
|
||||||
var _ AddressProcessor = (*DefaultAddrProc)(nil)
|
var _ AddressProcessor = (*DefaultAddrProc)(nil)
|
||||||
|
|
||||||
// Process implements the [AddressProcessor] interface for *DefaultAddrProc.
|
// Process implements the [AddressProcessor] interface for *DefaultAddrProc.
|
||||||
func (p *DefaultAddrProc) Process(ip netip.Addr) {
|
func (p *DefaultAddrProc) Process(ctx context.Context, ip netip.Addr) {
|
||||||
p.clientIPsMu.Lock()
|
p.clientIPsMu.Lock()
|
||||||
defer p.clientIPsMu.Unlock()
|
defer p.clientIPsMu.Unlock()
|
||||||
|
|
||||||
|
@ -222,38 +228,42 @@ func (p *DefaultAddrProc) Process(ip netip.Addr) {
|
||||||
case p.clientIPs <- ip:
|
case p.clientIPs <- ip:
|
||||||
// Go on.
|
// Go on.
|
||||||
default:
|
default:
|
||||||
log.Debug("clients: ip channel is full; len: %d", len(p.clientIPs))
|
p.logger.DebugContext(ctx, "ip channel is full", "len", len(p.clientIPs))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// process processes the incoming client IP-address information. It is intended
|
// process processes the incoming client IP-address information. It is intended
|
||||||
// to be used as a goroutine. Once clientIPs is closed, process exits.
|
// to be used as a goroutine. Once clientIPs is closed, process exits.
|
||||||
func (p *DefaultAddrProc) process(catchPanics bool) {
|
func (p *DefaultAddrProc) process(ctx context.Context, catchPanics bool) {
|
||||||
if catchPanics {
|
if catchPanics {
|
||||||
defer log.OnPanic("addrProcessor.process")
|
defer slogutil.RecoverAndLog(ctx, p.logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("clients: processing addresses")
|
p.logger.InfoContext(ctx, "processing addresses")
|
||||||
|
|
||||||
ctx := context.TODO()
|
|
||||||
|
|
||||||
for ip := range p.clientIPs {
|
for ip := range p.clientIPs {
|
||||||
host := p.processRDNS(ctx, ip)
|
host := p.processRDNS(ctx, ip)
|
||||||
info := p.processWHOIS(ctx, ip)
|
info := p.processWHOIS(ctx, ip)
|
||||||
|
|
||||||
p.addrUpdater.UpdateAddress(ip, host, info)
|
p.addrUpdater.UpdateAddress(ctx, ip, host, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("clients: finished processing addresses")
|
p.logger.InfoContext(ctx, "finished processing addresses")
|
||||||
}
|
}
|
||||||
|
|
||||||
// processRDNS resolves the clients' IP addresses using reverse DNS. host is
|
// processRDNS resolves the clients' IP addresses using reverse DNS. host is
|
||||||
// empty if there were errors or if the information hasn't changed.
|
// empty if there were errors or if the information hasn't changed.
|
||||||
func (p *DefaultAddrProc) processRDNS(ctx context.Context, ip netip.Addr) (host string) {
|
func (p *DefaultAddrProc) processRDNS(ctx context.Context, ip netip.Addr) (host string) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Debug("clients: processing %s with rdns", ip)
|
p.logger.DebugContext(ctx, "processing rdns", "ip", ip)
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Debug("clients: finished processing %s with rdns in %s", ip, time.Since(start))
|
p.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"finished processing rdns",
|
||||||
|
"ip", ip,
|
||||||
|
"host", host,
|
||||||
|
"elapsed", time.Since(start),
|
||||||
|
)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
ok := p.shouldResolve(ip)
|
ok := p.shouldResolve(ip)
|
||||||
|
@ -280,9 +290,15 @@ func (p *DefaultAddrProc) shouldResolve(ip netip.Addr) (ok bool) {
|
||||||
// hasn't changed.
|
// hasn't changed.
|
||||||
func (p *DefaultAddrProc) processWHOIS(ctx context.Context, ip netip.Addr) (info *whois.Info) {
|
func (p *DefaultAddrProc) processWHOIS(ctx context.Context, ip netip.Addr) (info *whois.Info) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
log.Debug("clients: processing %s with whois", ip)
|
p.logger.DebugContext(ctx, "processing whois", "ip", ip)
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Debug("clients: finished processing %s with whois in %s", ip, time.Since(start))
|
p.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"finished processing whois",
|
||||||
|
"ip", ip,
|
||||||
|
"whois", info,
|
||||||
|
"elapsed", time.Since(start),
|
||||||
|
)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// TODO(s.chzhen): Move the timeout logic from WHOIS configuration to the
|
// TODO(s.chzhen): Move the timeout logic from WHOIS configuration to the
|
||||||
|
|
|
@ -26,7 +26,8 @@ func TestEmptyAddrProc(t *testing.T) {
|
||||||
p := client.EmptyAddrProc{}
|
p := client.EmptyAddrProc{}
|
||||||
|
|
||||||
assert.NotPanics(t, func() {
|
assert.NotPanics(t, func() {
|
||||||
p.Process(testIP)
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
p.Process(ctx, testIP)
|
||||||
})
|
})
|
||||||
|
|
||||||
assert.NotPanics(t, func() {
|
assert.NotPanics(t, func() {
|
||||||
|
@ -120,7 +121,8 @@ func TestDefaultAddrProc_Process_rDNS(t *testing.T) {
|
||||||
})
|
})
|
||||||
testutil.CleanupAndRequireSuccess(t, p.Close)
|
testutil.CleanupAndRequireSuccess(t, p.Close)
|
||||||
|
|
||||||
p.Process(tc.ip)
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
p.Process(ctx, tc.ip)
|
||||||
|
|
||||||
if !tc.wantUpd {
|
if !tc.wantUpd {
|
||||||
return
|
return
|
||||||
|
@ -146,8 +148,8 @@ func newOnUpdateAddress(
|
||||||
ips chan<- netip.Addr,
|
ips chan<- netip.Addr,
|
||||||
hosts chan<- string,
|
hosts chan<- string,
|
||||||
infos chan<- *whois.Info,
|
infos chan<- *whois.Info,
|
||||||
) (f func(ip netip.Addr, host string, info *whois.Info)) {
|
) (f func(ctx context.Context, ip netip.Addr, host string, info *whois.Info)) {
|
||||||
return func(ip netip.Addr, host string, info *whois.Info) {
|
return func(ctx context.Context, ip netip.Addr, host string, info *whois.Info) {
|
||||||
if !want && (host != "" || info != nil) {
|
if !want && (host != "" || info != nil) {
|
||||||
panic(fmt.Errorf("got unexpected update for %v with %q and %v", ip, host, info))
|
panic(fmt.Errorf("got unexpected update for %v with %q and %v", ip, host, info))
|
||||||
}
|
}
|
||||||
|
@ -230,7 +232,8 @@ func TestDefaultAddrProc_Process_WHOIS(t *testing.T) {
|
||||||
})
|
})
|
||||||
testutil.CleanupAndRequireSuccess(t, p.Close)
|
testutil.CleanupAndRequireSuccess(t, p.Close)
|
||||||
|
|
||||||
p.Process(testIP)
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
p.Process(ctx, testIP)
|
||||||
|
|
||||||
if !tc.wantUpd {
|
if !tc.wantUpd {
|
||||||
return
|
return
|
||||||
|
@ -251,7 +254,9 @@ func TestDefaultAddrProc_Process_WHOIS(t *testing.T) {
|
||||||
func TestDefaultAddrProc_Close(t *testing.T) {
|
func TestDefaultAddrProc_Close(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{})
|
p := client.NewDefaultAddrProc(&client.DefaultAddrProcConfig{
|
||||||
|
BaseLogger: slogutil.NewDiscardLogger(),
|
||||||
|
})
|
||||||
|
|
||||||
err := p.Close()
|
err := p.Close()
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding"
|
"encoding"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"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/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
)
|
)
|
||||||
|
@ -136,7 +136,7 @@ type Persistent struct {
|
||||||
|
|
||||||
// validate returns an error if persistent client information contains errors.
|
// validate returns an error if persistent client information contains errors.
|
||||||
// allTags must be sorted.
|
// allTags must be sorted.
|
||||||
func (c *Persistent) validate(allTags []string) (err error) {
|
func (c *Persistent) validate(ctx context.Context, l *slog.Logger, allTags []string) (err error) {
|
||||||
switch {
|
switch {
|
||||||
case c.Name == "":
|
case c.Name == "":
|
||||||
return errors.Error("empty name")
|
return errors.Error("empty name")
|
||||||
|
@ -153,7 +153,7 @@ func (c *Persistent) validate(allTags []string) (err error) {
|
||||||
|
|
||||||
err = conf.Close()
|
err = conf.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("client: closing upstream config: %s", err)
|
l.ErrorContext(ctx, "client: closing upstream config", slogutil.KeyError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range c.Tags {
|
for _, t := range c.Tags {
|
||||||
|
@ -323,20 +323,3 @@ func (c *Persistent) CloseUpstreams() (err error) {
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetSafeSearch initializes and sets the safe search filter for this client.
|
|
||||||
func (c *Persistent) SetSafeSearch(
|
|
||||||
conf filtering.SafeSearchConfig,
|
|
||||||
cacheSize uint,
|
|
||||||
cacheTTL time.Duration,
|
|
||||||
) (err error) {
|
|
||||||
ss, err := safesearch.NewDefault(conf, fmt.Sprintf("client %q", c.Name), cacheSize, cacheTTL)
|
|
||||||
if err != nil {
|
|
||||||
// Don't wrap the error, because it's informative enough as is.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.SafeSearch = ss
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package client
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
|
@ -15,6 +16,7 @@ import (
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/hostsfile"
|
"github.com/AdguardTeam/golibs/hostsfile"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// allowedTags is the list of available client tags.
|
// allowedTags is the list of available client tags.
|
||||||
|
@ -83,6 +85,10 @@ type HostsContainer interface {
|
||||||
|
|
||||||
// StorageConfig is the client storage configuration structure.
|
// StorageConfig is the client storage configuration structure.
|
||||||
type StorageConfig struct {
|
type StorageConfig struct {
|
||||||
|
// Logger is used for logging the operation of the client storage. It must
|
||||||
|
// not be nil.
|
||||||
|
Logger *slog.Logger
|
||||||
|
|
||||||
// DHCP is used to match IPs against MACs of persistent clients and update
|
// DHCP is used to match IPs against MACs of persistent clients and update
|
||||||
// [SourceDHCP] runtime client information. It must not be nil.
|
// [SourceDHCP] runtime client information. It must not be nil.
|
||||||
DHCP DHCP
|
DHCP DHCP
|
||||||
|
@ -108,6 +114,10 @@ type StorageConfig struct {
|
||||||
|
|
||||||
// Storage contains information about persistent and runtime clients.
|
// Storage contains information about persistent and runtime clients.
|
||||||
type Storage struct {
|
type Storage struct {
|
||||||
|
// logger is used for logging the operation of the client storage. It must
|
||||||
|
// not be nil.
|
||||||
|
logger *slog.Logger
|
||||||
|
|
||||||
// mu protects indexes of persistent and runtime clients.
|
// mu protects indexes of persistent and runtime clients.
|
||||||
mu *sync.Mutex
|
mu *sync.Mutex
|
||||||
|
|
||||||
|
@ -145,12 +155,12 @@ type Storage struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewStorage returns initialized client storage. conf must not be nil.
|
// NewStorage returns initialized client storage. conf must not be nil.
|
||||||
func NewStorage(conf *StorageConfig) (s *Storage, err error) {
|
func NewStorage(ctx context.Context, conf *StorageConfig) (s *Storage, err error) {
|
||||||
tags := slices.Clone(allowedTags)
|
tags := slices.Clone(allowedTags)
|
||||||
slices.Sort(tags)
|
slices.Sort(tags)
|
||||||
|
|
||||||
s = &Storage{
|
s = &Storage{
|
||||||
allowedTags: tags,
|
logger: conf.Logger,
|
||||||
mu: &sync.Mutex{},
|
mu: &sync.Mutex{},
|
||||||
index: newIndex(),
|
index: newIndex(),
|
||||||
runtimeIndex: newRuntimeIndex(),
|
runtimeIndex: newRuntimeIndex(),
|
||||||
|
@ -158,18 +168,19 @@ func NewStorage(conf *StorageConfig) (s *Storage, err error) {
|
||||||
etcHosts: conf.EtcHosts,
|
etcHosts: conf.EtcHosts,
|
||||||
arpDB: conf.ARPDB,
|
arpDB: conf.ARPDB,
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
|
allowedTags: tags,
|
||||||
arpClientsUpdatePeriod: conf.ARPClientsUpdatePeriod,
|
arpClientsUpdatePeriod: conf.ARPClientsUpdatePeriod,
|
||||||
runtimeSourceDHCP: conf.RuntimeSourceDHCP,
|
runtimeSourceDHCP: conf.RuntimeSourceDHCP,
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, p := range conf.InitialClients {
|
for i, p := range conf.InitialClients {
|
||||||
err = s.Add(p)
|
err = s.Add(ctx, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("adding client %q at index %d: %w", p.Name, i, err)
|
return nil, fmt.Errorf("adding client %q at index %d: %w", p.Name, i, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s.ReloadARP()
|
s.ReloadARP(ctx)
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
}
|
}
|
||||||
|
@ -177,9 +188,9 @@ func NewStorage(conf *StorageConfig) (s *Storage, err error) {
|
||||||
// Start starts the goroutines for updating the runtime client information.
|
// Start starts the goroutines for updating the runtime client information.
|
||||||
//
|
//
|
||||||
// TODO(s.chzhen): Pass context.
|
// TODO(s.chzhen): Pass context.
|
||||||
func (s *Storage) Start(_ context.Context) (err error) {
|
func (s *Storage) Start(ctx context.Context) (err error) {
|
||||||
go s.periodicARPUpdate()
|
go s.periodicARPUpdate(ctx)
|
||||||
go s.handleHostsUpdates()
|
go s.handleHostsUpdates(ctx)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -195,15 +206,15 @@ func (s *Storage) Shutdown(_ context.Context) (err error) {
|
||||||
|
|
||||||
// periodicARPUpdate periodically reloads runtime clients from ARP. It is
|
// periodicARPUpdate periodically reloads runtime clients from ARP. It is
|
||||||
// intended to be used as a goroutine.
|
// intended to be used as a goroutine.
|
||||||
func (s *Storage) periodicARPUpdate() {
|
func (s *Storage) periodicARPUpdate(ctx context.Context) {
|
||||||
defer log.OnPanic("storage")
|
defer slogutil.RecoverAndLog(ctx, s.logger)
|
||||||
|
|
||||||
t := time.NewTicker(s.arpClientsUpdatePeriod)
|
t := time.NewTicker(s.arpClientsUpdatePeriod)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
s.ReloadARP()
|
s.ReloadARP(ctx)
|
||||||
case <-s.done:
|
case <-s.done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -211,28 +222,28 @@ func (s *Storage) periodicARPUpdate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReloadARP reloads runtime clients from ARP, if configured.
|
// ReloadARP reloads runtime clients from ARP, if configured.
|
||||||
func (s *Storage) ReloadARP() {
|
func (s *Storage) ReloadARP(ctx context.Context) {
|
||||||
if s.arpDB != nil {
|
if s.arpDB != nil {
|
||||||
s.addFromSystemARP()
|
s.addFromSystemARP(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// addFromSystemARP adds the IP-hostname pairings from the output of the arp -a
|
// addFromSystemARP adds the IP-hostname pairings from the output of the arp -a
|
||||||
// command.
|
// command.
|
||||||
func (s *Storage) addFromSystemARP() {
|
func (s *Storage) addFromSystemARP(ctx context.Context) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
if err := s.arpDB.Refresh(); err != nil {
|
if err := s.arpDB.Refresh(); err != nil {
|
||||||
s.arpDB = arpdb.Empty{}
|
s.arpDB = arpdb.Empty{}
|
||||||
log.Error("refreshing arp container: %s", err)
|
s.logger.ErrorContext(ctx, "refreshing arp container", slogutil.KeyError, err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ns := s.arpDB.Neighbors()
|
ns := s.arpDB.Neighbors()
|
||||||
if len(ns) == 0 {
|
if len(ns) == 0 {
|
||||||
log.Debug("refreshing arp container: the update is empty")
|
s.logger.DebugContext(ctx, "refreshing arp container: the update is empty")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -246,17 +257,22 @@ func (s *Storage) addFromSystemARP() {
|
||||||
|
|
||||||
removed := s.runtimeIndex.removeEmpty()
|
removed := s.runtimeIndex.removeEmpty()
|
||||||
|
|
||||||
log.Debug("storage: added %d, removed %d client aliases from arp neighborhood", len(ns), removed)
|
s.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"updating client aliases from arp neighborhood",
|
||||||
|
"added", len(ns),
|
||||||
|
"removed", removed,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleHostsUpdates receives the updates from the hosts container and adds
|
// handleHostsUpdates receives the updates from the hosts container and adds
|
||||||
// them to the clients storage. It is intended to be used as a goroutine.
|
// them to the clients storage. It is intended to be used as a goroutine.
|
||||||
func (s *Storage) handleHostsUpdates() {
|
func (s *Storage) handleHostsUpdates(ctx context.Context) {
|
||||||
if s.etcHosts == nil {
|
if s.etcHosts == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
defer log.OnPanic("storage")
|
defer slogutil.RecoverAndLog(ctx, s.logger)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -265,7 +281,7 @@ func (s *Storage) handleHostsUpdates() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.addFromHostsFile(upd)
|
s.addFromHostsFile(ctx, upd)
|
||||||
case <-s.done:
|
case <-s.done:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -274,7 +290,7 @@ func (s *Storage) handleHostsUpdates() {
|
||||||
|
|
||||||
// addFromHostsFile fills the client-hostname pairing index from the system's
|
// addFromHostsFile fills the client-hostname pairing index from the system's
|
||||||
// hosts files.
|
// hosts files.
|
||||||
func (s *Storage) addFromHostsFile(hosts *hostsfile.DefaultStorage) {
|
func (s *Storage) addFromHostsFile(ctx context.Context, hosts *hostsfile.DefaultStorage) {
|
||||||
s.mu.Lock()
|
s.mu.Lock()
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
|
|
||||||
|
@ -294,14 +310,19 @@ func (s *Storage) addFromHostsFile(hosts *hostsfile.DefaultStorage) {
|
||||||
})
|
})
|
||||||
|
|
||||||
removed := s.runtimeIndex.removeEmpty()
|
removed := s.runtimeIndex.removeEmpty()
|
||||||
log.Debug("storage: added %d, removed %d client aliases from system hosts file", added, removed)
|
s.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"updating client aliases from system hosts file",
|
||||||
|
"added", added,
|
||||||
|
"removed", removed,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// type check
|
// type check
|
||||||
var _ AddressUpdater = (*Storage)(nil)
|
var _ AddressUpdater = (*Storage)(nil)
|
||||||
|
|
||||||
// UpdateAddress implements the [AddressUpdater] interface for *Storage
|
// UpdateAddress implements the [AddressUpdater] interface for *Storage
|
||||||
func (s *Storage) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
func (s *Storage) UpdateAddress(ctx context.Context, ip netip.Addr, host string, info *whois.Info) {
|
||||||
// Common fast path optimization.
|
// Common fast path optimization.
|
||||||
if host == "" && info == nil {
|
if host == "" && info == nil {
|
||||||
return
|
return
|
||||||
|
@ -315,12 +336,12 @@ func (s *Storage) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if info != nil {
|
if info != nil {
|
||||||
s.setWHOISInfo(ip, info)
|
s.setWHOISInfo(ctx, ip, info)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateDHCP updates [SourceDHCP] runtime client information.
|
// UpdateDHCP updates [SourceDHCP] runtime client information.
|
||||||
func (s *Storage) UpdateDHCP() {
|
func (s *Storage) UpdateDHCP(ctx context.Context) {
|
||||||
if s.dhcp == nil || !s.runtimeSourceDHCP {
|
if s.dhcp == nil || !s.runtimeSourceDHCP {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -338,14 +359,23 @@ func (s *Storage) UpdateDHCP() {
|
||||||
}
|
}
|
||||||
|
|
||||||
removed := s.runtimeIndex.removeEmpty()
|
removed := s.runtimeIndex.removeEmpty()
|
||||||
log.Debug("storage: added %d, removed %d client aliases from dhcp", added, removed)
|
s.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"updating client aliases from dhcp",
|
||||||
|
"added", added,
|
||||||
|
"removed", removed,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// setWHOISInfo sets the WHOIS information for a runtime client.
|
// setWHOISInfo sets the WHOIS information for a runtime client.
|
||||||
func (s *Storage) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
func (s *Storage) setWHOISInfo(ctx context.Context, ip netip.Addr, wi *whois.Info) {
|
||||||
_, ok := s.index.findByIP(ip)
|
_, ok := s.index.findByIP(ip)
|
||||||
if ok {
|
if ok {
|
||||||
log.Debug("storage: client for %s is already created, ignore whois info", ip)
|
s.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"persistent client is already created, ignore whois info",
|
||||||
|
"ip", ip,
|
||||||
|
)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -358,14 +388,14 @@ func (s *Storage) setWHOISInfo(ip netip.Addr, wi *whois.Info) {
|
||||||
|
|
||||||
rc.setWHOIS(wi)
|
rc.setWHOIS(wi)
|
||||||
|
|
||||||
log.Debug("storage: set whois info for runtime client with ip %s: %+v", ip, wi)
|
s.logger.DebugContext(ctx, "set whois info for runtime client", "ip", ip, "whois", wi)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add stores persistent client information or returns an error.
|
// Add stores persistent client information or returns an error.
|
||||||
func (s *Storage) Add(p *Persistent) (err error) {
|
func (s *Storage) Add(ctx context.Context, p *Persistent) (err error) {
|
||||||
defer func() { err = errors.Annotate(err, "adding client: %w") }()
|
defer func() { err = errors.Annotate(err, "adding client: %w") }()
|
||||||
|
|
||||||
err = p.validate(s.allowedTags)
|
err = p.validate(ctx, s.logger, s.allowedTags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error since there is already an annotation deferred.
|
// Don't wrap the error since there is already an annotation deferred.
|
||||||
return err
|
return err
|
||||||
|
@ -388,7 +418,13 @@ func (s *Storage) Add(p *Persistent) (err error) {
|
||||||
|
|
||||||
s.index.add(p)
|
s.index.add(p)
|
||||||
|
|
||||||
log.Debug("client storage: added %q: IDs: %q [%d]", p.Name, p.IDs(), s.index.size())
|
s.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"client added",
|
||||||
|
"name", p.Name,
|
||||||
|
"ids", p.IDs(),
|
||||||
|
"clients_count", s.index.size(),
|
||||||
|
)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -490,10 +526,10 @@ func (s *Storage) RemoveByName(name string) (ok bool) {
|
||||||
|
|
||||||
// Update finds the stored persistent client by its name and updates its
|
// Update finds the stored persistent client by its name and updates its
|
||||||
// information from p.
|
// information from p.
|
||||||
func (s *Storage) Update(name string, p *Persistent) (err error) {
|
func (s *Storage) Update(ctx context.Context, name string, p *Persistent) (err error) {
|
||||||
defer func() { err = errors.Annotate(err, "updating client: %w") }()
|
defer func() { err = errors.Annotate(err, "updating client: %w") }()
|
||||||
|
|
||||||
err = p.validate(s.allowedTags)
|
err = p.validate(ctx, s.logger, s.allowedTags)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error since there is already an annotation deferred.
|
// Don't wrap the error since there is already an annotation deferred.
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -15,11 +15,25 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
"github.com/AdguardTeam/AdGuardHome/internal/dhcpsvc"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||||
"github.com/AdguardTeam/golibs/hostsfile"
|
"github.com/AdguardTeam/golibs/hostsfile"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/testutil"
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// newTestStorage is a helper function that returns initialized storage.
|
||||||
|
func newTestStorage(tb testing.TB) (s *client.Storage) {
|
||||||
|
tb.Helper()
|
||||||
|
|
||||||
|
ctx := testutil.ContextWithTimeout(tb, testTimeout)
|
||||||
|
s, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
})
|
||||||
|
require.NoError(tb, err)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
// testHostsContainer is a mock implementation of the [client.HostsContainer]
|
// testHostsContainer is a mock implementation of the [client.HostsContainer]
|
||||||
// interface.
|
// interface.
|
||||||
type testHostsContainer struct {
|
type testHostsContainer struct {
|
||||||
|
@ -110,7 +124,9 @@ func TestStorage_Add_hostsfile(t *testing.T) {
|
||||||
onUpd: func() (updates <-chan *hostsfile.DefaultStorage) { return hostCh },
|
onUpd: func() (updates <-chan *hostsfile.DefaultStorage) { return hostCh },
|
||||||
}
|
}
|
||||||
|
|
||||||
storage, err := client.NewStorage(&client.StorageConfig{
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
storage, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
DHCP: client.EmptyDHCP{},
|
DHCP: client.EmptyDHCP{},
|
||||||
EtcHosts: h,
|
EtcHosts: h,
|
||||||
ARPClientsUpdatePeriod: testTimeout / 10,
|
ARPClientsUpdatePeriod: testTimeout / 10,
|
||||||
|
@ -198,7 +214,9 @@ func TestStorage_Add_arp(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
storage, err := client.NewStorage(&client.StorageConfig{
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
storage, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
DHCP: client.EmptyDHCP{},
|
DHCP: client.EmptyDHCP{},
|
||||||
ARPDB: a,
|
ARPDB: a,
|
||||||
ARPClientsUpdatePeriod: testTimeout / 10,
|
ARPClientsUpdatePeriod: testTimeout / 10,
|
||||||
|
@ -273,8 +291,10 @@ func TestStorage_Add_whois(t *testing.T) {
|
||||||
cliName3 = "client_three"
|
cliName3 = "client_three"
|
||||||
)
|
)
|
||||||
|
|
||||||
storage, err := client.NewStorage(&client.StorageConfig{
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
DHCP: client.EmptyDHCP{},
|
storage, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
DHCP: client.EmptyDHCP{},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -284,7 +304,7 @@ func TestStorage_Add_whois(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("new_client", func(t *testing.T) {
|
t.Run("new_client", func(t *testing.T) {
|
||||||
storage.UpdateAddress(cliIP1, "", whois)
|
storage.UpdateAddress(ctx, cliIP1, "", whois)
|
||||||
cli1 := storage.ClientRuntime(cliIP1)
|
cli1 := storage.ClientRuntime(cliIP1)
|
||||||
require.NotNil(t, cli1)
|
require.NotNil(t, cli1)
|
||||||
|
|
||||||
|
@ -292,8 +312,8 @@ func TestStorage_Add_whois(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("existing_runtime_client", func(t *testing.T) {
|
t.Run("existing_runtime_client", func(t *testing.T) {
|
||||||
storage.UpdateAddress(cliIP2, cliName2, nil)
|
storage.UpdateAddress(ctx, cliIP2, cliName2, nil)
|
||||||
storage.UpdateAddress(cliIP2, "", whois)
|
storage.UpdateAddress(ctx, cliIP2, "", whois)
|
||||||
|
|
||||||
cli2 := storage.ClientRuntime(cliIP2)
|
cli2 := storage.ClientRuntime(cliIP2)
|
||||||
require.NotNil(t, cli2)
|
require.NotNil(t, cli2)
|
||||||
|
@ -304,14 +324,14 @@ func TestStorage_Add_whois(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("can't_set_persistent_client", func(t *testing.T) {
|
t.Run("can't_set_persistent_client", func(t *testing.T) {
|
||||||
err = storage.Add(&client.Persistent{
|
err = storage.Add(ctx, &client.Persistent{
|
||||||
Name: cliName3,
|
Name: cliName3,
|
||||||
UID: client.MustNewUID(),
|
UID: client.MustNewUID(),
|
||||||
IPs: []netip.Addr{cliIP3},
|
IPs: []netip.Addr{cliIP3},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
storage.UpdateAddress(cliIP3, "", whois)
|
storage.UpdateAddress(ctx, cliIP3, "", whois)
|
||||||
rc := storage.ClientRuntime(cliIP3)
|
rc := storage.ClientRuntime(cliIP3)
|
||||||
require.Nil(t, rc)
|
require.Nil(t, rc)
|
||||||
})
|
})
|
||||||
|
@ -364,7 +384,9 @@ func TestClientsDHCP(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
storage, err := client.NewStorage(&client.StorageConfig{
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
storage, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
DHCP: d,
|
DHCP: d,
|
||||||
RuntimeSourceDHCP: true,
|
RuntimeSourceDHCP: true,
|
||||||
})
|
})
|
||||||
|
@ -378,7 +400,7 @@ func TestClientsDHCP(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("find_persistent", func(t *testing.T) {
|
t.Run("find_persistent", func(t *testing.T) {
|
||||||
err = storage.Add(&client.Persistent{
|
err = storage.Add(ctx, &client.Persistent{
|
||||||
Name: prsCliName,
|
Name: prsCliName,
|
||||||
UID: client.MustNewUID(),
|
UID: client.MustNewUID(),
|
||||||
MACs: []net.HardwareAddr{prsCliMAC},
|
MACs: []net.HardwareAddr{prsCliMAC},
|
||||||
|
@ -393,7 +415,7 @@ func TestClientsDHCP(t *testing.T) {
|
||||||
|
|
||||||
t.Run("leases", func(t *testing.T) {
|
t.Run("leases", func(t *testing.T) {
|
||||||
delete(ipToHost, cliIP1)
|
delete(ipToHost, cliIP1)
|
||||||
storage.UpdateDHCP()
|
storage.UpdateDHCP(ctx)
|
||||||
|
|
||||||
cli1 := storage.ClientRuntime(cliIP1)
|
cli1 := storage.ClientRuntime(cliIP1)
|
||||||
require.Nil(t, cli1)
|
require.Nil(t, cli1)
|
||||||
|
@ -421,16 +443,19 @@ func TestClientsDHCP(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientsAddExisting(t *testing.T) {
|
func TestClientsAddExisting(t *testing.T) {
|
||||||
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
|
||||||
t.Run("simple", func(t *testing.T) {
|
t.Run("simple", func(t *testing.T) {
|
||||||
storage, err := client.NewStorage(&client.StorageConfig{
|
storage, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
DHCP: client.EmptyDHCP{},
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
DHCP: client.EmptyDHCP{},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
ip := netip.MustParseAddr("1.1.1.1")
|
ip := netip.MustParseAddr("1.1.1.1")
|
||||||
|
|
||||||
// Add a client.
|
// Add a client.
|
||||||
err = storage.Add(&client.Persistent{
|
err = storage.Add(ctx, &client.Persistent{
|
||||||
Name: "client1",
|
Name: "client1",
|
||||||
UID: client.MustNewUID(),
|
UID: client.MustNewUID(),
|
||||||
IPs: []netip.Addr{ip, netip.MustParseAddr("1:2:3::4")},
|
IPs: []netip.Addr{ip, netip.MustParseAddr("1:2:3::4")},
|
||||||
|
@ -440,7 +465,7 @@ func TestClientsAddExisting(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Now add an auto-client with the same IP.
|
// Now add an auto-client with the same IP.
|
||||||
storage.UpdateAddress(ip, "test", nil)
|
storage.UpdateAddress(ctx, ip, "test", nil)
|
||||||
rc := storage.ClientRuntime(ip)
|
rc := storage.ClientRuntime(ip)
|
||||||
assert.True(t, compareRuntimeInfo(rc, client.SourceRDNS, "test"))
|
assert.True(t, compareRuntimeInfo(rc, client.SourceRDNS, "test"))
|
||||||
})
|
})
|
||||||
|
@ -468,8 +493,9 @@ func TestClientsAddExisting(t *testing.T) {
|
||||||
dhcpServer, err := dhcpd.Create(config)
|
dhcpServer, err := dhcpd.Create(config)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
storage, err := client.NewStorage(&client.StorageConfig{
|
storage, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
DHCP: dhcpServer,
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
DHCP: dhcpServer,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -484,7 +510,7 @@ func TestClientsAddExisting(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Add a new client with the same IP as for a client with MAC.
|
// Add a new client with the same IP as for a client with MAC.
|
||||||
err = storage.Add(&client.Persistent{
|
err = storage.Add(ctx, &client.Persistent{
|
||||||
Name: "client2",
|
Name: "client2",
|
||||||
UID: client.MustNewUID(),
|
UID: client.MustNewUID(),
|
||||||
IPs: []netip.Addr{ip},
|
IPs: []netip.Addr{ip},
|
||||||
|
@ -492,7 +518,7 @@ func TestClientsAddExisting(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Add a new client with the IP from the first client's IP range.
|
// Add a new client with the IP from the first client's IP range.
|
||||||
err = storage.Add(&client.Persistent{
|
err = storage.Add(ctx, &client.Persistent{
|
||||||
Name: "client3",
|
Name: "client3",
|
||||||
UID: client.MustNewUID(),
|
UID: client.MustNewUID(),
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("2.2.2.2")},
|
IPs: []netip.Addr{netip.MustParseAddr("2.2.2.2")},
|
||||||
|
@ -506,14 +532,16 @@ func TestClientsAddExisting(t *testing.T) {
|
||||||
func newStorage(tb testing.TB, m []*client.Persistent) (s *client.Storage) {
|
func newStorage(tb testing.TB, m []*client.Persistent) (s *client.Storage) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
s, err := client.NewStorage(&client.StorageConfig{
|
ctx := testutil.ContextWithTimeout(tb, testTimeout)
|
||||||
DHCP: client.EmptyDHCP{},
|
s, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
DHCP: client.EmptyDHCP{},
|
||||||
})
|
})
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
for _, c := range m {
|
for _, c := range m {
|
||||||
c.UID = client.MustNewUID()
|
c.UID = client.MustNewUID()
|
||||||
require.NoError(tb, s.Add(c))
|
require.NoError(tb, s.Add(ctx, c))
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Equal(tb, len(m), s.Size())
|
require.Equal(tb, len(m), s.Size())
|
||||||
|
@ -555,9 +583,8 @@ func TestStorage_Add(t *testing.T) {
|
||||||
UID: existingClientUID,
|
UID: existingClientUID,
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := client.NewStorage(&client.StorageConfig{})
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
require.NoError(t, err)
|
s := newTestStorage(t)
|
||||||
|
|
||||||
tags := s.AllowedTags()
|
tags := s.AllowedTags()
|
||||||
require.NotZero(t, len(tags))
|
require.NotZero(t, len(tags))
|
||||||
require.True(t, slices.IsSorted(tags))
|
require.True(t, slices.IsSorted(tags))
|
||||||
|
@ -568,7 +595,7 @@ func TestStorage_Add(t *testing.T) {
|
||||||
_, ok = slices.BinarySearch(tags, notAllowedTag)
|
_, ok = slices.BinarySearch(tags, notAllowedTag)
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
|
|
||||||
err = s.Add(existingClient)
|
err := s.Add(ctx, existingClient)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@ -669,7 +696,7 @@ func TestStorage_Add(t *testing.T) {
|
||||||
|
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
err = s.Add(tc.cli)
|
err = s.Add(ctx, tc.cli)
|
||||||
|
|
||||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||||
})
|
})
|
||||||
|
@ -687,10 +714,9 @@ func TestStorage_RemoveByName(t *testing.T) {
|
||||||
UID: client.MustNewUID(),
|
UID: client.MustNewUID(),
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := client.NewStorage(&client.StorageConfig{})
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
require.NoError(t, err)
|
s := newTestStorage(t)
|
||||||
|
err := s.Add(ctx, existingClient)
|
||||||
err = s.Add(existingClient)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
|
@ -714,10 +740,8 @@ func TestStorage_RemoveByName(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("duplicate_remove", func(t *testing.T) {
|
t.Run("duplicate_remove", func(t *testing.T) {
|
||||||
s, err = client.NewStorage(&client.StorageConfig{})
|
s = newTestStorage(t)
|
||||||
require.NoError(t, err)
|
err = s.Add(ctx, existingClient)
|
||||||
|
|
||||||
err = s.Add(existingClient)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, s.RemoveByName(existingName))
|
assert.True(t, s.RemoveByName(existingName))
|
||||||
|
@ -1080,6 +1104,7 @@ func TestStorage_Update(t *testing.T) {
|
||||||
`uses the same ClientID "obstructing_client_id"`,
|
`uses the same ClientID "obstructing_client_id"`,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
for _, tc := range testCases {
|
for _, tc := range testCases {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
s := newStorage(
|
s := newStorage(
|
||||||
|
@ -1090,7 +1115,7 @@ func TestStorage_Update(t *testing.T) {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
err := s.Update(clientName, tc.cli)
|
err := s.Update(ctx, clientName, tc.cli)
|
||||||
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
testutil.AssertErrorMsg(t, tc.wantErrMsg, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,11 +3,12 @@ package dhcpsvc
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"maps"
|
||||||
"os"
|
"os"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/mapsutil"
|
|
||||||
"github.com/AdguardTeam/golibs/netutil"
|
"github.com/AdguardTeam/golibs/netutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -78,14 +79,13 @@ func (conf *Config) Validate() (err error) {
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
||||||
mapsutil.SortedRange(conf.Interfaces, func(iface string, ic *InterfaceConfig) (ok bool) {
|
for _, iface := range slices.Sorted(maps.Keys(conf.Interfaces)) {
|
||||||
|
ic := conf.Interfaces[iface]
|
||||||
err = ic.validate()
|
err = ic.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, fmt.Errorf("interface %q: %w", iface, err))
|
errs = append(errs, fmt.Errorf("interface %q: %w", iface, err))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return errors.Join(errs...)
|
return errors.Join(errs...)
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,15 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
"maps"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/mapsutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// DHCPServer is a DHCP server for both IPv4 and IPv6 address families.
|
// DHCPServer is a DHCP server for both IPv4 and IPv6 address families.
|
||||||
|
@ -107,7 +108,8 @@ func newInterfaces(
|
||||||
v6 = make(dhcpInterfacesV6, 0, len(ifaces))
|
v6 = make(dhcpInterfacesV6, 0, len(ifaces))
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
mapsutil.SortedRange(ifaces, func(name string, iface *InterfaceConfig) (cont bool) {
|
for _, name := range slices.Sorted(maps.Keys(ifaces)) {
|
||||||
|
iface := ifaces[name]
|
||||||
var i4 *dhcpInterfaceV4
|
var i4 *dhcpInterfaceV4
|
||||||
i4, err = newDHCPInterfaceV4(ctx, l, name, iface.IPv4)
|
i4, err = newDHCPInterfaceV4(ctx, l, name, iface.IPv4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -120,9 +122,8 @@ func newInterfaces(
|
||||||
if i6 != nil {
|
if i6 != nil {
|
||||||
v6 = append(v6, i6)
|
v6 = append(v6, i6)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
if err = errors.Join(errs...); err != nil {
|
if err = errors.Join(errs...); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -513,12 +513,14 @@ func TestSafeSearch(t *testing.T) {
|
||||||
SafeSearchCacheSize: 1000,
|
SafeSearchCacheSize: 1000,
|
||||||
CacheTime: 30,
|
CacheTime: 30,
|
||||||
}
|
}
|
||||||
safeSearch, err := safesearch.NewDefault(
|
|
||||||
safeSearchConf,
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
"",
|
safeSearch, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
filterConf.SafeSearchCacheSize,
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
time.Minute*time.Duration(filterConf.CacheTime),
|
ServicesConfig: safeSearchConf,
|
||||||
)
|
CacheSize: filterConf.SafeSearchCacheSize,
|
||||||
|
CacheTTL: time.Minute * time.Duration(filterConf.CacheTime),
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
filterConf.SafeSearch = safeSearch
|
filterConf.SafeSearch = safeSearch
|
||||||
|
@ -583,13 +585,15 @@ 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)
|
||||||
|
|
||||||
|
// TODO(a.garipov): Create our own helper for this.
|
||||||
var reply *dns.Msg
|
var reply *dns.Msg
|
||||||
require.Eventually(t, func() (ok bool) {
|
once := &sync.Once{}
|
||||||
reply, _, err = client.Exchange(req, addr)
|
require.EventuallyWithT(t, func(c *assert.CollectT) {
|
||||||
|
r, _, errExch := client.Exchange(req, addr)
|
||||||
return err == nil
|
if assert.NoError(c, errExch) {
|
||||||
|
once.Do(func() { reply = r })
|
||||||
|
}
|
||||||
}, testTimeout*10, testTimeout)
|
}, testTimeout*10, testTimeout)
|
||||||
require.NoErrorf(t, err, "couldn't talk to server %s: %s", addr, err)
|
|
||||||
|
|
||||||
if tc.wantCNAME != "" {
|
if tc.wantCNAME != "" {
|
||||||
require.Len(t, reply.Answer, 2)
|
require.Len(t, reply.Answer, 2)
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dnsforward
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
@ -203,7 +204,8 @@ func (s *Server) processClientIP(addr netip.Addr) {
|
||||||
s.serverLock.RLock()
|
s.serverLock.RLock()
|
||||||
defer s.serverLock.RUnlock()
|
defer s.serverLock.RUnlock()
|
||||||
|
|
||||||
s.addrProc.Process(addr)
|
// TODO(s.chzhen): Pass context.
|
||||||
|
s.addrProc.Process(context.TODO(), addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
// processDDRQuery responds to Discovery of Designated Resolvers (DDR) SVCB
|
// processDDRQuery responds to Discovery of Designated Resolvers (DDR) SVCB
|
||||||
|
|
|
@ -2,6 +2,7 @@ package dnsforward
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -90,7 +91,7 @@ func TestServer_ProcessInitial(t *testing.T) {
|
||||||
|
|
||||||
var gotAddr netip.Addr
|
var gotAddr netip.Addr
|
||||||
s.addrProc = &aghtest.AddressProcessor{
|
s.addrProc = &aghtest.AddressProcessor{
|
||||||
OnProcess: func(ip netip.Addr) { gotAddr = ip },
|
OnProcess: func(ctx context.Context, ip netip.Addr) { gotAddr = ip },
|
||||||
OnClose: func() (err error) { panic("not implemented") },
|
OnClose: func() (err error) { panic("not implemented") },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1057,7 +1057,7 @@ func New(c *Config, blockFilters []Filter) (d *DNSFilter, err error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.MkdirAll(filepath.Join(d.conf.DataDir, filterDir), aghos.DefaultPermDir)
|
err = aghos.MkdirAll(filepath.Join(d.conf.DataDir, filterDir), aghos.DefaultPermDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
d.Close()
|
d.Close()
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/aghos"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
|
@ -25,7 +26,7 @@ func (d *DNSFilter) validateFilterURL(urlStr string) (err error) {
|
||||||
|
|
||||||
if filepath.IsAbs(urlStr) {
|
if filepath.IsAbs(urlStr) {
|
||||||
urlStr = filepath.Clean(urlStr)
|
urlStr = filepath.Clean(urlStr)
|
||||||
_, err = os.Stat(urlStr)
|
_, err = aghos.Stat(urlStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error since it's informative enough as is.
|
// Don't wrap the error since it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
package filtering
|
package filtering
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
// SafeSearch interface describes a service for search engines hosts rewrites.
|
// SafeSearch interface describes a service for search engines hosts rewrites.
|
||||||
type SafeSearch interface {
|
type SafeSearch interface {
|
||||||
// CheckHost checks host with safe search filter. CheckHost must be safe
|
// CheckHost checks host with safe search filter. CheckHost must be safe
|
||||||
// for concurrent use. qtype must be either [dns.TypeA] or [dns.TypeAAAA].
|
// for concurrent use. qtype must be either [dns.TypeA] or [dns.TypeAAAA].
|
||||||
CheckHost(host string, qtype uint16) (res Result, err error)
|
CheckHost(ctx context.Context, host string, qtype uint16) (res Result, err error)
|
||||||
|
|
||||||
// Update updates the configuration of the safe search filter. Update must
|
// Update updates the configuration of the safe search filter. Update must
|
||||||
// be safe for concurrent use. An implementation of Update may ignore some
|
// be safe for concurrent use. An implementation of Update may ignore some
|
||||||
// fields, but it must document which.
|
// fields, but it must document which.
|
||||||
Update(conf SafeSearchConfig) (err error)
|
Update(ctx context.Context, conf SafeSearchConfig) (err error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SafeSearchConfig is a struct with safe search related settings.
|
// SafeSearchConfig is a struct with safe search related settings.
|
||||||
|
@ -40,10 +42,13 @@ func (d *DNSFilter) checkSafeSearch(
|
||||||
return Result{}, nil
|
return Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(s.chzhen): Pass context.
|
||||||
|
ctx := context.TODO()
|
||||||
|
|
||||||
clientSafeSearch := setts.ClientSafeSearch
|
clientSafeSearch := setts.ClientSafeSearch
|
||||||
if clientSafeSearch != nil {
|
if clientSafeSearch != nil {
|
||||||
return clientSafeSearch.CheckHost(host, qtype)
|
return clientSafeSearch.CheckHost(ctx, host, qtype)
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.safeSearch.CheckHost(host, qtype)
|
return d.safeSearch.CheckHost(ctx, host, qtype)
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,11 @@ package safesearch
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"encoding/gob"
|
"encoding/gob"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -14,13 +16,20 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||||
"github.com/AdguardTeam/golibs/cache"
|
"github.com/AdguardTeam/golibs/cache"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/urlfilter"
|
"github.com/AdguardTeam/urlfilter"
|
||||||
"github.com/AdguardTeam/urlfilter/filterlist"
|
"github.com/AdguardTeam/urlfilter/filterlist"
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
"github.com/AdguardTeam/urlfilter/rules"
|
||||||
|
"github.com/c2h5oh/datasize"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Attribute keys and values for logging.
|
||||||
|
const (
|
||||||
|
LogPrefix = "safesearch"
|
||||||
|
LogKeyClient = "client"
|
||||||
|
)
|
||||||
|
|
||||||
// Service is a enum with service names used as search providers.
|
// Service is a enum with service names used as search providers.
|
||||||
type Service string
|
type Service string
|
||||||
|
|
||||||
|
@ -57,9 +66,32 @@ func isServiceProtected(s filtering.SafeSearchConfig, service Service) (ok bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DefaultConfig is the configuration structure for [Default].
|
||||||
|
type DefaultConfig struct {
|
||||||
|
// Logger is used for logging the operation of the safe search filter.
|
||||||
|
Logger *slog.Logger
|
||||||
|
|
||||||
|
// ClientName is the name of the persistent client associated with the safe
|
||||||
|
// search filter, if there is one.
|
||||||
|
ClientName string
|
||||||
|
|
||||||
|
// CacheSize is the size of the filter results cache.
|
||||||
|
CacheSize uint
|
||||||
|
|
||||||
|
// CacheTTL is the Time to Live duration for cached items.
|
||||||
|
CacheTTL time.Duration
|
||||||
|
|
||||||
|
// ServicesConfig contains safe search settings for services. It must not
|
||||||
|
// be nil.
|
||||||
|
ServicesConfig filtering.SafeSearchConfig
|
||||||
|
}
|
||||||
|
|
||||||
// Default is the default safe search filter that uses filtering rules with the
|
// Default is the default safe search filter that uses filtering rules with the
|
||||||
// dnsrewrite modifier.
|
// dnsrewrite modifier.
|
||||||
type Default struct {
|
type Default struct {
|
||||||
|
// logger is used for logging the operation of the safe search filter.
|
||||||
|
logger *slog.Logger
|
||||||
|
|
||||||
// mu protects engine.
|
// mu protects engine.
|
||||||
mu *sync.RWMutex
|
mu *sync.RWMutex
|
||||||
|
|
||||||
|
@ -67,33 +99,28 @@ type Default struct {
|
||||||
// engine may be nil, which means that this safe search filter is disabled.
|
// engine may be nil, which means that this safe search filter is disabled.
|
||||||
engine *urlfilter.DNSEngine
|
engine *urlfilter.DNSEngine
|
||||||
|
|
||||||
cache cache.Cache
|
// cache stores safe search filtering results.
|
||||||
logPrefix string
|
cache cache.Cache
|
||||||
cacheTTL time.Duration
|
|
||||||
|
// cacheTTL is the Time to Live duration for cached items.
|
||||||
|
cacheTTL time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDefault returns an initialized default safe search filter. name is used
|
// NewDefault returns an initialized default safe search filter. ctx is used
|
||||||
// for logging.
|
// to log the initial refresh.
|
||||||
func NewDefault(
|
func NewDefault(ctx context.Context, conf *DefaultConfig) (ss *Default, err error) {
|
||||||
conf filtering.SafeSearchConfig,
|
|
||||||
name string,
|
|
||||||
cacheSize uint,
|
|
||||||
cacheTTL time.Duration,
|
|
||||||
) (ss *Default, err error) {
|
|
||||||
ss = &Default{
|
ss = &Default{
|
||||||
mu: &sync.RWMutex{},
|
logger: conf.Logger,
|
||||||
|
mu: &sync.RWMutex{},
|
||||||
cache: cache.New(cache.Config{
|
cache: cache.New(cache.Config{
|
||||||
EnableLRU: true,
|
EnableLRU: true,
|
||||||
MaxSize: cacheSize,
|
MaxSize: conf.CacheSize,
|
||||||
}),
|
}),
|
||||||
// Use %s, because the client safe-search names already contain double
|
cacheTTL: conf.CacheTTL,
|
||||||
// quotes.
|
|
||||||
logPrefix: fmt.Sprintf("safesearch %s: ", name),
|
|
||||||
cacheTTL: cacheTTL,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ss.resetEngine(rulelist.URLFilterIDSafeSearch, conf)
|
// TODO(s.chzhen): Move to [Default.InitialRefresh].
|
||||||
|
err = ss.resetEngine(ctx, rulelist.URLFilterIDSafeSearch, conf.ServicesConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error, because it's informative enough as is.
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -102,29 +129,15 @@ func NewDefault(
|
||||||
return ss, nil
|
return ss, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// log is a helper for logging that includes the name of the safe search
|
|
||||||
// filter. level must be one of [log.DEBUG], [log.INFO], and [log.ERROR].
|
|
||||||
func (ss *Default) log(level log.Level, msg string, args ...any) {
|
|
||||||
switch level {
|
|
||||||
case log.DEBUG:
|
|
||||||
log.Debug(ss.logPrefix+msg, args...)
|
|
||||||
case log.INFO:
|
|
||||||
log.Info(ss.logPrefix+msg, args...)
|
|
||||||
case log.ERROR:
|
|
||||||
log.Error(ss.logPrefix+msg, args...)
|
|
||||||
default:
|
|
||||||
panic(fmt.Errorf("safesearch: unsupported logging level %d", level))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// resetEngine creates new engine for provided safe search configuration and
|
// resetEngine creates new engine for provided safe search configuration and
|
||||||
// sets it in ss.
|
// sets it in ss.
|
||||||
func (ss *Default) resetEngine(
|
func (ss *Default) resetEngine(
|
||||||
|
ctx context.Context,
|
||||||
listID int,
|
listID int,
|
||||||
conf filtering.SafeSearchConfig,
|
conf filtering.SafeSearchConfig,
|
||||||
) (err error) {
|
) (err error) {
|
||||||
if !conf.Enabled {
|
if !conf.Enabled {
|
||||||
ss.log(log.INFO, "disabled")
|
ss.logger.DebugContext(ctx, "disabled")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -149,7 +162,7 @@ func (ss *Default) resetEngine(
|
||||||
|
|
||||||
ss.engine = urlfilter.NewDNSEngine(rs)
|
ss.engine = urlfilter.NewDNSEngine(rs)
|
||||||
|
|
||||||
ss.log(log.INFO, "reset %d rules", ss.engine.RulesCount)
|
ss.logger.InfoContext(ctx, "reset rules", "count", ss.engine.RulesCount)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -158,10 +171,14 @@ func (ss *Default) resetEngine(
|
||||||
var _ filtering.SafeSearch = (*Default)(nil)
|
var _ filtering.SafeSearch = (*Default)(nil)
|
||||||
|
|
||||||
// CheckHost implements the [filtering.SafeSearch] interface for *Default.
|
// CheckHost implements the [filtering.SafeSearch] interface for *Default.
|
||||||
func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Result, err error) {
|
func (ss *Default) CheckHost(
|
||||||
|
ctx context.Context,
|
||||||
|
host string,
|
||||||
|
qtype rules.RRType,
|
||||||
|
) (res filtering.Result, err error) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
defer func() {
|
defer func() {
|
||||||
ss.log(log.DEBUG, "lookup for %q finished in %s", host, time.Since(start))
|
ss.logger.DebugContext(ctx, "lookup finished", "host", host, "elapsed", time.Since(start))
|
||||||
}()
|
}()
|
||||||
|
|
||||||
switch qtype {
|
switch qtype {
|
||||||
|
@ -172,9 +189,9 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check cache. Return cached result if it was found
|
// Check cache. Return cached result if it was found
|
||||||
cachedValue, isFound := ss.getCachedResult(host, qtype)
|
cachedValue, isFound := ss.getCachedResult(ctx, host, qtype)
|
||||||
if isFound {
|
if isFound {
|
||||||
ss.log(log.DEBUG, "found in cache: %q", host)
|
ss.logger.DebugContext(ctx, "found in cache", "host", host)
|
||||||
|
|
||||||
return cachedValue, nil
|
return cachedValue, nil
|
||||||
}
|
}
|
||||||
|
@ -186,7 +203,7 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
|
||||||
|
|
||||||
fltRes, err := ss.newResult(rewrite, qtype)
|
fltRes, err := ss.newResult(rewrite, qtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ss.log(log.DEBUG, "looking up addresses for %q: %s", host, err)
|
ss.logger.ErrorContext(ctx, "looking up addresses", "host", host, slogutil.KeyError, err)
|
||||||
|
|
||||||
return filtering.Result{}, err
|
return filtering.Result{}, err
|
||||||
}
|
}
|
||||||
|
@ -195,7 +212,7 @@ func (ss *Default) CheckHost(host string, qtype rules.RRType) (res filtering.Res
|
||||||
|
|
||||||
// TODO(a.garipov): Consider switch back to resolving CNAME records IPs and
|
// TODO(a.garipov): Consider switch back to resolving CNAME records IPs and
|
||||||
// saving results to cache.
|
// saving results to cache.
|
||||||
ss.setCacheResult(host, qtype, res)
|
ss.setCacheResult(ctx, host, qtype, res)
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
@ -255,7 +272,12 @@ func (ss *Default) newResult(
|
||||||
|
|
||||||
// setCacheResult stores data in cache for host. qtype is expected to be either
|
// setCacheResult stores data in cache for host. qtype is expected to be either
|
||||||
// [dns.TypeA] or [dns.TypeAAAA].
|
// [dns.TypeA] or [dns.TypeAAAA].
|
||||||
func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering.Result) {
|
func (ss *Default) setCacheResult(
|
||||||
|
ctx context.Context,
|
||||||
|
host string,
|
||||||
|
qtype rules.RRType,
|
||||||
|
res filtering.Result,
|
||||||
|
) {
|
||||||
expire := uint32(time.Now().Add(ss.cacheTTL).Unix())
|
expire := uint32(time.Now().Add(ss.cacheTTL).Unix())
|
||||||
exp := make([]byte, 4)
|
exp := make([]byte, 4)
|
||||||
binary.BigEndian.PutUint32(exp, expire)
|
binary.BigEndian.PutUint32(exp, expire)
|
||||||
|
@ -263,7 +285,7 @@ func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering
|
||||||
|
|
||||||
err := gob.NewEncoder(buf).Encode(res)
|
err := gob.NewEncoder(buf).Encode(res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ss.log(log.ERROR, "cache encoding: %s", err)
|
ss.logger.ErrorContext(ctx, "cache encoding", slogutil.KeyError, err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -271,12 +293,18 @@ func (ss *Default) setCacheResult(host string, qtype rules.RRType, res filtering
|
||||||
val := buf.Bytes()
|
val := buf.Bytes()
|
||||||
_ = ss.cache.Set([]byte(dns.Type(qtype).String()+" "+host), val)
|
_ = ss.cache.Set([]byte(dns.Type(qtype).String()+" "+host), val)
|
||||||
|
|
||||||
ss.log(log.DEBUG, "stored in cache: %q, %d bytes", host, len(val))
|
ss.logger.DebugContext(
|
||||||
|
ctx,
|
||||||
|
"stored in cache",
|
||||||
|
"host", host,
|
||||||
|
"entry_size", datasize.ByteSize(len(val)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCachedResult returns stored data from cache for host. qtype is expected
|
// getCachedResult returns stored data from cache for host. qtype is expected
|
||||||
// to be either [dns.TypeA] or [dns.TypeAAAA].
|
// to be either [dns.TypeA] or [dns.TypeAAAA].
|
||||||
func (ss *Default) getCachedResult(
|
func (ss *Default) getCachedResult(
|
||||||
|
ctx context.Context,
|
||||||
host string,
|
host string,
|
||||||
qtype rules.RRType,
|
qtype rules.RRType,
|
||||||
) (res filtering.Result, ok bool) {
|
) (res filtering.Result, ok bool) {
|
||||||
|
@ -298,7 +326,7 @@ func (ss *Default) getCachedResult(
|
||||||
|
|
||||||
err := gob.NewDecoder(buf).Decode(&res)
|
err := gob.NewDecoder(buf).Decode(&res)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ss.log(log.ERROR, "cache decoding: %s", err)
|
ss.logger.ErrorContext(ctx, "cache decoding", slogutil.KeyError, err)
|
||||||
|
|
||||||
return filtering.Result{}, false
|
return filtering.Result{}, false
|
||||||
}
|
}
|
||||||
|
@ -308,11 +336,11 @@ func (ss *Default) getCachedResult(
|
||||||
|
|
||||||
// Update implements the [filtering.SafeSearch] interface for *Default. Update
|
// Update implements the [filtering.SafeSearch] interface for *Default. Update
|
||||||
// ignores the CustomResolver and Enabled fields.
|
// ignores the CustomResolver and Enabled fields.
|
||||||
func (ss *Default) Update(conf filtering.SafeSearchConfig) (err error) {
|
func (ss *Default) Update(ctx context.Context, conf filtering.SafeSearchConfig) (err error) {
|
||||||
ss.mu.Lock()
|
ss.mu.Lock()
|
||||||
defer ss.mu.Unlock()
|
defer ss.mu.Unlock()
|
||||||
|
|
||||||
err = ss.resetEngine(rulelist.URLFilterIDSafeSearch, conf)
|
err = ss.resetEngine(ctx, rulelist.URLFilterIDSafeSearch, conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error, because it's informative enough as is.
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/AdguardTeam/urlfilter/rules"
|
"github.com/AdguardTeam/urlfilter/rules"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
@ -21,6 +23,9 @@ const (
|
||||||
testCacheTTL = 30 * time.Minute
|
testCacheTTL = 30 * time.Minute
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// testTimeout is the common timeout for tests and contexts.
|
||||||
|
const testTimeout = 1 * time.Second
|
||||||
|
|
||||||
var defaultSafeSearchConf = filtering.SafeSearchConfig{
|
var defaultSafeSearchConf = filtering.SafeSearchConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Bing: true,
|
Bing: true,
|
||||||
|
@ -35,7 +40,12 @@ var defaultSafeSearchConf = filtering.SafeSearchConfig{
|
||||||
var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
|
var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
|
||||||
|
|
||||||
func newForTest(t testing.TB, ssConf filtering.SafeSearchConfig) (ss *Default) {
|
func newForTest(t testing.TB, ssConf filtering.SafeSearchConfig) (ss *Default) {
|
||||||
ss, err := NewDefault(ssConf, "", testCacheSize, testCacheTTL)
|
ss, err := NewDefault(testutil.ContextWithTimeout(t, testTimeout), &DefaultConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
ServicesConfig: ssConf,
|
||||||
|
CacheSize: testCacheSize,
|
||||||
|
CacheTTL: testCacheTTL,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
return ss
|
return ss
|
||||||
|
@ -52,16 +62,17 @@ func TestSafeSearchCacheYandex(t *testing.T) {
|
||||||
const domain = "yandex.ru"
|
const domain = "yandex.ru"
|
||||||
|
|
||||||
ss := newForTest(t, filtering.SafeSearchConfig{Enabled: false})
|
ss := newForTest(t, filtering.SafeSearchConfig{Enabled: false})
|
||||||
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
|
||||||
// Check host with disabled safesearch.
|
// Check host with disabled safesearch.
|
||||||
res, err := ss.CheckHost(domain, testQType)
|
res, err := ss.CheckHost(ctx, domain, testQType)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.False(t, res.IsFiltered)
|
assert.False(t, res.IsFiltered)
|
||||||
assert.Empty(t, res.Rules)
|
assert.Empty(t, res.Rules)
|
||||||
|
|
||||||
ss = newForTest(t, defaultSafeSearchConf)
|
ss = newForTest(t, defaultSafeSearchConf)
|
||||||
res, err = ss.CheckHost(domain, testQType)
|
res, err = ss.CheckHost(ctx, domain, testQType)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// For yandex we already know valid IP.
|
// For yandex we already know valid IP.
|
||||||
|
@ -70,7 +81,7 @@ func TestSafeSearchCacheYandex(t *testing.T) {
|
||||||
assert.Equal(t, res.Rules[0].IP, yandexIP)
|
assert.Equal(t, res.Rules[0].IP, yandexIP)
|
||||||
|
|
||||||
// Check cache.
|
// Check cache.
|
||||||
cachedValue, isFound := ss.getCachedResult(domain, testQType)
|
cachedValue, isFound := ss.getCachedResult(ctx, domain, testQType)
|
||||||
require.True(t, isFound)
|
require.True(t, isFound)
|
||||||
require.Len(t, cachedValue.Rules, 1)
|
require.Len(t, cachedValue.Rules, 1)
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,15 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/rulelist"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/testutil"
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/miekg/dns"
|
"github.com/miekg/dns"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
// testTimeout is the common timeout for tests and contexts.
|
||||||
testutil.DiscardLogOutput(m)
|
const testTimeout = 1 * time.Second
|
||||||
}
|
|
||||||
|
|
||||||
// Common test constants.
|
// Common test constants.
|
||||||
const (
|
const (
|
||||||
|
@ -47,7 +47,13 @@ var yandexIP = netip.AddrFrom4([4]byte{213, 180, 193, 56})
|
||||||
|
|
||||||
func TestDefault_CheckHost_yandex(t *testing.T) {
|
func TestDefault_CheckHost_yandex(t *testing.T) {
|
||||||
conf := testConf
|
conf := testConf
|
||||||
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
ServicesConfig: conf,
|
||||||
|
CacheSize: testCacheSize,
|
||||||
|
CacheTTL: testCacheTTL,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
hosts := []string{
|
hosts := []string{
|
||||||
|
@ -82,7 +88,7 @@ func TestDefault_CheckHost_yandex(t *testing.T) {
|
||||||
for _, host := range hosts {
|
for _, host := range hosts {
|
||||||
// Check host for each domain.
|
// Check host for each domain.
|
||||||
var res filtering.Result
|
var res filtering.Result
|
||||||
res, err = ss.CheckHost(host, tc.qt)
|
res, err = ss.CheckHost(ctx, host, tc.qt)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, res.IsFiltered)
|
assert.True(t, res.IsFiltered)
|
||||||
|
@ -103,7 +109,13 @@ func TestDefault_CheckHost_yandex(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefault_CheckHost_google(t *testing.T) {
|
func TestDefault_CheckHost_google(t *testing.T) {
|
||||||
ss, err := safesearch.NewDefault(testConf, "", testCacheSize, testCacheTTL)
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
ServicesConfig: testConf,
|
||||||
|
CacheSize: testCacheSize,
|
||||||
|
CacheTTL: testCacheTTL,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Check host for each domain.
|
// Check host for each domain.
|
||||||
|
@ -118,7 +130,7 @@ func TestDefault_CheckHost_google(t *testing.T) {
|
||||||
} {
|
} {
|
||||||
t.Run(host, func(t *testing.T) {
|
t.Run(host, func(t *testing.T) {
|
||||||
var res filtering.Result
|
var res filtering.Result
|
||||||
res, err = ss.CheckHost(host, testQType)
|
res, err = ss.CheckHost(ctx, host, testQType)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, res.IsFiltered)
|
assert.True(t, res.IsFiltered)
|
||||||
|
@ -149,13 +161,19 @@ func (r *testResolver) LookupIP(
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
|
func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
|
||||||
ss, err := safesearch.NewDefault(testConf, "", testCacheSize, testCacheTTL)
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
ServicesConfig: testConf,
|
||||||
|
CacheSize: testCacheSize,
|
||||||
|
CacheTTL: testCacheTTL,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// The DuckDuckGo safe-search addresses are resolved through CNAMEs, but
|
// The DuckDuckGo safe-search addresses are resolved through CNAMEs, but
|
||||||
// DuckDuckGo doesn't have a safe-search IPv6 address. The result should be
|
// DuckDuckGo doesn't have a safe-search IPv6 address. The result should be
|
||||||
// the same as the one for Yandex IPv6. That is, a NODATA response.
|
// the same as the one for Yandex IPv6. That is, a NODATA response.
|
||||||
res, err := ss.CheckHost("www.duckduckgo.com", dns.TypeAAAA)
|
res, err := ss.CheckHost(ctx, "www.duckduckgo.com", dns.TypeAAAA)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, res.IsFiltered)
|
assert.True(t, res.IsFiltered)
|
||||||
|
@ -166,32 +184,38 @@ func TestDefault_CheckHost_duckduckgoAAAA(t *testing.T) {
|
||||||
|
|
||||||
func TestDefault_Update(t *testing.T) {
|
func TestDefault_Update(t *testing.T) {
|
||||||
conf := testConf
|
conf := testConf
|
||||||
ss, err := safesearch.NewDefault(conf, "", testCacheSize, testCacheTTL)
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
ss, err := safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
ServicesConfig: conf,
|
||||||
|
CacheSize: testCacheSize,
|
||||||
|
CacheTTL: testCacheTTL,
|
||||||
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
res, err := ss.CheckHost("www.yandex.com", testQType)
|
res, err := ss.CheckHost(ctx, "www.yandex.com", testQType)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.True(t, res.IsFiltered)
|
assert.True(t, res.IsFiltered)
|
||||||
|
|
||||||
err = ss.Update(filtering.SafeSearchConfig{
|
err = ss.Update(ctx, filtering.SafeSearchConfig{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
Google: false,
|
Google: false,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
res, err = ss.CheckHost("www.yandex.com", testQType)
|
res, err = ss.CheckHost(ctx, "www.yandex.com", testQType)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.False(t, res.IsFiltered)
|
assert.False(t, res.IsFiltered)
|
||||||
|
|
||||||
err = ss.Update(filtering.SafeSearchConfig{
|
err = ss.Update(ctx, filtering.SafeSearchConfig{
|
||||||
Enabled: false,
|
Enabled: false,
|
||||||
Google: true,
|
Google: true,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
res, err = ss.CheckHost("www.yandex.com", testQType)
|
res, err = ss.CheckHost(ctx, "www.yandex.com", testQType)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assert.False(t, res.IsFiltered)
|
assert.False(t, res.IsFiltered)
|
||||||
|
|
|
@ -51,7 +51,7 @@ func (d *DNSFilter) handleSafeSearchSettings(w http.ResponseWriter, r *http.Requ
|
||||||
}
|
}
|
||||||
|
|
||||||
conf := *req
|
conf := *req
|
||||||
err = d.safeSearch.Update(conf)
|
err = d.safeSearch.Update(r.Context(), conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "updating: %s", err)
|
aghhttp.Error(r, w, http.StatusBadRequest, "updating: %s", err)
|
||||||
|
|
||||||
|
|
|
@ -2450,7 +2450,7 @@ var blockedServices = []blockedService{{
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
ID: "telegram",
|
ID: "telegram",
|
||||||
Name: "Telegram",
|
Name: "Telegram (Web)",
|
||||||
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M46.137,6.552c-0.75-0.636-1.928-0.727-3.146-0.238l-0.002,0C41.708,6.828,6.728,21.832,5.304,22.445 c-0.259,0.09-2.521,0.934-2.288,2.814c0.208,1.695,2.026,2.397,2.248,2.478l8.893,3.045c0.59,1.964,2.765,9.21,3.246,10.758 c0.3,0.965,0.789,2.233,1.646,2.494c0.752,0.29,1.5,0.025,1.984-0.355l5.437-5.043l8.777,6.845l0.209,0.125 c0.596,0.264,1.167,0.396,1.712,0.396c0.421,0,0.825-0.079,1.211-0.237c1.315-0.54,1.841-1.793,1.896-1.935l6.556-34.077 C47.231,7.933,46.675,7.007,46.137,6.552z M22,32l-3,8l-3-10l23-17L22,32z\" /></svg>"),
|
IconSVG: []byte("<svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"currentColor\" viewBox=\"0 0 50 50\"><path d=\"M46.137,6.552c-0.75-0.636-1.928-0.727-3.146-0.238l-0.002,0C41.708,6.828,6.728,21.832,5.304,22.445 c-0.259,0.09-2.521,0.934-2.288,2.814c0.208,1.695,2.026,2.397,2.248,2.478l8.893,3.045c0.59,1.964,2.765,9.21,3.246,10.758 c0.3,0.965,0.789,2.233,1.646,2.494c0.752,0.29,1.5,0.025,1.984-0.355l5.437-5.043l8.777,6.845l0.209,0.125 c0.596,0.264,1.167,0.396,1.712,0.396c0.421,0,0.825-0.079,1.211-0.237c1.315-0.54,1.841-1.793,1.896-1.935l6.556-34.077 C47.231,7.933,46.675,7.007,46.137,6.552z M22,32l-3,8l-3-10l23-17L22,32z\" /></svg>"),
|
||||||
Rules: []string{
|
Rules: []string{
|
||||||
"||comments.app^",
|
"||comments.app^",
|
||||||
|
|
|
@ -90,7 +90,11 @@ func InitAuth(
|
||||||
trustedProxies: trustedProxies,
|
trustedProxies: trustedProxies,
|
||||||
}
|
}
|
||||||
var err error
|
var err error
|
||||||
a.db, err = bbolt.Open(dbFilename, aghos.DefaultPermFile, nil)
|
|
||||||
|
opts := *bbolt.DefaultOptions
|
||||||
|
opts.OpenFile = aghos.OpenFile
|
||||||
|
|
||||||
|
a.db, err = bbolt.Open(dbFilename, aghos.DefaultPermFile, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("auth: open DB: %s: %s", dbFilename, err)
|
log.Error("auth: open DB: %s: %s", dbFilename, err)
|
||||||
if err.Error() == "invalid argument" {
|
if err.Error() == "invalid argument" {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package home
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"slices"
|
"slices"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -13,17 +14,23 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||||
"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/schedule"
|
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||||
"github.com/AdguardTeam/dnsproxy/proxy"
|
"github.com/AdguardTeam/dnsproxy/proxy"
|
||||||
"github.com/AdguardTeam/dnsproxy/upstream"
|
"github.com/AdguardTeam/dnsproxy/upstream"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
"github.com/AdguardTeam/golibs/stringutil"
|
"github.com/AdguardTeam/golibs/stringutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// clientsContainer is the storage of all runtime and persistent clients.
|
// clientsContainer is the storage of all runtime and persistent clients.
|
||||||
type clientsContainer struct {
|
type clientsContainer struct {
|
||||||
|
// baseLogger is used to create loggers with custom prefixes for safe search
|
||||||
|
// filter. It must not be nil.
|
||||||
|
baseLogger *slog.Logger
|
||||||
|
|
||||||
// storage stores information about persistent clients.
|
// storage stores information about persistent clients.
|
||||||
storage *client.Storage
|
storage *client.Storage
|
||||||
|
|
||||||
|
@ -61,6 +68,8 @@ type BlockedClientChecker interface {
|
||||||
// dhcpServer: optional
|
// dhcpServer: optional
|
||||||
// Note: this function must be called only once
|
// Note: this function must be called only once
|
||||||
func (clients *clientsContainer) Init(
|
func (clients *clientsContainer) Init(
|
||||||
|
ctx context.Context,
|
||||||
|
baseLogger *slog.Logger,
|
||||||
objects []*clientObject,
|
objects []*clientObject,
|
||||||
dhcpServer client.DHCP,
|
dhcpServer client.DHCP,
|
||||||
etcHosts *aghnet.HostsContainer,
|
etcHosts *aghnet.HostsContainer,
|
||||||
|
@ -72,13 +81,14 @@ func (clients *clientsContainer) Init(
|
||||||
return errors.Error("clients container already initialized")
|
return errors.Error("clients container already initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clients.baseLogger = baseLogger
|
||||||
clients.safeSearchCacheSize = filteringConf.SafeSearchCacheSize
|
clients.safeSearchCacheSize = filteringConf.SafeSearchCacheSize
|
||||||
clients.safeSearchCacheTTL = time.Minute * time.Duration(filteringConf.CacheTime)
|
clients.safeSearchCacheTTL = time.Minute * time.Duration(filteringConf.CacheTime)
|
||||||
|
|
||||||
confClients := make([]*client.Persistent, 0, len(objects))
|
confClients := make([]*client.Persistent, 0, len(objects))
|
||||||
for i, o := range objects {
|
for i, o := range objects {
|
||||||
var p *client.Persistent
|
var p *client.Persistent
|
||||||
p, err = o.toPersistent(clients.safeSearchCacheSize, clients.safeSearchCacheTTL)
|
p, err = o.toPersistent(ctx, baseLogger, clients.safeSearchCacheSize, clients.safeSearchCacheTTL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("init persistent client at index %d: %w", i, err)
|
return fmt.Errorf("init persistent client at index %d: %w", i, err)
|
||||||
}
|
}
|
||||||
|
@ -92,12 +102,13 @@ func (clients *clientsContainer) Init(
|
||||||
// TODO(e.burkov): The option should probably be returned, since hosts file
|
// TODO(e.burkov): The option should probably be returned, since hosts file
|
||||||
// currently used not only for clients' information enrichment, but also in
|
// currently used not only for clients' information enrichment, but also in
|
||||||
// the filtering module and upstream addresses resolution.
|
// the filtering module and upstream addresses resolution.
|
||||||
var hosts client.HostsContainer = etcHosts
|
var hosts client.HostsContainer
|
||||||
if !config.Clients.Sources.HostsFile {
|
if config.Clients.Sources.HostsFile && etcHosts != nil {
|
||||||
hosts = nil
|
hosts = etcHosts
|
||||||
}
|
}
|
||||||
|
|
||||||
clients.storage, err = client.NewStorage(&client.StorageConfig{
|
clients.storage, err = client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: baseLogger.With(slogutil.KeyPrefix, "client_storage"),
|
||||||
InitialClients: confClients,
|
InitialClients: confClients,
|
||||||
DHCP: dhcpServer,
|
DHCP: dhcpServer,
|
||||||
EtcHosts: hosts,
|
EtcHosts: hosts,
|
||||||
|
@ -168,6 +179,8 @@ type clientObject struct {
|
||||||
|
|
||||||
// toPersistent returns an initialized persistent client if there are no errors.
|
// toPersistent returns an initialized persistent client if there are no errors.
|
||||||
func (o *clientObject) toPersistent(
|
func (o *clientObject) toPersistent(
|
||||||
|
ctx context.Context,
|
||||||
|
baseLogger *slog.Logger,
|
||||||
safeSearchCacheSize uint,
|
safeSearchCacheSize uint,
|
||||||
safeSearchCacheTTL time.Duration,
|
safeSearchCacheTTL time.Duration,
|
||||||
) (cli *client.Persistent, err error) {
|
) (cli *client.Persistent, err error) {
|
||||||
|
@ -203,14 +216,23 @@ func (o *clientObject) toPersistent(
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.SafeSearchConf.Enabled {
|
if o.SafeSearchConf.Enabled {
|
||||||
err = cli.SetSafeSearch(
|
logger := baseLogger.With(
|
||||||
o.SafeSearchConf,
|
slogutil.KeyPrefix, safesearch.LogPrefix,
|
||||||
safeSearchCacheSize,
|
safesearch.LogKeyClient, cli.Name,
|
||||||
safeSearchCacheTTL,
|
|
||||||
)
|
)
|
||||||
|
var ss *safesearch.Default
|
||||||
|
ss, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
|
Logger: logger,
|
||||||
|
ServicesConfig: o.SafeSearchConf,
|
||||||
|
ClientName: cli.Name,
|
||||||
|
CacheSize: safeSearchCacheSize,
|
||||||
|
CacheTTL: safeSearchCacheTTL,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("init safesearch %q: %w", cli.Name, err)
|
return nil, fmt.Errorf("init safesearch %q: %w", cli.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cli.SafeSearch = ss
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.BlockedServices == nil {
|
if o.BlockedServices == nil {
|
||||||
|
@ -396,6 +418,12 @@ func (clients *clientsContainer) UpstreamConfigByID(
|
||||||
)
|
)
|
||||||
c.UpstreamConfig = conf
|
c.UpstreamConfig = conf
|
||||||
|
|
||||||
|
// TODO(s.chzhen): Pass context.
|
||||||
|
err = clients.storage.Update(context.TODO(), c.Name, c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("setting upstream config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return conf, nil
|
return conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,8 +432,13 @@ var _ client.AddressUpdater = (*clientsContainer)(nil)
|
||||||
|
|
||||||
// UpdateAddress implements the [client.AddressUpdater] interface for
|
// UpdateAddress implements the [client.AddressUpdater] interface for
|
||||||
// *clientsContainer
|
// *clientsContainer
|
||||||
func (clients *clientsContainer) UpdateAddress(ip netip.Addr, host string, info *whois.Info) {
|
func (clients *clientsContainer) UpdateAddress(
|
||||||
clients.storage.UpdateAddress(ip, host, info)
|
ctx context.Context,
|
||||||
|
ip netip.Addr,
|
||||||
|
host string,
|
||||||
|
info *whois.Info,
|
||||||
|
) {
|
||||||
|
clients.storage.UpdateAddress(ctx, ip, host, info)
|
||||||
}
|
}
|
||||||
|
|
||||||
// close gracefully closes all the client-specific upstream configurations of
|
// close gracefully closes all the client-specific upstream configurations of
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -20,16 +22,28 @@ func newClientsContainer(t *testing.T) (c *clientsContainer) {
|
||||||
testing: true,
|
testing: true,
|
||||||
}
|
}
|
||||||
|
|
||||||
require.NoError(t, c.Init(nil, client.EmptyDHCP{}, nil, nil, &filtering.Config{}))
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
err := c.Init(
|
||||||
|
ctx,
|
||||||
|
slogutil.NewDiscardLogger(),
|
||||||
|
nil,
|
||||||
|
client.EmptyDHCP{},
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
&filtering.Config{},
|
||||||
|
)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestClientsCustomUpstream(t *testing.T) {
|
func TestClientsCustomUpstream(t *testing.T) {
|
||||||
clients := newClientsContainer(t)
|
clients := newClientsContainer(t)
|
||||||
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
|
||||||
// Add client with upstreams.
|
// Add client with upstreams.
|
||||||
err := clients.storage.Add(&client.Persistent{
|
err := clients.storage.Add(ctx, &client.Persistent{
|
||||||
Name: "client1",
|
Name: "client1",
|
||||||
UID: client.MustNewUID(),
|
UID: client.MustNewUID(),
|
||||||
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("1:2:3::4")},
|
IPs: []netip.Addr{netip.MustParseAddr("1.1.1.1"), netip.MustParseAddr("1:2:3::4")},
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package home
|
package home
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -10,8 +11,10 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering/safesearch"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
"github.com/AdguardTeam/AdGuardHome/internal/whois"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// clientJSON is a common structure used by several handlers to deal with
|
// clientJSON is a common structure used by several handlers to deal with
|
||||||
|
@ -103,7 +106,7 @@ func (clients *clientsContainer) handleGetClients(w http.ResponseWriter, r *http
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
|
|
||||||
clients.storage.UpdateDHCP()
|
clients.storage.UpdateDHCP(r.Context())
|
||||||
|
|
||||||
clients.storage.RangeRuntime(func(rc *client.Runtime) (cont bool) {
|
clients.storage.RangeRuntime(func(rc *client.Runtime) (cont bool) {
|
||||||
src, host := rc.Info()
|
src, host := rc.Info()
|
||||||
|
@ -181,6 +184,7 @@ func initPrev(cj clientJSON, prev *client.Persistent) (c *client.Persistent, err
|
||||||
// jsonToClient converts JSON object to persistent client object if there are no
|
// jsonToClient converts JSON object to persistent client object if there are no
|
||||||
// errors.
|
// errors.
|
||||||
func (clients *clientsContainer) jsonToClient(
|
func (clients *clientsContainer) jsonToClient(
|
||||||
|
ctx context.Context,
|
||||||
cj clientJSON,
|
cj clientJSON,
|
||||||
prev *client.Persistent,
|
prev *client.Persistent,
|
||||||
) (c *client.Persistent, err error) {
|
) (c *client.Persistent, err error) {
|
||||||
|
@ -207,14 +211,23 @@ func (clients *clientsContainer) jsonToClient(
|
||||||
c.UseOwnBlockedServices = !cj.UseGlobalBlockedServices
|
c.UseOwnBlockedServices = !cj.UseGlobalBlockedServices
|
||||||
|
|
||||||
if c.SafeSearchConf.Enabled {
|
if c.SafeSearchConf.Enabled {
|
||||||
err = c.SetSafeSearch(
|
logger := clients.baseLogger.With(
|
||||||
c.SafeSearchConf,
|
slogutil.KeyPrefix, safesearch.LogPrefix,
|
||||||
clients.safeSearchCacheSize,
|
safesearch.LogKeyClient, c.Name,
|
||||||
clients.safeSearchCacheTTL,
|
|
||||||
)
|
)
|
||||||
|
var ss *safesearch.Default
|
||||||
|
ss, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
|
Logger: logger,
|
||||||
|
ServicesConfig: c.SafeSearchConf,
|
||||||
|
ClientName: c.Name,
|
||||||
|
CacheSize: clients.safeSearchCacheSize,
|
||||||
|
CacheTTL: clients.safeSearchCacheTTL,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("creating safesearch for client %q: %w", c.Name, err)
|
return nil, fmt.Errorf("creating safesearch for client %q: %w", c.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.SafeSearch = ss
|
||||||
}
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
|
@ -321,14 +334,14 @@ func (clients *clientsContainer) handleAddClient(w http.ResponseWriter, r *http.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := clients.jsonToClient(cj, nil)
|
c, err := clients.jsonToClient(r.Context(), cj, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = clients.storage.Add(c)
|
err = clients.storage.Add(r.Context(), c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||||
|
|
||||||
|
@ -391,14 +404,14 @@ func (clients *clientsContainer) handleUpdateClient(w http.ResponseWriter, r *ht
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c, err := clients.jsonToClient(dj.Data, nil)
|
c, err := clients.jsonToClient(r.Context(), dj.Data, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = clients.storage.Update(dj.Name, c)
|
err = clients.storage.Update(r.Context(), dj.Name, c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
aghhttp.Error(r, w, http.StatusBadRequest, "%s", err)
|
||||||
|
|
||||||
|
|
|
@ -11,14 +11,19 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
||||||
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// testTimeout is the common timeout for tests and contexts.
|
||||||
|
const testTimeout = 1 * time.Second
|
||||||
|
|
||||||
const (
|
const (
|
||||||
testClientIP1 = "1.1.1.1"
|
testClientIP1 = "1.1.1.1"
|
||||||
testClientIP2 = "2.2.2.2"
|
testClientIP2 = "2.2.2.2"
|
||||||
|
@ -103,9 +108,10 @@ func assertPersistentClients(tb testing.TB, clients *clientsContainer, want []*c
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
var got []*client.Persistent
|
var got []*client.Persistent
|
||||||
|
ctx := testutil.ContextWithTimeout(tb, testTimeout)
|
||||||
for _, cj := range clientList.Clients {
|
for _, cj := range clientList.Clients {
|
||||||
var c *client.Persistent
|
var c *client.Persistent
|
||||||
c, err = clients.jsonToClient(*cj, nil)
|
c, err = clients.jsonToClient(ctx, *cj, nil)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
got = append(got, c)
|
got = append(got, c)
|
||||||
|
@ -125,10 +131,11 @@ func assertPersistentClientsData(
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
var got []*client.Persistent
|
var got []*client.Persistent
|
||||||
|
ctx := testutil.ContextWithTimeout(tb, testTimeout)
|
||||||
for _, cm := range data {
|
for _, cm := range data {
|
||||||
for _, cj := range cm {
|
for _, cj := range cm {
|
||||||
var c *client.Persistent
|
var c *client.Persistent
|
||||||
c, err := clients.jsonToClient(*cj, nil)
|
c, err := clients.jsonToClient(ctx, *cj, nil)
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
got = append(got, c)
|
got = append(got, c)
|
||||||
|
@ -196,13 +203,14 @@ func TestClientsContainer_HandleAddClient(t *testing.T) {
|
||||||
|
|
||||||
func TestClientsContainer_HandleDelClient(t *testing.T) {
|
func TestClientsContainer_HandleDelClient(t *testing.T) {
|
||||||
clients := newClientsContainer(t)
|
clients := newClientsContainer(t)
|
||||||
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
|
||||||
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
|
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
|
||||||
err := clients.storage.Add(clientOne)
|
err := clients.storage.Add(ctx, clientOne)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
|
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
|
||||||
err = clients.storage.Add(clientTwo)
|
err = clients.storage.Add(ctx, clientTwo)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
|
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
|
||||||
|
@ -258,9 +266,10 @@ func TestClientsContainer_HandleDelClient(t *testing.T) {
|
||||||
|
|
||||||
func TestClientsContainer_HandleUpdateClient(t *testing.T) {
|
func TestClientsContainer_HandleUpdateClient(t *testing.T) {
|
||||||
clients := newClientsContainer(t)
|
clients := newClientsContainer(t)
|
||||||
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
|
||||||
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
|
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
|
||||||
err := clients.storage.Add(clientOne)
|
err := clients.storage.Add(ctx, clientOne)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assertPersistentClients(t, clients, []*client.Persistent{clientOne})
|
assertPersistentClients(t, clients, []*client.Persistent{clientOne})
|
||||||
|
@ -341,12 +350,14 @@ func TestClientsContainer_HandleFindClient(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx := testutil.ContextWithTimeout(t, testTimeout)
|
||||||
|
|
||||||
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
|
clientOne := newPersistentClientWithIDs(t, "client1", []string{testClientIP1})
|
||||||
err := clients.storage.Add(clientOne)
|
err := clients.storage.Add(ctx, clientOne)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
|
clientTwo := newPersistentClientWithIDs(t, "client2", []string{testClientIP2})
|
||||||
err = clients.storage.Add(clientTwo)
|
err = clients.storage.Add(ctx, clientTwo)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
|
assertPersistentClients(t, clients, []*client.Persistent{clientOne, clientTwo})
|
||||||
|
|
|
@ -708,7 +708,7 @@ func (c *configuration) write() (err error) {
|
||||||
return fmt.Errorf("generating config file: %w", err)
|
return fmt.Errorf("generating config file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = maybe.WriteFile(confPath, buf.Bytes(), aghos.DefaultPermFile)
|
err = aghos.WriteFile(confPath, buf.Bytes(), aghos.DefaultPermFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("writing config file: %w", err)
|
return fmt.Errorf("writing config file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,8 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
"github.com/AdguardTeam/AdGuardHome/internal/client"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
"github.com/AdguardTeam/AdGuardHome/internal/filtering"
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
"github.com/AdguardTeam/AdGuardHome/internal/schedule"
|
||||||
|
"github.com/AdguardTeam/golibs/logutil/slogutil"
|
||||||
|
"github.com/AdguardTeam/golibs/testutil"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -18,12 +20,15 @@ var testIPv4 = netip.AddrFrom4([4]byte{1, 2, 3, 4})
|
||||||
func newStorage(tb testing.TB, clients []*client.Persistent) (s *client.Storage) {
|
func newStorage(tb testing.TB, clients []*client.Persistent) (s *client.Storage) {
|
||||||
tb.Helper()
|
tb.Helper()
|
||||||
|
|
||||||
s, err := client.NewStorage(&client.StorageConfig{})
|
ctx := testutil.ContextWithTimeout(tb, testTimeout)
|
||||||
|
s, err := client.NewStorage(ctx, &client.StorageConfig{
|
||||||
|
Logger: slogutil.NewDiscardLogger(),
|
||||||
|
})
|
||||||
require.NoError(tb, err)
|
require.NoError(tb, err)
|
||||||
|
|
||||||
for _, p := range clients {
|
for _, p := range clients {
|
||||||
p.UID = client.MustNewUID()
|
p.UID = client.MustNewUID()
|
||||||
require.NoError(tb, s.Add(p))
|
require.NoError(tb, s.Add(ctx, p))
|
||||||
}
|
}
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
|
|
@ -115,15 +115,16 @@ func Main(clientBuildFS fs.FS) {
|
||||||
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP, syscall.SIGQUIT)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
ctx := context.Background()
|
||||||
for {
|
for {
|
||||||
sig := <-signals
|
sig := <-signals
|
||||||
log.Info("Received signal %q", sig)
|
log.Info("Received signal %q", sig)
|
||||||
switch sig {
|
switch sig {
|
||||||
case syscall.SIGHUP:
|
case syscall.SIGHUP:
|
||||||
Context.clients.storage.ReloadARP()
|
Context.clients.storage.ReloadARP(ctx)
|
||||||
Context.tls.reload()
|
Context.tls.reload()
|
||||||
default:
|
default:
|
||||||
cleanup(context.Background())
|
cleanup(ctx)
|
||||||
cleanupAlways()
|
cleanupAlways()
|
||||||
close(done)
|
close(done)
|
||||||
}
|
}
|
||||||
|
@ -148,6 +149,14 @@ func setupContext(opts options) (err error) {
|
||||||
Context.tlsRoots = aghtls.SystemRootCAs()
|
Context.tlsRoots = aghtls.SystemRootCAs()
|
||||||
Context.mux = http.NewServeMux()
|
Context.mux = http.NewServeMux()
|
||||||
|
|
||||||
|
if !opts.noEtcHosts {
|
||||||
|
err = setupHostsContainer()
|
||||||
|
if err != nil {
|
||||||
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if Context.firstRun {
|
if Context.firstRun {
|
||||||
log.Info("This is the first time AdGuard Home is launched")
|
log.Info("This is the first time AdGuard Home is launched")
|
||||||
checkPermissions()
|
checkPermissions()
|
||||||
|
@ -168,14 +177,6 @@ func setupContext(opts options) (err error) {
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !opts.noEtcHosts {
|
|
||||||
err = setupHostsContainer()
|
|
||||||
if err != nil {
|
|
||||||
// Don't wrap the error, because it's informative enough as is.
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,8 +279,8 @@ func setupOpts(opts options) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// initContextClients initializes Context clients and related fields.
|
// initContextClients initializes Context clients and related fields.
|
||||||
func initContextClients(logger *slog.Logger) (err error) {
|
func initContextClients(ctx context.Context, logger *slog.Logger) (err error) {
|
||||||
err = setupDNSFilteringConf(config.Filtering)
|
err = setupDNSFilteringConf(ctx, logger, config.Filtering)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Don't wrap the error, because it's informative enough as is.
|
// Don't wrap the error, because it's informative enough as is.
|
||||||
return err
|
return err
|
||||||
|
@ -306,6 +307,8 @@ func initContextClients(logger *slog.Logger) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return Context.clients.Init(
|
return Context.clients.Init(
|
||||||
|
ctx,
|
||||||
|
logger,
|
||||||
config.Clients.Persistent,
|
config.Clients.Persistent,
|
||||||
Context.dhcpServer,
|
Context.dhcpServer,
|
||||||
Context.etcHosts,
|
Context.etcHosts,
|
||||||
|
@ -355,7 +358,11 @@ func setupBindOpts(opts options) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setupDNSFilteringConf sets up DNS filtering configuration settings.
|
// setupDNSFilteringConf sets up DNS filtering configuration settings.
|
||||||
func setupDNSFilteringConf(conf *filtering.Config) (err error) {
|
func setupDNSFilteringConf(
|
||||||
|
ctx context.Context,
|
||||||
|
baseLogger *slog.Logger,
|
||||||
|
conf *filtering.Config,
|
||||||
|
) (err error) {
|
||||||
const (
|
const (
|
||||||
dnsTimeout = 3 * time.Second
|
dnsTimeout = 3 * time.Second
|
||||||
|
|
||||||
|
@ -446,12 +453,13 @@ func setupDNSFilteringConf(conf *filtering.Config) (err error) {
|
||||||
conf.ParentalBlockHost = host
|
conf.ParentalBlockHost = host
|
||||||
}
|
}
|
||||||
|
|
||||||
conf.SafeSearch, err = safesearch.NewDefault(
|
logger := baseLogger.With(slogutil.KeyPrefix, safesearch.LogPrefix)
|
||||||
conf.SafeSearchConf,
|
conf.SafeSearch, err = safesearch.NewDefault(ctx, &safesearch.DefaultConfig{
|
||||||
"default",
|
Logger: logger,
|
||||||
conf.SafeSearchCacheSize,
|
ServicesConfig: conf.SafeSearchConf,
|
||||||
cacheTime,
|
CacheSize: conf.SafeSearchCacheSize,
|
||||||
)
|
CacheTTL: cacheTime,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("initializing safesearch: %w", err)
|
return fmt.Errorf("initializing safesearch: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -584,7 +592,10 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
|
||||||
// data first, but also to avoid relying on automatic Go init() function.
|
// data first, but also to avoid relying on automatic Go init() function.
|
||||||
filtering.InitModule()
|
filtering.InitModule()
|
||||||
|
|
||||||
err = initContextClients(slogLogger)
|
// TODO(s.chzhen): Use it for the entire initialization process.
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
err = initContextClients(ctx, slogLogger)
|
||||||
fatalOnError(err)
|
fatalOnError(err)
|
||||||
|
|
||||||
err = setupOpts(opts)
|
err = setupOpts(opts)
|
||||||
|
@ -632,7 +643,7 @@ func run(opts options, clientBuildFS fs.FS, done chan struct{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
dataDir := Context.getDataDir()
|
dataDir := Context.getDataDir()
|
||||||
err = os.MkdirAll(dataDir, aghos.DefaultPermDir)
|
err = aghos.MkdirAll(dataDir, aghos.DefaultPermDir)
|
||||||
fatalOnError(errors.Annotate(err, "creating DNS data dir at %s: %w", dataDir))
|
fatalOnError(errors.Annotate(err, "creating DNS data dir at %s: %w", dataDir))
|
||||||
|
|
||||||
GLMode = opts.glinetMode
|
GLMode = opts.glinetMode
|
||||||
|
|
|
@ -24,10 +24,15 @@ func newSlogLogger(ls *logSettings) (l *slog.Logger) {
|
||||||
return slogutil.NewDiscardLogger()
|
return slogutil.NewDiscardLogger()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
lvl := slog.LevelInfo
|
||||||
|
if ls.Verbose {
|
||||||
|
lvl = slog.LevelDebug
|
||||||
|
}
|
||||||
|
|
||||||
return slogutil.New(&slogutil.Config{
|
return slogutil.New(&slogutil.Config{
|
||||||
Format: slogutil.FormatAdGuardLegacy,
|
Format: slogutil.FormatAdGuardLegacy,
|
||||||
|
Level: lvl,
|
||||||
AddTimestamp: true,
|
AddTimestamp: true,
|
||||||
Verbose: ls.Verbose,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
|
"github.com/AdguardTeam/AdGuardHome/internal/next/configmgr"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/golibs/osutil"
|
"github.com/AdguardTeam/golibs/osutil"
|
||||||
"github.com/google/renameio/v2/maybe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// signalHandler processes incoming signals and shuts services down.
|
// signalHandler processes incoming signals and shuts services down.
|
||||||
|
@ -142,7 +141,7 @@ func (h *signalHandler) writePID() {
|
||||||
data = strconv.AppendInt(data, int64(os.Getpid()), 10)
|
data = strconv.AppendInt(data, int64(os.Getpid()), 10)
|
||||||
data = append(data, '\n')
|
data = append(data, '\n')
|
||||||
|
|
||||||
err := maybe.WriteFile(h.pidFile, data, 0o644)
|
err := aghos.WriteFile(h.pidFile, data, 0o644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("sighdlr: writing pidfile: %s", err)
|
log.Error("sighdlr: writing pidfile: %s", err)
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ import (
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/log"
|
"github.com/AdguardTeam/golibs/log"
|
||||||
"github.com/AdguardTeam/golibs/timeutil"
|
"github.com/AdguardTeam/golibs/timeutil"
|
||||||
"github.com/google/renameio/v2/maybe"
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -183,7 +182,7 @@ func (m *Manager) write() (err error) {
|
||||||
return fmt.Errorf("encoding: %w", err)
|
return fmt.Errorf("encoding: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = maybe.WriteFile(m.fileName, b, aghos.DefaultPermFile)
|
err = aghos.WriteFile(m.fileName, b, aghos.DefaultPermFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("writing: %w", err)
|
return fmt.Errorf("writing: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
//
|
//
|
||||||
// TODO(a.garipov): Consider ways to detect this better.
|
// TODO(a.garipov): Consider ways to detect this better.
|
||||||
func NeedsMigration(confFilePath string) (ok bool) {
|
func NeedsMigration(confFilePath string) (ok bool) {
|
||||||
s, err := os.Stat(confFilePath)
|
s, err := aghos.Stat(confFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
// Likely a first run. Don't check.
|
// Likely a first run. Don't check.
|
||||||
|
@ -70,7 +70,7 @@ func chmodFile(filePath string) {
|
||||||
// chmodPath changes the permissions of a single filesystem entity. The results
|
// chmodPath changes the permissions of a single filesystem entity. The results
|
||||||
// are logged at the appropriate level.
|
// are logged at the appropriate level.
|
||||||
func chmodPath(entPath, fileType string, fm fs.FileMode) {
|
func chmodPath(entPath, fileType string, fm fs.FileMode) {
|
||||||
err := os.Chmod(entPath, fm)
|
err := aghos.Chmod(entPath, fm)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
log.Info("permcheck: changed permissions for %s %q", fileType, entPath)
|
log.Info("permcheck: changed permissions for %s %q", fileType, entPath)
|
||||||
|
|
||||||
|
|
|
@ -60,7 +60,7 @@ func checkFile(filePath string) {
|
||||||
// checkPath checks the permissions of a single filesystem entity. The results
|
// checkPath checks the permissions of a single filesystem entity. The results
|
||||||
// are logged at the appropriate level.
|
// are logged at the appropriate level.
|
||||||
func checkPath(entPath, fileType string, want fs.FileMode) {
|
func checkPath(entPath, fileType string, want fs.FileMode) {
|
||||||
s, err := os.Stat(entPath)
|
s, err := aghos.Stat(entPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logFunc := log.Error
|
logFunc := log.Error
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
|
|
@ -57,6 +57,7 @@ type qLogFile struct {
|
||||||
|
|
||||||
// newQLogFile initializes a new instance of the qLogFile.
|
// newQLogFile initializes a new instance of the qLogFile.
|
||||||
func newQLogFile(path string) (qf *qLogFile, err error) {
|
func newQLogFile(path string) (qf *qLogFile, err error) {
|
||||||
|
// Don't use [aghos.OpenFile] here, because the file is expected to exist.
|
||||||
f, err := os.OpenFile(path, os.O_RDONLY, aghos.DefaultPermFile)
|
f, err := os.OpenFile(path, os.O_RDONLY, aghos.DefaultPermFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -71,7 +71,7 @@ func (l *queryLog) flushToFile(b *bytes.Buffer) (err error) {
|
||||||
|
|
||||||
filename := l.logFile
|
filename := l.logFile
|
||||||
|
|
||||||
f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, aghos.DefaultPermFile)
|
f, err := aghos.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, aghos.DefaultPermFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating file %q: %w", filename, err)
|
return fmt.Errorf("creating file %q: %w", filename, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -384,7 +384,13 @@ func (s *StatsCtx) openDB() (err error) {
|
||||||
s.logger.Debug("opening database")
|
s.logger.Debug("opening database")
|
||||||
|
|
||||||
var db *bbolt.DB
|
var db *bbolt.DB
|
||||||
db, err = bbolt.Open(s.filename, aghos.DefaultPermFile, nil)
|
|
||||||
|
opts := *bbolt.DefaultOptions
|
||||||
|
// Use the custom OpenFile function to properly handle access rights on
|
||||||
|
// Windows.
|
||||||
|
opts.OpenFile = aghos.OpenFile
|
||||||
|
|
||||||
|
db, err = bbolt.Open(s.filename, aghos.DefaultPermFile, &opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "invalid argument" {
|
if err.Error() == "invalid argument" {
|
||||||
const lines = `AdGuard Home cannot be initialized due to an incompatible file system.
|
const lines = `AdGuard Home cannot be initialized due to an incompatible file system.
|
||||||
|
@ -467,7 +473,7 @@ func (s *StatsCtx) flushDB(id, limit uint32, ptr *unit) (cont bool, sleepFor tim
|
||||||
if delErr != nil {
|
if delErr != nil {
|
||||||
// TODO(e.burkov): Improve the algorithm of deleting the oldest bucket
|
// TODO(e.burkov): Improve the algorithm of deleting the oldest bucket
|
||||||
// to avoid the error.
|
// to avoid the error.
|
||||||
lvl := slog.LevelWarn
|
lvl := slog.LevelDebug
|
||||||
if !errors.Is(delErr, bbolt.ErrBucketNotFound) {
|
if !errors.Is(delErr, bbolt.ErrBucketNotFound) {
|
||||||
isCommitable = false
|
isCommitable = false
|
||||||
lvl = slog.LevelError
|
lvl = slog.LevelError
|
||||||
|
|
|
@ -6,11 +6,11 @@ require (
|
||||||
github.com/fzipp/gocyclo v0.6.0
|
github.com/fzipp/gocyclo v0.6.0
|
||||||
github.com/golangci/misspell v0.6.0
|
github.com/golangci/misspell v0.6.0
|
||||||
github.com/gordonklaus/ineffassign v0.1.0
|
github.com/gordonklaus/ineffassign v0.1.0
|
||||||
github.com/kisielk/errcheck v1.7.0
|
github.com/kisielk/errcheck v1.8.0
|
||||||
github.com/kyoh86/looppointer v0.2.1
|
github.com/kyoh86/looppointer v0.2.1
|
||||||
github.com/securego/gosec/v2 v2.21.4
|
github.com/securego/gosec/v2 v2.21.4
|
||||||
github.com/uudashr/gocognit v1.1.3
|
github.com/uudashr/gocognit v1.1.3
|
||||||
golang.org/x/tools v0.25.0
|
golang.org/x/tools v0.26.0
|
||||||
golang.org/x/vuln v1.1.3
|
golang.org/x/vuln v1.1.3
|
||||||
honnef.co/go/tools v0.5.1
|
honnef.co/go/tools v0.5.1
|
||||||
mvdan.cc/gofumpt v0.7.0
|
mvdan.cc/gofumpt v0.7.0
|
||||||
|
@ -18,12 +18,12 @@ require (
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
cloud.google.com/go v0.115.1 // indirect
|
cloud.google.com/go v0.116.0 // indirect
|
||||||
cloud.google.com/go/ai v0.8.2 // indirect
|
cloud.google.com/go/ai v0.8.2 // indirect
|
||||||
cloud.google.com/go/auth v0.9.7 // indirect
|
cloud.google.com/go/auth v0.9.9 // indirect
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
|
||||||
cloud.google.com/go/compute/metadata v0.5.2 // indirect
|
cloud.google.com/go/compute/metadata v0.5.2 // indirect
|
||||||
cloud.google.com/go/longrunning v0.6.1 // indirect
|
cloud.google.com/go/longrunning v0.6.2 // indirect
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
||||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||||
|
@ -40,26 +40,26 @@ require (
|
||||||
github.com/kyoh86/nolint v0.0.1 // indirect
|
github.com/kyoh86/nolint v0.0.1 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
go.opencensus.io v0.24.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 // indirect
|
||||||
go.opentelemetry.io/otel v1.30.0 // indirect
|
go.opentelemetry.io/otel v1.31.0 // indirect
|
||||||
go.opentelemetry.io/otel/metric v1.30.0 // indirect
|
go.opentelemetry.io/otel/metric v1.31.0 // indirect
|
||||||
go.opentelemetry.io/otel/trace v1.30.0 // indirect
|
go.opentelemetry.io/otel/trace v1.31.0 // indirect
|
||||||
golang.org/x/crypto v0.27.0 // indirect
|
golang.org/x/crypto v0.28.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 // indirect
|
golang.org/x/exp/typeparams v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||||
golang.org/x/mod v0.21.0 // indirect
|
golang.org/x/mod v0.21.0 // indirect
|
||||||
golang.org/x/net v0.29.0 // indirect
|
golang.org/x/net v0.30.0 // indirect
|
||||||
golang.org/x/oauth2 v0.23.0 // indirect
|
golang.org/x/oauth2 v0.23.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.8.0 // indirect
|
||||||
golang.org/x/sys v0.25.0 // indirect
|
golang.org/x/sys v0.26.0 // indirect
|
||||||
golang.org/x/telemetry v0.0.0-20240927214544-e9e6960092dd // indirect
|
golang.org/x/telemetry v0.0.0-20241028140143-9c0d19e65ba0 // indirect
|
||||||
golang.org/x/text v0.18.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
golang.org/x/time v0.6.0 // indirect
|
golang.org/x/time v0.7.0 // indirect
|
||||||
google.golang.org/api v0.199.0 // indirect
|
google.golang.org/api v0.203.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
||||||
google.golang.org/grpc v1.67.1 // indirect
|
google.golang.org/grpc v1.67.1 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.35.1 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
|
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||||
cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
|
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||||
cloud.google.com/go/ai v0.8.2 h1:LEaQwqBv+k2ybrcdTtCTc9OPZXoEdcQaGrfvDYS6Bnk=
|
cloud.google.com/go/ai v0.8.2 h1:LEaQwqBv+k2ybrcdTtCTc9OPZXoEdcQaGrfvDYS6Bnk=
|
||||||
cloud.google.com/go/ai v0.8.2/go.mod h1:Wb3EUUGWwB6yHBaUf/+oxUq/6XbCaU1yh0GrwUS8lr4=
|
cloud.google.com/go/ai v0.8.2/go.mod h1:Wb3EUUGWwB6yHBaUf/+oxUq/6XbCaU1yh0GrwUS8lr4=
|
||||||
cloud.google.com/go/auth v0.9.7 h1:ha65jNwOfI48YmUzNfMaUDfqt5ykuYIUnSartpU1+BA=
|
cloud.google.com/go/auth v0.9.9 h1:BmtbpNQozo8ZwW2t7QJjnrQtdganSdmqeIBxHxNkEZQ=
|
||||||
cloud.google.com/go/auth v0.9.7/go.mod h1:Xo0n7n66eHyOWWCnitop6870Ilwo3PiZyodVkkH1xWM=
|
cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
|
||||||
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
|
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
|
||||||
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
|
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
|
||||||
cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc=
|
cloud.google.com/go/longrunning v0.6.2 h1:xjDfh1pQcWPEvnfjZmwjKQEcHnpz6lHjfy7Fo0MK+hc=
|
||||||
cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0=
|
cloud.google.com/go/longrunning v0.6.2/go.mod h1:k/vIs83RN4bE3YCswdXC5PFfWVILjm3hpEUlSko4PiI=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
|
@ -86,8 +86,8 @@ github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||||
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
|
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
|
||||||
github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
|
github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
|
||||||
github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0=
|
github.com/kisielk/errcheck v1.8.0 h1:ZX/URYa7ilESY19ik/vBmCn6zdGQLxACwjAcWbHlYlg=
|
||||||
github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
|
github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
@ -125,26 +125,26 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0 h1:yMkBS9yViCc7U7yeLzJPM2XizlfdVvBRSmsQDWu6qc0=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.56.0/go.mod h1:n8MR6/liuGB5EmTETUBeU5ZgqMOlqKRxUaqPQBOANZ8=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
||||||
go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts=
|
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
||||||
go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc=
|
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
||||||
go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w=
|
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
||||||
go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ=
|
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
||||||
go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc=
|
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
||||||
go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o=
|
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||||
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0 h1:bVwtbF629Xlyxk6xLQq2TDYmqP0uiWaet5LwRebuY0k=
|
golang.org/x/exp/typeparams v0.0.0-20241009180824-f66d83c29e7c h1:F/15/6p7LyGUSoP0GE5CB/U9+TNEER1foNOP5sWLLnI=
|
||||||
golang.org/x/exp/typeparams v0.0.0-20240909161429-701f63a606c0/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
golang.org/x/exp/typeparams v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||||
|
@ -161,8 +161,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
||||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
|
@ -181,17 +181,17 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/telemetry v0.0.0-20240927214544-e9e6960092dd h1:cSUM3UgI7q2QZ4WBwDOGo5eFhZG4eGUkpdFporYHwpQ=
|
golang.org/x/telemetry v0.0.0-20241028140143-9c0d19e65ba0 h1:od0RE4kmouF+x/o4zkTTSvBnGPZ2azGgCUmZdrbnEXM=
|
||||||
golang.org/x/telemetry v0.0.0-20240927214544-e9e6960092dd/go.mod h1:PsFMgI0jiuY7j+qwXANuh9a/x5kQESTSnRow3gapUyk=
|
golang.org/x/telemetry v0.0.0-20241028140143-9c0d19e65ba0/go.mod h1:8nZWdGp9pq73ZI//QJyckMQab3yq7hoWi7SI0UIusVI=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
|
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
||||||
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
|
@ -200,25 +200,25 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
|
||||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE=
|
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||||
golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg=
|
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||||
golang.org/x/vuln v1.1.3 h1:NPGnvPOTgnjBc9HTaUx+nj+EaUYxl5SJOWqaDYGaFYw=
|
golang.org/x/vuln v1.1.3 h1:NPGnvPOTgnjBc9HTaUx+nj+EaUYxl5SJOWqaDYGaFYw=
|
||||||
golang.org/x/vuln v1.1.3/go.mod h1:7Le6Fadm5FOqE9C926BCD0g12NWyhg7cxV4BwcPFuNY=
|
golang.org/x/vuln v1.1.3/go.mod h1:7Le6Fadm5FOqE9C926BCD0g12NWyhg7cxV4BwcPFuNY=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.199.0 h1:aWUXClp+VFJmqE0JPvpZOK3LDQMyFKYIow4etYd9qxs=
|
google.golang.org/api v0.203.0 h1:SrEeuwU3S11Wlscsn+LA1kb/Y5xT8uggJSkIhD08NAU=
|
||||||
google.golang.org/api v0.199.0/go.mod h1:ohG4qSztDJmZdjK/Ar6MhbAmb/Rpi4JHOqagsh90K28=
|
google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI=
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||||
|
@ -235,8 +235,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -264,7 +264,7 @@ func (u *Updater) check() (err error) {
|
||||||
// ignores the configuration file if firstRun is true.
|
// ignores the configuration file if firstRun is true.
|
||||||
func (u *Updater) backup(firstRun bool) (err error) {
|
func (u *Updater) backup(firstRun bool) (err error) {
|
||||||
log.Debug("updater: backing up current configuration")
|
log.Debug("updater: backing up current configuration")
|
||||||
_ = os.Mkdir(u.backupDir, aghos.DefaultPermDir)
|
_ = aghos.Mkdir(u.backupDir, aghos.DefaultPermDir)
|
||||||
if !firstRun {
|
if !firstRun {
|
||||||
err = copyFile(u.confName, filepath.Join(u.backupDir, "AdGuardHome.yaml"))
|
err = copyFile(u.confName, filepath.Join(u.backupDir, "AdGuardHome.yaml"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -338,12 +338,12 @@ func (u *Updater) downloadPackageFile() (err error) {
|
||||||
return fmt.Errorf("io.ReadAll() failed: %w", err)
|
return fmt.Errorf("io.ReadAll() failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = os.Mkdir(u.updateDir, aghos.DefaultPermDir)
|
_ = aghos.Mkdir(u.updateDir, aghos.DefaultPermDir)
|
||||||
|
|
||||||
log.Debug("updater: saving package to file")
|
log.Debug("updater: saving package to file")
|
||||||
err = os.WriteFile(u.packageName, body, aghos.DefaultPermFile)
|
err = aghos.WriteFile(u.packageName, body, aghos.DefaultPermFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("os.WriteFile() failed: %w", err)
|
return fmt.Errorf("writing package file: %w", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -360,15 +360,15 @@ func tarGzFileUnpackOne(outDir string, tr *tar.Reader, hdr *tar.Header) (name st
|
||||||
if name == "AdGuardHome" {
|
if name == "AdGuardHome" {
|
||||||
// Top-level AdGuardHome/. Skip it.
|
// Top-level AdGuardHome/. Skip it.
|
||||||
//
|
//
|
||||||
// TODO(a.garipov): This whole package needs to be
|
// TODO(a.garipov): This whole package needs to be rewritten and
|
||||||
// rewritten and covered in more integration tests. It
|
// covered in more integration tests. It has weird assumptions and
|
||||||
// has weird assumptions and file mode issues.
|
// file mode issues.
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Mkdir(outputName, os.FileMode(hdr.Mode&0o755))
|
err = aghos.Mkdir(outputName, os.FileMode(hdr.Mode&0o755))
|
||||||
if err != nil && !errors.Is(err, os.ErrExist) {
|
if err != nil && !errors.Is(err, os.ErrExist) {
|
||||||
return "", fmt.Errorf("os.Mkdir(%q): %w", outputName, err)
|
return "", fmt.Errorf("creating directory %q: %w", outputName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("updater: created directory %q", outputName)
|
log.Debug("updater: created directory %q", outputName)
|
||||||
|
@ -383,7 +383,7 @@ func tarGzFileUnpackOne(outDir string, tr *tar.Reader, hdr *tar.Header) (name st
|
||||||
}
|
}
|
||||||
|
|
||||||
var wc io.WriteCloser
|
var wc io.WriteCloser
|
||||||
wc, err = os.OpenFile(
|
wc, err = aghos.OpenFile(
|
||||||
outputName,
|
outputName,
|
||||||
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
|
os.O_WRONLY|os.O_CREATE|os.O_TRUNC,
|
||||||
os.FileMode(hdr.Mode&0o755),
|
os.FileMode(hdr.Mode&0o755),
|
||||||
|
@ -464,14 +464,13 @@ func zipFileUnpackOne(outDir string, zf *zip.File) (name string, err error) {
|
||||||
if name == "AdGuardHome" {
|
if name == "AdGuardHome" {
|
||||||
// Top-level AdGuardHome/. Skip it.
|
// Top-level AdGuardHome/. Skip it.
|
||||||
//
|
//
|
||||||
// TODO(a.garipov): See the similar todo in
|
// TODO(a.garipov): See the similar todo in tarGzFileUnpack.
|
||||||
// tarGzFileUnpack.
|
|
||||||
return "", nil
|
return "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = os.Mkdir(outputName, fi.Mode())
|
err = aghos.Mkdir(outputName, fi.Mode())
|
||||||
if err != nil && !errors.Is(err, os.ErrExist) {
|
if err != nil && !errors.Is(err, os.ErrExist) {
|
||||||
return "", fmt.Errorf("os.Mkdir(%q): %w", outputName, err)
|
return "", fmt.Errorf("creating directory %q: %w", outputName, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debug("updater: created directory %q", outputName)
|
log.Debug("updater: created directory %q", outputName)
|
||||||
|
@ -480,7 +479,7 @@ func zipFileUnpackOne(outDir string, zf *zip.File) (name string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var wc io.WriteCloser
|
var wc io.WriteCloser
|
||||||
wc, err = os.OpenFile(outputName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
|
wc, err = aghos.OpenFile(outputName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("os.OpenFile(): %w", err)
|
return "", fmt.Errorf("os.OpenFile(): %w", err)
|
||||||
}
|
}
|
||||||
|
@ -523,15 +522,19 @@ func zipFileUnpack(zipfile, outDir string) (files []string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy file on disk
|
// Copy file on disk
|
||||||
func copyFile(src, dst string) error {
|
func copyFile(src, dst string) (err error) {
|
||||||
d, e := os.ReadFile(src)
|
d, err := os.ReadFile(src)
|
||||||
if e != nil {
|
if err != nil {
|
||||||
return e
|
// Don't wrap the error, since it's informative enough as is.
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
e = os.WriteFile(dst, d, aghos.DefaultPermFile)
|
|
||||||
if e != nil {
|
err = aghos.WriteFile(dst, d, aghos.DefaultPermFile)
|
||||||
return e
|
if err != nil {
|
||||||
|
// Don't wrap the error, since it's informative enough as is.
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,9 +54,12 @@ set -f -u
|
||||||
#
|
#
|
||||||
# * Package unsafe is… unsafe.
|
# * Package unsafe is… unsafe.
|
||||||
#
|
#
|
||||||
# Currently, the only standard exception are files generated from protobuf
|
# If your project needs more exceptions, add and document them. Currently,
|
||||||
# schemas, which use package reflect. If your project needs more exceptions,
|
# there are only two standard exceptions:
|
||||||
# add and document them.
|
#
|
||||||
|
# * Files generated from protobuf schemas, which use package reflect.
|
||||||
|
#
|
||||||
|
# * Windows-specific code caused by golang.org/x/sys/windows API design.
|
||||||
#
|
#
|
||||||
# TODO(a.garipov): Add golibs/log.
|
# TODO(a.garipov): Add golibs/log.
|
||||||
#
|
#
|
||||||
|
@ -75,6 +78,7 @@ blocklist_imports() {
|
||||||
-n\
|
-n\
|
||||||
-- '*.go'\
|
-- '*.go'\
|
||||||
':!*.pb.go'\
|
':!*.pb.go'\
|
||||||
|
':!./internal/aghos/permission_windows.go'\
|
||||||
| sed -e 's/^\([^[:space:]]\+\)\(.*\)$/\1 blocked import:\2/'\
|
| sed -e 's/^\([^[:space:]]\+\)\(.*\)$/\1 blocked import:\2/'\
|
||||||
|| exit 0
|
|| exit 0
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,16 +4,17 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"maps"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
|
||||||
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
"github.com/AdguardTeam/AdGuardHome/internal/aghhttp"
|
||||||
"github.com/AdguardTeam/golibs/errors"
|
"github.com/AdguardTeam/golibs/errors"
|
||||||
"github.com/AdguardTeam/golibs/httphdr"
|
"github.com/AdguardTeam/golibs/httphdr"
|
||||||
"github.com/AdguardTeam/golibs/mapsutil"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// upload base translation.
|
// upload base translation.
|
||||||
|
@ -52,9 +53,11 @@ func prepareMultipartMsg(
|
||||||
w := multipart.NewWriter(buf)
|
w := multipart.NewWriter(buf)
|
||||||
var fw io.Writer
|
var fw io.Writer
|
||||||
|
|
||||||
err = mapsutil.SortedRangeError(formData, w.WriteField)
|
for _, k := range slices.Sorted(maps.Keys(formData)) {
|
||||||
if err != nil {
|
err = w.WriteField(k, formData[k])
|
||||||
return nil, "", fmt.Errorf("writing field: %w", err)
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("writing field %q: %w", k, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
file, err := os.Open(basePath)
|
file, err := os.Open(basePath)
|
||||||
|
|
Loading…
Reference in New Issue