Merge pull request #18565 from poettering/randomize-answers

resolved: randomize order in local query replies
This commit is contained in:
Luca Boccassi 2021-02-14 19:35:54 +00:00 committed by GitHub
commit acc8890a8a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 87 additions and 4 deletions

View file

@ -494,3 +494,22 @@ int random_write_entropy(int fd, const void *seed, size_t size, bool credit) {
return 1;
}
int random_u64_range(uint64_t m) {
uint64_t x, remainder;
/* Generates a random number in the range 0…m-1, unbiased. (Java's algorithm) */
if (m == 0) /* Let's take m == 0 as special case to return an integer from the full range */
return random_u64();
if (m == 1)
return 0;
remainder = UINT64_MAX % m;
do {
x = random_u64();
} while (x >= UINT64_MAX - remainder);
return x % m;
}

View file

@ -40,3 +40,5 @@ int rdrand(unsigned long *ret);
size_t random_pool_size(void);
int random_write_entropy(int fd, const void *seed, size_t size, bool credit);
int random_u64_range(uint64_t max);

View file

@ -4,6 +4,7 @@
#include "alloc-util.h"
#include "dns-domain.h"
#include "random-util.h"
#include "resolved-dns-answer.h"
#include "resolved-dns-dnssec.h"
#include "string-util.h"
@ -712,10 +713,7 @@ void dns_answer_order_by_scope(DnsAnswer *a, bool prefer_link_local) {
items = newa(DnsAnswerItem, a->n_rrs);
for (i = 0; i < a->n_rrs; i++) {
if (a->items[i].rr->key->class == DNS_CLASS_IN &&
((a->items[i].rr->key->type == DNS_TYPE_A && in_addr_is_link_local(AF_INET, (union in_addr_union*) &a->items[i].rr->a.in_addr) != prefer_link_local) ||
(a->items[i].rr->key->type == DNS_TYPE_AAAA && in_addr_is_link_local(AF_INET6, (union in_addr_union*) &a->items[i].rr->aaaa.in6_addr) != prefer_link_local)))
if (dns_resource_record_is_link_local_address(a->items[i].rr) != prefer_link_local)
/* Order address records that are not preferred to the end of the array */
items[end--] = a->items[i];
else
@ -898,3 +896,23 @@ int dns_answer_has_dname_for_cname(DnsAnswer *a, DnsResourceRecord *cname) {
return 0;
}
void dns_answer_randomize(DnsAnswer *a) {
size_t n;
/* Permutes the answer list randomly (Knuth shuffle) */
n = dns_answer_size(a);
if (n <= 1)
return;
for (size_t i = 0; i < n; i++) {
size_t k;
k = random_u64_range(n);
if (k == i)
continue;
SWAP_TWO(a->items[i], a->items[k]);
}
}

View file

@ -80,6 +80,8 @@ static inline bool dns_answer_isempty(DnsAnswer *a) {
void dns_answer_dump(DnsAnswer *answer, FILE *f);
void dns_answer_randomize(DnsAnswer *a);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnsAnswer*, dns_answer_unref);
#define _DNS_ANSWER_FOREACH(q, kk, a) \

View file

@ -1721,6 +1721,20 @@ int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl) {
return 1;
}
bool dns_resource_record_is_link_local_address(DnsResourceRecord *rr) {
if (rr->key->class != DNS_CLASS_IN)
return false;
if (rr->key->type == DNS_TYPE_A)
return in4_addr_is_link_local(&rr->a.in_addr);
if (rr->key->type == DNS_TYPE_AAAA)
return IN6_IS_ADDR_LINKLOCAL(&rr->aaaa.in6_addr);
return false;
}
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i) {
DnsTxtItem *n;

View file

@ -324,6 +324,8 @@ int dns_resource_record_is_synthetic(DnsResourceRecord *rr);
int dns_resource_record_clamp_ttl(DnsResourceRecord **rr, uint32_t max_ttl);
bool dns_resource_record_is_link_local_address(DnsResourceRecord *rr);
DnsTxtItem *dns_txt_item_free_all(DnsTxtItem *i);
bool dns_txt_item_equal(DnsTxtItem *a, DnsTxtItem *b);
DnsTxtItem *dns_txt_item_copy(DnsTxtItem *i);

View file

@ -1410,6 +1410,30 @@ static usec_t transaction_get_resend_timeout(DnsTransaction *t) {
}
}
static void dns_transaction_randomize_answer(DnsTransaction *t) {
int r;
assert(t);
/* Randomizes the order of the answer array. This is done for all cached responses, so that we return
* a different order each time. We do this only for DNS traffic, in order to do some minimal, crappy
* load balancing. We don't do this for LLMNR or mDNS, since the order (preferring link-local
* addresses, and such like) might have meaning there, and load balancing is pointless. */
if (t->scope->protocol != DNS_PROTOCOL_DNS)
return;
/* No point in randomizing, if there's just one RR */
if (dns_answer_size(t->answer) <= 1)
return;
r = dns_answer_reserve_or_clone(&t->answer, 0);
if (r < 0) /* If this fails, just don't randomize, this is non-essential stuff after all */
return (void) log_debug_errno(r, "Failed to clone answer record, not randomizing RR order of answer: %m");
dns_answer_randomize(t->answer);
}
static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
int r;
@ -1530,6 +1554,8 @@ static int dns_transaction_prepare(DnsTransaction *t, usec_t ts) {
if (r < 0)
return r;
if (r > 0) {
dns_transaction_randomize_answer(t);
if (t->bypass && t->scope->protocol == DNS_PROTOCOL_DNS && !t->received)
/* When bypass mode is on, do not use cached data unless it came with a full
* packet. */