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