Skip to content

Commit 320cefe

Browse files
committed
ares_set_servers_*() should allow an empty server list
For historic reasons, we have users depending on ares_set_servers_*() to return ARES_SUCCESS when passing no servers and actually *clear* the server list. It appears they do this for test cases to simulate DNS unavailable or similar. Presumably they could achieve the same effect in other ways (point to localhost on a port that isn't in use). But it seems like this might be wide-spread enough to cause headaches so we just will document and test for this behavior, clearly it hasn't caused "issues" for anyone with the old behavior. See: nodejs/node#50800 Fix By: Brad House (@bradh352)
1 parent 4982f76 commit 320cefe

File tree

7 files changed

+104
-50
lines changed

7 files changed

+104
-50
lines changed

docs/ares_set_servers.3

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ The name server linked list pointer argument may be the result of a previous
4242
call to \fBares_get_servers(3)\fP or a linked list of \fBares_addr_node\fP structs
4343
set up by other means.
4444
.PP
45-
The \fBares_set_servers(3)\fP function also allows the specification of UDP and
45+
The \fBares_set_servers_ports(3)\fP function also allows the specification of UDP and
4646
TCP ports to be used for communication on a per-server basis. The provided
4747
linked list argument may be the result of a previous call to
4848
\fBares_get_servers_ports(3)\fP or a linked list of \fBares_addr_port_node\fP structs
@@ -51,7 +51,9 @@ set up by other means.
5151
This function replaces any potentially previously configured name servers
5252
with the ones given in the linked list. So, in order to configure a channel
5353
with more than one name server all the desired ones must be specified in a
54-
single list.
54+
single list. Though not recommended, passing NULL will clear all configured
55+
servers and make an inoperable channel, this may be advantageous for test
56+
simulation but unlikely to be useful in production.
5557
.PP
5658
The function does not take ownership of the linked list argument.
5759
The caller is responsible for freeing the linked list when no longer needed.

docs/ares_set_servers_csv.3

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,13 @@ int ares_set_servers_ports_csv(ares_channel_t *\fIchannel\fP, const char* \fIser
3030
The \fBares_set_servers_csv\fP and \fBares_set_servers_ports_csv\fPfunctions set
3131
the list of DNS servers that ARES will query. As of v1.22.0 this function can
3232
be called on an active channel with running queries, previously it would return
33-
ARES_ENOTIMP. The format of the servers option is:
33+
ARES_ENOTIMP.
34+
35+
Though not recommended, passing NULL for servers will clear all configured
36+
servers and make an inoperable channel, this may be advantageous for test
37+
simulation but unlikely to be useful in production.
38+
39+
The format of the servers option is:
3440

3541
host[:port][,host[:port]]...
3642

src/lib/ares_options.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,12 +234,12 @@ static ares_status_t ares__init_options_servers(ares_channel_t *channel,
234234
const struct in_addr *servers,
235235
size_t nservers)
236236
{
237-
ares__llist_t *slist;
237+
ares__llist_t *slist = NULL;
238238
ares_status_t status;
239239

240-
slist = ares_in_addr_to_server_config_llist(servers, nservers);
241-
if (slist == NULL) {
242-
return ARES_ENOMEM;
240+
status = ares_in_addr_to_server_config_llist(servers, nservers, &slist);
241+
if (status != ARES_SUCCESS) {
242+
return status;
243243
}
244244

245245
status = ares__servers_update(channel, slist, ARES_TRUE);

src/lib/ares_private.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -477,9 +477,10 @@ ares_status_t ares__sconfig_append(ares__llist_t **sconfig,
477477
unsigned short tcp_port);
478478
ares_status_t ares__sconfig_append_fromstr(ares__llist_t **sconfig,
479479
const char *str);
480-
ares__llist_t *
480+
ares_status_t
481481
ares_in_addr_to_server_config_llist(const struct in_addr *servers,
482-
size_t nservers);
482+
size_t nservers,
483+
ares__llist_t **llist);
483484

484485
struct ares_hosts_entry;
485486
typedef struct ares_hosts_entry ares_hosts_entry_t;

src/lib/ares_process.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ static struct server_state *ares__random_server(ares_channel_t *channel)
777777

778778
/* Silence coverity, not possible */
779779
if (num_servers == 0)
780-
num_servers = 1;
780+
return NULL;
781781

782782
ares__rand_bytes(channel->rand_state, &c, 1);
783783

@@ -816,9 +816,8 @@ static size_t ares__calc_query_timeout(const struct query *query)
816816
size_t rounds;
817817
size_t num_servers = ares__slist_len(channel->servers);
818818

819-
/* Silence coverity, not possible */
820819
if (num_servers == 0)
821-
num_servers = 1;
820+
return 0;
822821

823822
/* For each trip through the entire server list, we want to double the
824823
* retry from the last retry */
@@ -878,6 +877,11 @@ ares_status_t ares__send_query(struct query *query, struct timeval *now)
878877
server = ares__slist_first_val(channel->servers);
879878
}
880879

880+
if (server == NULL) {
881+
end_query(channel, query, ARES_ESERVFAIL /* ? */, NULL, 0);
882+
return ARES_ECONNREFUSED;
883+
}
884+
881885
if (query->using_tcp) {
882886
size_t prior_len = 0;
883887
/* Make sure the TCP socket for this server is set up and queue

src/lib/ares_update_servers.c

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -531,11 +531,14 @@ ares_status_t ares__servers_update(ares_channel_t *channel,
531531
size_t idx = 0;
532532
ares_status_t status;
533533

534-
if (channel == NULL || server_list == NULL ||
535-
ares__llist_len(server_list) == 0) {
534+
if (channel == NULL) {
536535
return ARES_EFORMERR;
537536
}
538537

538+
/* NOTE: a NULL or zero entry server list is considered valid due to
539+
* real-world people needing support for this for their test harnesses
540+
*/
541+
539542
/* Add new entries */
540543
for (node = ares__llist_node_first(server_list); node != NULL;
541544
node = ares__llist_node_next(node)) {
@@ -589,19 +592,18 @@ ares_status_t ares__servers_update(ares_channel_t *channel,
589592
return status;
590593
}
591594

592-
static ares__llist_t *
593-
ares_addr_node_to_server_config_llist(const struct ares_addr_node *servers)
595+
static ares_status_t
596+
ares_addr_node_to_server_config_llist(const struct ares_addr_node *servers,
597+
ares__llist_t **llist)
594598
{
595599
const struct ares_addr_node *node;
596600
ares__llist_t *s;
597601

598-
if (servers == NULL) {
599-
return NULL;
600-
}
602+
*llist = NULL;
601603

602604
s = ares__llist_create(ares_free);
603605
if (s == NULL) {
604-
return NULL;
606+
goto fail;
605607
}
606608

607609
for (node = servers; node != NULL; node = node->next) {
@@ -632,26 +634,26 @@ static ares__llist_t *
632634
}
633635
}
634636

635-
return s;
637+
*llist = s;
638+
return ARES_SUCCESS;
636639

637640
fail:
638641
ares__llist_destroy(s);
639-
return NULL;
642+
return ARES_ENOMEM;
640643
}
641644

642-
static ares__llist_t *ares_addr_port_node_to_server_config_llist(
643-
const struct ares_addr_port_node *servers)
645+
static ares_status_t ares_addr_port_node_to_server_config_llist(
646+
const struct ares_addr_port_node *servers,
647+
ares__llist_t **llist)
644648
{
645649
const struct ares_addr_port_node *node;
646650
ares__llist_t *s;
647651

648-
if (servers == NULL) {
649-
return NULL;
650-
}
652+
*llist = NULL;
651653

652654
s = ares__llist_create(ares_free);
653655
if (s == NULL) {
654-
return NULL;
656+
goto fail;
655657
}
656658

657659
for (node = servers; node != NULL; node = node->next) {
@@ -685,30 +687,30 @@ static ares__llist_t *ares_addr_port_node_to_server_config_llist(
685687
}
686688
}
687689

688-
return s;
690+
*llist = s;
691+
return ARES_SUCCESS;
689692

690693
fail:
691694
ares__llist_destroy(s);
692-
return NULL;
695+
return ARES_ENOMEM;
693696
}
694697

695-
ares__llist_t *
698+
ares_status_t
696699
ares_in_addr_to_server_config_llist(const struct in_addr *servers,
697-
size_t nservers)
700+
size_t nservers,
701+
ares__llist_t **llist)
698702
{
699703
size_t i;
700704
ares__llist_t *s;
701705

702-
if (servers == NULL || nservers == 0) {
703-
return NULL;
704-
}
706+
*llist = NULL;
705707

706708
s = ares__llist_create(ares_free);
707709
if (s == NULL) {
708-
return NULL;
710+
goto fail;
709711
}
710712

711-
for (i = 0; i < nservers; i++) {
713+
for (i = 0; servers != NULL && i < nservers; i++) {
712714
ares_sconfig_t *sconfig;
713715

714716
sconfig = ares_malloc_zero(sizeof(*sconfig));
@@ -725,11 +727,12 @@ ares__llist_t *
725727
}
726728
}
727729

728-
return s;
730+
*llist = s;
731+
return ARES_SUCCESS;
729732

730733
fail:
731734
ares__llist_destroy(s);
732-
return NULL;
735+
return ARES_ENOMEM;
733736
}
734737

735738
int ares_get_servers(ares_channel_t *channel, struct ares_addr_node **servers)
@@ -842,13 +845,13 @@ int ares_set_servers(ares_channel_t *channel,
842845
ares__llist_t *slist;
843846
ares_status_t status;
844847

845-
if (channel == NULL || servers == NULL) {
848+
if (channel == NULL) {
846849
return ARES_ENODATA;
847850
}
848851

849-
slist = ares_addr_node_to_server_config_llist(servers);
850-
if (slist == NULL) {
851-
return ARES_ENOMEM;
852+
status = ares_addr_node_to_server_config_llist(servers, &slist);
853+
if (status != ARES_SUCCESS) {
854+
return (int)status;
852855
}
853856

854857
status = ares__servers_update(channel, slist, ARES_TRUE);
@@ -864,13 +867,13 @@ int ares_set_servers_ports(ares_channel_t *channel,
864867
ares__llist_t *slist;
865868
ares_status_t status;
866869

867-
if (channel == NULL || servers == NULL) {
870+
if (channel == NULL) {
868871
return ARES_ENODATA;
869872
}
870873

871-
slist = ares_addr_port_node_to_server_config_llist(servers);
872-
if (slist == NULL) {
873-
return ARES_ENOMEM;
874+
status = ares_addr_port_node_to_server_config_llist(servers, &slist);
875+
if (status != ARES_SUCCESS) {
876+
return (int)status;
874877
}
875878

876879
status = ares__servers_update(channel, slist, ARES_TRUE);
@@ -904,7 +907,8 @@ static ares_status_t set_servers_csv(ares_channel_t *channel, const char *_csv,
904907

905908
i = ares_strlen(_csv);
906909
if (i == 0) {
907-
return ARES_SUCCESS; /* blank all servers */
910+
/* blank all servers */
911+
return (ares_status_t)ares_set_servers_ports(channel, NULL);
908912
}
909913

910914
csv = ares_malloc(i + 2);

test/ares-test-misc.cc

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,21 @@ TEST_F(DefaultChannelTest, GetServersFailures) {
4545
}
4646

4747
TEST_F(DefaultChannelTest, SetServers) {
48-
EXPECT_EQ(ARES_ENODATA, ares_set_servers(channel_, nullptr));
48+
/* NOTE: This test is because we have actual external users doing test case
49+
* simulation and removing all servers to generate various error
50+
* conditions in their own code. It would make more sense to return
51+
* ARES_ENODATA, but due to historical users, we can't break them.
52+
* See: https://github.com/nodejs/node/pull/50800
53+
*/
54+
EXPECT_EQ(ARES_SUCCESS, ares_set_servers(channel_, nullptr));
55+
std::vector<std::string> expected_empty = { };
56+
EXPECT_EQ(expected_empty, GetNameServers(channel_));
57+
HostResult result;
58+
ares_gethostbyname(channel_, "www.google.com.", AF_INET, HostCallback, &result);
59+
Process();
60+
EXPECT_TRUE(result.done_);
61+
EXPECT_EQ(ARES_ESERVFAIL, result.status_);
62+
4963

5064
struct ares_addr_node server1;
5165
struct ares_addr_node server2;
@@ -63,7 +77,15 @@ TEST_F(DefaultChannelTest, SetServers) {
6377
}
6478

6579
TEST_F(DefaultChannelTest, SetServersPorts) {
66-
EXPECT_EQ(ARES_ENODATA, ares_set_servers_ports(channel_, nullptr));
80+
/* NOTE: This test is because we have actual external users doing test case
81+
* simulation and removing all servers to generate various error
82+
* conditions in their own code. It would make more sense to return
83+
* ARES_ENODATA, but due to historical users, we can't break them.
84+
* See: https://github.com/nodejs/node/pull/50800
85+
*/
86+
EXPECT_EQ(ARES_SUCCESS, ares_set_servers_ports(channel_, nullptr));
87+
std::vector<std::string> expected_empty = { };
88+
EXPECT_EQ(expected_empty, GetNameServers(channel_));
6789

6890
struct ares_addr_port_node server1;
6991
struct ares_addr_port_node server2;
@@ -91,6 +113,21 @@ TEST_F(DefaultChannelTest, SetServersCSV) {
91113
EXPECT_EQ(ARES_ENODATA, ares_set_servers_csv(nullptr, "1.2.3.4.5"));
92114
EXPECT_EQ(ARES_ENODATA, ares_set_servers_csv(nullptr, "1:2:3:4:5"));
93115

116+
/* NOTE: This test is because we have actual external users doing test case
117+
* simulation and removing all servers to generate various error
118+
* conditions in their own code. It would make more sense to return
119+
* ARES_ENODATA, but due to historical users, we can't break them.
120+
* See: https://github.com/nodejs/node/pull/50800
121+
*/
122+
EXPECT_EQ(ARES_SUCCESS, ares_set_servers_csv(channel_, NULL));
123+
std::vector<std::string> expected_empty = { };
124+
EXPECT_EQ(expected_empty, GetNameServers(channel_));
125+
EXPECT_EQ(ARES_SUCCESS, ares_set_servers_csv(channel_, ""));
126+
EXPECT_EQ(expected_empty, GetNameServers(channel_));
127+
128+
129+
130+
94131
EXPECT_EQ(ARES_SUCCESS,
95132
ares_set_servers_csv(channel_, "1.2.3.4,0102:0304:0506:0708:0910:1112:1314:1516,2.3.4.5"));
96133
std::vector<std::string> expected = {"1.2.3.4:53", "[0102:0304:0506:0708:0910:1112:1314:1516]:53", "2.3.4.5:53"};

0 commit comments

Comments
 (0)