summaryrefslogtreecommitdiff
path: root/src/resolve
diff options
context:
space:
mode:
Diffstat (limited to 'src/resolve')
-rw-r--r--src/resolve/RFCs53
-rw-r--r--src/resolve/resolved-bus.c18
-rw-r--r--src/resolve/resolved-dns-answer.c24
-rw-r--r--src/resolve/resolved-dns-cache.c3
-rw-r--r--src/resolve/resolved-dns-dnssec.c33
-rw-r--r--src/resolve/resolved-dns-packet.c40
-rw-r--r--src/resolve/resolved-dns-packet.h13
-rw-r--r--src/resolve/resolved-dns-rr.c15
-rw-r--r--src/resolve/resolved-dns-rr.h35
-rw-r--r--src/resolve/resolved-dns-server.c2
10 files changed, 212 insertions, 24 deletions
diff --git a/src/resolve/RFCs b/src/resolve/RFCs
new file mode 100644
index 0000000000..8cad108d2c
--- /dev/null
+++ b/src/resolve/RFCs
@@ -0,0 +1,53 @@
+Y = Comprehensively Implemented, to the point appropriate for resolved
+D = Comprehensively Implemented, by a dependency of resolved
+! = Missing and something we might want to implement
+~ = Needs no explicit support or doesn't apply
+? = Is this relevant today?
+ = We are working on this
+
+Y https://tools.ietf.org/html/rfc1034 → DOMAIN NAMES - CONCEPTS AND FACILITIES
+Y https://tools.ietf.org/html/rfc1035 → DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION
+? https://tools.ietf.org/html/rfc1101 → DNS Encoding of Network Names and Other Types
+Y https://tools.ietf.org/html/rfc1123 → Requirements for Internet Hosts -- Application and Support
+Y https://tools.ietf.org/html/rfc1536 → Common DNS Implementation Errors and Suggested Fixes
+Y https://tools.ietf.org/html/rfc1876 → A Means for Expressing Location Information in the Domain Name System
+Y https://tools.ietf.org/html/rfc2181 → Clarifications to the DNS Specification
+ https://tools.ietf.org/html/rfc2308 → Negative Caching of DNS Queries (DNS NCACHE)
+Y https://tools.ietf.org/html/rfc2782 → A DNS RR for specifying the location of services (DNS SRV)
+D https://tools.ietf.org/html/rfc3492 → Punycode: A Bootstring encoding of Unicode for Internationalized Domain Names in Applications (IDNA)
+Y https://tools.ietf.org/html/rfc3596 → DNS Extensions to Support IP Version 6
+Y https://tools.ietf.org/html/rfc3597 → Handling of Unknown DNS Resource Record (RR) Types
+ https://tools.ietf.org/html/rfc4033 → DNS Security Introduction and Requirements
+ https://tools.ietf.org/html/rfc4034 → Resource Records for the DNS Security Extensions
+ https://tools.ietf.org/html/rfc4035 → Protocol Modifications for the DNS Security Extensions
+! https://tools.ietf.org/html/rfc4183 → A Suggested Scheme for DNS Resolution of Networks and Gateways
+Y https://tools.ietf.org/html/rfc4255 → Using DNS to Securely Publish Secure Shell (SSH) Key Fingerprints
+Y https://tools.ietf.org/html/rfc4343 → Domain Name System (DNS) Case Insensitivity Clarification
+~ https://tools.ietf.org/html/rfc4470 → Minimally Covering NSEC Records and DNSSEC On-line Signing
+Y https://tools.ietf.org/html/rfc4509 → Use of SHA-256 in DNSSEC Delegation Signer (DS) Resource Records (RRs)
+~ https://tools.ietf.org/html/rfc4592 → The Role of Wildcards in the Domain Name System
+Y https://tools.ietf.org/html/rfc4795 → Link-Local Multicast Name Resolution (LLMNR)
+! https://tools.ietf.org/html/rfc5011 → Automated Updates of DNS Security (DNSSEC) Trust Anchors
+ https://tools.ietf.org/html/rfc5155 → DNS Security (DNSSEC) Hashed Authenticated Denial of Existence
+Y https://tools.ietf.org/html/rfc5702 → Use of SHA-2 Algorithms with RSA in DNSKEY and RRSIG Resource Records for DNSSEC
+Y https://tools.ietf.org/html/rfc5890 → Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework
+Y https://tools.ietf.org/html/rfc5891 → Internationalized Domain Names in Applications (IDNA): Protocol
+Y https://tools.ietf.org/html/rfc5966 → DNS Transport over TCP - Implementation Requirements
+Y https://tools.ietf.org/html/rfc6303 → Locally Served DNS Zones
+Y https://tools.ietf.org/html/rfc6605 → Elliptic Curve Digital Signature Algorithm (DSA) for DNSSEC
+ https://tools.ietf.org/html/rfc6672 → DNAME Redirection in the DNS
+Y https://tools.ietf.org/html/rfc6761 → Special-Use Domain Names
+ https://tools.ietf.org/html/rfc6762 → Multicast DNS
+ https://tools.ietf.org/html/rfc6763 → DNS-Based Service Discovery
+ https://tools.ietf.org/html/rfc6781 → DNSSEC Operational Practices, Version 2
+ https://tools.ietf.org/html/rfc6840 → Clarifications and Implementation Notes for DNS Security (DNSSEC)
+Y https://tools.ietf.org/html/rfc6891 → Extension Mechanisms for DNS (EDNS(0))
+Y https://tools.ietf.org/html/rfc6944 → Applicability Statement: DNS Security (DNSSEC) DNSKEY Algorithm Implementation Status
+Y https://tools.ietf.org/html/rfc6975 → Signaling Cryptographic Algorithm Understanding in DNS Security Extensions (DNSSEC)
+ https://tools.ietf.org/html/rfc7129 → Authenticated Denial of Existence in the DNS
+! https://tools.ietf.org/html/rfc7646 → Definition and Use of DNSSEC Negative Trust Anchors
+~ https://tools.ietf.org/html/rfc7719 → DNS Terminology
+
+Also relevant:
+
+ https://www.iab.org/documents/correspondence-reports-documents/2013-2/iab-statement-dotless-domains-considered-harmful/
diff --git a/src/resolve/resolved-bus.c b/src/resolve/resolved-bus.c
index 5c7893d01c..4d4c1ca014 100644
--- a/src/resolve/resolved-bus.c
+++ b/src/resolve/resolved-bus.c
@@ -976,6 +976,7 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
return;
if (q->answer) {
+ bool has_root_domain = false;
DnsResourceRecord *rr;
int ifindex;
@@ -989,6 +990,11 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
if (rr->key->type != DNS_TYPE_SRV)
continue;
+ if (dns_name_is_root(rr->srv.name)) {
+ has_root_domain = true;
+ continue;
+ }
+
if ((q->flags & SD_RESOLVED_NO_ADDRESS) == 0) {
q->block_all_complete ++;
r = resolve_service_hostname(q, rr, ifindex);
@@ -1000,6 +1006,18 @@ static void bus_method_resolve_service_complete(DnsQuery *q) {
found++;
}
+
+ if (has_root_domain && found == 0) {
+ /* If there's exactly one SRV RR and it uses
+ * the root domain as host name, then the
+ * service is explicitly not offered on the
+ * domain. Report this as a recognizable
+ * error. See RFC 2782, Section "Usage
+ * Rules". */
+ r = sd_bus_reply_method_errorf(q->request, BUS_ERROR_NO_SUCH_SERVICE, "'%s' does not provide the requested service", dns_question_first_name(q->question));
+ goto finish;
+ }
+
}
if (found <= 0) {
diff --git a/src/resolve/resolved-dns-answer.c b/src/resolve/resolved-dns-answer.c
index 399b518644..445999f545 100644
--- a/src/resolve/resolved-dns-answer.c
+++ b/src/resolve/resolved-dns-answer.c
@@ -125,10 +125,13 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl
if (r < 0)
return r;
if (r > 0) {
- /* Entry already exists, keep the entry with
- * the higher RR, or the one with TTL 0 */
+ /* Don't mix contradicting TTLs (see below) */
+ if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
+ return -EINVAL;
- if (rr->ttl == 0 || (rr->ttl > a->items[i].rr->ttl && a->items[i].rr->ttl != 0)) {
+ /* Entry already exists, keep the entry with
+ * the higher RR. */
+ if (rr->ttl > a->items[i].rr->ttl) {
dns_resource_record_ref(rr);
dns_resource_record_unref(a->items[i].rr);
a->items[i].rr = rr;
@@ -137,6 +140,21 @@ int dns_answer_add(DnsAnswer *a, DnsResourceRecord *rr, int ifindex, DnsAnswerFl
a->items[i].flags |= flags;
return 0;
}
+
+ r = dns_resource_key_equal(a->items[i].rr->key, rr->key);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* There's already an RR of the same RRset in
+ * place! Let's see if the TTLs more or less
+ * match. We don't really care if they match
+ * precisely, but we do care whether one is 0
+ * and the other is not. See RFC 2181, Section
+ * 5.2.*/
+
+ if ((rr->ttl == 0) != (a->items[i].rr->ttl == 0))
+ return -EINVAL;
+ }
}
return dns_answer_add_raw(a, rr, ifindex, flags);
diff --git a/src/resolve/resolved-dns-cache.c b/src/resolve/resolved-dns-cache.c
index 3193985542..1c7dd56b3b 100644
--- a/src/resolve/resolved-dns-cache.c
+++ b/src/resolve/resolved-dns-cache.c
@@ -26,7 +26,8 @@
#include "resolved-dns-packet.h"
#include "string-util.h"
-/* Never cache more than 4K entries */
+/* Never cache more than 4K entries. RFC 1536, Section 5 suggests to
+ * leave DNS caches unbounded, but that's crazy. */
#define CACHE_MAX 4096
/* We never keep any item longer than 2h in our cache */
diff --git a/src/resolve/resolved-dns-dnssec.c b/src/resolve/resolved-dns-dnssec.c
index a3aa90e98d..e4b32c7e4b 100644
--- a/src/resolve/resolved-dns-dnssec.c
+++ b/src/resolve/resolved-dns-dnssec.c
@@ -42,7 +42,8 @@
* - per-interface DNSSEC setting
* - nxdomain on qname
* - retry on failed validation?
- * - DSA support?
+ * - DNSSEC key revocation support? https://tools.ietf.org/html/rfc5011
+ * - when doing negative caching, use NSEC/NSEC3 RR instead of SOA for TTL
*
* */
@@ -458,7 +459,15 @@ static int dnssec_rrsig_expired(DnsResourceRecord *rrsig, usec_t realtime) {
static int algorithm_to_gcrypt_md(uint8_t algorithm) {
- /* Translates a DNSSEC signature algorithm into a gcrypt digest identifier */
+ /* Translates a DNSSEC signature algorithm into a gcrypt
+ * digest identifier.
+ *
+ * Note that we implement all algorithms listed as "Must
+ * implement" and "Recommended to Implement" in RFC6944. We
+ * don't implement any algorithms that are listed as
+ * "Optional" or "Must Not Implement". Specifically, we do not
+ * implement RSAMD5, DSASHA1, DH, DSA-NSEC3-SHA1, and
+ * GOST-ECC. */
switch (algorithm) {
@@ -1048,6 +1057,20 @@ int dnssec_verify_dnskey_search(DnsResourceRecord *dnskey, DnsAnswer *validated_
return 0;
}
+static int nsec3_hash_to_gcrypt_md(uint8_t algorithm) {
+
+ /* Translates a DNSSEC NSEC3 hash algorithm into a gcrypt digest identifier */
+
+ switch (algorithm) {
+
+ case NSEC3_ALGORITHM_SHA1:
+ return GCRY_MD_SHA1;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
uint8_t wire_format[DNS_WIRE_FOMAT_HOSTNAME_MAX];
gcry_md_hd_t md = NULL;
@@ -1064,7 +1087,7 @@ int dnssec_nsec3_hash(DnsResourceRecord *nsec3, const char *name, void *ret) {
if (nsec3->key->type != DNS_TYPE_NSEC3)
return -EINVAL;
- algorithm = digest_to_gcrypt_md(nsec3->nsec3.algorithm);
+ algorithm = nsec3_hash_to_gcrypt_md(nsec3->nsec3.algorithm);
if (algorithm < 0)
return algorithm;
@@ -1129,6 +1152,10 @@ static int nsec3_is_good(DnsResourceRecord *rr, DnsAnswerFlags flags, DnsResourc
if (!IN_SET(rr->nsec3.flags, 0, 1))
return 0;
+ /* Ignore NSEC3 RRs whose algorithm we don't know */
+ if (nsec3_hash_to_gcrypt_md(rr->nsec3.algorithm) < 0)
+ return 0;
+
if (!nsec3)
return 1;
diff --git a/src/resolve/resolved-dns-packet.c b/src/resolve/resolved-dns-packet.c
index 5f79701296..5cc96308da 100644
--- a/src/resolve/resolved-dns-packet.c
+++ b/src/resolve/resolved-dns-packet.c
@@ -723,7 +723,40 @@ int dns_packet_append_opt(DnsPacket *p, uint16_t max_udp_size, bool edns0_do, si
goto fail;
/* RDLENGTH */
- r = dns_packet_append_uint16(p, 0, NULL);
+
+ if (edns0_do) {
+ /* If DO is on, also append RFC6975 Algorithm data */
+
+ static const uint8_t rfc6975[] = {
+
+ 0, 5, /* OPTION_CODE: DAU */
+ 0, 6, /* LIST_LENGTH */
+ DNSSEC_ALGORITHM_RSASHA1,
+ DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
+ DNSSEC_ALGORITHM_RSASHA256,
+ DNSSEC_ALGORITHM_RSASHA512,
+ DNSSEC_ALGORITHM_ECDSAP256SHA256,
+ DNSSEC_ALGORITHM_ECDSAP384SHA384,
+
+ 0, 6, /* OPTION_CODE: DHU */
+ 0, 3, /* LIST_LENGTH */
+ DNSSEC_DIGEST_SHA1,
+ DNSSEC_DIGEST_SHA256,
+ DNSSEC_DIGEST_SHA384,
+
+ 0, 7, /* OPTION_CODE: N3U */
+ 0, 1, /* LIST_LENGTH */
+ NSEC3_ALGORITHM_SHA1,
+ };
+
+ r = dns_packet_append_uint16(p, sizeof(rfc6975), NULL);
+ if (r < 0)
+ goto fail;
+
+ r = dns_packet_append_blob(p, rfc6975, sizeof(rfc6975), NULL);
+ } else
+ r = dns_packet_append_uint16(p, 0, NULL);
+
if (r < 0)
goto fail;
@@ -1580,6 +1613,11 @@ int dns_packet_read_rr(DnsPacket *p, DnsResourceRecord **ret, bool *ret_cache_fl
if (r < 0)
goto fail;
+ /* RFC 2181, Section 8, suggests to
+ * treat a TTL with the MSB set as a zero TTL. */
+ if (rr->ttl & UINT32_C(0x80000000))
+ rr->ttl = 0;
+
r = dns_packet_read_uint16(p, &rdlength, NULL);
if (r < 0)
goto fail;
diff --git a/src/resolve/resolved-dns-packet.h b/src/resolve/resolved-dns-packet.h
index a09ace5b75..6821be73e4 100644
--- a/src/resolve/resolved-dns-packet.h
+++ b/src/resolve/resolved-dns-packet.h
@@ -119,7 +119,17 @@ static inline uint8_t* DNS_PACKET_DATA(DnsPacket *p) {
#define DNS_PACKET_RA(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 7) & 1)
#define DNS_PACKET_AD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 5) & 1)
#define DNS_PACKET_CD(p) ((be16toh(DNS_PACKET_HEADER(p)->flags) >> 4) & 1)
-#define DNS_PACKET_RCODE(p) (be16toh(DNS_PACKET_HEADER(p)->flags) & 15)
+
+static inline uint16_t DNS_PACKET_RCODE(DnsPacket *p) {
+ uint16_t rcode;
+
+ if (p->opt)
+ rcode = (uint16_t) (p->opt->ttl >> 24);
+ else
+ rcode = 0;
+
+ return rcode | (be16toh(DNS_PACKET_HEADER(p)->flags) & 15);
+}
/* LLMNR defines some bits differently */
#define DNS_PACKET_LLMNR_C(p) DNS_PACKET_AA(p)
@@ -203,6 +213,7 @@ static inline bool DNS_PACKET_SHALL_CACHE(DnsPacket *p) {
return in_addr_is_localhost(p->family, &p->sender) == 0;
}
+/* https://www.iana.org/assignments/dns-parameters/dns-parameters.xhtml#dns-parameters-6 */
enum {
DNS_RCODE_SUCCESS = 0,
DNS_RCODE_FORMERR = 1,
diff --git a/src/resolve/resolved-dns-rr.c b/src/resolve/resolved-dns-rr.c
index d479de7125..274e857586 100644
--- a/src/resolve/resolved-dns-rr.c
+++ b/src/resolve/resolved-dns-rr.c
@@ -311,6 +311,9 @@ int dns_resource_key_to_string(const DnsResourceKey *key, char **ret) {
const char *c, *t;
char *s;
+ /* If we cannot convert the CLASS/TYPE into a known string,
+ use the format recommended by RFC 3597, Section 5. */
+
c = dns_class_to_string(key->class);
if (!c) {
sprintf(cbuf, "CLASS%u", key->class);
@@ -1021,6 +1024,7 @@ const char *dns_resource_record_to_string(DnsResourceRecord *rr) {
if (!t)
return NULL;
+ /* Format as documented in RFC 3597, Section 5 */
r = asprintf(&s, "%s \\# %zu %s", k, rr->generic.size, t);
if (r < 0)
return NULL;
@@ -1109,6 +1113,7 @@ bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b) {
}
static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] = {
+ /* Mnemonics as listed on https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
[DNSSEC_ALGORITHM_RSAMD5] = "RSAMD5",
[DNSSEC_ALGORITHM_DH] = "DH",
[DNSSEC_ALGORITHM_DSA] = "DSA",
@@ -1118,6 +1123,9 @@ static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] =
[DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1] = "RSASHA1-NSEC3-SHA1",
[DNSSEC_ALGORITHM_RSASHA256] = "RSASHA256",
[DNSSEC_ALGORITHM_RSASHA512] = "RSASHA512",
+ [DNSSEC_ALGORITHM_ECC_GOST] = "ECC-GOST",
+ [DNSSEC_ALGORITHM_ECDSAP256SHA256] = "ECDSAP256SHA256",
+ [DNSSEC_ALGORITHM_ECDSAP384SHA384] = "ECDSAP384SHA384",
[DNSSEC_ALGORITHM_INDIRECT] = "INDIRECT",
[DNSSEC_ALGORITHM_PRIVATEDNS] = "PRIVATEDNS",
[DNSSEC_ALGORITHM_PRIVATEOID] = "PRIVATEOID",
@@ -1125,7 +1133,10 @@ static const char* const dnssec_algorithm_table[_DNSSEC_ALGORITHM_MAX_DEFINED] =
DEFINE_STRING_TABLE_LOOKUP(dnssec_algorithm, int);
static const char* const dnssec_digest_table[_DNSSEC_DIGEST_MAX_DEFINED] = {
- [DNSSEC_DIGEST_SHA1] = "SHA1",
- [DNSSEC_DIGEST_SHA256] = "SHA256",
+ /* Names as listed on https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
+ [DNSSEC_DIGEST_SHA1] = "SHA-1",
+ [DNSSEC_DIGEST_SHA256] = "SHA-256",
+ [DNSSEC_DIGEST_GOST_R_34_11_94] = "GOST_R_34.11-94",
+ [DNSSEC_DIGEST_SHA384] = "SHA-384",
};
DEFINE_STRING_TABLE_LOOKUP(dnssec_digest, int);
diff --git a/src/resolve/resolved-dns-rr.h b/src/resolve/resolved-dns-rr.h
index fccc4dba6a..27c5f5384e 100644
--- a/src/resolve/resolved-dns-rr.h
+++ b/src/resolve/resolved-dns-rr.h
@@ -51,8 +51,9 @@ enum {
DNSSEC_ALGORITHM_RSASHA1,
DNSSEC_ALGORITHM_DSA_NSEC3_SHA1,
DNSSEC_ALGORITHM_RSASHA1_NSEC3_SHA1,
- DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */
- DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */
+ DNSSEC_ALGORITHM_RSASHA256 = 8, /* RFC 5702 */
+ DNSSEC_ALGORITHM_RSASHA512 = 10, /* RFC 5702 */
+ DNSSEC_ALGORITHM_ECC_GOST = 12, /* RFC 5933 */
DNSSEC_ALGORITHM_ECDSAP256SHA256 = 13, /* RFC 6605 */
DNSSEC_ALGORITHM_ECDSAP384SHA384 = 14, /* RFC 6605 */
DNSSEC_ALGORITHM_INDIRECT = 252,
@@ -65,11 +66,19 @@ enum {
* https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
enum {
DNSSEC_DIGEST_SHA1 = 1,
- DNSSEC_DIGEST_SHA256 = 2,
- DNSSEC_DIGEST_SHA384 = 4,
+ DNSSEC_DIGEST_SHA256 = 2, /* RFC 4509 */
+ DNSSEC_DIGEST_GOST_R_34_11_94 = 3, /* RFC 5933 */
+ DNSSEC_DIGEST_SHA384 = 4, /* RFC 6605 */
_DNSSEC_DIGEST_MAX_DEFINED
};
+/* DNSSEC NSEC3 hash algorithms, see
+ * https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml */
+enum {
+ NSEC3_ALGORITHM_SHA1 = 1,
+ _NSEC3_ALGORITHM_MAX_DEFINED
+};
+
struct DnsResourceKey {
unsigned n_ref;
uint16_t class, type;
@@ -155,6 +164,7 @@ struct DnsResourceRecord {
char *exchange;
} mx;
+ /* https://tools.ietf.org/html/rfc1876 */
struct {
uint8_t version;
uint8_t size;
@@ -165,14 +175,6 @@ struct DnsResourceRecord {
uint32_t altitude;
} loc;
- struct {
- uint16_t key_tag;
- uint8_t algorithm;
- uint8_t digest_type;
- void *digest;
- size_t digest_size;
- } ds;
-
/* https://tools.ietf.org/html/rfc4255#section-3.1 */
struct {
uint8_t algorithm;
@@ -210,6 +212,15 @@ struct DnsResourceRecord {
Bitmap *types;
} nsec;
+ /* https://tools.ietf.org/html/rfc4034#section-5.1 */
+ struct {
+ uint16_t key_tag;
+ uint8_t algorithm;
+ uint8_t digest_type;
+ void *digest;
+ size_t digest_size;
+ } ds;
+
struct {
uint8_t algorithm;
uint8_t flags;
diff --git a/src/resolve/resolved-dns-server.c b/src/resolve/resolved-dns-server.c
index a57385d47d..fbd1c27c14 100644
--- a/src/resolve/resolved-dns-server.c
+++ b/src/resolve/resolved-dns-server.c
@@ -253,7 +253,7 @@ void dns_server_packet_received(DnsServer *s, DnsServerFeatureLevel level, usec_
if (s->max_rtt < rtt) {
s->max_rtt = rtt;
- s->resend_timeout = MIN(MAX(DNS_TIMEOUT_MIN_USEC, s->max_rtt * 2), DNS_TIMEOUT_MAX_USEC);
+ s->resend_timeout = CLAMP(s->max_rtt * 2, DNS_TIMEOUT_MIN_USEC, DNS_TIMEOUT_MAX_USEC);
}
}