summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/hostip.c74
-rw-r--r--lib/hostip.h13
-rw-r--r--lib/setopt.c3
-rw-r--r--lib/urldata.h2
4 files changed, 90 insertions, 2 deletions
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, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, 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"
@@ -367,6 +368,70 @@ Curl_fetch_addr(struct connectdata *conn,
}
/*
+ * 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.
*
* When calling Curl_resolv() has resulted in a response with a returned
@@ -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 */
diff --git a/lib/hostip.h b/lib/hostip.h
index 298eeeee3..1de4bee8d 100644
--- a/lib/hostip.h
+++ b/lib/hostip.h
@@ -7,7 +7,7 @@
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
@@ -182,6 +182,17 @@ struct Curl_dns_entry *
Curl_fetch_addr(struct connectdata *conn,
const char *hostname,
int port);
+
+/*
+ * 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.
+ */
+CURLcode Curl_shuffle_addr(struct Curl_easy *data, Curl_addrinfo **addr);
+
/*
* Curl_cache_addr() stores a 'Curl_addrinfo' struct in the DNS cache.
*
diff --git a/lib/setopt.c b/lib/setopt.c
index 737a60f85..da364fa81 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -2561,6 +2561,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
return CURLE_BAD_FUNCTION_ARGUMENT;
data->set.happy_eyeballs_timeout = arg;
break;
+ case CURLOPT_DNS_SHUFFLE_ADDRESSES:
+ data->set.dns_shuffle_addresses = (0 != va_arg(param, long)) ? TRUE:FALSE;
+ break;
default:
/* unknown tag and its companion, just ignore: */
result = CURLE_UNKNOWN_OPTION;
diff --git a/lib/urldata.h b/lib/urldata.h
index dad31cd4e..35bbb8590 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -1674,6 +1674,8 @@ struct UserDefined {
bool suppress_connect_headers; /* suppress proxy CONNECT response headers
from user callbacks */
+ bool dns_shuffle_addresses; /* whether to shuffle addresses before use */
+
struct Curl_easy *stream_depends_on;
bool stream_depends_e; /* set or don't set the Exclusive bit */
int stream_weight;