From dbf71b3f68992fc665b076c9a3329ee1e66228db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= Date: Tue, 22 Jun 2021 16:35:46 +0200 Subject: [PATCH 1/3] Parse 'timeout' and 'attempts' from resolv.conf It was supported by lwres in BIND 9.11, and is still mentioned in the manual page. Restore support for it by adding it to libirs. --- bin/dig/dighost.c | 13 ++++++- lib/irs/include/irs/resconf.h | 20 +++++++++++ lib/irs/resconf.c | 64 ++++++++++++++++++++++++++++------- 3 files changed, 84 insertions(+), 13 deletions(-) diff --git a/bin/dig/dighost.c b/bin/dig/dighost.c index 05c7ed5598..b6131e0831 100644 --- a/bin/dig/dighost.c +++ b/bin/dig/dighost.c @@ -114,7 +114,7 @@ isc_sockaddr_t localaddr; isc_refcount_t sendcount = ATOMIC_VAR_INIT(0); isc_refcount_t recvcount = ATOMIC_VAR_INIT(0); int ndots = -1; -int tries = 3; +int tries = -1; int lookup_counter = 0; static char servercookie[256]; @@ -1276,6 +1276,17 @@ setup_system(bool ipv4only, bool ipv6only) { ndots = irs_resconf_getndots(resconf); debug("ndots is %d.", ndots); } + if (timeout == 0) { + timeout = irs_resconf_gettimeout(resconf); + debug("timeout is %d.", timeout); + } + if (tries == -1) { + tries = irs_resconf_getattempts(resconf); + if (tries == 0) { + tries = 3; + } + debug("retries is %d.", tries); + } /* If user doesn't specify server use nameservers from resolv.conf. */ if (ISC_LIST_EMPTY(server_list)) { diff --git a/lib/irs/include/irs/resconf.h b/lib/irs/include/irs/resconf.h index d95c150a64..f5ec355765 100644 --- a/lib/irs/include/irs/resconf.h +++ b/lib/irs/include/irs/resconf.h @@ -115,4 +115,24 @@ irs_resconf_getndots(irs_resconf_t *conf); *\li 'conf' is a valid resconf object. */ +unsigned int +irs_resconf_getattempts(irs_resconf_t *conf); +/*%< + * Return the 'attempts' value stored in 'conf'. + * + * Requires: + * + *\li 'conf' is a valid resconf object. + */ + +unsigned int +irs_resconf_gettimeout(irs_resconf_t *conf); +/*%< + * Return the 'timeout' value stored in 'conf'. + * + * Requires: + * + *\li 'conf' is a valid resconf object. + */ + ISC_LANG_ENDDECLS diff --git a/lib/irs/resconf.c b/lib/irs/resconf.c index 8aa723c21c..746f65f534 100644 --- a/lib/irs/resconf.c +++ b/lib/irs/resconf.c @@ -74,6 +74,13 @@ #define RESCONFMAXLINELEN 256U /*%< max size of a line */ #define RESCONFMAXSORTLIST 10U /*%< max 10 */ +#define CHECK(op) \ + do { \ + result = (op); \ + if (result != ISC_R_SUCCESS) \ + goto cleanup; \ + } while (0) + /*! * configuration data structure */ @@ -108,6 +115,10 @@ struct irs_resconf { uint8_t resdebug; /*%< set to n in 'options ndots:n' */ uint8_t ndots; + /*%< set to n in 'options attempts:n' */ + uint8_t attempts; + /*%< set to n in 'options timeout:n' */ + uint8_t timeout; }; static isc_result_t @@ -165,8 +176,8 @@ eatwhite(FILE *fp) { */ static int getword(FILE *fp, char *buffer, size_t size) { + char *p = NULL; int ch; - char *p; REQUIRE(buffer != NULL); REQUIRE(size > 0U); @@ -446,11 +457,26 @@ resconf_parsesortlist(irs_resconf_t *conf, FILE *fp) { return (ISC_R_SUCCESS); } +static isc_result_t +resconf_optionnumber(const char *word, uint8_t *number) { + char *p; + long n; + + n = strtol(word, &p, 10); + if (*p != '\0') { /* Bad string. */ + return (ISC_R_UNEXPECTEDTOKEN); + } + if (n < 0 || n > 0xff) { /* Out of range. */ + return (ISC_R_RANGE); + } + *number = n; + return (ISC_R_SUCCESS); +} + static isc_result_t resconf_parseoption(irs_resconf_t *conf, FILE *fp) { int delim; - long ndots; - char *p; + isc_result_t result = ISC_R_SUCCESS; char word[RESCONFMAXLINELEN]; delim = getword(fp, word, sizeof(word)); @@ -462,14 +488,11 @@ resconf_parseoption(irs_resconf_t *conf, FILE *fp) { if (strcmp("debug", word) == 0) { conf->resdebug = 1; } else if (strncmp("ndots:", word, 6) == 0) { - ndots = strtol(word + 6, &p, 10); - if (*p != '\0') { /* Bad string. */ - return (ISC_R_UNEXPECTEDTOKEN); - } - if (ndots < 0 || ndots > 0xff) { /* Out of range. */ - return (ISC_R_RANGE); - } - conf->ndots = (uint8_t)ndots; + CHECK(resconf_optionnumber(word + 6, &conf->ndots)); + } else if (strncmp("attempts:", word, 9) == 0) { + CHECK(resconf_optionnumber(word + 9, &conf->attempts)); + } else if (strncmp("timeout:", word, 8) == 0) { + CHECK(resconf_optionnumber(word + 8, &conf->timeout)); } if (delim == EOF || delim == '\n') { @@ -479,7 +502,8 @@ resconf_parseoption(irs_resconf_t *conf, FILE *fp) { } } - return (ISC_R_SUCCESS); +cleanup: + return (result); } static isc_result_t @@ -521,6 +545,8 @@ irs_resconf_load(isc_mem_t *mctx, const char *filename, irs_resconf_t **confp) { conf->sortlistnxt = 0; conf->resdebug = 0; conf->ndots = 1; + conf->attempts = 3; + conf->timeout = 0; for (i = 0; i < RESCONFMAXSEARCH; i++) { conf->search[i] = NULL; } @@ -669,3 +695,17 @@ irs_resconf_getndots(irs_resconf_t *conf) { return ((unsigned int)conf->ndots); } + +unsigned int +irs_resconf_getattempts(irs_resconf_t *conf) { + REQUIRE(IRS_RESCONF_VALID(conf)); + + return ((unsigned int)conf->attempts); +} + +unsigned int +irs_resconf_gettimeout(irs_resconf_t *conf) { + REQUIRE(IRS_RESCONF_VALID(conf)); + + return ((unsigned int)conf->timeout); +} From 48d5b503137aebffeb5d3374a1bb48e5cbbc7793 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= Date: Tue, 20 Jul 2021 19:34:42 +0200 Subject: [PATCH 2/3] Check parsed resconf values Add 'attempts' check, fix 'ndots' data. Create a bunch of verification functions and check parsed values, not just return codes. --- lib/irs/tests/resconf_test.c | 46 ++++++++++++++++++-- lib/irs/tests/testdata/options-attempts.conf | 10 +++++ lib/irs/tests/testdata/options-ndots.conf | 2 +- 3 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 lib/irs/tests/testdata/options-attempts.conf diff --git a/lib/irs/tests/resconf_test.c b/lib/irs/tests/resconf_test.c index 8ca9762bff..4befe01f15 100644 --- a/lib/irs/tests/resconf_test.c +++ b/lib/irs/tests/resconf_test.c @@ -42,6 +42,43 @@ setup_test(void) { assert_return_code(chdir(TESTS_DIR), 0); } +static isc_result_t +check_number(unsigned int n, unsigned int expected) { + return ((n == expected) ? ISC_R_SUCCESS : ISC_R_BADNUMBER); +} + +static isc_result_t +check_attempts(irs_resconf_t *resconf) { + return (check_number(irs_resconf_getattempts(resconf), 4)); +} + +static isc_result_t +check_timeout(irs_resconf_t *resconf) { + return (check_number(irs_resconf_gettimeout(resconf), 1)); +} + +static isc_result_t +check_ndots(irs_resconf_t *resconf) { + return (check_number(irs_resconf_getndots(resconf), 2)); +} + +static isc_result_t +check_options(irs_resconf_t *resconf) { + if (irs_resconf_getattempts(resconf) != 3) { + return ISC_R_BADNUMBER; /* default value only */ + } + + if (irs_resconf_getndots(resconf) != 2) { + return ISC_R_BADNUMBER; + } + + if (irs_resconf_gettimeout(resconf) != 1) { + return ISC_R_BADNUMBER; + } + + return (ISC_R_SUCCESS); +} + /* test irs_resconf_load() */ static void irs_resconf_load_test(void **state) { @@ -61,15 +98,18 @@ irs_resconf_load_test(void **state) { ISC_R_SUCCESS }, { "testdata/nameserver-v6-scoped.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/options-attempts.conf", ISC_R_SUCCESS, + check_attempts, ISC_R_SUCCESS }, { "testdata/options-debug.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, - { "testdata/options-ndots.conf", ISC_R_SUCCESS, NULL, + { "testdata/options-ndots.conf", ISC_R_SUCCESS, check_ndots, ISC_R_SUCCESS }, - { "testdata/options-timeout.conf", ISC_R_SUCCESS, NULL, + { "testdata/options-timeout.conf", ISC_R_SUCCESS, check_timeout, ISC_R_SUCCESS }, { "testdata/options-unknown.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, - { "testdata/options.conf", ISC_R_SUCCESS, NULL, ISC_R_SUCCESS }, + { "testdata/options.conf", ISC_R_SUCCESS, check_options, + ISC_R_SUCCESS }, { "testdata/options-bad-ndots.conf", ISC_R_RANGE, NULL, ISC_R_SUCCESS }, { "testdata/options-empty.conf", ISC_R_UNEXPECTEDEND, NULL, diff --git a/lib/irs/tests/testdata/options-attempts.conf b/lib/irs/tests/testdata/options-attempts.conf new file mode 100644 index 0000000000..453864331d --- /dev/null +++ b/lib/irs/tests/testdata/options-attempts.conf @@ -0,0 +1,10 @@ +# Copyright (C) Internet Systems Consortium, Inc. ("ISC") +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at https://mozilla.org/MPL/2.0/. +# +# See the COPYRIGHT file distributed with this work for additional +# information regarding copyright ownership. + +options attempts:4 diff --git a/lib/irs/tests/testdata/options-ndots.conf b/lib/irs/tests/testdata/options-ndots.conf index ff4c7e99ee..58b21b9f82 100644 --- a/lib/irs/tests/testdata/options-ndots.conf +++ b/lib/irs/tests/testdata/options-ndots.conf @@ -7,4 +7,4 @@ # See the COPYRIGHT file distributed with this work for additional # information regarding copyright ownership. -option ndots:2 +options ndots:2 From abfbb575a52a567089bb88be9e98b4af2e9125e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= Date: Tue, 20 Jul 2021 19:45:16 +0200 Subject: [PATCH 3/3] CHANGES [GL #2785] --- CHANGES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGES b/CHANGES index 0a9afeef7e..02e21e6513 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +5693. [func] Restore support for reading 'timeout' and 'attempts' + options from /etc/resolv.conf, and use their values + in dig, host and nslookup. (Previously this was + supported by liblwres, and was still mentioned + in man pages, but had stopped working after liblwres + was deprecated in favor of libirs.) [GL #2785] + 5692. [bug] Fix a rare crash in the DoH code caused by detaching from an HTTP/2 session handle too early when sending data. [GL #2851]