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