1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2019, Daniel Stenberg, <[email protected]>, et al.
|
---|
9 | *
|
---|
10 | * This software is licensed as described in the file COPYING, which
|
---|
11 | * you should have received as part of this distribution. The terms
|
---|
12 | * are also available at https://curl.haxx.se/docs/copyright.html.
|
---|
13 | *
|
---|
14 | * You may opt to use, copy, modify, merge, publish, distribute and/or sell
|
---|
15 | * copies of the Software, and permit persons to whom the Software is
|
---|
16 | * furnished to do so, under the terms of the COPYING file.
|
---|
17 | *
|
---|
18 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
---|
19 | * KIND, either express or implied.
|
---|
20 | *
|
---|
21 | ***************************************************************************/
|
---|
22 |
|
---|
23 | #include "curl_setup.h"
|
---|
24 |
|
---|
25 | /***********************************************************************
|
---|
26 | * Only for ares-enabled builds
|
---|
27 | * And only for functions that fulfill the asynch resolver backend API
|
---|
28 | * as defined in asyn.h, nothing else belongs in this file!
|
---|
29 | **********************************************************************/
|
---|
30 |
|
---|
31 | #ifdef CURLRES_ARES
|
---|
32 |
|
---|
33 | #include <limits.h>
|
---|
34 | #ifdef HAVE_NETINET_IN_H
|
---|
35 | #include <netinet/in.h>
|
---|
36 | #endif
|
---|
37 | #ifdef HAVE_NETDB_H
|
---|
38 | #include <netdb.h>
|
---|
39 | #endif
|
---|
40 | #ifdef HAVE_ARPA_INET_H
|
---|
41 | #include <arpa/inet.h>
|
---|
42 | #endif
|
---|
43 | #ifdef __VMS
|
---|
44 | #include <in.h>
|
---|
45 | #include <inet.h>
|
---|
46 | #endif
|
---|
47 |
|
---|
48 | #ifdef HAVE_PROCESS_H
|
---|
49 | #include <process.h>
|
---|
50 | #endif
|
---|
51 |
|
---|
52 | #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
|
---|
53 | #undef in_addr_t
|
---|
54 | #define in_addr_t unsigned long
|
---|
55 | #endif
|
---|
56 |
|
---|
57 | #include "urldata.h"
|
---|
58 | #include "sendf.h"
|
---|
59 | #include "hostip.h"
|
---|
60 | #include "hash.h"
|
---|
61 | #include "share.h"
|
---|
62 | #include "strerror.h"
|
---|
63 | #include "url.h"
|
---|
64 | #include "multiif.h"
|
---|
65 | #include "inet_pton.h"
|
---|
66 | #include "connect.h"
|
---|
67 | #include "select.h"
|
---|
68 | #include "progress.h"
|
---|
69 |
|
---|
70 | # if defined(CURL_STATICLIB) && !defined(CARES_STATICLIB) && \
|
---|
71 | (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__))
|
---|
72 | # define CARES_STATICLIB
|
---|
73 | # endif
|
---|
74 | # include <ares.h>
|
---|
75 | # include <ares_version.h> /* really old c-ares didn't include this by
|
---|
76 | itself */
|
---|
77 |
|
---|
78 | #if ARES_VERSION >= 0x010500
|
---|
79 | /* c-ares 1.5.0 or later, the callback proto is modified */
|
---|
80 | #define HAVE_CARES_CALLBACK_TIMEOUTS 1
|
---|
81 | #endif
|
---|
82 |
|
---|
83 | /* The last 3 #include files should be in this order */
|
---|
84 | #include "curl_printf.h"
|
---|
85 | #include "curl_memory.h"
|
---|
86 | #include "memdebug.h"
|
---|
87 |
|
---|
88 | struct ResolverResults {
|
---|
89 | int num_pending; /* number of ares_gethostbyname() requests */
|
---|
90 | Curl_addrinfo *temp_ai; /* intermediary result while fetching c-ares parts */
|
---|
91 | int last_status;
|
---|
92 | };
|
---|
93 |
|
---|
94 | /*
|
---|
95 | * Curl_resolver_global_init() - the generic low-level asynchronous name
|
---|
96 | * resolve API. Called from curl_global_init() to initialize global resolver
|
---|
97 | * environment. Initializes ares library.
|
---|
98 | */
|
---|
99 | int Curl_resolver_global_init(void)
|
---|
100 | {
|
---|
101 | #ifdef CARES_HAVE_ARES_LIBRARY_INIT
|
---|
102 | if(ares_library_init(ARES_LIB_INIT_ALL)) {
|
---|
103 | return CURLE_FAILED_INIT;
|
---|
104 | }
|
---|
105 | #endif
|
---|
106 | return CURLE_OK;
|
---|
107 | }
|
---|
108 |
|
---|
109 | /*
|
---|
110 | * Curl_resolver_global_cleanup()
|
---|
111 | *
|
---|
112 | * Called from curl_global_cleanup() to destroy global resolver environment.
|
---|
113 | * Deinitializes ares library.
|
---|
114 | */
|
---|
115 | void Curl_resolver_global_cleanup(void)
|
---|
116 | {
|
---|
117 | #ifdef CARES_HAVE_ARES_LIBRARY_CLEANUP
|
---|
118 | ares_library_cleanup();
|
---|
119 | #endif
|
---|
120 | }
|
---|
121 |
|
---|
122 |
|
---|
123 | static void Curl_ares_sock_state_cb(void *data, ares_socket_t socket_fd,
|
---|
124 | int readable, int writable)
|
---|
125 | {
|
---|
126 | struct Curl_easy *easy = data;
|
---|
127 | if(!readable && !writable) {
|
---|
128 | DEBUGASSERT(easy);
|
---|
129 | Curl_multi_closed(easy, socket_fd);
|
---|
130 | }
|
---|
131 | }
|
---|
132 |
|
---|
133 | /*
|
---|
134 | * Curl_resolver_init()
|
---|
135 | *
|
---|
136 | * Called from curl_easy_init() -> Curl_open() to initialize resolver
|
---|
137 | * URL-state specific environment ('resolver' member of the UrlState
|
---|
138 | * structure). Fills the passed pointer by the initialized ares_channel.
|
---|
139 | */
|
---|
140 | CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
|
---|
141 | {
|
---|
142 | int status;
|
---|
143 | struct ares_options options;
|
---|
144 | int optmask = ARES_OPT_SOCK_STATE_CB;
|
---|
145 | options.sock_state_cb = Curl_ares_sock_state_cb;
|
---|
146 | options.sock_state_cb_data = easy;
|
---|
147 | status = ares_init_options((ares_channel*)resolver, &options, optmask);
|
---|
148 | if(status != ARES_SUCCESS) {
|
---|
149 | if(status == ARES_ENOMEM)
|
---|
150 | return CURLE_OUT_OF_MEMORY;
|
---|
151 | else
|
---|
152 | return CURLE_FAILED_INIT;
|
---|
153 | }
|
---|
154 | return CURLE_OK;
|
---|
155 | /* make sure that all other returns from this function should destroy the
|
---|
156 | ares channel before returning error! */
|
---|
157 | }
|
---|
158 |
|
---|
159 | /*
|
---|
160 | * Curl_resolver_cleanup()
|
---|
161 | *
|
---|
162 | * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
|
---|
163 | * URL-state specific environment ('resolver' member of the UrlState
|
---|
164 | * structure). Destroys the ares channel.
|
---|
165 | */
|
---|
166 | void Curl_resolver_cleanup(void *resolver)
|
---|
167 | {
|
---|
168 | ares_destroy((ares_channel)resolver);
|
---|
169 | }
|
---|
170 |
|
---|
171 | /*
|
---|
172 | * Curl_resolver_duphandle()
|
---|
173 | *
|
---|
174 | * Called from curl_easy_duphandle() to duplicate resolver URL-state specific
|
---|
175 | * environment ('resolver' member of the UrlState structure). Duplicates the
|
---|
176 | * 'from' ares channel and passes the resulting channel to the 'to' pointer.
|
---|
177 | */
|
---|
178 | CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
|
---|
179 | {
|
---|
180 | (void)from;
|
---|
181 | /*
|
---|
182 | * it would be better to call ares_dup instead, but right now
|
---|
183 | * it is not possible to set 'sock_state_cb_data' outside of
|
---|
184 | * ares_init_options
|
---|
185 | */
|
---|
186 | return Curl_resolver_init(easy, to);
|
---|
187 | }
|
---|
188 |
|
---|
189 | static void destroy_async_data(struct Curl_async *async);
|
---|
190 |
|
---|
191 | /*
|
---|
192 | * Cancel all possibly still on-going resolves for this connection.
|
---|
193 | */
|
---|
194 | void Curl_resolver_cancel(struct connectdata *conn)
|
---|
195 | {
|
---|
196 | if(conn->data && conn->data->state.resolver)
|
---|
197 | ares_cancel((ares_channel)conn->data->state.resolver);
|
---|
198 | destroy_async_data(&conn->async);
|
---|
199 | }
|
---|
200 |
|
---|
201 | /*
|
---|
202 | * We're equivalent to Curl_resolver_cancel() for the c-ares resolver. We
|
---|
203 | * never block.
|
---|
204 | */
|
---|
205 | void Curl_resolver_kill(struct connectdata *conn)
|
---|
206 | {
|
---|
207 | /* We don't need to check the resolver state because we can be called safely
|
---|
208 | at any time and we always do the same thing. */
|
---|
209 | Curl_resolver_cancel(conn);
|
---|
210 | }
|
---|
211 |
|
---|
212 | /*
|
---|
213 | * destroy_async_data() cleans up async resolver data.
|
---|
214 | */
|
---|
215 | static void destroy_async_data(struct Curl_async *async)
|
---|
216 | {
|
---|
217 | free(async->hostname);
|
---|
218 |
|
---|
219 | if(async->os_specific) {
|
---|
220 | struct ResolverResults *res = (struct ResolverResults *)async->os_specific;
|
---|
221 | if(res) {
|
---|
222 | if(res->temp_ai) {
|
---|
223 | Curl_freeaddrinfo(res->temp_ai);
|
---|
224 | res->temp_ai = NULL;
|
---|
225 | }
|
---|
226 | free(res);
|
---|
227 | }
|
---|
228 | async->os_specific = NULL;
|
---|
229 | }
|
---|
230 |
|
---|
231 | async->hostname = NULL;
|
---|
232 | }
|
---|
233 |
|
---|
234 | /*
|
---|
235 | * Curl_resolver_getsock() is called when someone from the outside world
|
---|
236 | * (using curl_multi_fdset()) wants to get our fd_set setup and we're talking
|
---|
237 | * with ares. The caller must make sure that this function is only called when
|
---|
238 | * we have a working ares channel.
|
---|
239 | *
|
---|
240 | * Returns: sockets-in-use-bitmap
|
---|
241 | */
|
---|
242 |
|
---|
243 | int Curl_resolver_getsock(struct connectdata *conn,
|
---|
244 | curl_socket_t *socks,
|
---|
245 | int numsocks)
|
---|
246 |
|
---|
247 | {
|
---|
248 | struct timeval maxtime;
|
---|
249 | struct timeval timebuf;
|
---|
250 | struct timeval *timeout;
|
---|
251 | long milli;
|
---|
252 | int max = ares_getsock((ares_channel)conn->data->state.resolver,
|
---|
253 | (ares_socket_t *)socks, numsocks);
|
---|
254 |
|
---|
255 | maxtime.tv_sec = CURL_TIMEOUT_RESOLVE;
|
---|
256 | maxtime.tv_usec = 0;
|
---|
257 |
|
---|
258 | timeout = ares_timeout((ares_channel)conn->data->state.resolver, &maxtime,
|
---|
259 | &timebuf);
|
---|
260 | milli = (timeout->tv_sec * 1000) + (timeout->tv_usec/1000);
|
---|
261 | if(milli == 0)
|
---|
262 | milli += 10;
|
---|
263 | Curl_expire(conn->data, milli, EXPIRE_ASYNC_NAME);
|
---|
264 |
|
---|
265 | return max;
|
---|
266 | }
|
---|
267 |
|
---|
268 | /*
|
---|
269 | * waitperform()
|
---|
270 | *
|
---|
271 | * 1) Ask ares what sockets it currently plays with, then
|
---|
272 | * 2) wait for the timeout period to check for action on ares' sockets.
|
---|
273 | * 3) tell ares to act on all the sockets marked as "with action"
|
---|
274 | *
|
---|
275 | * return number of sockets it worked on
|
---|
276 | */
|
---|
277 |
|
---|
278 | static int waitperform(struct connectdata *conn, int timeout_ms)
|
---|
279 | {
|
---|
280 | struct Curl_easy *data = conn->data;
|
---|
281 | int nfds;
|
---|
282 | int bitmask;
|
---|
283 | ares_socket_t socks[ARES_GETSOCK_MAXNUM];
|
---|
284 | struct pollfd pfd[ARES_GETSOCK_MAXNUM];
|
---|
285 | int i;
|
---|
286 | int num = 0;
|
---|
287 |
|
---|
288 | bitmask = ares_getsock((ares_channel)data->state.resolver, socks,
|
---|
289 | ARES_GETSOCK_MAXNUM);
|
---|
290 |
|
---|
291 | for(i = 0; i < ARES_GETSOCK_MAXNUM; i++) {
|
---|
292 | pfd[i].events = 0;
|
---|
293 | pfd[i].revents = 0;
|
---|
294 | if(ARES_GETSOCK_READABLE(bitmask, i)) {
|
---|
295 | pfd[i].fd = socks[i];
|
---|
296 | pfd[i].events |= POLLRDNORM|POLLIN;
|
---|
297 | }
|
---|
298 | if(ARES_GETSOCK_WRITABLE(bitmask, i)) {
|
---|
299 | pfd[i].fd = socks[i];
|
---|
300 | pfd[i].events |= POLLWRNORM|POLLOUT;
|
---|
301 | }
|
---|
302 | if(pfd[i].events != 0)
|
---|
303 | num++;
|
---|
304 | else
|
---|
305 | break;
|
---|
306 | }
|
---|
307 |
|
---|
308 | if(num)
|
---|
309 | nfds = Curl_poll(pfd, num, timeout_ms);
|
---|
310 | else
|
---|
311 | nfds = 0;
|
---|
312 |
|
---|
313 | if(!nfds)
|
---|
314 | /* Call ares_process() unconditonally here, even if we simply timed out
|
---|
315 | above, as otherwise the ares name resolve won't timeout! */
|
---|
316 | ares_process_fd((ares_channel)data->state.resolver, ARES_SOCKET_BAD,
|
---|
317 | ARES_SOCKET_BAD);
|
---|
318 | else {
|
---|
319 | /* move through the descriptors and ask for processing on them */
|
---|
320 | for(i = 0; i < num; i++)
|
---|
321 | ares_process_fd((ares_channel)data->state.resolver,
|
---|
322 | pfd[i].revents & (POLLRDNORM|POLLIN)?
|
---|
323 | pfd[i].fd:ARES_SOCKET_BAD,
|
---|
324 | pfd[i].revents & (POLLWRNORM|POLLOUT)?
|
---|
325 | pfd[i].fd:ARES_SOCKET_BAD);
|
---|
326 | }
|
---|
327 | return nfds;
|
---|
328 | }
|
---|
329 |
|
---|
330 | /*
|
---|
331 | * Curl_resolver_is_resolved() is called repeatedly to check if a previous
|
---|
332 | * name resolve request has completed. It should also make sure to time-out if
|
---|
333 | * the operation seems to take too long.
|
---|
334 | *
|
---|
335 | * Returns normal CURLcode errors.
|
---|
336 | */
|
---|
337 | CURLcode Curl_resolver_is_resolved(struct connectdata *conn,
|
---|
338 | struct Curl_dns_entry **dns)
|
---|
339 | {
|
---|
340 | struct Curl_easy *data = conn->data;
|
---|
341 | struct ResolverResults *res = (struct ResolverResults *)
|
---|
342 | conn->async.os_specific;
|
---|
343 | CURLcode result = CURLE_OK;
|
---|
344 |
|
---|
345 | if(dns)
|
---|
346 | *dns = NULL;
|
---|
347 |
|
---|
348 | waitperform(conn, 0);
|
---|
349 |
|
---|
350 | if(res && !res->num_pending) {
|
---|
351 | if(dns) {
|
---|
352 | (void)Curl_addrinfo_callback(conn, res->last_status, res->temp_ai);
|
---|
353 | /* temp_ai ownership is moved to the connection, so we need not free-up
|
---|
354 | them */
|
---|
355 | res->temp_ai = NULL;
|
---|
356 | }
|
---|
357 | if(!conn->async.dns) {
|
---|
358 | failf(data, "Could not resolve: %s (%s)",
|
---|
359 | conn->async.hostname, ares_strerror(conn->async.status));
|
---|
360 | result = conn->bits.proxy?CURLE_COULDNT_RESOLVE_PROXY:
|
---|
361 | CURLE_COULDNT_RESOLVE_HOST;
|
---|
362 | }
|
---|
363 | else if(dns)
|
---|
364 | *dns = conn->async.dns;
|
---|
365 |
|
---|
366 | destroy_async_data(&conn->async);
|
---|
367 | }
|
---|
368 |
|
---|
369 | return result;
|
---|
370 | }
|
---|
371 |
|
---|
372 | /*
|
---|
373 | * Curl_resolver_wait_resolv()
|
---|
374 | *
|
---|
375 | * Waits for a resolve to finish. This function should be avoided since using
|
---|
376 | * this risk getting the multi interface to "hang".
|
---|
377 | *
|
---|
378 | * If 'entry' is non-NULL, make it point to the resolved dns entry
|
---|
379 | *
|
---|
380 | * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
|
---|
381 | * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
|
---|
382 | */
|
---|
383 | CURLcode Curl_resolver_wait_resolv(struct connectdata *conn,
|
---|
384 | struct Curl_dns_entry **entry)
|
---|
385 | {
|
---|
386 | CURLcode result = CURLE_OK;
|
---|
387 | struct Curl_easy *data = conn->data;
|
---|
388 | timediff_t timeout;
|
---|
389 | struct curltime now = Curl_now();
|
---|
390 | struct Curl_dns_entry *temp_entry;
|
---|
391 |
|
---|
392 | if(entry)
|
---|
393 | *entry = NULL; /* clear on entry */
|
---|
394 |
|
---|
395 | timeout = Curl_timeleft(data, &now, TRUE);
|
---|
396 | if(timeout < 0) {
|
---|
397 | /* already expired! */
|
---|
398 | connclose(conn, "Timed out before name resolve started");
|
---|
399 | return CURLE_OPERATION_TIMEDOUT;
|
---|
400 | }
|
---|
401 | if(!timeout)
|
---|
402 | timeout = CURL_TIMEOUT_RESOLVE * 1000; /* default name resolve timeout */
|
---|
403 |
|
---|
404 | /* Wait for the name resolve query to complete. */
|
---|
405 | while(!result) {
|
---|
406 | struct timeval *tvp, tv, store;
|
---|
407 | int itimeout;
|
---|
408 | int timeout_ms;
|
---|
409 |
|
---|
410 | itimeout = (timeout > (long)INT_MAX) ? INT_MAX : (int)timeout;
|
---|
411 |
|
---|
412 | store.tv_sec = itimeout/1000;
|
---|
413 | store.tv_usec = (itimeout%1000)*1000;
|
---|
414 |
|
---|
415 | tvp = ares_timeout((ares_channel)data->state.resolver, &store, &tv);
|
---|
416 |
|
---|
417 | /* use the timeout period ares returned to us above if less than one
|
---|
418 | second is left, otherwise just use 1000ms to make sure the progress
|
---|
419 | callback gets called frequent enough */
|
---|
420 | if(!tvp->tv_sec)
|
---|
421 | timeout_ms = (int)(tvp->tv_usec/1000);
|
---|
422 | else
|
---|
423 | timeout_ms = 1000;
|
---|
424 |
|
---|
425 | waitperform(conn, timeout_ms);
|
---|
426 | result = Curl_resolver_is_resolved(conn, entry?&temp_entry:NULL);
|
---|
427 |
|
---|
428 | if(result || conn->async.done)
|
---|
429 | break;
|
---|
430 |
|
---|
431 | if(Curl_pgrsUpdate(conn))
|
---|
432 | result = CURLE_ABORTED_BY_CALLBACK;
|
---|
433 | else {
|
---|
434 | struct curltime now2 = Curl_now();
|
---|
435 | timediff_t timediff = Curl_timediff(now2, now); /* spent time */
|
---|
436 | if(timediff <= 0)
|
---|
437 | timeout -= 1; /* always deduct at least 1 */
|
---|
438 | else if(timediff > timeout)
|
---|
439 | timeout = -1;
|
---|
440 | else
|
---|
441 | timeout -= (long)timediff;
|
---|
442 | now = now2; /* for next loop */
|
---|
443 | }
|
---|
444 | if(timeout < 0)
|
---|
445 | result = CURLE_OPERATION_TIMEDOUT;
|
---|
446 | }
|
---|
447 | if(result)
|
---|
448 | /* failure, so we cancel the ares operation */
|
---|
449 | ares_cancel((ares_channel)data->state.resolver);
|
---|
450 |
|
---|
451 | /* Operation complete, if the lookup was successful we now have the entry
|
---|
452 | in the cache. */
|
---|
453 | if(entry)
|
---|
454 | *entry = conn->async.dns;
|
---|
455 |
|
---|
456 | if(result)
|
---|
457 | /* close the connection, since we can't return failure here without
|
---|
458 | cleaning up this connection properly.
|
---|
459 | TODO: remove this action from here, it is not a name resolver decision.
|
---|
460 | */
|
---|
461 | connclose(conn, "c-ares resolve failed");
|
---|
462 |
|
---|
463 | return result;
|
---|
464 | }
|
---|
465 |
|
---|
466 | /* Connects results to the list */
|
---|
467 | static void compound_results(struct ResolverResults *res,
|
---|
468 | Curl_addrinfo *ai)
|
---|
469 | {
|
---|
470 | Curl_addrinfo *ai_tail;
|
---|
471 | if(!ai)
|
---|
472 | return;
|
---|
473 | ai_tail = ai;
|
---|
474 |
|
---|
475 | while(ai_tail->ai_next)
|
---|
476 | ai_tail = ai_tail->ai_next;
|
---|
477 |
|
---|
478 | /* Add the new results to the list of old results. */
|
---|
479 | ai_tail->ai_next = res->temp_ai;
|
---|
480 | res->temp_ai = ai;
|
---|
481 | }
|
---|
482 |
|
---|
483 | /*
|
---|
484 | * ares_query_completed_cb() is the callback that ares will call when
|
---|
485 | * the host query initiated by ares_gethostbyname() from Curl_getaddrinfo(),
|
---|
486 | * when using ares, is completed either successfully or with failure.
|
---|
487 | */
|
---|
488 | static void query_completed_cb(void *arg, /* (struct connectdata *) */
|
---|
489 | int status,
|
---|
490 | #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
|
---|
491 | int timeouts,
|
---|
492 | #endif
|
---|
493 | struct hostent *hostent)
|
---|
494 | {
|
---|
495 | struct connectdata *conn = (struct connectdata *)arg;
|
---|
496 | struct ResolverResults *res;
|
---|
497 |
|
---|
498 | #ifdef HAVE_CARES_CALLBACK_TIMEOUTS
|
---|
499 | (void)timeouts; /* ignored */
|
---|
500 | #endif
|
---|
501 |
|
---|
502 | if(ARES_EDESTRUCTION == status)
|
---|
503 | /* when this ares handle is getting destroyed, the 'arg' pointer may not
|
---|
504 | be valid so only defer it when we know the 'status' says its fine! */
|
---|
505 | return;
|
---|
506 |
|
---|
507 | res = (struct ResolverResults *)conn->async.os_specific;
|
---|
508 | if(res) {
|
---|
509 | res->num_pending--;
|
---|
510 |
|
---|
511 | if(CURL_ASYNC_SUCCESS == status) {
|
---|
512 | Curl_addrinfo *ai = Curl_he2ai(hostent, conn->async.port);
|
---|
513 | if(ai) {
|
---|
514 | compound_results(res, ai);
|
---|
515 | }
|
---|
516 | }
|
---|
517 | /* A successful result overwrites any previous error */
|
---|
518 | if(res->last_status != ARES_SUCCESS)
|
---|
519 | res->last_status = status;
|
---|
520 | }
|
---|
521 | }
|
---|
522 |
|
---|
523 | /*
|
---|
524 | * Curl_resolver_getaddrinfo() - when using ares
|
---|
525 | *
|
---|
526 | * Returns name information about the given hostname and port number. If
|
---|
527 | * successful, the 'hostent' is returned and the forth argument will point to
|
---|
528 | * memory we need to free after use. That memory *MUST* be freed with
|
---|
529 | * Curl_freeaddrinfo(), nothing else.
|
---|
530 | */
|
---|
531 | Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
|
---|
532 | const char *hostname,
|
---|
533 | int port,
|
---|
534 | int *waitp)
|
---|
535 | {
|
---|
536 | char *bufp;
|
---|
537 | struct Curl_easy *data = conn->data;
|
---|
538 | struct in_addr in;
|
---|
539 | int family = PF_INET;
|
---|
540 | #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
|
---|
541 | struct in6_addr in6;
|
---|
542 | #endif /* CURLRES_IPV6 */
|
---|
543 |
|
---|
544 | *waitp = 0; /* default to synchronous response */
|
---|
545 |
|
---|
546 | /* First check if this is an IPv4 address string */
|
---|
547 | if(Curl_inet_pton(AF_INET, hostname, &in) > 0) {
|
---|
548 | /* This is a dotted IP address 123.123.123.123-style */
|
---|
549 | return Curl_ip2addr(AF_INET, &in, hostname, port);
|
---|
550 | }
|
---|
551 |
|
---|
552 | #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
|
---|
553 | /* Otherwise, check if this is an IPv6 address string */
|
---|
554 | if(Curl_inet_pton (AF_INET6, hostname, &in6) > 0)
|
---|
555 | /* This must be an IPv6 address literal. */
|
---|
556 | return Curl_ip2addr(AF_INET6, &in6, hostname, port);
|
---|
557 |
|
---|
558 | switch(conn->ip_version) {
|
---|
559 | default:
|
---|
560 | #if ARES_VERSION >= 0x010601
|
---|
561 | family = PF_UNSPEC; /* supported by c-ares since 1.6.1, so for older
|
---|
562 | c-ares versions this just falls through and defaults
|
---|
563 | to PF_INET */
|
---|
564 | break;
|
---|
565 | #endif
|
---|
566 | case CURL_IPRESOLVE_V4:
|
---|
567 | family = PF_INET;
|
---|
568 | break;
|
---|
569 | case CURL_IPRESOLVE_V6:
|
---|
570 | family = PF_INET6;
|
---|
571 | break;
|
---|
572 | }
|
---|
573 | #endif /* CURLRES_IPV6 */
|
---|
574 |
|
---|
575 | bufp = strdup(hostname);
|
---|
576 | if(bufp) {
|
---|
577 | struct ResolverResults *res = NULL;
|
---|
578 | free(conn->async.hostname);
|
---|
579 | conn->async.hostname = bufp;
|
---|
580 | conn->async.port = port;
|
---|
581 | conn->async.done = FALSE; /* not done */
|
---|
582 | conn->async.status = 0; /* clear */
|
---|
583 | conn->async.dns = NULL; /* clear */
|
---|
584 | res = calloc(sizeof(struct ResolverResults), 1);
|
---|
585 | if(!res) {
|
---|
586 | free(conn->async.hostname);
|
---|
587 | conn->async.hostname = NULL;
|
---|
588 | return NULL;
|
---|
589 | }
|
---|
590 | conn->async.os_specific = res;
|
---|
591 |
|
---|
592 | /* initial status - failed */
|
---|
593 | res->last_status = ARES_ENOTFOUND;
|
---|
594 | #ifdef ENABLE_IPV6 /* CURLRES_IPV6 */
|
---|
595 | if(family == PF_UNSPEC) {
|
---|
596 | if(Curl_ipv6works()) {
|
---|
597 | res->num_pending = 2;
|
---|
598 |
|
---|
599 | /* areschannel is already setup in the Curl_open() function */
|
---|
600 | ares_gethostbyname((ares_channel)data->state.resolver, hostname,
|
---|
601 | PF_INET, query_completed_cb, conn);
|
---|
602 | ares_gethostbyname((ares_channel)data->state.resolver, hostname,
|
---|
603 | PF_INET6, query_completed_cb, conn);
|
---|
604 | }
|
---|
605 | else {
|
---|
606 | res->num_pending = 1;
|
---|
607 |
|
---|
608 | /* areschannel is already setup in the Curl_open() function */
|
---|
609 | ares_gethostbyname((ares_channel)data->state.resolver, hostname,
|
---|
610 | PF_INET, query_completed_cb, conn);
|
---|
611 | }
|
---|
612 | }
|
---|
613 | else
|
---|
614 | #endif /* CURLRES_IPV6 */
|
---|
615 | {
|
---|
616 | res->num_pending = 1;
|
---|
617 |
|
---|
618 | /* areschannel is already setup in the Curl_open() function */
|
---|
619 | ares_gethostbyname((ares_channel)data->state.resolver, hostname, family,
|
---|
620 | query_completed_cb, conn);
|
---|
621 | }
|
---|
622 |
|
---|
623 | *waitp = 1; /* expect asynchronous response */
|
---|
624 | }
|
---|
625 | return NULL; /* no struct yet */
|
---|
626 | }
|
---|
627 |
|
---|
628 | CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
---|
629 | char *servers)
|
---|
630 | {
|
---|
631 | CURLcode result = CURLE_NOT_BUILT_IN;
|
---|
632 | int ares_result;
|
---|
633 |
|
---|
634 | /* If server is NULL or empty, this would purge all DNS servers
|
---|
635 | * from ares library, which will cause any and all queries to fail.
|
---|
636 | * So, just return OK if none are configured and don't actually make
|
---|
637 | * any changes to c-ares. This lets c-ares use it's defaults, which
|
---|
638 | * it gets from the OS (for instance from /etc/resolv.conf on Linux).
|
---|
639 | */
|
---|
640 | if(!(servers && servers[0]))
|
---|
641 | return CURLE_OK;
|
---|
642 |
|
---|
643 | #if (ARES_VERSION >= 0x010704)
|
---|
644 | ares_result = ares_set_servers_csv(data->state.resolver, servers);
|
---|
645 | switch(ares_result) {
|
---|
646 | case ARES_SUCCESS:
|
---|
647 | result = CURLE_OK;
|
---|
648 | break;
|
---|
649 | case ARES_ENOMEM:
|
---|
650 | result = CURLE_OUT_OF_MEMORY;
|
---|
651 | break;
|
---|
652 | case ARES_ENOTINITIALIZED:
|
---|
653 | case ARES_ENODATA:
|
---|
654 | case ARES_EBADSTR:
|
---|
655 | default:
|
---|
656 | result = CURLE_BAD_FUNCTION_ARGUMENT;
|
---|
657 | break;
|
---|
658 | }
|
---|
659 | #else /* too old c-ares version! */
|
---|
660 | (void)data;
|
---|
661 | (void)(ares_result);
|
---|
662 | #endif
|
---|
663 | return result;
|
---|
664 | }
|
---|
665 |
|
---|
666 | CURLcode Curl_set_dns_interface(struct Curl_easy *data,
|
---|
667 | const char *interf)
|
---|
668 | {
|
---|
669 | #if (ARES_VERSION >= 0x010704)
|
---|
670 | if(!interf)
|
---|
671 | interf = "";
|
---|
672 |
|
---|
673 | ares_set_local_dev((ares_channel)data->state.resolver, interf);
|
---|
674 |
|
---|
675 | return CURLE_OK;
|
---|
676 | #else /* c-ares version too old! */
|
---|
677 | (void)data;
|
---|
678 | (void)interf;
|
---|
679 | return CURLE_NOT_BUILT_IN;
|
---|
680 | #endif
|
---|
681 | }
|
---|
682 |
|
---|
683 | CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
|
---|
684 | const char *local_ip4)
|
---|
685 | {
|
---|
686 | #if (ARES_VERSION >= 0x010704)
|
---|
687 | struct in_addr a4;
|
---|
688 |
|
---|
689 | if((!local_ip4) || (local_ip4[0] == 0)) {
|
---|
690 | a4.s_addr = 0; /* disabled: do not bind to a specific address */
|
---|
691 | }
|
---|
692 | else {
|
---|
693 | if(Curl_inet_pton(AF_INET, local_ip4, &a4) != 1) {
|
---|
694 | return CURLE_BAD_FUNCTION_ARGUMENT;
|
---|
695 | }
|
---|
696 | }
|
---|
697 |
|
---|
698 | ares_set_local_ip4((ares_channel)data->state.resolver, ntohl(a4.s_addr));
|
---|
699 |
|
---|
700 | return CURLE_OK;
|
---|
701 | #else /* c-ares version too old! */
|
---|
702 | (void)data;
|
---|
703 | (void)local_ip4;
|
---|
704 | return CURLE_NOT_BUILT_IN;
|
---|
705 | #endif
|
---|
706 | }
|
---|
707 |
|
---|
708 | CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
|
---|
709 | const char *local_ip6)
|
---|
710 | {
|
---|
711 | #if (ARES_VERSION >= 0x010704) && defined(ENABLE_IPV6)
|
---|
712 | unsigned char a6[INET6_ADDRSTRLEN];
|
---|
713 |
|
---|
714 | if((!local_ip6) || (local_ip6[0] == 0)) {
|
---|
715 | /* disabled: do not bind to a specific address */
|
---|
716 | memset(a6, 0, sizeof(a6));
|
---|
717 | }
|
---|
718 | else {
|
---|
719 | if(Curl_inet_pton(AF_INET6, local_ip6, a6) != 1) {
|
---|
720 | return CURLE_BAD_FUNCTION_ARGUMENT;
|
---|
721 | }
|
---|
722 | }
|
---|
723 |
|
---|
724 | ares_set_local_ip6((ares_channel)data->state.resolver, a6);
|
---|
725 |
|
---|
726 | return CURLE_OK;
|
---|
727 | #else /* c-ares version too old! */
|
---|
728 | (void)data;
|
---|
729 | (void)local_ip6;
|
---|
730 | return CURLE_NOT_BUILT_IN;
|
---|
731 | #endif
|
---|
732 | }
|
---|
733 | #endif /* CURLRES_ARES */
|
---|