• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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]; /* 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     sclose(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, avoid AF_LOCAL since it doesn't build on Solaris */
237   if(Curl_socketpair(AF_UNIX, SOCK_STREAM, 0, &tsd->sock_pair[0]) < 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     sclose(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 doesn't 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 ENABLE_IPV6, create a resolver thread
286  * and wait on it.
287  */
getaddrinfo_thread(void * arg)288 static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
289 {
290   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
291   struct thread_data *td = tsd->td;
292   char service[12];
293   int rc;
294 #ifndef CURL_DISABLE_SOCKETPAIR
295   char buf[1];
296 #endif
297 
298   msnprintf(service, sizeof(service), "%d", tsd->port);
299 
300   rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
301 
302   if(rc) {
303     tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
304     if(tsd->sock_error == 0)
305       tsd->sock_error = RESOLVER_ENOMEM;
306   }
307   else {
308     Curl_addrinfo_set_port(tsd->res, tsd->port);
309   }
310 
311   Curl_mutex_acquire(tsd->mtx);
312   if(tsd->done) {
313     /* too late, gotta clean up the mess */
314     Curl_mutex_release(tsd->mtx);
315     destroy_thread_sync_data(tsd);
316     free(td);
317   }
318   else {
319 #ifndef CURL_DISABLE_SOCKETPAIR
320     if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
321       /* DNS has been resolved, signal client task */
322       buf[0] = 1;
323       if(swrite(tsd->sock_pair[1],  buf, sizeof(buf)) < 0) {
324         /* update sock_erro to errno */
325         tsd->sock_error = SOCKERRNO;
326       }
327     }
328 #endif
329     tsd->done = 1;
330     Curl_mutex_release(tsd->mtx);
331   }
332 
333   return 0;
334 }
335 
336 #else /* HAVE_GETADDRINFO */
337 
338 /*
339  * gethostbyname_thread() resolves a name and then exits.
340  */
gethostbyname_thread(void * arg)341 static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
342 {
343   struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
344   struct thread_data *td = tsd->td;
345 
346   tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
347 
348   if(!tsd->res) {
349     tsd->sock_error = SOCKERRNO;
350     if(tsd->sock_error == 0)
351       tsd->sock_error = RESOLVER_ENOMEM;
352   }
353 
354   Curl_mutex_acquire(tsd->mtx);
355   if(tsd->done) {
356     /* too late, gotta clean up the mess */
357     Curl_mutex_release(tsd->mtx);
358     destroy_thread_sync_data(tsd);
359     free(td);
360   }
361   else {
362     tsd->done = 1;
363     Curl_mutex_release(tsd->mtx);
364   }
365 
366   return 0;
367 }
368 
369 #endif /* HAVE_GETADDRINFO */
370 
371 /*
372  * destroy_async_data() cleans up async resolver data and thread handle.
373  */
destroy_async_data(struct Curl_async * async)374 static void destroy_async_data(struct Curl_async *async)
375 {
376   if(async->tdata) {
377     struct thread_data *td = async->tdata;
378     int done;
379 #ifndef CURL_DISABLE_SOCKETPAIR
380     curl_socket_t sock_rd = td->tsd.sock_pair[0];
381     struct Curl_easy *data = td->tsd.data;
382 #endif
383 
384     /*
385      * if the thread is still blocking in the resolve syscall, detach it and
386      * let the thread do the cleanup...
387      */
388     Curl_mutex_acquire(td->tsd.mtx);
389     done = td->tsd.done;
390     td->tsd.done = 1;
391     Curl_mutex_release(td->tsd.mtx);
392 
393     if(!done) {
394       Curl_thread_destroy(td->thread_hnd);
395     }
396     else {
397       if(td->thread_hnd != curl_thread_t_null)
398         Curl_thread_join(&td->thread_hnd);
399 
400       destroy_thread_sync_data(&td->tsd);
401 
402       free(async->tdata);
403     }
404 #ifndef CURL_DISABLE_SOCKETPAIR
405     /*
406      * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
407      * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
408      */
409     Curl_multi_closed(data, sock_rd);
410     sclose(sock_rd);
411 #endif
412   }
413   async->tdata = NULL;
414 
415   free(async->hostname);
416   async->hostname = NULL;
417 }
418 
419 /*
420  * init_resolve_thread() starts a new thread that performs the actual
421  * resolve. This function returns before the resolve is done.
422  *
423  * Returns FALSE in case of failure, otherwise TRUE.
424  */
init_resolve_thread(struct Curl_easy * data,const char * hostname,int port,const struct addrinfo * hints)425 static bool init_resolve_thread(struct Curl_easy *data,
426                                 const char *hostname, int port,
427                                 const struct addrinfo *hints)
428 {
429   struct thread_data *td = calloc(1, sizeof(struct thread_data));
430   int err = ENOMEM;
431   struct Curl_async *asp = &data->state.async;
432 
433   data->state.async.tdata = td;
434   if(!td)
435     goto errno_exit;
436 
437   asp->port = port;
438   asp->done = FALSE;
439   asp->status = 0;
440   asp->dns = NULL;
441   td->thread_hnd = curl_thread_t_null;
442 
443   if(!init_thread_sync_data(td, hostname, port, hints)) {
444     asp->tdata = NULL;
445     free(td);
446     goto errno_exit;
447   }
448 
449   free(asp->hostname);
450   asp->hostname = strdup(hostname);
451   if(!asp->hostname)
452     goto err_exit;
453 
454   /* The thread will set this to 1 when complete. */
455   td->tsd.done = 0;
456 
457 #ifdef HAVE_GETADDRINFO
458   td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
459 #else
460   td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
461 #endif
462 
463   if(!td->thread_hnd) {
464     /* The thread never started, so mark it as done here for proper cleanup. */
465     td->tsd.done = 1;
466     err = errno;
467     goto err_exit;
468   }
469 
470   return TRUE;
471 
472 err_exit:
473   destroy_async_data(asp);
474 
475 errno_exit:
476   errno = err;
477   return FALSE;
478 }
479 
480 /*
481  * 'entry' may be NULL and then no data is returned
482  */
thread_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry,bool report)483 static CURLcode thread_wait_resolv(struct Curl_easy *data,
484                                    struct Curl_dns_entry **entry,
485                                    bool report)
486 {
487   struct thread_data *td;
488   CURLcode result = CURLE_OK;
489 
490   DEBUGASSERT(data);
491   td = data->state.async.tdata;
492   DEBUGASSERT(td);
493   DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
494 
495   /* wait for the thread to resolve the name */
496   if(Curl_thread_join(&td->thread_hnd)) {
497     if(entry)
498       result = getaddrinfo_complete(data);
499   }
500   else
501     DEBUGASSERT(0);
502 
503   data->state.async.done = TRUE;
504 
505   if(entry)
506     *entry = data->state.async.dns;
507 
508   if(!data->state.async.dns && report)
509     /* a name was not resolved, report error */
510     result = Curl_resolver_error(data);
511 
512   destroy_async_data(&data->state.async);
513 
514   if(!data->state.async.dns && report)
515     connclose(data->conn, "asynch resolve failed");
516 
517   return result;
518 }
519 
520 
521 /*
522  * Until we gain a way to signal the resolver threads to stop early, we must
523  * simply wait for them and ignore their results.
524  */
Curl_resolver_kill(struct Curl_easy * data)525 void Curl_resolver_kill(struct Curl_easy *data)
526 {
527   struct thread_data *td = data->state.async.tdata;
528 
529   /* If we're still resolving, we must wait for the threads to fully clean up,
530      unfortunately.  Otherwise, we can simply cancel to clean up any resolver
531      data. */
532   if(td && td->thread_hnd != curl_thread_t_null
533      && (data->set.quick_exit != 1L))
534     (void)thread_wait_resolv(data, NULL, FALSE);
535   else
536     Curl_resolver_cancel(data);
537 }
538 
539 /*
540  * Curl_resolver_wait_resolv()
541  *
542  * Waits for a resolve to finish. This function should be avoided since using
543  * this risk getting the multi interface to "hang".
544  *
545  * If 'entry' is non-NULL, make it point to the resolved dns entry
546  *
547  * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
548  * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
549  *
550  * This is the version for resolves-in-a-thread.
551  */
Curl_resolver_wait_resolv(struct Curl_easy * data,struct Curl_dns_entry ** entry)552 CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
553                                    struct Curl_dns_entry **entry)
554 {
555   return thread_wait_resolv(data, entry, TRUE);
556 }
557 
558 /*
559  * Curl_resolver_is_resolved() is called repeatedly to check if a previous
560  * name resolve request has completed. It should also make sure to time-out if
561  * the operation seems to take too long.
562  */
Curl_resolver_is_resolved(struct Curl_easy * data,struct Curl_dns_entry ** entry)563 CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
564                                    struct Curl_dns_entry **entry)
565 {
566   struct thread_data *td = data->state.async.tdata;
567   int done = 0;
568 
569   DEBUGASSERT(entry);
570   *entry = NULL;
571 
572   if(!td) {
573     DEBUGASSERT(td);
574     return CURLE_COULDNT_RESOLVE_HOST;
575   }
576 
577   Curl_mutex_acquire(td->tsd.mtx);
578   done = td->tsd.done;
579   Curl_mutex_release(td->tsd.mtx);
580 
581   if(done) {
582     getaddrinfo_complete(data);
583 
584     if(!data->state.async.dns) {
585       CURLcode result = Curl_resolver_error(data);
586       destroy_async_data(&data->state.async);
587       return result;
588     }
589     destroy_async_data(&data->state.async);
590     *entry = data->state.async.dns;
591   }
592   else {
593     /* poll for name lookup done with exponential backoff up to 250ms */
594     /* should be fine even if this converts to 32 bit */
595     timediff_t elapsed = Curl_timediff(Curl_now(),
596                                        data->progress.t_startsingle);
597     if(elapsed < 0)
598       elapsed = 0;
599 
600     if(td->poll_interval == 0)
601       /* Start at 1ms poll interval */
602       td->poll_interval = 1;
603     else if(elapsed >= td->interval_end)
604       /* Back-off exponentially if last interval expired  */
605       td->poll_interval *= 2;
606 
607     if(td->poll_interval > 250)
608       td->poll_interval = 250;
609 
610     td->interval_end = elapsed + td->poll_interval;
611     Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
612   }
613 
614   return CURLE_OK;
615 }
616 
Curl_resolver_getsock(struct Curl_easy * data,curl_socket_t * socks)617 int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
618 {
619   int ret_val = 0;
620   timediff_t milli;
621   timediff_t ms;
622   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
623 #ifndef CURL_DISABLE_SOCKETPAIR
624   struct thread_data *td = data->state.async.tdata;
625 #else
626   (void)socks;
627 #endif
628 
629 #ifndef CURL_DISABLE_SOCKETPAIR
630   if(td) {
631     /* return read fd to client for polling the DNS resolution status */
632     socks[0] = td->tsd.sock_pair[0];
633     td->tsd.data = data;
634     ret_val = GETSOCK_READSOCK(0);
635   }
636   else {
637 #endif
638     ms = Curl_timediff(Curl_now(), reslv->start);
639     if(ms < 3)
640       milli = 0;
641     else if(ms <= 50)
642       milli = ms/3;
643     else if(ms <= 250)
644       milli = 50;
645     else
646       milli = 200;
647     Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
648 #ifndef CURL_DISABLE_SOCKETPAIR
649   }
650 #endif
651 
652 
653   return ret_val;
654 }
655 
656 #ifndef HAVE_GETADDRINFO
657 /*
658  * Curl_getaddrinfo() - for platforms without getaddrinfo
659  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)660 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
661                                                 const char *hostname,
662                                                 int port,
663                                                 int *waitp)
664 {
665   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
666 
667   *waitp = 0; /* default to synchronous response */
668 
669   reslv->start = Curl_now();
670 
671   /* fire up a new resolver thread! */
672   if(init_resolve_thread(data, hostname, port, NULL)) {
673     *waitp = 1; /* expect asynchronous response */
674     return NULL;
675   }
676 
677   failf(data, "getaddrinfo() thread failed");
678 
679   return NULL;
680 }
681 
682 #else /* !HAVE_GETADDRINFO */
683 
684 /*
685  * Curl_resolver_getaddrinfo() - for getaddrinfo
686  */
Curl_resolver_getaddrinfo(struct Curl_easy * data,const char * hostname,int port,int * waitp)687 struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
688                                                 const char *hostname,
689                                                 int port,
690                                                 int *waitp)
691 {
692   struct addrinfo hints;
693   int pf = PF_INET;
694   struct resdata *reslv = (struct resdata *)data->state.async.resolver;
695 
696   *waitp = 0; /* default to synchronous response */
697 
698 #ifdef CURLRES_IPV6
699   if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
700     /* The stack seems to be IPv6-enabled */
701     if(data->conn->ip_version == CURL_IPRESOLVE_V6)
702       pf = PF_INET6;
703     else
704       pf = PF_UNSPEC;
705   }
706 #endif /* CURLRES_IPV6 */
707 
708   memset(&hints, 0, sizeof(hints));
709   hints.ai_family = pf;
710   hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
711     SOCK_STREAM : SOCK_DGRAM;
712 
713   reslv->start = Curl_now();
714   /* fire up a new resolver thread! */
715   if(init_resolve_thread(data, hostname, port, &hints)) {
716     *waitp = 1; /* expect asynchronous response */
717     return NULL;
718   }
719 
720   failf(data, "getaddrinfo() thread failed to start");
721   return NULL;
722 
723 }
724 
725 #endif /* !HAVE_GETADDRINFO */
726 
Curl_set_dns_servers(struct Curl_easy * data,char * servers)727 CURLcode Curl_set_dns_servers(struct Curl_easy *data,
728                               char *servers)
729 {
730   (void)data;
731   (void)servers;
732   return CURLE_NOT_BUILT_IN;
733 
734 }
735 
Curl_set_dns_interface(struct Curl_easy * data,const char * interf)736 CURLcode Curl_set_dns_interface(struct Curl_easy *data,
737                                 const char *interf)
738 {
739   (void)data;
740   (void)interf;
741   return CURLE_NOT_BUILT_IN;
742 }
743 
Curl_set_dns_local_ip4(struct Curl_easy * data,const char * local_ip4)744 CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
745                                 const char *local_ip4)
746 {
747   (void)data;
748   (void)local_ip4;
749   return CURLE_NOT_BUILT_IN;
750 }
751 
Curl_set_dns_local_ip6(struct Curl_easy * data,const char * local_ip6)752 CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
753                                 const char *local_ip6)
754 {
755   (void)data;
756   (void)local_ip6;
757   return CURLE_NOT_BUILT_IN;
758 }
759 
760 #endif /* CURLRES_THREADED */
761