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