diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 37d61e56..36b0f74a 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -102,6 +102,9 @@ the best way. For crashes, please provide a full failure log. 'label': 'Action' 'value': | + Replace the following command with the one you're calling or a + description of the failing action: + ```sh nslookup -debug -type=a 'www.example.com' '$YOUR_AGH_ADDRESS' ``` diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8a3af2fe..9804dae2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,7 +1,7 @@ 'name': 'build' 'env': - 'GO_VERSION': '1.20.12' + 'GO_VERSION': '1.21.7' 'NODE_VERSION': '16' 'on': diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1ba27d28..b418e66f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,7 +1,7 @@ 'name': 'lint' 'env': - 'GO_VERSION': '1.20.12' + 'GO_VERSION': '1.21.7' 'on': 'push': diff --git a/CHANGELOG.md b/CHANGELOG.md index 6054e0ad..6eb77546 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,18 +14,68 @@ and this project adheres to +### Security + +- Go version has been updated to prevent the possibility of exploiting the Go + vulnerabilities fixed in Go 1.21.6 and Go 1.21.7. + ### Added -- Etc timezones to the timezone list ([#6568]). +- Context menu item in the Query Log to add a Client to the Persistent client + list ([#6679]). + +### Changed + +- Starting with this release our scripts are using Go's [forward compatibility + mechanism][go-toolchain] for updating the Go version. + + **Important note for porters:** This change means that if your `go` version + is 1.21+ but is different from the one required by AdGuard Home, the `go` tool + will automatically download the required version. + + If you want to use the version installed on your builder, run: + + ```sh + go get go@$YOUR_VERSION + go mod tidy + ``` + + and call `make` with `GOTOOLCHAIN=local`. + +### Deprecated + +- Go 1.21 support. Future versions will require at least Go 1.22 to build. + +### Removed + +- Go 1.20 support, as it has reached end of life. + +[#6679]: https://github.com/AdguardTeam/AdGuardHome/issues/6679 + +[go-toolchain]: https://go.dev/blog/toolchain + + + + + +## [v0.107.44] - 2024-02-06 + +See also the [v0.107.44 GitHub milestone][ms-v0.107.44]. + +### Added + +- Timezones in the Etc/ area to the timezone list ([#6568]). - The schema version of the configuration file to the output of running `AdGuardHome` (or `AdGuardHome.exe`) with `-v --version` command-line options ([#6545]). @@ -34,8 +84,8 @@ NOTE: Add new changes BELOW THIS COMMENT. ### Changed -- The bootstrapped upstream addresses now updated according to the TTL of the - bootstrap DNS response ([#6321]). +- The bootstrapped upstream addresses are now updated according to the TTL of + the bootstrap DNS response ([#6321]). - Logging level of timeout errors is now `error` instead of `debug` ([#6574]). - The field `"upstream_mode"` in `POST /control/dns_config` and `GET /control/dns_info` HTTP APIs now accepts `load_balance` value. Check @@ -45,8 +95,8 @@ NOTE: Add new changes BELOW THIS COMMENT. In this release, the schema version has changed from 27 to 28. -- The new property `clients.persistent.*.uid`, which is unique identifier of the - persistent client. +- The new property `clients.persistent.*.uid`, which is a unique identifier of + the persistent client. - The properties `dns.all_servers` and `dns.fastest_addr` were removed, their values migrated to newly added field `dns.upstream_mode` that describes the logic through which upstreams will be used. See also a [Wiki @@ -100,11 +150,8 @@ In this release, the schema version has changed from 27 to 28. [#6584]: https://github.com/AdguardTeam/AdGuardHome/issues/6584 [#6644]: https://github.com/AdguardTeam/AdGuardHome/issues/6644 -[wiki-config]: https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration - - +[ms-v0.107.44]: https://github.com/AdguardTeam/AdGuardHome/milestone/79?closed=1 +[wiki-config]: https://github.com/AdguardTeam/AdGuardHome/wiki/Configuration @@ -2752,11 +2799,12 @@ See also the [v0.104.2 GitHub milestone][ms-v0.104.2]. -[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.43...HEAD +[Unreleased]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.44...HEAD +[v0.107.44]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.43...v0.107.44 [v0.107.43]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.42...v0.107.43 [v0.107.42]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.41...v0.107.42 [v0.107.41]: https://github.com/AdguardTeam/AdGuardHome/compare/v0.107.40...v0.107.41 diff --git a/Makefile b/Makefile index b40b4cd0..2ec8699b 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ # Makefile. Bump this number every time a significant change is made to # this Makefile. # -# AdGuard-Project-Version: 2 +# AdGuard-Project-Version: 4 # Don't name these macros "GO" etc., because GNU Make apparently makes # them exported environment variables with the literal value of @@ -27,6 +27,7 @@ DIST_DIR = dist GOAMD64 = v1 GOPROXY = https://goproxy.cn|https://proxy.golang.org|direct GOSUMDB = sum.golang.google.cn +GOTOOLCHAIN = go1.21.7 GPG_KEY = devteam@adguard.com GPG_KEY_PASSPHRASE = not-a-real-password NPM = npm @@ -56,15 +57,16 @@ BUILD_RELEASE_DEPS_0 = deps js-build BUILD_RELEASE_DEPS_1 = go-deps ENV = env\ - COMMIT='$(COMMIT)'\ CHANNEL='$(CHANNEL)'\ - GPG_KEY='$(GPG_KEY)'\ - GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\ + COMMIT='$(COMMIT)'\ DIST_DIR='$(DIST_DIR)'\ GO="$(GO.MACRO)"\ GOAMD64="$(GOAMD64)"\ GOPROXY='$(GOPROXY)'\ GOSUMDB='$(GOSUMDB)'\ + GOTOOLCHAIN='$(GOTOOLCHAIN)'\ + GPG_KEY='$(GPG_KEY)'\ + GPG_KEY_PASSPHRASE='$(GPG_KEY_PASSPHRASE)'\ PATH="$${PWD}/bin:$$( "$(GO.MACRO)" env GOPATH )/bin:$${PATH}"\ RACE='$(RACE)'\ SIGN='$(SIGN)'\ @@ -117,6 +119,8 @@ go-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-tools.sh # targets. go-test: ; $(ENV) RACE='1' "$(SHELL)" ./scripts/make/go-test.sh +go-upd-tools: ; $(ENV) "$(SHELL)" ./scripts/make/go-upd-tools.sh + go-check: go-tools go-lint go-test # A quick check to make sure that all supported operating systems can be @@ -132,10 +136,3 @@ openapi-lint: ; cd ./openapi/ && $(YARN) test openapi-show: ; cd ./openapi/ && $(YARN) start txt-lint: ; $(ENV) "$(SHELL)" ./scripts/make/txt-lint.sh - -# TODO(a.garipov): Consider adding to scripts/ and the common project -# structure. -go-upd-tools: - cd ./internal/tools/ &&\ - "$(GO.MACRO)" get -u &&\ - "$(GO.MACRO)" mod tidy diff --git a/bamboo-specs/release.yaml b/bamboo-specs/release.yaml index e7004570..5ca3ac56 100644 --- a/bamboo-specs/release.yaml +++ b/bamboo-specs/release.yaml @@ -7,7 +7,7 @@ # Make sure to sync any changes with the branch overrides below. 'variables': 'channel': 'edge' - 'dockerGo': 'adguard/golang-ubuntu:7.6' + 'dockerGo': 'adguard/golang-ubuntu:8.0' 'stages': - 'Build frontend': @@ -272,7 +272,7 @@ # need to build a few of these. 'variables': 'channel': 'beta' - 'dockerGo': 'adguard/golang-ubuntu:7.6' + 'dockerGo': 'adguard/golang-ubuntu:8.0' # release-vX.Y.Z branches are the branches from which the actual final # release is built. - '^release-v[0-9]+\.[0-9]+\.[0-9]+': @@ -287,4 +287,4 @@ # are the ones that actually get released. 'variables': 'channel': 'release' - 'dockerGo': 'adguard/golang-ubuntu:7.6' + 'dockerGo': 'adguard/golang-ubuntu:8.0' diff --git a/bamboo-specs/snapcraft.yaml b/bamboo-specs/snapcraft.yaml index 77948f13..06f85937 100644 --- a/bamboo-specs/snapcraft.yaml +++ b/bamboo-specs/snapcraft.yaml @@ -10,7 +10,7 @@ # Make sure to sync any changes with the branch overrides below. 'variables': 'channel': 'edge' - 'dockerGo': 'adguard/golang-ubuntu:7.6' + 'dockerGo': 'adguard/golang-ubuntu:8.0' 'snapcraftChannel': 'edge' 'stages': @@ -191,7 +191,7 @@ # need to build a few of these. 'variables': 'channel': 'beta' - 'dockerGo': 'adguard/golang-ubuntu:7.6' + 'dockerGo': 'adguard/golang-ubuntu:8.0' 'snapcraftChannel': 'beta' # release-vX.Y.Z branches are the branches from which the actual final # release is built. @@ -207,5 +207,5 @@ # are the ones that actually get released. 'variables': 'channel': 'release' - 'dockerGo': 'adguard/golang-ubuntu:7.6' + 'dockerGo': 'adguard/golang-ubuntu:8.0' 'snapcraftChannel': 'candidate' diff --git a/bamboo-specs/test.yaml b/bamboo-specs/test.yaml index 86e08cc4..239fe8a7 100644 --- a/bamboo-specs/test.yaml +++ b/bamboo-specs/test.yaml @@ -5,7 +5,7 @@ 'key': 'AHBRTSPECS' 'name': 'AdGuard Home - Build and run tests' 'variables': - 'dockerGo': 'adguard/golang-ubuntu:7.6' + 'dockerGo': 'adguard/golang-ubuntu:8.0' 'stages': - 'Tests': diff --git a/client/src/__locales/en.json b/client/src/__locales/en.json index 112928be..bc3e459c 100644 --- a/client/src/__locales/en.json +++ b/client/src/__locales/en.json @@ -236,6 +236,7 @@ "updated_upstream_dns_toast": "Upstream servers successfully saved", "dns_test_ok_toast": "Specified DNS servers are working correctly", "dns_test_not_ok_toast": "Server \"{{key}}\": could not be used, please check that you've written it correctly", + "dns_test_parsing_error_toast": "Section {{section}}: line {{line}}: could not be used, please check that you've written it correctly", "dns_test_warning_toast": "Upstream \"{{key}}\" does not respond to test requests and may not work properly", "unblock": "Unblock", "block": "Block", @@ -243,6 +244,7 @@ "allow_this_client": "Allow this client", "block_for_this_client_only": "Block for this client only", "unblock_for_this_client_only": "Unblock for this client only", + "add_persistent_client": "Add as persistent client", "time_table_header": "Time", "date": "Date", "domain_name_table_header": "Domain name", @@ -465,6 +467,7 @@ "form_add_id": "Add identifier", "form_client_name": "Enter client name", "name": "Name", + "client_name": "Client {{id}}", "client_global_settings": "Use global settings", "client_deleted": "Client \"{{key}}\" successfully deleted", "client_added": "Client \"{{key}}\" successfully added", diff --git a/client/src/actions/index.js b/client/src/actions/index.js index e0e50841..c7af4fa0 100644 --- a/client/src/actions/index.js +++ b/client/src/actions/index.js @@ -403,6 +403,11 @@ export const testUpstream = ( const message = upstreamResponse[key]; if (message.startsWith('WARNING:')) { dispatch(addErrorToast({ error: i18next.t('dns_test_warning_toast', { key }) })); + } else if (message.endsWith(': parsing error')) { + const info = message.substring(0, message.indexOf(':')); + const [sectionKey, line] = info.split(' '); + const section = i18next.t(sectionKey); + dispatch(addErrorToast({ error: i18next.t('dns_test_parsing_error_toast', { section, line }) })); } else if (message !== 'OK') { dispatch(addErrorToast({ error: i18next.t('dns_test_not_ok_toast', { key }) })); } diff --git a/client/src/components/Filters/Modal.js b/client/src/components/Filters/Modal.js index aba53b30..5144a6f2 100644 --- a/client/src/components/Filters/Modal.js +++ b/client/src/components/Filters/Modal.js @@ -13,6 +13,8 @@ ReactModal.setAppElement('#root'); const MODAL_TYPE_TO_TITLE_TYPE_MAP = { [MODAL_TYPE.EDIT_FILTERS]: 'edit', [MODAL_TYPE.ADD_FILTERS]: 'new', + [MODAL_TYPE.EDIT_CLIENT]: 'edit', + [MODAL_TYPE.ADD_CLIENT]: 'new', [MODAL_TYPE.SELECT_MODAL_TYPE]: 'new', [MODAL_TYPE.CHOOSE_FILTERING_LIST]: 'choose', }; diff --git a/client/src/components/Logs/Cells/ClientCell.js b/client/src/components/Logs/Cells/ClientCell.js index d0dd3168..da15efc3 100644 --- a/client/src/components/Logs/Cells/ClientCell.js +++ b/client/src/components/Logs/Cells/ClientCell.js @@ -3,7 +3,7 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux'; import { nanoid } from 'nanoid'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; -import { Link } from 'react-router-dom'; +import { Link, useHistory } from 'react-router-dom'; import propTypes from 'prop-types'; import { checkFiltered, getBlockingClientName } from '../../../helpers/helpers'; @@ -25,12 +25,14 @@ const ClientCell = ({ }) => { const { t } = useTranslation(); const dispatch = useDispatch(); + const history = useHistory(); const autoClients = useSelector((state) => state.dashboard.autoClients, shallowEqual); const isDetailed = useSelector((state) => state.queryLogs.isDetailed); const allowedСlients = useSelector((state) => state.access.allowed_clients, shallowEqual); const [isOptionsOpened, setOptionsOpened] = useState(false); const autoClient = autoClients.find((autoClient) => autoClient.name === client); + const clients = useSelector((state) => state.dashboard.clients); const source = autoClient?.source; const whoisAvailable = client_info && Object.keys(client_info.whois).length > 0; const clientName = client_info?.name || client_id; @@ -55,6 +57,8 @@ const ClientCell = ({ const isFiltered = checkFiltered(reason); + const clientIds = clients.map((c) => c.ids).flat(); + const nameClass = classNames('w-90 o-hidden d-flex flex-column', { 'mt-2': isDetailed && !client_info?.name && !whoisAvailable, 'white-space--nowrap': isDetailed, @@ -66,7 +70,6 @@ const ClientCell = ({ const renderBlockingButton = (isFiltered, domain) => { const buttonType = isFiltered ? BLOCK_ACTIONS.UNBLOCK : BLOCK_ACTIONS.BLOCK; - const clients = useSelector((state) => state.dashboard.clients); const { confirmMessage, @@ -118,6 +121,15 @@ const ClientCell = ({ }, ]; + if (!clientIds.includes(client)) { + BUTTON_OPTIONS.push({ + name: 'add_persistent_client', + onClick: () => { + history.push(`/#clients?clientId=${client}`); + }, + }); + } + const getOptions = (options) => { if (options.length === 0) { return null; diff --git a/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js b/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js index f4744a5a..3f82ee98 100644 --- a/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js +++ b/client/src/components/Settings/Clients/ClientsTable/ClientsTable.js @@ -4,6 +4,7 @@ import React, { useEffect } from 'react'; import PropTypes from 'prop-types'; import { Trans, useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; +import { useHistory, useLocation } from 'react-router-dom'; import ReactTable from 'react-table'; import { getAllBlockedServices, getBlockedServices } from '../../../../actions/services'; @@ -39,8 +40,12 @@ const ClientsTable = ({ }) => { const [t] = useTranslation(); const dispatch = useDispatch(); + const location = useLocation(); + const history = useHistory(); const services = useSelector((store) => store?.services); const globalSettings = useSelector((store) => store?.settings.settingsList) || {}; + const params = new URLSearchParams(location.search); + const clientId = params.get('clientId'); const { safesearch } = globalSettings; @@ -48,6 +53,12 @@ const ClientsTable = ({ dispatch(getAllBlockedServices()); dispatch(getBlockedServices()); dispatch(initSettings()); + + if (clientId) { + toggleClientModal({ + type: MODAL_TYPE.ADD_CLIENT, + }); + } }, []); const handleFormAdd = (values) => { @@ -85,11 +96,15 @@ const ClientsTable = ({ } } - if (modalType === MODAL_TYPE.EDIT_FILTERS) { + if (modalType === MODAL_TYPE.EDIT_CLIENT) { handleFormUpdate(config, modalClientName); } else { handleFormAdd(config); } + + if (clientId) { + history.push('/#clients'); + } }; const getOptionsWithLabels = (options) => ( @@ -133,6 +148,14 @@ const ClientsTable = ({ } }; + const handleClose = () => { + toggleClientModal(); + + if (clientId) { + history.push('/#clients'); + } + }; + const columns = [ { Header: t('table_client'), @@ -298,7 +321,7 @@ const ClientsTable = ({ type="button" className="btn btn-icon btn-outline-primary btn-sm mr-2" onClick={() => toggleClientModal({ - type: MODAL_TYPE.EDIT_FILTERS, + type: MODAL_TYPE.EDIT_CLIENT, name: clientName, }) } @@ -371,12 +394,13 @@ const ClientsTable = ({ > diff --git a/client/src/components/Settings/Clients/Form.js b/client/src/components/Settings/Clients/Form.js index 2df5b655..46c90928 100644 --- a/client/src/components/Settings/Clients/Form.js +++ b/client/src/components/Settings/Clients/Form.js @@ -147,7 +147,7 @@ let Form = (props) => { useGlobalSettings, useGlobalServices, blockedServicesSchedule, - toggleClientModal, + handleClose, processingAdding, processingUpdating, invalid, @@ -427,7 +427,7 @@ let Form = (props) => { disabled={submitting} onClick={() => { reset(); - toggleClientModal(); + handleClose(); }} > cancel_btn @@ -456,7 +456,7 @@ Form.propTypes = { reset: PropTypes.func.isRequired, change: PropTypes.func.isRequired, submitting: PropTypes.bool.isRequired, - toggleClientModal: PropTypes.func.isRequired, + handleClose: PropTypes.func.isRequired, useGlobalSettings: PropTypes.bool, useGlobalServices: PropTypes.bool, blockedServicesSchedule: PropTypes.object, diff --git a/client/src/components/Settings/Clients/Modal.js b/client/src/components/Settings/Clients/Modal.js index 9c244a21..321e663c 100644 --- a/client/src/components/Settings/Clients/Modal.js +++ b/client/src/components/Settings/Clients/Modal.js @@ -6,7 +6,9 @@ import ReactModal from 'react-modal'; import { MODAL_TYPE } from '../../../helpers/constants'; import Form from './Form'; -const getInitialData = (initial) => { +const getInitialData = ({ + initial, modalType, clientId, clientName, +}) => { if (initial && initial.blocked_services) { const { blocked_services } = initial; const blocked = {}; @@ -21,46 +23,60 @@ const getInitialData = (initial) => { }; } + if (modalType !== MODAL_TYPE.EDIT_CLIENT && clientId) { + return { + ...initial, + name: clientName, + ids: [clientId], + }; + } + return initial; }; -const Modal = (props) => { - const { - isModalOpen, +const Modal = ({ + isModalOpen, + modalType, + currentClientData, + handleSubmit, + handleClose, + processingAdding, + processingUpdating, + tagsOptions, + clientId, + t, +}) => { + const initialData = getInitialData({ + initial: currentClientData, modalType, - currentClientData, - handleSubmit, - toggleClientModal, - processingAdding, - processingUpdating, - tagsOptions, - } = props; - const initialData = getInitialData(currentClientData); + clientId, + clientName: t('client_name', { id: clientId }), + }); return ( toggleClientModal()} + onRequestClose={handleClose} > - {modalType === MODAL_TYPE.EDIT_FILTERS ? ( + {modalType === MODAL_TYPE.EDIT_CLIENT ? ( client_edit ) : ( client_new )} - toggleClientModal()}> + Close limit { + line = line[:limit] + line[limit-1] = '…' } + + results = append(results, &parseResult{ + original: string(line), + err: parseErr, + }) } - ups, err := upstream.AddressToUpstream(addr, opts) + return results +} - return &upstreamResult{ - server: ups, - err: err, - original: addr, +// insertConfResults parses conf and inserts the upstream result into results. +// It can insert multiple results as well as none. +func insertConfResults(conf *proxy.UpstreamConfig, results map[string]*upstreamResult) { + insertListResults(conf.Upstreams, results, false) + + for _, ups := range conf.DomainReservedUpstreams { + insertListResults(ups, results, true) + } + + for _, ups := range conf.SpecifiedDomainUpstreams { + insertListResults(ups, results, true) + } +} + +// insertListResults constructs upstream results from the upstream list and +// inserts them into results. It can insert multiple results as well as none. +func insertListResults(ups []upstream.Upstream, results map[string]*upstreamResult, specific bool) { + for _, u := range ups { + addr := u.Address() + _, ok := results[addr] + if ok { + continue + } + + results[addr] = &upstreamResult{ + server: u, + isSpecific: specific, + } } } @@ -187,35 +191,30 @@ func (cv *upstreamConfigValidator) check() { } wg := &sync.WaitGroup{} - wg.Add(len(cv.general) + len(cv.fallback) + len(cv.private)) + wg.Add(len(cv.generalUpstreamResults) + + len(cv.fallbackUpstreamResults) + + len(cv.privateUpstreamResults)) - for _, res := range cv.general { - go cv.checkSrv(res, wg, commonChecker) + for _, res := range cv.generalUpstreamResults { + go checkSrv(res, wg, commonChecker) } - for _, res := range cv.fallback { - go cv.checkSrv(res, wg, commonChecker) + for _, res := range cv.fallbackUpstreamResults { + go checkSrv(res, wg, commonChecker) } - for _, res := range cv.private { - go cv.checkSrv(res, wg, arpaChecker) + for _, res := range cv.privateUpstreamResults { + go checkSrv(res, wg, arpaChecker) } wg.Wait() } // checkSrv runs hc on the server from res, if any, and stores any occurred -// error in res. wg is always marked done in the end. It used to be called in -// a separate goroutine. -func (cv *upstreamConfigValidator) checkSrv( - res *upstreamResult, - wg *sync.WaitGroup, - hc *healthchecker, -) { +// error in res. wg is always marked done in the end. It is intended to be +// used as a goroutine. +func checkSrv(res *upstreamResult, wg *sync.WaitGroup, hc *healthchecker) { + defer log.OnPanic(fmt.Sprintf("dnsforward: checking upstream %s", res.server.Address())) defer wg.Done() - if res.server == nil { - return - } - res.err = hc.check(res.server) if res.err != nil && res.isSpecific { res.err = domainSpecificTestError{Err: res.err} @@ -225,65 +224,126 @@ func (cv *upstreamConfigValidator) checkSrv( // close closes all the upstreams that were successfully parsed. It enriches // the results with deferred closing errors. func (cv *upstreamConfigValidator) close() { - for _, slice := range [][]*upstreamResult{cv.general, cv.fallback, cv.private} { - for _, r := range slice { - if r.server != nil { - r.err = errors.WithDeferred(r.err, r.server.Close()) - } + all := []map[string]*upstreamResult{ + cv.generalUpstreamResults, + cv.fallbackUpstreamResults, + cv.privateUpstreamResults, + } + + for _, m := range all { + for _, r := range m { + r.err = errors.WithDeferred(r.err, r.server.Close()) } } } +// sections of the upstream configuration according to the text label of the +// localization. +// +// Keep in sync with client/src/__locales/en.json. +// +// TODO(s.chzhen): Refactor. +const ( + generalTextLabel = "upstream_dns" + fallbackTextLabel = "fallback_dns_title" + privateTextLabel = "local_ptr_title" +) + // status returns all the data collected during parsing, healthcheck, and // closing of the upstreams. The returned map is keyed by the original upstream // configuration piece and contains the corresponding error or "OK" if there was // no error. func (cv *upstreamConfigValidator) status() (results map[string]string) { - result := map[string]string{} + // Names of the upstream configuration sections for logging. + const ( + generalSection = "general" + fallbackSection = "fallback" + privateSection = "private" + ) - for _, res := range cv.general { - resultToStatus("general", res, result) + results = map[string]string{} + + for original, res := range cv.generalUpstreamResults { + upstreamResultToStatus(generalSection, string(original), res, results) } - for _, res := range cv.fallback { - resultToStatus("fallback", res, result) + for original, res := range cv.fallbackUpstreamResults { + upstreamResultToStatus(fallbackSection, string(original), res, results) } - for _, res := range cv.private { - resultToStatus("private", res, result) + for original, res := range cv.privateUpstreamResults { + upstreamResultToStatus(privateSection, string(original), res, results) } - return result + parseResultToStatus(generalTextLabel, generalSection, cv.generalParseResults, results) + parseResultToStatus(fallbackTextLabel, fallbackSection, cv.fallbackParseResults, results) + parseResultToStatus(privateTextLabel, privateSection, cv.privateParseResults, results) + + return results } -// resultToStatus puts "OK" or an error message from res into resMap. section -// is the name of the upstream configuration section, i.e. "general", +// upstreamResultToStatus puts "OK" or an error message from res into resMap. +// section is the name of the upstream configuration section, i.e. "general", // "fallback", or "private", and only used for logging. // // TODO(e.burkov): Currently, the HTTP handler expects that all the results are // put together in a single map, which may lead to collisions, see AG-27539. // Improve the results compilation. -func resultToStatus(section string, res *upstreamResult, resMap map[string]string) { +func upstreamResultToStatus( + section string, + original string, + res *upstreamResult, + resMap map[string]string, +) { val := "OK" if res.err != nil { val = res.err.Error() } - prevVal := resMap[res.original] + prevVal := resMap[original] switch prevVal { case "": - resMap[res.original] = val + resMap[original] = val case val: - log.Debug("dnsforward: duplicating %s config line %q", section, res.original) + log.Debug("dnsforward: duplicating %s config line %q", section, original) default: log.Debug( "dnsforward: warning: %s config line %q (%v) had different result %v", section, val, - res.original, + original, prevVal, ) } } +// parseResultToStatus puts parsing error messages from results into resMap. +// section is the name of the upstream configuration section, i.e. "general", +// "fallback", or "private", and only used for logging. +// +// Parsing error message has the following format: +// +// sectionTextLabel line: parsing error +// +// Where sectionTextLabel is a section text label of a localization and line is +// a line number. +func parseResultToStatus( + textLabel string, + section string, + results []*parseResult, + resMap map[string]string, +) { + for _, res := range results { + original := res.original + _, ok := resMap[original] + if ok { + log.Debug("dnsforward: duplicating %s parsing error %q", section, original) + + continue + } + + resMap[original] = fmt.Sprintf("%s %d: parsing error", textLabel, res.err.Idx+1) + } +} + // domainSpecificTestError is a wrapper for errors returned by checkDNS to mark // the tested upstream domain-specific and therefore consider its errors // non-critical. @@ -342,7 +402,7 @@ func (h *healthchecker) check(u upstream.Upstream) (err error) { if err != nil { return fmt.Errorf("couldn't communicate with upstream: %w", err) } else if h.ansEmpty && len(reply.Answer) > 0 { - return errWrongResponse + return errors.Error("wrong response") } return nil diff --git a/internal/dnsforward/dnsforward.go b/internal/dnsforward/dnsforward.go index 9c0097b9..6ad06333 100644 --- a/internal/dnsforward/dnsforward.go +++ b/internal/dnsforward/dnsforward.go @@ -9,6 +9,7 @@ import ( "net/http" "net/netip" "runtime" + "slices" "strings" "sync" "sync/atomic" @@ -30,7 +31,6 @@ import ( "github.com/AdguardTeam/golibs/netutil/sysresolv" "github.com/AdguardTeam/golibs/stringutil" "github.com/miekg/dns" - "golang.org/x/exp/slices" ) // DefaultTimeout is the default upstream timeout diff --git a/internal/dnsforward/filter.go b/internal/dnsforward/filter.go index a92cccd6..46599c11 100644 --- a/internal/dnsforward/filter.go +++ b/internal/dnsforward/filter.go @@ -4,6 +4,7 @@ import ( "encoding/binary" "fmt" "net" + "slices" "strings" "github.com/AdguardTeam/AdGuardHome/internal/aghnet" @@ -12,7 +13,6 @@ import ( "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/urlfilter/rules" "github.com/miekg/dns" - "golang.org/x/exp/slices" ) // beforeRequestHandler is the handler that is called before any other diff --git a/internal/dnsforward/http.go b/internal/dnsforward/http.go index 2c222d07..9ad2395a 100644 --- a/internal/dnsforward/http.go +++ b/internal/dnsforward/http.go @@ -6,16 +6,17 @@ import ( "io" "net/http" "net/netip" + "slices" "time" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/filtering" + "github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/dnsproxy/upstream" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/stringutil" - "golang.org/x/exp/slices" ) // jsonDNSConfig is the JSON representation of the DNS server configuration. @@ -294,7 +295,7 @@ func (req *jsonDNSConfig) checkFallbacks() (err error) { return nil } - err = ValidateUpstreams(*req.Fallbacks) + _, err = proxy.ParseUpstreamsConfig(*req.Fallbacks, &upstream.Options{}) if err != nil { return fmt.Errorf("fallback servers: %w", err) } @@ -344,7 +345,7 @@ func (req *jsonDNSConfig) validate(privateNets netutil.SubnetSet) (err error) { // validateUpstreamDNSServers returns an error if any field of req is invalid. func (req *jsonDNSConfig) validateUpstreamDNSServers(privateNets netutil.SubnetSet) (err error) { if req.Upstreams != nil { - err = ValidateUpstreams(*req.Upstreams) + _, err = proxy.ParseUpstreamsConfig(*req.Upstreams, &upstream.Options{}) if err != nil { return fmt.Errorf("upstream servers: %w", err) } @@ -580,9 +581,6 @@ func (s *Server) handleTestUpstreamDNS(w http.ResponseWriter, r *http.Request) { return } - req.Upstreams = stringutil.FilterOut(req.Upstreams, IsCommentOrEmpty) - req.FallbackDNS = stringutil.FilterOut(req.FallbackDNS, IsCommentOrEmpty) - req.PrivateUpstreams = stringutil.FilterOut(req.PrivateUpstreams, IsCommentOrEmpty) req.BootstrapDNS = stringutil.FilterOut(req.BootstrapDNS, IsCommentOrEmpty) opts := &upstream.Options{ diff --git a/internal/dnsforward/http_test.go b/internal/dnsforward/http_test.go index 2e28039f..b642eb2c 100644 --- a/internal/dnsforward/http_test.go +++ b/internal/dnsforward/http_test.go @@ -223,8 +223,9 @@ func TestDNSForwardHTTP_handleSetConfig(t *testing.T) { wantSet: "", }, { name: "upstream_dns_bad", - wantSet: `validating dns config: ` + - `upstream servers: validating upstream "!!!": not an ip:port`, + wantSet: `validating dns config: upstream servers: parsing error at index 0: ` + + `cannot prepare the upstream: invalid address !!!: bad hostname "!!!": ` + + `bad top-level domain name label "!!!": bad top-level domain name label rune '!'`, }, { name: "bootstraps_bad", wantSet: `validating dns config: checking bootstrap a: not a bootstrap: ParseAddr("a"): ` + @@ -313,98 +314,6 @@ func TestIsCommentOrEmpty(t *testing.T) { } } -func TestValidateUpstreams(t *testing.T) { - const sdnsStamp = `sdns://AQMAAAAAAAAAFDE3Ni4xMDMuMTMwLjEzMDo1NDQzINErR_J` + - `S3PLCu_iZEIbq95zkSV2LFsigxDIuUso_OQhzIjIuZG5zY3J5cHQuZGVmYXVsdC5uczE` + - `uYWRndWFyZC5jb20` - - testCases := []struct { - name string - wantErr string - set []string - }{{ - name: "empty", - wantErr: ``, - set: nil, - }, { - name: "comment", - wantErr: ``, - set: []string{"# comment"}, - }, { - name: "no_default", - wantErr: `no default upstreams specified`, - set: []string{ - "[/host.com/]1.1.1.1", - "[//]tls://1.1.1.1", - "[/www.host.com/]#", - "[/host.com/google.com/]8.8.8.8", - "[/host/]" + sdnsStamp, - }, - }, { - name: "with_default", - wantErr: ``, - set: []string{ - "[/host.com/]1.1.1.1", - "[//]tls://1.1.1.1", - "[/www.host.com/]#", - "[/host.com/google.com/]8.8.8.8", - "[/host/]" + sdnsStamp, - "8.8.8.8", - }, - }, { - name: "invalid", - wantErr: `validating upstream "dhcp://fake.dns": bad protocol "dhcp"`, - set: []string{"dhcp://fake.dns"}, - }, { - name: "invalid", - wantErr: `validating upstream "1.2.3.4.5": not an ip:port`, - set: []string{"1.2.3.4.5"}, - }, { - name: "invalid", - wantErr: `validating upstream "123.3.7m": not an ip:port`, - set: []string{"123.3.7m"}, - }, { - name: "invalid", - wantErr: `splitting upstream line "[/host.com]tls://dns.adguard.com": ` + - `missing separator`, - set: []string{"[/host.com]tls://dns.adguard.com"}, - }, { - name: "invalid", - wantErr: `validating upstream "[host.ru]#": not an ip:port`, - set: []string{"[host.ru]#"}, - }, { - name: "valid_default", - wantErr: ``, - set: []string{ - "1.1.1.1", - "tls://1.1.1.1", - "https://dns.adguard.com/dns-query", - sdnsStamp, - "udp://dns.google", - "udp://8.8.8.8", - "[/host.com/]1.1.1.1", - "[//]tls://1.1.1.1", - "[/www.host.com/]#", - "[/host.com/google.com/]8.8.8.8", - "[/host/]" + sdnsStamp, - "[/пример.рф/]8.8.8.8", - }, - }, { - name: "bad_domain", - wantErr: `splitting upstream line "[/!/]8.8.8.8": domain at index 0: ` + - `bad domain name "!": bad top-level domain name label "!": ` + - `bad top-level domain name label rune '!'`, - set: []string{"[/!/]8.8.8.8"}, - }} - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := ValidateUpstreams(tc.set) - testutil.AssertErrorMsg(t, tc.wantErr, err) - }) - } -} - func TestValidateUpstreamsPrivate(t *testing.T) { ss := netutil.SubnetSetFunc(netutil.IsLocallyServed) diff --git a/internal/dnsforward/msg.go b/internal/dnsforward/msg.go index eeb2c963..10dceb11 100644 --- a/internal/dnsforward/msg.go +++ b/internal/dnsforward/msg.go @@ -2,13 +2,13 @@ package dnsforward import ( "net/netip" + "slices" "github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/dnsproxy/proxy" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/urlfilter/rules" "github.com/miekg/dns" - "golang.org/x/exp/slices" ) // makeResponse creates a DNS response by req and sets necessary flags. It also diff --git a/internal/dnsforward/upstreams.go b/internal/dnsforward/upstreams.go index 5fed582a..c7f6677e 100644 --- a/internal/dnsforward/upstreams.go +++ b/internal/dnsforward/upstreams.go @@ -2,10 +2,9 @@ package dnsforward import ( "fmt" - "net" "net/netip" "os" - "strings" + "slices" "time" "github.com/AdguardTeam/AdGuardHome/internal/aghnet" @@ -16,29 +15,6 @@ import ( "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/stringutil" "golang.org/x/exp/maps" - "golang.org/x/exp/slices" -) - -const ( - // errNotDomainSpecific is returned when the upstream should be - // domain-specific, but isn't. - errNotDomainSpecific errors.Error = "not a domain-specific upstream" - - // errMissingSeparator is returned when the domain-specific part of the - // upstream configuration line isn't closed. - errMissingSeparator errors.Error = "missing separator" - - // errDupSeparator is returned when the domain-specific part of the upstream - // configuration line contains more than one ending separator. - errDupSeparator errors.Error = "duplicated separator" - - // errNoDefaultUpstreams is returned when there are no default upstreams - // specified in the upstream configuration. - errNoDefaultUpstreams errors.Error = "no default upstreams specified" - - // errWrongResponse is returned when the checked upstream replies in an - // unexpected way. - errWrongResponse errors.Error = "wrong response" ) // loadUpstreams parses upstream DNS servers from the configured file or from @@ -199,84 +175,12 @@ func IsCommentOrEmpty(s string) (ok bool) { return len(s) == 0 || s[0] == '#' } -// newUpstreamConfig validates upstreams and returns an appropriate upstream -// configuration or nil if it can't be built. -// -// TODO(e.burkov): Perhaps proxy.ParseUpstreamsConfig should validate upstreams -// slice already so that this function may be considered useless. -func newUpstreamConfig(upstreams []string) (conf *proxy.UpstreamConfig, err error) { - // No need to validate comments and empty lines. - upstreams = stringutil.FilterOut(upstreams, IsCommentOrEmpty) - if len(upstreams) == 0 { - // Consider this case valid since it means the default server should be - // used. - return nil, nil - } - - err = validateUpstreamConfig(upstreams) - if err != nil { - // Don't wrap the error since it's informative enough as is. - return nil, err - } - - conf, err = proxy.ParseUpstreamsConfig( - upstreams, - &upstream.Options{ - Bootstrap: net.DefaultResolver, - Timeout: DefaultTimeout, - }, - ) - if err != nil { - // Don't wrap the error since it's informative enough as is. - return nil, err - } else if len(conf.Upstreams) == 0 { - return nil, errNoDefaultUpstreams - } - - return conf, nil -} - -// validateUpstreamConfig validates each upstream from the upstream -// configuration and returns an error if any upstream is invalid. -// -// TODO(e.burkov): Merge with [upstreamConfigValidator] somehow. -func validateUpstreamConfig(conf []string) (err error) { - for _, u := range conf { - var ups []string - var isSpecific bool - ups, isSpecific, err = splitUpstreamLine(u) - if err != nil { - // Don't wrap the error since it's informative enough as is. - return err - } - - for _, addr := range ups { - _, err = validateUpstream(addr, isSpecific) - if err != nil { - return fmt.Errorf("validating upstream %q: %w", addr, err) - } - } - } - - return nil -} - -// ValidateUpstreams validates each upstream and returns an error if any -// upstream is invalid or if there are no default upstreams specified. -// -// TODO(e.burkov): Merge with [upstreamConfigValidator] somehow. -func ValidateUpstreams(upstreams []string) (err error) { - _, err = newUpstreamConfig(upstreams) - - return err -} - // ValidateUpstreamsPrivate validates each upstream and returns an error if any // upstream is invalid or if there are no default upstreams specified. It also // checks each domain of domain-specific upstreams for being ARPA pointing to // a locally-served network. privateNets must not be nil. func ValidateUpstreamsPrivate(upstreams []string, privateNets netutil.SubnetSet) (err error) { - conf, err := newUpstreamConfig(upstreams) + conf, err := proxy.ParseUpstreamsConfig(upstreams, &upstream.Options{}) if err != nil { return fmt.Errorf("creating config: %w", err) } @@ -308,66 +212,3 @@ func ValidateUpstreamsPrivate(upstreams []string, privateNets netutil.SubnetSet) return errors.Annotate(errors.Join(errs...), "checking domain-specific upstreams: %w") } - -// protocols are the supported URL schemes for upstreams. -var protocols = []string{"h3", "https", "quic", "sdns", "tcp", "tls", "udp"} - -// validateUpstream returns an error if u alongside with domains is not a valid -// upstream configuration. useDefault is true if the upstream is -// domain-specific and is configured to point at the default upstream server -// which is validated separately. The upstream is considered domain-specific -// only if domains is at least not nil. -func validateUpstream(u string, isSpecific bool) (useDefault bool, err error) { - // The special server address '#' means that default server must be used. - if useDefault = u == "#" && isSpecific; useDefault { - return useDefault, nil - } - - // Check if the upstream has a valid protocol prefix. - // - // TODO(e.burkov): Validate the domain name. - if proto, _, ok := strings.Cut(u, "://"); ok { - if !slices.Contains(protocols, proto) { - return false, fmt.Errorf("bad protocol %q", proto) - } - } else if _, err = netip.ParseAddr(u); err == nil { - return false, nil - } else if _, err = netip.ParseAddrPort(u); err == nil { - return false, nil - } - - return false, err -} - -// splitUpstreamLine returns the upstreams and the specified domains. domains -// is nil when the upstream is not domains-specific. Otherwise it may also be -// empty. -func splitUpstreamLine(upstreamStr string) (upstreams []string, isSpecific bool, err error) { - if !strings.HasPrefix(upstreamStr, "[/") { - return []string{upstreamStr}, false, nil - } - - defer func() { err = errors.Annotate(err, "splitting upstream line %q: %w", upstreamStr) }() - - doms, ups, found := strings.Cut(upstreamStr[2:], "/]") - if !found { - return nil, false, errMissingSeparator - } else if strings.Contains(ups, "/]") { - return nil, false, errDupSeparator - } - - for i, host := range strings.Split(doms, "/") { - if host == "" { - continue - } - - err = netutil.ValidateDomainName(strings.TrimPrefix(host, "*.")) - if err != nil { - return nil, false, fmt.Errorf("domain at index %d: %w", i, err) - } - - isSpecific = true - } - - return strings.Fields(ups), isSpecific, nil -} diff --git a/internal/dnsforward/upstreams_internal_test.go b/internal/dnsforward/upstreams_internal_test.go index 9bf2f5cf..128c3eeb 100644 --- a/internal/dnsforward/upstreams_internal_test.go +++ b/internal/dnsforward/upstreams_internal_test.go @@ -100,8 +100,7 @@ func TestUpstreamConfigValidator(t *testing.T) { name: "bad_specification", general: []string{"[/domain.example/]/]1.2.3.4"}, want: map[string]string{ - "[/domain.example/]/]1.2.3.4": `splitting upstream line ` + - `"[/domain.example/]/]1.2.3.4": duplicated separator`, + "[/domain.example/]/]1.2.3.4": generalTextLabel + " 1: parsing error", }, }, { name: "all_different", @@ -120,23 +119,9 @@ func TestUpstreamConfigValidator(t *testing.T) { fallback: []string{"[/example/" + goodUps}, private: []string{"[/example//bad.123/]" + goodUps}, want: map[string]string{ - `[/example/]/]` + goodUps: `splitting upstream line ` + - `"[/example/]/]` + goodUps + `": duplicated separator`, - `[/example/` + goodUps: `splitting upstream line ` + - `"[/example/` + goodUps + `": missing separator`, - `[/example//bad.123/]` + goodUps: `splitting upstream line ` + - `"[/example//bad.123/]` + goodUps + `": domain at index 2: ` + - `bad domain name "bad.123": ` + - `bad top-level domain name label "123": all octets are numeric`, - }, - }, { - name: "non-specific_default", - general: []string{ - "#", - "[/example/]#", - }, - want: map[string]string{ - "#": "not a domain-specific upstream", + "[/example/]/]" + goodUps: generalTextLabel + " 1: parsing error", + "[/example/" + goodUps: fallbackTextLabel + " 1: parsing error", + "[/example//bad.123/]" + goodUps: privateTextLabel + " 1: parsing error", }, }, { name: "bad_proto", @@ -144,7 +129,15 @@ func TestUpstreamConfigValidator(t *testing.T) { "bad://1.2.3.4", }, want: map[string]string{ - "bad://1.2.3.4": `bad protocol "bad"`, + "bad://1.2.3.4": generalTextLabel + " 1: parsing error", + }, + }, { + name: "truncated_line", + general: []string{ + "This is a very long line. It will cause a parsing error and will be truncated here.", + }, + want: map[string]string{ + "This is a very long line. It will cause a parsing error and will be truncated …": "upstream_dns 1: parsing error", }, }} diff --git a/internal/filtering/blocked.go b/internal/filtering/blocked.go index cd388853..3e0764d2 100644 --- a/internal/filtering/blocked.go +++ b/internal/filtering/blocked.go @@ -4,13 +4,13 @@ import ( "encoding/json" "fmt" "net/http" + "slices" "time" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/AdGuardHome/internal/schedule" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/urlfilter/rules" - "golang.org/x/exp/slices" ) // serviceRules maps a service ID to its filtering rules. diff --git a/internal/filtering/filter.go b/internal/filtering/filter.go index 64befc73..65ef1b7a 100644 --- a/internal/filtering/filter.go +++ b/internal/filtering/filter.go @@ -6,6 +6,7 @@ import ( "net/http" "os" "path/filepath" + "slices" "strconv" "strings" "time" @@ -15,7 +16,6 @@ import ( "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/stringutil" - "golang.org/x/exp/slices" ) // filterDir is the subdirectory of a data directory to store downloaded diff --git a/internal/filtering/filtering.go b/internal/filtering/filtering.go index 2d382530..b145ed3b 100644 --- a/internal/filtering/filtering.go +++ b/internal/filtering/filtering.go @@ -12,6 +12,7 @@ import ( "path/filepath" "runtime" "runtime/debug" + "slices" "strings" "sync" "sync/atomic" @@ -29,7 +30,6 @@ import ( "github.com/AdguardTeam/urlfilter/filterlist" "github.com/AdguardTeam/urlfilter/rules" "github.com/miekg/dns" - "golang.org/x/exp/slices" ) // The IDs of built-in filter lists. @@ -1113,8 +1113,7 @@ func (d *DNSFilter) periodicallyRefreshFilters(ivl time.Duration) (nextIvl time. ivl = maxInterval } else if isNetErr { ivl *= 2 - // TODO(s.chzhen): Use built-in function max in Go 1.21. - ivl = mathutil.Max(ivl, maxInterval) + ivl = max(ivl, maxInterval) } return ivl diff --git a/internal/filtering/hashprefix/hashprefix.go b/internal/filtering/hashprefix/hashprefix.go index 00255254..fb1128e4 100644 --- a/internal/filtering/hashprefix/hashprefix.go +++ b/internal/filtering/hashprefix/hashprefix.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "slices" "strings" "time" @@ -14,7 +15,6 @@ import ( "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/stringutil" "github.com/miekg/dns" - "golang.org/x/exp/slices" "golang.org/x/net/publicsuffix" ) diff --git a/internal/filtering/hashprefix/hashprefix_internal_test.go b/internal/filtering/hashprefix/hashprefix_internal_test.go index 97290661..a6a95ad5 100644 --- a/internal/filtering/hashprefix/hashprefix_internal_test.go +++ b/internal/filtering/hashprefix/hashprefix_internal_test.go @@ -3,6 +3,7 @@ package hashprefix import ( "crypto/sha256" "encoding/hex" + "slices" "strings" "testing" "time" @@ -12,7 +13,6 @@ import ( "github.com/miekg/dns" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "golang.org/x/exp/slices" ) const ( diff --git a/internal/filtering/http.go b/internal/filtering/http.go index 362ef0e4..310338c0 100644 --- a/internal/filtering/http.go +++ b/internal/filtering/http.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "path/filepath" + "slices" "sync" "time" @@ -15,7 +16,6 @@ import ( "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" "github.com/miekg/dns" - "golang.org/x/exp/slices" ) // validateFilterURL validates the filter list URL or file name. diff --git a/internal/filtering/rewrite/storage.go b/internal/filtering/rewrite/storage.go index 31b2e094..cdf3e52c 100644 --- a/internal/filtering/rewrite/storage.go +++ b/internal/filtering/rewrite/storage.go @@ -3,6 +3,7 @@ package rewrite import ( "fmt" + "slices" "strings" "sync" @@ -12,7 +13,6 @@ import ( "github.com/AdguardTeam/urlfilter/filterlist" "github.com/AdguardTeam/urlfilter/rules" "github.com/miekg/dns" - "golang.org/x/exp/slices" ) // Storage is a storage for rewrite rules. diff --git a/internal/filtering/rewritehttp.go b/internal/filtering/rewritehttp.go index ed34bb4b..af2ddf1f 100644 --- a/internal/filtering/rewritehttp.go +++ b/internal/filtering/rewritehttp.go @@ -3,10 +3,10 @@ package filtering import ( "encoding/json" "net/http" + "slices" "github.com/AdguardTeam/AdGuardHome/internal/aghhttp" "github.com/AdguardTeam/golibs/log" - "golang.org/x/exp/slices" ) // TODO(d.kolyshev): Use [rewrite.Item] instead. diff --git a/internal/filtering/rewrites.go b/internal/filtering/rewrites.go index 4a73cc37..ba759f6c 100644 --- a/internal/filtering/rewrites.go +++ b/internal/filtering/rewrites.go @@ -3,13 +3,13 @@ package filtering import ( "fmt" "net/netip" + "slices" "strings" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/mathutil" "github.com/miekg/dns" - "golang.org/x/exp/slices" ) // Legacy DNS rewrites diff --git a/internal/filtering/rulelist/parser.go b/internal/filtering/rulelist/parser.go index 24d19b9c..0f48b245 100644 --- a/internal/filtering/rulelist/parser.go +++ b/internal/filtering/rulelist/parser.go @@ -6,9 +6,9 @@ import ( "fmt" "hash/crc32" "io" + "slices" "github.com/AdguardTeam/golibs/errors" - "golang.org/x/exp/slices" ) // Parser is a filtering-rule parser that collects data, such as the checksum diff --git a/internal/home/client.go b/internal/home/client.go index 64e9b677..742754c7 100644 --- a/internal/home/client.go +++ b/internal/home/client.go @@ -5,6 +5,7 @@ import ( "fmt" "net" "net/netip" + "slices" "strings" "time" @@ -16,7 +17,6 @@ import ( "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/stringutil" "github.com/google/uuid" - "golang.org/x/exp/slices" ) // UID is the type for the unique IDs of persistent clients. diff --git a/internal/home/clients.go b/internal/home/clients.go index 07256fea..3da8572f 100644 --- a/internal/home/clients.go +++ b/internal/home/clients.go @@ -4,6 +4,7 @@ import ( "fmt" "net" "net/netip" + "slices" "strings" "sync" "time" @@ -23,7 +24,6 @@ import ( "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/stringutil" "golang.org/x/exp/maps" - "golang.org/x/exp/slices" ) // DHCP is an interface for accessing DHCP lease data the [clientsContainer] @@ -613,7 +613,7 @@ func (clients *clientsContainer) check(c *persistentClient) (err error) { // TODO(s.chzhen): Move to the constructor. slices.Sort(c.Tags) - err = dnsforward.ValidateUpstreams(c.Upstreams) + _, err = proxy.ParseUpstreamsConfig(c.Upstreams, &upstream.Options{}) if err != nil { return fmt.Errorf("invalid upstream servers: %w", err) } diff --git a/internal/home/home.go b/internal/home/home.go index 701e4820..e3230424 100644 --- a/internal/home/home.go +++ b/internal/home/home.go @@ -14,6 +14,7 @@ import ( "path" "path/filepath" "runtime" + "slices" "sync" "syscall" "time" @@ -40,7 +41,6 @@ import ( "github.com/AdguardTeam/golibs/netutil" "github.com/AdguardTeam/golibs/osutil" "github.com/AdguardTeam/golibs/stringutil" - "golang.org/x/exp/slices" ) // Global context diff --git a/internal/next/cmd/opt.go b/internal/next/cmd/opt.go index 5959af76..27d95b92 100644 --- a/internal/next/cmd/opt.go +++ b/internal/next/cmd/opt.go @@ -8,13 +8,13 @@ import ( "io/fs" "net/netip" "os" + "slices" "strings" "github.com/AdguardTeam/AdGuardHome/internal/configmigrate" "github.com/AdguardTeam/AdGuardHome/internal/next/configmgr" "github.com/AdguardTeam/AdGuardHome/internal/version" "github.com/AdguardTeam/golibs/log" - "golang.org/x/exp/slices" ) // options contains all command-line options for the AdGuardHome(.exe) binary. diff --git a/internal/next/configmgr/configmgr.go b/internal/next/configmgr/configmgr.go index a361c87d..84e4e61a 100644 --- a/internal/next/configmgr/configmgr.go +++ b/internal/next/configmgr/configmgr.go @@ -10,6 +10,7 @@ import ( "io/fs" "net/netip" "os" + "slices" "sync" "time" @@ -20,7 +21,6 @@ import ( "github.com/AdguardTeam/golibs/log" "github.com/AdguardTeam/golibs/timeutil" "github.com/google/renameio/v2/maybe" - "golang.org/x/exp/slices" "gopkg.in/yaml.v3" ) diff --git a/internal/querylog/json.go b/internal/querylog/json.go index 76a406eb..07a9d62b 100644 --- a/internal/querylog/json.go +++ b/internal/querylog/json.go @@ -1,6 +1,7 @@ package querylog import ( + "slices" "strconv" "strings" "time" @@ -9,7 +10,6 @@ import ( "github.com/AdguardTeam/AdGuardHome/internal/filtering" "github.com/AdguardTeam/golibs/log" "github.com/miekg/dns" - "golang.org/x/exp/slices" "golang.org/x/net/idna" ) diff --git a/internal/querylog/search.go b/internal/querylog/search.go index 1a992e20..b5e0c4ec 100644 --- a/internal/querylog/search.go +++ b/internal/querylog/search.go @@ -3,11 +3,11 @@ package querylog import ( "fmt" "io" + "slices" "time" "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" - "golang.org/x/exp/slices" ) // client finds the client info, if any, by its ClientID and IP address, diff --git a/internal/rdns/rdns.go b/internal/rdns/rdns.go index 93898b3e..7130ccf3 100644 --- a/internal/rdns/rdns.go +++ b/internal/rdns/rdns.go @@ -7,7 +7,6 @@ import ( "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" - "github.com/AdguardTeam/golibs/mathutil" "github.com/bluele/gcache" ) @@ -88,8 +87,7 @@ func (r *Default) Process(ip netip.Addr) (host string, changed bool) { log.Debug("rdns: resolving %q: %s", ip, err) } - // TODO(s.chzhen): Use built-in function max in Go 1.21. - ttl = mathutil.Max(ttl, r.cacheTTL) + ttl = max(ttl, r.cacheTTL) item := &cacheItem{ expiry: time.Now().Add(ttl), diff --git a/internal/stats/unit.go b/internal/stats/unit.go index 478d9f66..b49256ee 100644 --- a/internal/stats/unit.go +++ b/internal/stats/unit.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "encoding/gob" "fmt" + "slices" "time" "github.com/AdguardTeam/AdGuardHome/internal/aghnet" @@ -12,7 +13,6 @@ import ( "github.com/AdguardTeam/golibs/log" "go.etcd.io/bbolt" "golang.org/x/exp/maps" - "golang.org/x/exp/slices" ) const ( diff --git a/internal/tools/go.mod b/internal/tools/go.mod index c6af873d..9dd018ed 100644 --- a/internal/tools/go.mod +++ b/internal/tools/go.mod @@ -1,17 +1,17 @@ module github.com/AdguardTeam/AdGuardHome/internal/tools -go 1.20 +go 1.21.7 require ( github.com/fzipp/gocyclo v0.6.0 github.com/golangci/misspell v0.4.1 github.com/gordonklaus/ineffassign v0.1.0 - github.com/kisielk/errcheck v1.6.3 + github.com/kisielk/errcheck v1.7.0 github.com/kyoh86/looppointer v0.2.1 github.com/securego/gosec/v2 v2.18.2 github.com/uudashr/gocognit v1.1.2 golang.org/x/tools v0.17.0 - golang.org/x/vuln v1.0.3 + golang.org/x/vuln v1.0.4 honnef.co/go/tools v0.4.6 mvdan.cc/gofumpt v0.6.0 mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 @@ -26,9 +26,9 @@ require ( github.com/kyoh86/nolint v0.0.1 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect - golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a // indirect - golang.org/x/mod v0.14.0 // indirect + golang.org/x/exp/typeparams v0.0.0-20240205201215-2c58cdc269a3 // indirect + golang.org/x/mod v0.15.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.17.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/internal/tools/go.sum b/internal/tools/go.sum index fbd0a6c9..450eea75 100644 --- a/internal/tools/go.sum +++ b/internal/tools/go.sum @@ -3,39 +3,53 @@ github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo= github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/golangci/misspell v0.4.1 h1:+y73iSicVy2PqyX7kmUefHusENlrP9YwuHZHPLGQj/g= github.com/golangci/misspell v0.4.1/go.mod h1:9mAN1quEo3DlpbaIKKyEvRxK1pwqR9s/Sea1bJCtlNI= github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786 h1:rcv+Ippz6RAtvaGgKxc+8FQIpxHgsF+HBzPyYL2cyVU= +github.com/google/go-cmdtest v0.4.1-0.20220921163831-55ab3332a786/go.mod h1:apVn/GCasLZUVpAJ6oWAuyP7Ne7CEsQbTnc0plM3m+o= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 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/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= 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/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= -github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8= -github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= +github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= +github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kyoh86/looppointer v0.2.1 h1:Jx9fnkBj/JrIryBLMTYNTj9rvc2SrPS98Dg0w7fxdJg= github.com/kyoh86/looppointer v0.2.1/go.mod h1:q358WcM8cMWU+5vzqukvaZtnJi1kw/MpRHQm3xvTrjw= github.com/kyoh86/nolint v0.0.1 h1:GjNxDEkVn2wAxKHtP7iNTrRxytRZ1wXxLV5j4XzGfRU= github.com/kyoh86/nolint v0.0.1/go.mod h1:1ZiZZ7qqrZ9dZegU96phwVcdQOMKIqRzFJL3ewq9gtI= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= +github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= +github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/securego/gosec/v2 v2.18.2 h1:DkDt3wCiOtAHf1XkiXZBhQ6m6mK/b9T/wD257R3/c+I= github.com/securego/gosec/v2 v2.18.2/go.mod h1:xUuqSF6i0So56Y2wwohWAmB07EdBkUN6crbLlHwbyJs= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI= github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k= github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= @@ -49,14 +63,13 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20230321023759-10a507213a29 h1:ooxPy7fPvB4kwsA2h+iBNHkAbp/4JxTSwCmvdjEYmug= golang.org/x/exp v0.0.0-20230321023759-10a507213a29/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a h1:8qmSSA8Gz/1kTrCe0nqR0R3Gb/NDhykzWw2q2mWZydM= -golang.org/x/exp/typeparams v0.0.0-20240119083558-1b970713d09a/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20240205201215-2c58cdc269a3 h1:1hsZWSQgrpqFJbqlg8HNhQ2/U/7IQELXYWTjuCfHNcM= +golang.org/x/exp/typeparams v0.0.0-20240205201215-2c58cdc269a3/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.15.0 h1:SernR4v+D55NyBH2QiEQrlBAnj1ECL6AGrA5+dPaMY8= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= @@ -64,6 +77,7 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -79,24 +93,24 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220702020025-31831981b65f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201007032633-0806396f153e/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4= golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= -golang.org/x/vuln v1.0.3 h1:k2wzzWGpdntQzNsCOLTabCFk76oTe69BPwad5H52F4w= -golang.org/x/vuln v1.0.3/go.mod h1:NbJdUQhX8jY++FtuhrXs2Eyx0yePo9pF7nPlIjo9aaQ= +golang.org/x/vuln v1.0.4 h1:SP0mPeg2PmGCu03V+61EcQiOjmpri2XijexKdzv8Z1I= +golang.org/x/vuln v1.0.4/go.mod h1:NbJdUQhX8jY++FtuhrXs2Eyx0yePo9pF7nPlIjo9aaQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/internal/updater/check.go b/internal/updater/check.go index 71dc9582..12f4bece 100644 --- a/internal/updater/check.go +++ b/internal/updater/check.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "net/http" + "slices" "time" "github.com/AdguardTeam/AdGuardHome/internal/aghalg" @@ -12,7 +13,6 @@ import ( "github.com/AdguardTeam/golibs/ioutil" "github.com/AdguardTeam/golibs/log" "golang.org/x/exp/maps" - "golang.org/x/exp/slices" ) // TODO(a.garipov): Make configurable. diff --git a/scripts/README.md b/scripts/README.md index b567cbd4..c756c382 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -87,6 +87,22 @@ Optional environment: * `VERSION`: release version. Will be set by `version.sh` if it is unset or if it has the default `Makefile` value of `v0.0.0`. +We're using Go's [forward compatibility mechanism][go-toolchain] for updating +the Go version. This means that if your `go` version is 1.21+ but is different +from the one required by AdGuard Home, the `go` tool will automatically download +the required version. + +If you want to use the version installed on your builder, run: + +```sh +go get go@$YOUR_VERSION +go mod tidy +``` + +and call `make` with `GOTOOLCHAIN=local`. + +[go-toolchain]: https://go.dev/blog/toolchain + ### `clean.sh`: Cleanup diff --git a/scripts/blocked-services/main.go b/scripts/blocked-services/main.go index 5e57860a..bb0b97cc 100644 --- a/scripts/blocked-services/main.go +++ b/scripts/blocked-services/main.go @@ -9,12 +9,12 @@ import ( "net/http" "net/url" "os" + "slices" "strings" "text/template" "time" "github.com/AdguardTeam/golibs/log" - "golang.org/x/exp/slices" ) func main() { diff --git a/scripts/hooks/pre-commit b/scripts/hooks/pre-commit index 42d1e413..b0cce8d6 100755 --- a/scripts/hooks/pre-commit +++ b/scripts/hooks/pre-commit @@ -6,7 +6,9 @@ set -e -f -u # Bump this number every time a significant change is made to this # script. # -# AdGuard-Project-Version: 1 +# AdGuard-Project-Version: 2 + +# TODO(a.garipov): Add pre-merge-commit. # Only show interactive prompts if there a terminal is attached to # stdout. While this technically doesn't guarantee that reading from diff --git a/scripts/make/go-lint.sh b/scripts/make/go-lint.sh index ce8718dc..3ce644ef 100644 --- a/scripts/make/go-lint.sh +++ b/scripts/make/go-lint.sh @@ -30,33 +30,6 @@ set -f -u -# Warnings - -go_version="$( "${GO:-go}" version )" -readonly go_version - -go_min_version='go1.20.12' -go_version_msg=" -warning: your go version (${go_version}) is different from the recommended minimal one (${go_min_version}). -if you have the version installed, please set the GO environment variable. -for example: - - export GO='${go_min_version}' -" -readonly go_min_version go_version_msg - -case "$go_version" -in -('go version'*"$go_min_version"*) - # Go on. - ;; -(*) - echo "$go_version_msg" 1>&2 - ;; -esac - - - # Simple analyzers # blocklist_imports is a simple check against unwanted packages. The following @@ -74,18 +47,22 @@ esac # # See https://github.com/golang/go/issues/45200. # -# * Package sort is replaced by golang.org/x/exp/slices. +# * Package sort is replaced by package slices. # # * Package unsafe is… unsafe. # +# * Package golang.org/x/exp/slices has been moved into stdlib. +# # * Package golang.org/x/net/context has been moved into stdlib. # # Currently, the only standard exception are files generated from protobuf # schemas, which use package reflect. If your project needs more exceptions, # add and document them. # -# TODO(a.garipov): Add deprecated packages golang.org/x/exp/maps and -# golang.org/x/exp/slices once all projects switch to Go 1.21. +# TODO(a.garipov): Add goilbs/log. +# +# TODO(a.garipov): Add deprecated package golang.org/x/exp/maps once all +# projects switch to Go 1.22. blocklist_imports() { git grep\ -e '[[:space:]]"errors"$'\ @@ -94,6 +71,7 @@ blocklist_imports() { -e '[[:space:]]"reflect"$'\ -e '[[:space:]]"sort"$'\ -e '[[:space:]]"unsafe"$'\ + -e '[[:space:]]"golang.org/x/exp/slices"$'\ -e '[[:space:]]"golang.org/x/net/context"$'\ -n\ -- '*.go'\ @@ -124,12 +102,10 @@ underscores() { underscore_files="$( git ls-files '*_*.go'\ | grep -F\ - -e '_big.go'\ -e '_bsd.go'\ -e '_darwin.go'\ -e '_freebsd.go'\ -e '_linux.go'\ - -e '_little.go'\ -e '_next.go'\ -e '_openbsd.go'\ -e '_others.go'\ diff --git a/scripts/make/go-test.sh b/scripts/make/go-test.sh index 03d2c100..3727587e 100644 --- a/scripts/make/go-test.sh +++ b/scripts/make/go-test.sh @@ -51,9 +51,9 @@ readonly count_flags cover_flags shuffle_flags timeout_flags "$go" test\ "$count_flags"\ "$cover_flags"\ - "$shuffle_flags"\ "$race_flags"\ + "$shuffle_flags"\ "$timeout_flags"\ - "$x_flags"\ "$v_flags"\ + "$x_flags"\ ./... diff --git a/scripts/make/go-tools.sh b/scripts/make/go-tools.sh index fdb70f86..9d705103 100644 --- a/scripts/make/go-tools.sh +++ b/scripts/make/go-tools.sh @@ -30,8 +30,6 @@ set -e -f -u go="${GO:-go}" readonly go -# TODO(a.garipov): Add goconst? - # Remove only the actual binaries in the bin/ directory, as developers may add # their own scripts there. Most commonly, a script named “go” for tools that # call the go binary and need a particular version. diff --git a/scripts/make/go-upd-tools.sh b/scripts/make/go-upd-tools.sh new file mode 100644 index 00000000..7486661d --- /dev/null +++ b/scripts/make/go-upd-tools.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# This comment is used to simplify checking local copies of the script. Bump +# this number every time a significant change is made to this script. +# +# AdGuard-Project-Version: 1 + +verbose="${VERBOSE:-0}" +readonly verbose + +if [ "$verbose" -gt '1' ] +then + env + set -x + x_flags='-x=1' +elif [ "$verbose" -gt '0' ] +then + set -x + x_flags='-x=0' +else + set +x + x_flags='-x=0' +fi +readonly x_flags + +set -e -f -u + +go="${GO:-go}" +readonly go + +cd ./internal/tools/ +"$go" get -u +"$go" mod tidy diff --git a/scripts/translations/download.go b/scripts/translations/download.go index 123cf350..d83f0bac 100644 --- a/scripts/translations/download.go +++ b/scripts/translations/download.go @@ -8,6 +8,7 @@ import ( "net/url" "os" "path/filepath" + "slices" "strings" "sync" "time" @@ -15,7 +16,6 @@ import ( "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/ioutil" "github.com/AdguardTeam/golibs/log" - "golang.org/x/exp/slices" ) // download and save all translations. diff --git a/scripts/translations/main.go b/scripts/translations/main.go index cff6d03c..e03dcb10 100644 --- a/scripts/translations/main.go +++ b/scripts/translations/main.go @@ -11,6 +11,7 @@ import ( "os" "os/exec" "path/filepath" + "slices" "strings" "time" @@ -18,7 +19,6 @@ import ( "github.com/AdguardTeam/golibs/errors" "github.com/AdguardTeam/golibs/log" "golang.org/x/exp/maps" - "golang.org/x/exp/slices" ) const (