1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 1998 - 2022, Daniel Stenberg, <[email protected]>, 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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 |
|
---|
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
|
---|
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
|
---|
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 |
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 | */
|
---|
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 |
|
---|
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 | */
|
---|
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 | */
|
---|
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 | pf = PF_UNSPEC;
|
---|
702 | #endif /* CURLRES_IPV6 */
|
---|
703 |
|
---|
704 | memset(&hints, 0, sizeof(hints));
|
---|
705 | hints.ai_family = pf;
|
---|
706 | hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
|
---|
707 | SOCK_STREAM : SOCK_DGRAM;
|
---|
708 |
|
---|
709 | reslv->start = Curl_now();
|
---|
710 | /* fire up a new resolver thread! */
|
---|
711 | if(init_resolve_thread(data, hostname, port, &hints)) {
|
---|
712 | *waitp = 1; /* expect asynchronous response */
|
---|
713 | return NULL;
|
---|
714 | }
|
---|
715 |
|
---|
716 | failf(data, "getaddrinfo() thread failed to start");
|
---|
717 | return NULL;
|
---|
718 |
|
---|
719 | }
|
---|
720 |
|
---|
721 | #endif /* !HAVE_GETADDRINFO */
|
---|
722 |
|
---|
723 | CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
---|
724 | char *servers)
|
---|
725 | {
|
---|
726 | (void)data;
|
---|
727 | (void)servers;
|
---|
728 | return CURLE_NOT_BUILT_IN;
|
---|
729 |
|
---|
730 | }
|
---|
731 |
|
---|
732 | CURLcode Curl_set_dns_interface(struct Curl_easy *data,
|
---|
733 | const char *interf)
|
---|
734 | {
|
---|
735 | (void)data;
|
---|
736 | (void)interf;
|
---|
737 | return CURLE_NOT_BUILT_IN;
|
---|
738 | }
|
---|
739 |
|
---|
740 | CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
|
---|
741 | const char *local_ip4)
|
---|
742 | {
|
---|
743 | (void)data;
|
---|
744 | (void)local_ip4;
|
---|
745 | return CURLE_NOT_BUILT_IN;
|
---|
746 | }
|
---|
747 |
|
---|
748 | CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
|
---|
749 | const char *local_ip6)
|
---|
750 | {
|
---|
751 | (void)data;
|
---|
752 | (void)local_ip6;
|
---|
753 | return CURLE_NOT_BUILT_IN;
|
---|
754 | }
|
---|
755 |
|
---|
756 | #endif /* CURLRES_THREADED */
|
---|