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