1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2019, 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 | #include "socketpair.h" |
25 | |
26 | /*********************************************************************** |
27 | * Only for threaded name resolves builds |
28 | **********************************************************************/ |
29 | #ifdef CURLRES_THREADED |
30 | |
31 | #ifdef HAVE_NETINET_IN_H |
32 | #include <netinet/in.h> |
33 | #endif |
34 | #ifdef HAVE_NETDB_H |
35 | #include <netdb.h> |
36 | #endif |
37 | #ifdef HAVE_ARPA_INET_H |
38 | #include <arpa/inet.h> |
39 | #endif |
40 | #ifdef __VMS |
41 | #include <in.h> |
42 | #include <inet.h> |
43 | #endif |
44 | |
45 | #if defined(USE_THREADS_POSIX) |
46 | # ifdef HAVE_PTHREAD_H |
47 | # include <pthread.h> |
48 | # endif |
49 | #elif defined(USE_THREADS_WIN32) |
50 | # ifdef HAVE_PROCESS_H |
51 | # include <process.h> |
52 | # endif |
53 | #endif |
54 | |
55 | #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) |
56 | #undef in_addr_t |
57 | #define in_addr_t unsigned long |
58 | #endif |
59 | |
60 | #ifdef HAVE_GETADDRINFO |
61 | # define RESOLVER_ENOMEM EAI_MEMORY |
62 | #else |
63 | # define RESOLVER_ENOMEM ENOMEM |
64 | #endif |
65 | |
66 | #include "urldata.h" |
67 | #include "sendf.h" |
68 | #include "hostip.h" |
69 | #include "hash.h" |
70 | #include "share.h" |
71 | #include "strerror.h" |
72 | #include "url.h" |
73 | #include "multiif.h" |
74 | #include "inet_pton.h" |
75 | #include "inet_ntop.h" |
76 | #include "curl_threads.h" |
77 | #include "connect.h" |
78 | #include "socketpair.h" |
79 | /* The last 3 #include files should be in this order */ |
80 | #include "curl_printf.h" |
81 | #include "curl_memory.h" |
82 | #include "memdebug.h" |
83 | |
84 | struct resdata { |
85 | struct curltime start; |
86 | }; |
87 | |
88 | /* |
89 | * Curl_resolver_global_init() |
90 | * Called from curl_global_init() to initialize global resolver environment. |
91 | * Does nothing here. |
92 | */ |
93 | int Curl_resolver_global_init(void) |
94 | { |
95 | return CURLE_OK; |
96 | } |
97 | |
98 | /* |
99 | * Curl_resolver_global_cleanup() |
100 | * Called from curl_global_cleanup() to destroy global resolver environment. |
101 | * Does nothing here. |
102 | */ |
103 | void Curl_resolver_global_cleanup(void) |
104 | { |
105 | } |
106 | |
107 | /* |
108 | * Curl_resolver_init() |
109 | * Called from curl_easy_init() -> Curl_open() to initialize resolver |
110 | * URL-state specific environment ('resolver' member of the UrlState |
111 | * structure). |
112 | */ |
113 | CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver) |
114 | { |
115 | (void)easy; |
116 | *resolver = calloc(1, sizeof(struct resdata)); |
117 | if(!*resolver) |
118 | return CURLE_OUT_OF_MEMORY; |
119 | return CURLE_OK; |
120 | } |
121 | |
122 | /* |
123 | * Curl_resolver_cleanup() |
124 | * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver |
125 | * URL-state specific environment ('resolver' member of the UrlState |
126 | * structure). |
127 | */ |
128 | void Curl_resolver_cleanup(void *resolver) |
129 | { |
130 | free(resolver); |
131 | } |
132 | |
133 | /* |
134 | * Curl_resolver_duphandle() |
135 | * Called from curl_easy_duphandle() to duplicate resolver URL state-specific |
136 | * environment ('resolver' member of the UrlState structure). |
137 | */ |
138 | CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from) |
139 | { |
140 | (void)from; |
141 | return Curl_resolver_init(easy, to); |
142 | } |
143 | |
144 | static void destroy_async_data(struct Curl_async *); |
145 | |
146 | /* |
147 | * Cancel all possibly still on-going resolves for this connection. |
148 | */ |
149 | void Curl_resolver_cancel(struct connectdata *conn) |
150 | { |
151 | destroy_async_data(&conn->async); |
152 | } |
153 | |
154 | /* This function is used to init a threaded resolve */ |
155 | static bool init_resolve_thread(struct connectdata *conn, |
156 | const char *hostname, int port, |
157 | const struct addrinfo *hints); |
158 | |
159 | |
160 | /* Data for synchronization between resolver thread and its parent */ |
161 | struct thread_sync_data { |
162 | curl_mutex_t * mtx; |
163 | int done; |
164 | |
165 | char *hostname; /* hostname to resolve, Curl_async.hostname |
166 | duplicate */ |
167 | int port; |
168 | #ifdef USE_SOCKETPAIR |
169 | struct connectdata *conn; |
170 | curl_socket_t sock_pair[2]; /* socket pair */ |
171 | #endif |
172 | int sock_error; |
173 | Curl_addrinfo *res; |
174 | #ifdef HAVE_GETADDRINFO |
175 | struct addrinfo hints; |
176 | #endif |
177 | struct thread_data *td; /* for thread-self cleanup */ |
178 | }; |
179 | |
180 | struct thread_data { |
181 | curl_thread_t thread_hnd; |
182 | unsigned int poll_interval; |
183 | time_t interval_end; |
184 | struct thread_sync_data tsd; |
185 | }; |
186 | |
187 | static struct thread_sync_data *conn_thread_sync_data(struct connectdata *conn) |
188 | { |
189 | return &(((struct thread_data *)conn->async.os_specific)->tsd); |
190 | } |
191 | |
192 | /* Destroy resolver thread synchronization data */ |
193 | static |
194 | void destroy_thread_sync_data(struct thread_sync_data * tsd) |
195 | { |
196 | if(tsd->mtx) { |
197 | Curl_mutex_destroy(tsd->mtx); |
198 | free(tsd->mtx); |
199 | } |
200 | |
201 | free(tsd->hostname); |
202 | |
203 | if(tsd->res) |
204 | Curl_freeaddrinfo(tsd->res); |
205 | |
206 | #ifdef USE_SOCKETPAIR |
207 | /* |
208 | * close one end of the socket pair (may be done in resolver thread); |
209 | * the other end (for reading) is always closed in the parent thread. |
210 | */ |
211 | if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { |
212 | sclose(tsd->sock_pair[1]); |
213 | } |
214 | #endif |
215 | memset(tsd, 0, sizeof(*tsd)); |
216 | } |
217 | |
218 | /* Initialize resolver thread synchronization data */ |
219 | static |
220 | int init_thread_sync_data(struct thread_data * td, |
221 | const char *hostname, |
222 | int port, |
223 | const struct addrinfo *hints) |
224 | { |
225 | struct thread_sync_data *tsd = &td->tsd; |
226 | |
227 | memset(tsd, 0, sizeof(*tsd)); |
228 | |
229 | tsd->td = td; |
230 | tsd->port = port; |
231 | /* Treat the request as done until the thread actually starts so any early |
232 | * cleanup gets done properly. |
233 | */ |
234 | tsd->done = 1; |
235 | #ifdef HAVE_GETADDRINFO |
236 | DEBUGASSERT(hints); |
237 | tsd->hints = *hints; |
238 | #else |
239 | (void) hints; |
240 | #endif |
241 | |
242 | tsd->mtx = malloc(sizeof(curl_mutex_t)); |
243 | if(tsd->mtx == NULL) |
244 | goto err_exit; |
245 | |
246 | Curl_mutex_init(tsd->mtx); |
247 | |
248 | #ifdef USE_SOCKETPAIR |
249 | /* create socket pair, avoid AF_LOCAL since it doesn't build on Solaris */ |
250 | if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 0) { |
251 | tsd->sock_pair[0] = CURL_SOCKET_BAD; |
252 | tsd->sock_pair[1] = CURL_SOCKET_BAD; |
253 | goto err_exit; |
254 | } |
255 | #endif |
256 | tsd->sock_error = CURL_ASYNC_SUCCESS; |
257 | |
258 | /* Copying hostname string because original can be destroyed by parent |
259 | * thread during gethostbyname execution. |
260 | */ |
261 | tsd->hostname = strdup(hostname); |
262 | if(!tsd->hostname) |
263 | goto err_exit; |
264 | |
265 | return 1; |
266 | |
267 | err_exit: |
268 | /* Memory allocation failed */ |
269 | destroy_thread_sync_data(tsd); |
270 | return 0; |
271 | } |
272 | |
273 | static int getaddrinfo_complete(struct connectdata *conn) |
274 | { |
275 | struct thread_sync_data *tsd = conn_thread_sync_data(conn); |
276 | int rc; |
277 | |
278 | rc = Curl_addrinfo_callback(conn, tsd->sock_error, tsd->res); |
279 | /* The tsd->res structure has been copied to async.dns and perhaps the DNS |
280 | cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it. |
281 | */ |
282 | tsd->res = NULL; |
283 | |
284 | return rc; |
285 | } |
286 | |
287 | |
288 | #ifdef HAVE_GETADDRINFO |
289 | |
290 | /* |
291 | * getaddrinfo_thread() resolves a name and then exits. |
292 | * |
293 | * For builds without ARES, but with ENABLE_IPV6, create a resolver thread |
294 | * and wait on it. |
295 | */ |
296 | static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg) |
297 | { |
298 | struct thread_sync_data *tsd = (struct thread_sync_data*)arg; |
299 | struct thread_data *td = tsd->td; |
300 | char service[12]; |
301 | int rc; |
302 | #ifdef USE_SOCKETPAIR |
303 | char buf[1]; |
304 | #endif |
305 | |
306 | msnprintf(service, sizeof(service), "%d" , tsd->port); |
307 | |
308 | rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); |
309 | |
310 | if(rc != 0) { |
311 | tsd->sock_error = SOCKERRNO?SOCKERRNO:rc; |
312 | if(tsd->sock_error == 0) |
313 | tsd->sock_error = RESOLVER_ENOMEM; |
314 | } |
315 | else { |
316 | Curl_addrinfo_set_port(tsd->res, tsd->port); |
317 | } |
318 | |
319 | Curl_mutex_acquire(tsd->mtx); |
320 | if(tsd->done) { |
321 | /* too late, gotta clean up the mess */ |
322 | Curl_mutex_release(tsd->mtx); |
323 | destroy_thread_sync_data(tsd); |
324 | free(td); |
325 | } |
326 | else { |
327 | #ifdef USE_SOCKETPAIR |
328 | if(tsd->sock_pair[1] != CURL_SOCKET_BAD) { |
329 | /* DNS has been resolved, signal client task */ |
330 | buf[0] = 1; |
331 | if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) { |
332 | /* update sock_erro to errno */ |
333 | tsd->sock_error = SOCKERRNO; |
334 | } |
335 | } |
336 | #endif |
337 | tsd->done = 1; |
338 | Curl_mutex_release(tsd->mtx); |
339 | } |
340 | |
341 | return 0; |
342 | } |
343 | |
344 | #else /* HAVE_GETADDRINFO */ |
345 | |
346 | /* |
347 | * gethostbyname_thread() resolves a name and then exits. |
348 | */ |
349 | static unsigned int CURL_STDCALL gethostbyname_thread(void *arg) |
350 | { |
351 | struct thread_sync_data *tsd = (struct thread_sync_data *)arg; |
352 | struct thread_data *td = tsd->td; |
353 | |
354 | tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); |
355 | |
356 | if(!tsd->res) { |
357 | tsd->sock_error = SOCKERRNO; |
358 | if(tsd->sock_error == 0) |
359 | tsd->sock_error = RESOLVER_ENOMEM; |
360 | } |
361 | |
362 | Curl_mutex_acquire(tsd->mtx); |
363 | if(tsd->done) { |
364 | /* too late, gotta clean up the mess */ |
365 | Curl_mutex_release(tsd->mtx); |
366 | destroy_thread_sync_data(tsd); |
367 | free(td); |
368 | } |
369 | else { |
370 | tsd->done = 1; |
371 | Curl_mutex_release(tsd->mtx); |
372 | } |
373 | |
374 | return 0; |
375 | } |
376 | |
377 | #endif /* HAVE_GETADDRINFO */ |
378 | |
379 | /* |
380 | * destroy_async_data() cleans up async resolver data and thread handle. |
381 | */ |
382 | static void destroy_async_data(struct Curl_async *async) |
383 | { |
384 | if(async->os_specific) { |
385 | struct thread_data *td = (struct thread_data*) async->os_specific; |
386 | int done; |
387 | #ifdef USE_SOCKETPAIR |
388 | curl_socket_t sock_rd = td->tsd.sock_pair[0]; |
389 | struct connectdata *conn = td->tsd.conn; |
390 | #endif |
391 | |
392 | /* |
393 | * if the thread is still blocking in the resolve syscall, detach it and |
394 | * let the thread do the cleanup... |
395 | */ |
396 | Curl_mutex_acquire(td->tsd.mtx); |
397 | done = td->tsd.done; |
398 | td->tsd.done = 1; |
399 | Curl_mutex_release(td->tsd.mtx); |
400 | |
401 | if(!done) { |
402 | Curl_thread_destroy(td->thread_hnd); |
403 | } |
404 | else { |
405 | if(td->thread_hnd != curl_thread_t_null) |
406 | Curl_thread_join(&td->thread_hnd); |
407 | |
408 | destroy_thread_sync_data(&td->tsd); |
409 | |
410 | free(async->os_specific); |
411 | } |
412 | #ifdef USE_SOCKETPAIR |
413 | /* |
414 | * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE |
415 | * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL |
416 | */ |
417 | if(conn) |
418 | Curl_multi_closed(conn->data, sock_rd); |
419 | sclose(sock_rd); |
420 | #endif |
421 | } |
422 | async->os_specific = NULL; |
423 | |
424 | free(async->hostname); |
425 | async->hostname = NULL; |
426 | } |
427 | |
428 | /* |
429 | * init_resolve_thread() starts a new thread that performs the actual |
430 | * resolve. This function returns before the resolve is done. |
431 | * |
432 | * Returns FALSE in case of failure, otherwise TRUE. |
433 | */ |
434 | static bool init_resolve_thread(struct connectdata *conn, |
435 | const char *hostname, int port, |
436 | const struct addrinfo *hints) |
437 | { |
438 | struct thread_data *td = calloc(1, sizeof(struct thread_data)); |
439 | int err = ENOMEM; |
440 | |
441 | conn->async.os_specific = (void *)td; |
442 | if(!td) |
443 | goto errno_exit; |
444 | |
445 | conn->async.port = port; |
446 | conn->async.done = FALSE; |
447 | conn->async.status = 0; |
448 | conn->async.dns = NULL; |
449 | td->thread_hnd = curl_thread_t_null; |
450 | |
451 | if(!init_thread_sync_data(td, hostname, port, hints)) { |
452 | conn->async.os_specific = NULL; |
453 | free(td); |
454 | goto errno_exit; |
455 | } |
456 | |
457 | free(conn->async.hostname); |
458 | conn->async.hostname = strdup(hostname); |
459 | if(!conn->async.hostname) |
460 | goto err_exit; |
461 | |
462 | /* The thread will set this to 1 when complete. */ |
463 | td->tsd.done = 0; |
464 | |
465 | #ifdef HAVE_GETADDRINFO |
466 | td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd); |
467 | #else |
468 | td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd); |
469 | #endif |
470 | |
471 | if(!td->thread_hnd) { |
472 | /* The thread never started, so mark it as done here for proper cleanup. */ |
473 | td->tsd.done = 1; |
474 | err = errno; |
475 | goto err_exit; |
476 | } |
477 | |
478 | return TRUE; |
479 | |
480 | err_exit: |
481 | destroy_async_data(&conn->async); |
482 | |
483 | errno_exit: |
484 | errno = err; |
485 | return FALSE; |
486 | } |
487 | |
488 | /* |
489 | * resolver_error() calls failf() with the appropriate message after a resolve |
490 | * error |
491 | */ |
492 | |
493 | static CURLcode resolver_error(struct connectdata *conn) |
494 | { |
495 | const char *host_or_proxy; |
496 | CURLcode result; |
497 | |
498 | if(conn->bits.httpproxy) { |
499 | host_or_proxy = "proxy" ; |
500 | result = CURLE_COULDNT_RESOLVE_PROXY; |
501 | } |
502 | else { |
503 | host_or_proxy = "host" ; |
504 | result = CURLE_COULDNT_RESOLVE_HOST; |
505 | } |
506 | |
507 | failf(conn->data, "Could not resolve %s: %s" , host_or_proxy, |
508 | conn->async.hostname); |
509 | |
510 | return result; |
511 | } |
512 | |
513 | static CURLcode thread_wait_resolv(struct connectdata *conn, |
514 | struct Curl_dns_entry **entry, |
515 | bool report) |
516 | { |
517 | struct thread_data *td = (struct thread_data*) conn->async.os_specific; |
518 | CURLcode result = CURLE_OK; |
519 | |
520 | DEBUGASSERT(conn && td); |
521 | DEBUGASSERT(td->thread_hnd != curl_thread_t_null); |
522 | |
523 | /* wait for the thread to resolve the name */ |
524 | if(Curl_thread_join(&td->thread_hnd)) { |
525 | if(entry) |
526 | result = getaddrinfo_complete(conn); |
527 | } |
528 | else |
529 | DEBUGASSERT(0); |
530 | |
531 | conn->async.done = TRUE; |
532 | |
533 | if(entry) |
534 | *entry = conn->async.dns; |
535 | |
536 | if(!conn->async.dns && report) |
537 | /* a name was not resolved, report error */ |
538 | result = resolver_error(conn); |
539 | |
540 | destroy_async_data(&conn->async); |
541 | |
542 | if(!conn->async.dns && report) |
543 | connclose(conn, "asynch resolve failed" ); |
544 | |
545 | return result; |
546 | } |
547 | |
548 | |
549 | /* |
550 | * Until we gain a way to signal the resolver threads to stop early, we must |
551 | * simply wait for them and ignore their results. |
552 | */ |
553 | void Curl_resolver_kill(struct connectdata *conn) |
554 | { |
555 | struct thread_data *td = (struct thread_data*) conn->async.os_specific; |
556 | |
557 | /* If we're still resolving, we must wait for the threads to fully clean up, |
558 | unfortunately. Otherwise, we can simply cancel to clean up any resolver |
559 | data. */ |
560 | if(td && td->thread_hnd != curl_thread_t_null) |
561 | (void)thread_wait_resolv(conn, NULL, FALSE); |
562 | else |
563 | Curl_resolver_cancel(conn); |
564 | } |
565 | |
566 | /* |
567 | * Curl_resolver_wait_resolv() |
568 | * |
569 | * Waits for a resolve to finish. This function should be avoided since using |
570 | * this risk getting the multi interface to "hang". |
571 | * |
572 | * If 'entry' is non-NULL, make it point to the resolved dns entry |
573 | * |
574 | * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, |
575 | * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors. |
576 | * |
577 | * This is the version for resolves-in-a-thread. |
578 | */ |
579 | CURLcode Curl_resolver_wait_resolv(struct connectdata *conn, |
580 | struct Curl_dns_entry **entry) |
581 | { |
582 | return thread_wait_resolv(conn, entry, TRUE); |
583 | } |
584 | |
585 | /* |
586 | * Curl_resolver_is_resolved() is called repeatedly to check if a previous |
587 | * name resolve request has completed. It should also make sure to time-out if |
588 | * the operation seems to take too long. |
589 | */ |
590 | CURLcode Curl_resolver_is_resolved(struct connectdata *conn, |
591 | struct Curl_dns_entry **entry) |
592 | { |
593 | struct Curl_easy *data = conn->data; |
594 | struct thread_data *td = (struct thread_data*) conn->async.os_specific; |
595 | int done = 0; |
596 | |
597 | *entry = NULL; |
598 | |
599 | if(!td) { |
600 | DEBUGASSERT(td); |
601 | return CURLE_COULDNT_RESOLVE_HOST; |
602 | } |
603 | |
604 | Curl_mutex_acquire(td->tsd.mtx); |
605 | done = td->tsd.done; |
606 | Curl_mutex_release(td->tsd.mtx); |
607 | |
608 | if(done) { |
609 | getaddrinfo_complete(conn); |
610 | |
611 | if(!conn->async.dns) { |
612 | CURLcode result = resolver_error(conn); |
613 | destroy_async_data(&conn->async); |
614 | return result; |
615 | } |
616 | destroy_async_data(&conn->async); |
617 | *entry = conn->async.dns; |
618 | } |
619 | else { |
620 | /* poll for name lookup done with exponential backoff up to 250ms */ |
621 | /* should be fine even if this converts to 32 bit */ |
622 | time_t elapsed = (time_t)Curl_timediff(Curl_now(), |
623 | data->progress.t_startsingle); |
624 | if(elapsed < 0) |
625 | elapsed = 0; |
626 | |
627 | if(td->poll_interval == 0) |
628 | /* Start at 1ms poll interval */ |
629 | td->poll_interval = 1; |
630 | else if(elapsed >= td->interval_end) |
631 | /* Back-off exponentially if last interval expired */ |
632 | td->poll_interval *= 2; |
633 | |
634 | if(td->poll_interval > 250) |
635 | td->poll_interval = 250; |
636 | |
637 | td->interval_end = elapsed + td->poll_interval; |
638 | Curl_expire(conn->data, td->poll_interval, EXPIRE_ASYNC_NAME); |
639 | } |
640 | |
641 | return CURLE_OK; |
642 | } |
643 | |
644 | int Curl_resolver_getsock(struct connectdata *conn, |
645 | curl_socket_t *socks) |
646 | { |
647 | int ret_val = 0; |
648 | time_t milli; |
649 | timediff_t ms; |
650 | struct Curl_easy *data = conn->data; |
651 | struct resdata *reslv = (struct resdata *)data->state.resolver; |
652 | #ifdef USE_SOCKETPAIR |
653 | struct thread_data *td = (struct thread_data*)conn->async.os_specific; |
654 | #else |
655 | (void)socks; |
656 | #endif |
657 | |
658 | #ifdef USE_SOCKETPAIR |
659 | if(td) { |
660 | /* return read fd to client for polling the DNS resolution status */ |
661 | socks[0] = td->tsd.sock_pair[0]; |
662 | DEBUGASSERT(td->tsd.conn == conn || !td->tsd.conn); |
663 | td->tsd.conn = conn; |
664 | ret_val = GETSOCK_READSOCK(0); |
665 | } |
666 | else { |
667 | #endif |
668 | ms = Curl_timediff(Curl_now(), reslv->start); |
669 | if(ms < 3) |
670 | milli = 0; |
671 | else if(ms <= 50) |
672 | milli = (time_t)ms/3; |
673 | else if(ms <= 250) |
674 | milli = 50; |
675 | else |
676 | milli = 200; |
677 | Curl_expire(data, milli, EXPIRE_ASYNC_NAME); |
678 | #ifdef USE_SOCKETPAIR |
679 | } |
680 | #endif |
681 | |
682 | |
683 | return ret_val; |
684 | } |
685 | |
686 | #ifndef HAVE_GETADDRINFO |
687 | /* |
688 | * Curl_getaddrinfo() - for platforms without getaddrinfo |
689 | */ |
690 | Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, |
691 | const char *hostname, |
692 | int port, |
693 | int *waitp) |
694 | { |
695 | struct in_addr in; |
696 | struct Curl_easy *data = conn->data; |
697 | struct resdata *reslv = (struct resdata *)data->state.resolver; |
698 | |
699 | *waitp = 0; /* default to synchronous response */ |
700 | |
701 | #ifdef ENABLE_IPV6 |
702 | { |
703 | struct in6_addr in6; |
704 | /* check if this is an IPv6 address string */ |
705 | if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) |
706 | /* This is an IPv6 address literal */ |
707 | return Curl_ip2addr(AF_INET6, &in6, hostname, port); |
708 | } |
709 | #endif /* ENABLE_IPV6 */ |
710 | |
711 | if(Curl_inet_pton(AF_INET, hostname, &in) > 0) |
712 | /* This is a dotted IP address 123.123.123.123-style */ |
713 | return Curl_ip2addr(AF_INET, &in, hostname, port); |
714 | |
715 | reslv->start = Curl_now(); |
716 | |
717 | /* fire up a new resolver thread! */ |
718 | if(init_resolve_thread(conn, hostname, port, NULL)) { |
719 | *waitp = 1; /* expect asynchronous response */ |
720 | return NULL; |
721 | } |
722 | |
723 | failf(conn->data, "getaddrinfo() thread failed\n" ); |
724 | |
725 | return NULL; |
726 | } |
727 | |
728 | #else /* !HAVE_GETADDRINFO */ |
729 | |
730 | /* |
731 | * Curl_resolver_getaddrinfo() - for getaddrinfo |
732 | */ |
733 | Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn, |
734 | const char *hostname, |
735 | int port, |
736 | int *waitp) |
737 | { |
738 | struct addrinfo hints; |
739 | char sbuf[12]; |
740 | int pf = PF_INET; |
741 | struct Curl_easy *data = conn->data; |
742 | struct resdata *reslv = (struct resdata *)data->state.resolver; |
743 | |
744 | *waitp = 0; /* default to synchronous response */ |
745 | |
746 | #ifndef USE_RESOLVE_ON_IPS |
747 | { |
748 | struct in_addr in; |
749 | /* First check if this is an IPv4 address string */ |
750 | if(Curl_inet_pton(AF_INET, hostname, &in) > 0) |
751 | /* This is a dotted IP address 123.123.123.123-style */ |
752 | return Curl_ip2addr(AF_INET, &in, hostname, port); |
753 | } |
754 | #ifdef ENABLE_IPV6 |
755 | { |
756 | struct in6_addr in6; |
757 | /* check if this is an IPv6 address string */ |
758 | if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0) |
759 | /* This is an IPv6 address literal */ |
760 | return Curl_ip2addr(AF_INET6, &in6, hostname, port); |
761 | } |
762 | #endif /* ENABLE_IPV6 */ |
763 | #endif /* !USE_RESOLVE_ON_IPS */ |
764 | |
765 | #ifdef CURLRES_IPV6 |
766 | /* |
767 | * Check if a limited name resolve has been requested. |
768 | */ |
769 | switch(conn->ip_version) { |
770 | case CURL_IPRESOLVE_V4: |
771 | pf = PF_INET; |
772 | break; |
773 | case CURL_IPRESOLVE_V6: |
774 | pf = PF_INET6; |
775 | break; |
776 | default: |
777 | pf = PF_UNSPEC; |
778 | break; |
779 | } |
780 | |
781 | if((pf != PF_INET) && !Curl_ipv6works()) |
782 | /* The stack seems to be a non-IPv6 one */ |
783 | pf = PF_INET; |
784 | #endif /* CURLRES_IPV6 */ |
785 | |
786 | memset(&hints, 0, sizeof(hints)); |
787 | hints.ai_family = pf; |
788 | hints.ai_socktype = (conn->transport == TRNSPRT_TCP)? |
789 | SOCK_STREAM : SOCK_DGRAM; |
790 | |
791 | msnprintf(sbuf, sizeof(sbuf), "%d" , port); |
792 | |
793 | reslv->start = Curl_now(); |
794 | /* fire up a new resolver thread! */ |
795 | if(init_resolve_thread(conn, hostname, port, &hints)) { |
796 | *waitp = 1; /* expect asynchronous response */ |
797 | return NULL; |
798 | } |
799 | |
800 | failf(data, "getaddrinfo() thread failed to start\n" ); |
801 | return NULL; |
802 | |
803 | } |
804 | |
805 | #endif /* !HAVE_GETADDRINFO */ |
806 | |
807 | CURLcode Curl_set_dns_servers(struct Curl_easy *data, |
808 | char *servers) |
809 | { |
810 | (void)data; |
811 | (void)servers; |
812 | return CURLE_NOT_BUILT_IN; |
813 | |
814 | } |
815 | |
816 | CURLcode Curl_set_dns_interface(struct Curl_easy *data, |
817 | const char *interf) |
818 | { |
819 | (void)data; |
820 | (void)interf; |
821 | return CURLE_NOT_BUILT_IN; |
822 | } |
823 | |
824 | CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data, |
825 | const char *local_ip4) |
826 | { |
827 | (void)data; |
828 | (void)local_ip4; |
829 | return CURLE_NOT_BUILT_IN; |
830 | } |
831 | |
832 | CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data, |
833 | const char *local_ip6) |
834 | { |
835 | (void)data; |
836 | (void)local_ip6; |
837 | return CURLE_NOT_BUILT_IN; |
838 | } |
839 | |
840 | #endif /* CURLRES_THREADED */ |
841 | |