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