Skip to content

Commit e701b9a

Browse files
author
emotional-engineering
committed
Adds new features, that allow to set new DNS servers and perform new queries, while older requests are handled using old servers.
1 parent bba4dc5 commit e701b9a

5 files changed

Lines changed: 280 additions & 2 deletions

File tree

Makefile.inc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ CSOURCES = ares__close_sockets.c \
4646
bitncmp.c \
4747
inet_net_pton.c \
4848
inet_ntop.c \
49-
windows_port.c
49+
windows_port.c \
50+
ares_change_servers.c
5051

5152
HHEADERS = ares.h \
5253
ares_build.h \

ares.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,10 @@ CARES_EXTERN int ares_set_servers_csv(ares_channel channel,
572572

573573
CARES_EXTERN int ares_get_servers(ares_channel channel,
574574
struct ares_addr_node **servers);
575+
576+
CARES_EXTERN int ares_change_servers(ares_channel channel, struct ares_addr_node* source_servers);
577+
578+
CARES_EXTERN int ares_close_old_servers(ares_channel channel);
575579

576580
CARES_EXTERN const char *ares_inet_ntop(int af, const void *src, char *dst,
577581
ares_socklen_t size);

ares_change_servers.c

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
2+
#include "ares_setup.h"
3+
#include "ares.h"
4+
#include "ares_private.h"
5+
6+
int ares_rebuild_queries_list(ares_channel channel, int skip_first, int skip_last, bool is_shift);
7+
int ares_close_old_servers(ares_channel channel);
8+
int ares_server_shift(ares_channel channel);
9+
int ares_finish_change_servers(ares_channel channel);
10+
11+
/**
12+
* Add new servers to the channel. Used together with ares_close_old_servers.
13+
*/
14+
15+
int ares_change_servers(ares_channel channel, struct ares_addr_node* source_servers)
16+
{
17+
18+
struct ares_addr_node *source_server;
19+
int nservers;
20+
21+
struct server_state *server;
22+
23+
int new_servers_count = 0;
24+
25+
int i;
26+
27+
for (source_server = source_servers; source_server; source_server = source_server->next)
28+
{
29+
new_servers_count++;
30+
}
31+
32+
nservers = channel->nservers + new_servers_count;
33+
34+
channel->servers = realloc(channel->servers, nservers * sizeof(struct server_state));
35+
36+
if (!channel->servers) {
37+
return ARES_ENOMEM;
38+
}
39+
40+
for (i = channel->nservers, source_server = source_servers; i < nservers; i++, source_server = source_server->next)
41+
{
42+
43+
server = &channel->servers[i];
44+
45+
server->addr.family = source_server->family;
46+
47+
if (source_server->family == AF_INET)
48+
memcpy(&channel->servers[i].addr.addrV4, &source_server->addrV4,
49+
sizeof(source_server->addrV4));
50+
else
51+
memcpy(&channel->servers[i].addr.addrV6, &source_server->addrV6,
52+
sizeof(source_server->addrV6));
53+
54+
/*
55+
* Watch ares__init_servers_state()
56+
*/
57+
58+
server->udp_socket = ARES_SOCKET_BAD;
59+
server->tcp_socket = ARES_SOCKET_BAD;
60+
server->tcp_connection_generation = ++channel->tcp_connection_generation;
61+
server->tcp_lenbuf_pos = 0;
62+
server->tcp_buffer_pos = 0;
63+
server->tcp_buffer = NULL;
64+
server->tcp_length = 0;
65+
server->qhead = NULL;
66+
server->qtail = NULL;
67+
ares__init_list_head(&server->queries_to_server);
68+
server->channel = channel;
69+
server->is_broken = 0;
70+
71+
}
72+
73+
channel->nservers_change = channel->nservers; /* how many servers will be shifted */
74+
75+
/*
76+
* First new server will be used for new dns requests.
77+
*/
78+
79+
channel->last_server = channel->nservers;
80+
channel->nservers = nservers;
81+
82+
channel->rotation_state = channel->rotate; /* save rotation state */
83+
channel->rotate = 0; /* turn off rotation, use one server */
84+
85+
/*
86+
* Need to change references in all queries to new server->queries_to_server reference. Skip last new servers, because they have right references in lists
87+
*/
88+
89+
ares_rebuild_queries_list(channel, 0, new_servers_count, false);
90+
91+
return ARES_SUCCESS;
92+
93+
}
94+
95+
/*
96+
* Close old servers if they finish all queries.
97+
* Must be called at the end of ares process pool if ares_change_servers used.
98+
*/
99+
100+
int ares_close_old_servers(ares_channel channel)
101+
{
102+
103+
struct server_state *server;
104+
int i;
105+
int status;
106+
107+
if (channel->nservers_change < 1)
108+
{
109+
/*
110+
* Now nothing is changing.
111+
*/
112+
113+
return ARES_SUCCESS;
114+
}
115+
116+
if (channel->servers)
117+
{
118+
for (i = 0; i < channel->last_server; i++)
119+
{
120+
121+
server = &channel->servers[i];
122+
123+
if (ares__is_list_empty(&server->queries_to_server))
124+
{
125+
126+
ares__close_sockets(channel, server);
127+
128+
status = ares_server_shift(channel);
129+
130+
if (status != ARES_SUCCESS)
131+
{
132+
return status;
133+
}
134+
135+
ares_rebuild_queries_list(channel, i, 0, true);
136+
137+
if (channel->nservers_change > 0)
138+
{
139+
channel->nservers_change--;
140+
}
141+
142+
i--;
143+
}
144+
145+
}
146+
147+
if (channel->last_server == 0)
148+
{
149+
ares_finish_change_servers(channel);
150+
}
151+
152+
}
153+
154+
return ARES_SUCCESS;
155+
156+
}
157+
158+
/*
159+
* Remove first server from channel.
160+
*/
161+
162+
int ares_server_shift(ares_channel channel)
163+
{
164+
165+
int i;
166+
int nservers = channel->nservers - 1;
167+
168+
struct server_state *servers_buff = malloc(nservers * sizeof(struct server_state));
169+
170+
if (!servers_buff) {
171+
return ARES_ENOMEM;
172+
}
173+
174+
for (i = 1; i < channel->nservers; i++)
175+
{
176+
servers_buff[i - 1] = channel->servers[i];
177+
}
178+
179+
free(channel->servers);
180+
181+
channel->servers = servers_buff;
182+
channel->nservers = nservers;
183+
channel->last_server--;
184+
185+
return ARES_SUCCESS;
186+
187+
}
188+
189+
/*
190+
* Rebuild links in linked lists server->queries_to_server. Used after channel->servers memory relocation
191+
*/
192+
193+
int ares_rebuild_queries_list(ares_channel channel, int skip_first, int skip_last, bool is_shift)
194+
{
195+
196+
struct server_state *server;
197+
198+
struct list_node* list_head;
199+
struct list_node* list_node;
200+
201+
struct query *query;
202+
203+
int i;
204+
205+
for (i = 0; i < (channel->nservers - skip_last); i++)
206+
{
207+
208+
server = &channel->servers[i];
209+
210+
list_head = &server->queries_to_server;
211+
212+
if (list_head->next->data == NULL)
213+
{
214+
/*
215+
* "empty list" with old "next" and "prev" pointer to head. "list_head->next" same "list_head"
216+
*/
217+
218+
ares__init_list_head(list_head);
219+
}
220+
else
221+
{
222+
/*
223+
* Change next and previous link in neighbor nodes
224+
*/
225+
226+
list_head->prev->next = list_head;
227+
list_head->next->prev = list_head;
228+
229+
/*
230+
* Servers can be shifted before query will be responsed. query save server id in query->server.
231+
* Decrement query->server of each query of this server->queries_to_server.
232+
* Is used after shift one server.
233+
*/
234+
235+
if(is_shift && i >= skip_first)
236+
{
237+
for (list_node = list_head->next; list_node != list_head; list_node = list_node->next)
238+
{
239+
query = list_node->data;
240+
query->server = query->server - 1;
241+
242+
}
243+
}
244+
}
245+
}
246+
247+
return ARES_SUCCESS;
248+
249+
}
250+
251+
/*
252+
* Restoration of the previous state of the channel.
253+
*/
254+
255+
int ares_finish_change_servers(ares_channel channel)
256+
{
257+
258+
channel->rotate = channel->rotation_state;
259+
channel->rotation_state = -1;
260+
261+
return ARES_SUCCESS;
262+
263+
}

ares_options.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,17 @@ int ares_get_servers(ares_channel channel,
3636
struct ares_addr_node *srvr_curr;
3737
int status = ARES_SUCCESS;
3838
int i;
39+
int first_server = 0;
3940

4041
if (!channel)
4142
return ARES_ENODATA;
43+
44+
if (channel->nservers_change > 0)
45+
{
46+
first_server = channel->last_server;
47+
}
4248

43-
for (i = 0; i < channel->nservers; i++)
49+
for (i = first_server; i < channel->nservers; i++)
4450
{
4551
/* Allocate storage for this server node appending it to the list */
4652
srvr_curr = ares_malloc_data(ARES_DATATYPE_ADDR_NODE);

ares_private.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,10 @@ struct ares_channeldata {
305305

306306
ares_sock_create_callback sock_create_cb;
307307
void *sock_create_cb_data;
308+
309+
/* Providing gradual addition of servers */
310+
int nservers_change; /* how many servers are changing now */
311+
int rotation_state;
308312
};
309313

310314
/* return true if now is exactly check time or later */

0 commit comments

Comments
 (0)