From d95f3dc0b117b0677700dae8dd51145c9b6f2d08 Mon Sep 17 00:00:00 2001 From: Rick Deist Date: Sat, 17 Mar 2018 20:10:04 +0100 Subject: resolve: add CURLOPT_DNS_SHUFFLE_ADDRESSES This patch adds CURLOPT_DNS_SHUFFLE_ADDRESSES to explicitly request shuffling of IP addresses returned for a hostname when there is more than one. This is useful when the application knows that a round robin approach is appropriate and is willing to accept the consequences of potentially discarding some preference order returned by the system's implementation. Closes #1694 --- lib/hostip.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'lib/hostip.c') diff --git a/lib/hostip.c b/lib/hostip.c index 8554d39d1..c2f9defd9 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2017, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2018, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -54,6 +54,7 @@ #include "sendf.h" #include "hostip.h" #include "hash.h" +#include "rand.h" #include "share.h" #include "strerror.h" #include "url.h" @@ -366,6 +367,70 @@ Curl_fetch_addr(struct connectdata *conn, return dns; } +/* + * Curl_shuffle_addr() shuffles the order of addresses in a 'Curl_addrinfo' + * struct by re-linking its linked list. + * + * The addr argument should be the address of a pointer to the head node of a + * `Curl_addrinfo` list and it will be modified to point to the new head after + * shuffling. + * + * Not declared static only to make it easy to use in a unit test! + * + * @unittest: 1608 + */ +CURLcode Curl_shuffle_addr(struct Curl_easy *data, Curl_addrinfo **addr) +{ + CURLcode result = CURLE_OK; + const int num_addrs = Curl_num_addresses(*addr); + + if(num_addrs > 1) { + Curl_addrinfo **nodes; + infof(data, "Shuffling %i addresses", num_addrs); + + nodes = malloc(num_addrs*sizeof(*nodes)); + if(nodes) { + int i; + unsigned int *rnd; + const size_t rnd_size = num_addrs * sizeof(*rnd); + + /* build a plain array of Curl_addrinfo pointers */ + nodes[0] = *addr; + for(i = 1; i < num_addrs; i++) { + nodes[i] = nodes[i-1]->ai_next; + } + + rnd = malloc(rnd_size); + if(rnd) { + /* Fisher-Yates shuffle */ + if(Curl_rand(data, (unsigned char *)rnd, rnd_size) == CURLE_OK) { + Curl_addrinfo *swap_tmp; + for(i = num_addrs - 1; i > 0; i--) { + swap_tmp = nodes[rnd[i] % (i + 1)]; + nodes[rnd[i] % (i + 1)] = nodes[i]; + nodes[i] = swap_tmp; + } + + /* relink list in the new order */ + for(i = 1; i < num_addrs; i++) { + nodes[i-1]->ai_next = nodes[i]; + } + + nodes[num_addrs-1]->ai_next = NULL; + *addr = nodes[0]; + } + free(rnd); + } + else + result = CURLE_OUT_OF_MEMORY; + free(nodes); + } + else + result = CURLE_OUT_OF_MEMORY; + } + return result; +} + /* * Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache. * @@ -386,6 +451,13 @@ Curl_cache_addr(struct Curl_easy *data, struct Curl_dns_entry *dns; struct Curl_dns_entry *dns2; + /* shuffle addresses if requested */ + if(data->set.dns_shuffle_addresses) { + CURLcode result = Curl_shuffle_addr(data, &addr); + if(!result) + return NULL; + } + /* Create an entry id, based upon the hostname and port */ entry_id = create_hostcache_id(hostname, port); /* If we can't create the entry id, fail */ -- cgit v1.2.3