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