1 | /***************************************************************************
|
---|
2 | * _ _ ____ _
|
---|
3 | * Project ___| | | | _ \| |
|
---|
4 | * / __| | | | |_) | |
|
---|
5 | * | (__| |_| | _ <| |___
|
---|
6 | * \___|\___/|_| \_\_____|
|
---|
7 | *
|
---|
8 | * Copyright (C) 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 "system_win32.h"
|
---|
58 | #include "urldata.h"
|
---|
59 | #include "sendf.h"
|
---|
60 | #include "hostip.h"
|
---|
61 | #include "hash.h"
|
---|
62 | #include "share.h"
|
---|
63 | #include "url.h"
|
---|
64 | #include "multiif.h"
|
---|
65 | #include "inet_ntop.h"
|
---|
66 | #include "curl_threads.h"
|
---|
67 | #include "connect.h"
|
---|
68 | /* The last 3 #include files should be in this order */
|
---|
69 | #include "curl_printf.h"
|
---|
70 | #include "curl_memory.h"
|
---|
71 | #include "memdebug.h"
|
---|
72 |
|
---|
73 | struct resdata {
|
---|
74 | struct curltime start;
|
---|
75 | };
|
---|
76 |
|
---|
77 | /*
|
---|
78 | * Curl_resolver_global_init()
|
---|
79 | * Called from curl_global_init() to initialize global resolver environment.
|
---|
80 | * Does nothing here.
|
---|
81 | */
|
---|
82 | int Curl_resolver_global_init(void)
|
---|
83 | {
|
---|
84 | return CURLE_OK;
|
---|
85 | }
|
---|
86 |
|
---|
87 | /*
|
---|
88 | * Curl_resolver_global_cleanup()
|
---|
89 | * Called from curl_global_cleanup() to destroy global resolver environment.
|
---|
90 | * Does nothing here.
|
---|
91 | */
|
---|
92 | void Curl_resolver_global_cleanup(void)
|
---|
93 | {
|
---|
94 | }
|
---|
95 |
|
---|
96 | /*
|
---|
97 | * Curl_resolver_init()
|
---|
98 | * Called from curl_easy_init() -> Curl_open() to initialize resolver
|
---|
99 | * URL-state specific environment ('resolver' member of the UrlState
|
---|
100 | * structure).
|
---|
101 | */
|
---|
102 | CURLcode Curl_resolver_init(struct Curl_easy *easy, void **resolver)
|
---|
103 | {
|
---|
104 | (void)easy;
|
---|
105 | *resolver = calloc(1, sizeof(struct resdata));
|
---|
106 | if(!*resolver)
|
---|
107 | return CURLE_OUT_OF_MEMORY;
|
---|
108 | return CURLE_OK;
|
---|
109 | }
|
---|
110 |
|
---|
111 | /*
|
---|
112 | * Curl_resolver_cleanup()
|
---|
113 | * Called from curl_easy_cleanup() -> Curl_close() to cleanup resolver
|
---|
114 | * URL-state specific environment ('resolver' member of the UrlState
|
---|
115 | * structure).
|
---|
116 | */
|
---|
117 | void Curl_resolver_cleanup(void *resolver)
|
---|
118 | {
|
---|
119 | free(resolver);
|
---|
120 | }
|
---|
121 |
|
---|
122 | /*
|
---|
123 | * Curl_resolver_duphandle()
|
---|
124 | * Called from curl_easy_duphandle() to duplicate resolver URL state-specific
|
---|
125 | * environment ('resolver' member of the UrlState structure).
|
---|
126 | */
|
---|
127 | CURLcode Curl_resolver_duphandle(struct Curl_easy *easy, void **to, void *from)
|
---|
128 | {
|
---|
129 | (void)from;
|
---|
130 | return Curl_resolver_init(easy, to);
|
---|
131 | }
|
---|
132 |
|
---|
133 | static void destroy_async_data(struct Curl_async *);
|
---|
134 |
|
---|
135 | /*
|
---|
136 | * Cancel all possibly still on-going resolves for this connection.
|
---|
137 | */
|
---|
138 | void Curl_resolver_cancel(struct Curl_easy *data)
|
---|
139 | {
|
---|
140 | destroy_async_data(&data->state.async);
|
---|
141 | }
|
---|
142 |
|
---|
143 | /* This function is used to init a threaded resolve */
|
---|
144 | static bool init_resolve_thread(struct Curl_easy *data,
|
---|
145 | const char *hostname, int port,
|
---|
146 | const struct addrinfo *hints);
|
---|
147 |
|
---|
148 | #ifdef _WIN32
|
---|
149 | /* Thread sync data used by GetAddrInfoExW for win8+ */
|
---|
150 | struct thread_sync_data_w8
|
---|
151 | {
|
---|
152 | OVERLAPPED overlapped;
|
---|
153 | ADDRINFOEXW_ *res;
|
---|
154 | HANDLE cancel_ev;
|
---|
155 | ADDRINFOEXW_ hints;
|
---|
156 | };
|
---|
157 | #endif
|
---|
158 |
|
---|
159 | /* Data for synchronization between resolver thread and its parent */
|
---|
160 | struct thread_sync_data {
|
---|
161 | #ifdef _WIN32
|
---|
162 | struct thread_sync_data_w8 w8;
|
---|
163 | #endif
|
---|
164 | curl_mutex_t *mtx;
|
---|
165 | int done;
|
---|
166 | int port;
|
---|
167 | char *hostname; /* hostname to resolve, Curl_async.hostname
|
---|
168 | duplicate */
|
---|
169 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
170 | struct Curl_easy *data;
|
---|
171 | curl_socket_t sock_pair[2]; /* socket pair */
|
---|
172 | #endif
|
---|
173 | int sock_error;
|
---|
174 | struct Curl_addrinfo *res;
|
---|
175 | #ifdef HAVE_GETADDRINFO
|
---|
176 | struct addrinfo hints;
|
---|
177 | #endif
|
---|
178 | struct thread_data *td; /* for thread-self cleanup */
|
---|
179 | };
|
---|
180 |
|
---|
181 | struct thread_data {
|
---|
182 | #ifdef _WIN32
|
---|
183 | HANDLE complete_ev;
|
---|
184 | #endif
|
---|
185 | curl_thread_t thread_hnd;
|
---|
186 | unsigned int poll_interval;
|
---|
187 | timediff_t interval_end;
|
---|
188 | struct thread_sync_data tsd;
|
---|
189 | };
|
---|
190 |
|
---|
191 | static struct thread_sync_data *conn_thread_sync_data(struct Curl_easy *data)
|
---|
192 | {
|
---|
193 | return &(data->state.async.tdata->tsd);
|
---|
194 | }
|
---|
195 |
|
---|
196 | /* Destroy resolver thread synchronization data */
|
---|
197 | static
|
---|
198 | void destroy_thread_sync_data(struct thread_sync_data *tsd)
|
---|
199 | {
|
---|
200 | if(tsd->mtx) {
|
---|
201 | Curl_mutex_destroy(tsd->mtx);
|
---|
202 | free(tsd->mtx);
|
---|
203 | }
|
---|
204 |
|
---|
205 | free(tsd->hostname);
|
---|
206 |
|
---|
207 | if(tsd->res)
|
---|
208 | Curl_freeaddrinfo(tsd->res);
|
---|
209 |
|
---|
210 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
211 | /*
|
---|
212 | * close one end of the socket pair (may be done in resolver thread);
|
---|
213 | * the other end (for reading) is always closed in the parent thread.
|
---|
214 | */
|
---|
215 | if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
|
---|
216 | wakeup_close(tsd->sock_pair[1]);
|
---|
217 | }
|
---|
218 | #endif
|
---|
219 | memset(tsd, 0, sizeof(*tsd));
|
---|
220 | }
|
---|
221 |
|
---|
222 | /* Initialize resolver thread synchronization data */
|
---|
223 | static
|
---|
224 | int init_thread_sync_data(struct thread_data *td,
|
---|
225 | const char *hostname,
|
---|
226 | int port,
|
---|
227 | const struct addrinfo *hints)
|
---|
228 | {
|
---|
229 | struct thread_sync_data *tsd = &td->tsd;
|
---|
230 |
|
---|
231 | memset(tsd, 0, sizeof(*tsd));
|
---|
232 |
|
---|
233 | tsd->td = td;
|
---|
234 | tsd->port = port;
|
---|
235 | /* Treat the request as done until the thread actually starts so any early
|
---|
236 | * cleanup gets done properly.
|
---|
237 | */
|
---|
238 | tsd->done = 1;
|
---|
239 | #ifdef HAVE_GETADDRINFO
|
---|
240 | DEBUGASSERT(hints);
|
---|
241 | tsd->hints = *hints;
|
---|
242 | #else
|
---|
243 | (void) hints;
|
---|
244 | #endif
|
---|
245 |
|
---|
246 | tsd->mtx = malloc(sizeof(curl_mutex_t));
|
---|
247 | if(!tsd->mtx)
|
---|
248 | goto err_exit;
|
---|
249 |
|
---|
250 | Curl_mutex_init(tsd->mtx);
|
---|
251 |
|
---|
252 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
253 | /* create socket pair or pipe */
|
---|
254 | if(wakeup_create(&tsd->sock_pair[0]) < 0) {
|
---|
255 | tsd->sock_pair[0] = CURL_SOCKET_BAD;
|
---|
256 | tsd->sock_pair[1] = CURL_SOCKET_BAD;
|
---|
257 | goto err_exit;
|
---|
258 | }
|
---|
259 | #endif
|
---|
260 | tsd->sock_error = CURL_ASYNC_SUCCESS;
|
---|
261 |
|
---|
262 | /* Copying hostname string because original can be destroyed by parent
|
---|
263 | * thread during gethostbyname execution.
|
---|
264 | */
|
---|
265 | tsd->hostname = strdup(hostname);
|
---|
266 | if(!tsd->hostname)
|
---|
267 | goto err_exit;
|
---|
268 |
|
---|
269 | return 1;
|
---|
270 |
|
---|
271 | err_exit:
|
---|
272 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
273 | if(tsd->sock_pair[0] != CURL_SOCKET_BAD) {
|
---|
274 | wakeup_close(tsd->sock_pair[0]);
|
---|
275 | tsd->sock_pair[0] = CURL_SOCKET_BAD;
|
---|
276 | }
|
---|
277 | #endif
|
---|
278 | destroy_thread_sync_data(tsd);
|
---|
279 | return 0;
|
---|
280 | }
|
---|
281 |
|
---|
282 | static CURLcode getaddrinfo_complete(struct Curl_easy *data)
|
---|
283 | {
|
---|
284 | struct thread_sync_data *tsd = conn_thread_sync_data(data);
|
---|
285 | CURLcode result;
|
---|
286 |
|
---|
287 | result = Curl_addrinfo_callback(data, tsd->sock_error, tsd->res);
|
---|
288 | /* The tsd->res structure has been copied to async.dns and perhaps the DNS
|
---|
289 | cache. Set our copy to NULL so destroy_thread_sync_data doesn't free it.
|
---|
290 | */
|
---|
291 | tsd->res = NULL;
|
---|
292 |
|
---|
293 | return result;
|
---|
294 | }
|
---|
295 |
|
---|
296 | #ifdef _WIN32
|
---|
297 | static VOID WINAPI
|
---|
298 | query_complete(DWORD err, DWORD bytes, LPWSAOVERLAPPED overlapped)
|
---|
299 | {
|
---|
300 | size_t ss_size;
|
---|
301 | const ADDRINFOEXW_ *ai;
|
---|
302 | struct Curl_addrinfo *ca;
|
---|
303 | struct Curl_addrinfo *cafirst = NULL;
|
---|
304 | struct Curl_addrinfo *calast = NULL;
|
---|
305 | #ifdef __clang__
|
---|
306 | #pragma clang diagnostic push
|
---|
307 | #pragma clang diagnostic ignored "-Wcast-align"
|
---|
308 | #endif
|
---|
309 | struct thread_sync_data *tsd =
|
---|
310 | CONTAINING_RECORD(overlapped, struct thread_sync_data, w8.overlapped);
|
---|
311 | #ifdef __clang__
|
---|
312 | #pragma clang diagnostic pop
|
---|
313 | #endif
|
---|
314 | struct thread_data *td = tsd->td;
|
---|
315 | const ADDRINFOEXW_ *res = tsd->w8.res;
|
---|
316 | int error = (int)err;
|
---|
317 | (void)bytes;
|
---|
318 |
|
---|
319 | if(error == ERROR_SUCCESS) {
|
---|
320 | /* traverse the addrinfo list */
|
---|
321 |
|
---|
322 | for(ai = res; ai != NULL; ai = ai->ai_next) {
|
---|
323 | size_t namelen = ai->ai_canonname ? wcslen(ai->ai_canonname) + 1 : 0;
|
---|
324 | /* ignore elements with unsupported address family, */
|
---|
325 | /* settle family-specific sockaddr structure size. */
|
---|
326 | if(ai->ai_family == AF_INET)
|
---|
327 | ss_size = sizeof(struct sockaddr_in);
|
---|
328 | #ifdef ENABLE_IPV6
|
---|
329 | else if(ai->ai_family == AF_INET6)
|
---|
330 | ss_size = sizeof(struct sockaddr_in6);
|
---|
331 | #endif
|
---|
332 | else
|
---|
333 | continue;
|
---|
334 |
|
---|
335 | /* ignore elements without required address info */
|
---|
336 | if(!ai->ai_addr || !(ai->ai_addrlen > 0))
|
---|
337 | continue;
|
---|
338 |
|
---|
339 | /* ignore elements with bogus address size */
|
---|
340 | if((size_t)ai->ai_addrlen < ss_size)
|
---|
341 | continue;
|
---|
342 |
|
---|
343 | ca = malloc(sizeof(struct Curl_addrinfo) + ss_size + namelen);
|
---|
344 | if(!ca) {
|
---|
345 | error = EAI_MEMORY;
|
---|
346 | break;
|
---|
347 | }
|
---|
348 |
|
---|
349 | /* copy each structure member individually, member ordering, */
|
---|
350 | /* size, or padding might be different for each platform. */
|
---|
351 | ca->ai_flags = ai->ai_flags;
|
---|
352 | ca->ai_family = ai->ai_family;
|
---|
353 | ca->ai_socktype = ai->ai_socktype;
|
---|
354 | ca->ai_protocol = ai->ai_protocol;
|
---|
355 | ca->ai_addrlen = (curl_socklen_t)ss_size;
|
---|
356 | ca->ai_addr = NULL;
|
---|
357 | ca->ai_canonname = NULL;
|
---|
358 | ca->ai_next = NULL;
|
---|
359 |
|
---|
360 | ca->ai_addr = (void *)((char *)ca + sizeof(struct Curl_addrinfo));
|
---|
361 | memcpy(ca->ai_addr, ai->ai_addr, ss_size);
|
---|
362 |
|
---|
363 | if(namelen) {
|
---|
364 | size_t i;
|
---|
365 | ca->ai_canonname = (void *)((char *)ca->ai_addr + ss_size);
|
---|
366 | for(i = 0; i < namelen; ++i) /* convert wide string to ascii */
|
---|
367 | ca->ai_canonname[i] = (char)ai->ai_canonname[i];
|
---|
368 | ca->ai_canonname[namelen] = '\0';
|
---|
369 | }
|
---|
370 |
|
---|
371 | /* if the return list is empty, this becomes the first element */
|
---|
372 | if(!cafirst)
|
---|
373 | cafirst = ca;
|
---|
374 |
|
---|
375 | /* add this element last in the return list */
|
---|
376 | if(calast)
|
---|
377 | calast->ai_next = ca;
|
---|
378 | calast = ca;
|
---|
379 | }
|
---|
380 |
|
---|
381 | /* if we failed, also destroy the Curl_addrinfo list */
|
---|
382 | if(error) {
|
---|
383 | Curl_freeaddrinfo(cafirst);
|
---|
384 | cafirst = NULL;
|
---|
385 | }
|
---|
386 | else if(!cafirst) {
|
---|
387 | #ifdef EAI_NONAME
|
---|
388 | /* rfc3493 conformant */
|
---|
389 | error = EAI_NONAME;
|
---|
390 | #else
|
---|
391 | /* rfc3493 obsoleted */
|
---|
392 | error = EAI_NODATA;
|
---|
393 | #endif
|
---|
394 | #ifdef USE_WINSOCK
|
---|
395 | SET_SOCKERRNO(error);
|
---|
396 | #endif
|
---|
397 | }
|
---|
398 | tsd->res = cafirst;
|
---|
399 | }
|
---|
400 |
|
---|
401 | if(tsd->w8.res) {
|
---|
402 | Curl_FreeAddrInfoExW(tsd->w8.res);
|
---|
403 | tsd->w8.res = NULL;
|
---|
404 | }
|
---|
405 |
|
---|
406 | if(error) {
|
---|
407 | tsd->sock_error = SOCKERRNO?SOCKERRNO:error;
|
---|
408 | if(tsd->sock_error == 0)
|
---|
409 | tsd->sock_error = RESOLVER_ENOMEM;
|
---|
410 | }
|
---|
411 | else {
|
---|
412 | Curl_addrinfo_set_port(tsd->res, tsd->port);
|
---|
413 | }
|
---|
414 |
|
---|
415 | Curl_mutex_acquire(tsd->mtx);
|
---|
416 | if(tsd->done) {
|
---|
417 | /* too late, gotta clean up the mess */
|
---|
418 | Curl_mutex_release(tsd->mtx);
|
---|
419 | destroy_thread_sync_data(tsd);
|
---|
420 | free(td);
|
---|
421 | }
|
---|
422 | else {
|
---|
423 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
424 | char buf[1];
|
---|
425 | if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
|
---|
426 | /* DNS has been resolved, signal client task */
|
---|
427 | buf[0] = 1;
|
---|
428 | if(swrite(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
|
---|
429 | /* update sock_erro to errno */
|
---|
430 | tsd->sock_error = SOCKERRNO;
|
---|
431 | }
|
---|
432 | }
|
---|
433 | #endif
|
---|
434 | tsd->done = 1;
|
---|
435 | Curl_mutex_release(tsd->mtx);
|
---|
436 | if(td->complete_ev)
|
---|
437 | SetEvent(td->complete_ev); /* Notify caller that the query completed */
|
---|
438 | }
|
---|
439 | }
|
---|
440 | #endif
|
---|
441 |
|
---|
442 | #ifdef HAVE_GETADDRINFO
|
---|
443 |
|
---|
444 | /*
|
---|
445 | * getaddrinfo_thread() resolves a name and then exits.
|
---|
446 | *
|
---|
447 | * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
|
---|
448 | * and wait on it.
|
---|
449 | */
|
---|
450 | static unsigned int CURL_STDCALL getaddrinfo_thread(void *arg)
|
---|
451 | {
|
---|
452 | struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
|
---|
453 | struct thread_data *td = tsd->td;
|
---|
454 | char service[12];
|
---|
455 | int rc;
|
---|
456 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
457 | char buf[1];
|
---|
458 | #endif
|
---|
459 |
|
---|
460 | msnprintf(service, sizeof(service), "%d", tsd->port);
|
---|
461 |
|
---|
462 | rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res);
|
---|
463 |
|
---|
464 | if(rc) {
|
---|
465 | tsd->sock_error = SOCKERRNO?SOCKERRNO:rc;
|
---|
466 | if(tsd->sock_error == 0)
|
---|
467 | tsd->sock_error = RESOLVER_ENOMEM;
|
---|
468 | }
|
---|
469 | else {
|
---|
470 | Curl_addrinfo_set_port(tsd->res, tsd->port);
|
---|
471 | }
|
---|
472 |
|
---|
473 | Curl_mutex_acquire(tsd->mtx);
|
---|
474 | if(tsd->done) {
|
---|
475 | /* too late, gotta clean up the mess */
|
---|
476 | Curl_mutex_release(tsd->mtx);
|
---|
477 | destroy_thread_sync_data(tsd);
|
---|
478 | free(td);
|
---|
479 | }
|
---|
480 | else {
|
---|
481 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
482 | if(tsd->sock_pair[1] != CURL_SOCKET_BAD) {
|
---|
483 | /* DNS has been resolved, signal client task */
|
---|
484 | buf[0] = 1;
|
---|
485 | if(wakeup_write(tsd->sock_pair[1], buf, sizeof(buf)) < 0) {
|
---|
486 | /* update sock_erro to errno */
|
---|
487 | tsd->sock_error = SOCKERRNO;
|
---|
488 | }
|
---|
489 | }
|
---|
490 | #endif
|
---|
491 | tsd->done = 1;
|
---|
492 | Curl_mutex_release(tsd->mtx);
|
---|
493 | }
|
---|
494 |
|
---|
495 | return 0;
|
---|
496 | }
|
---|
497 |
|
---|
498 | #else /* HAVE_GETADDRINFO */
|
---|
499 |
|
---|
500 | /*
|
---|
501 | * gethostbyname_thread() resolves a name and then exits.
|
---|
502 | */
|
---|
503 | static unsigned int CURL_STDCALL gethostbyname_thread(void *arg)
|
---|
504 | {
|
---|
505 | struct thread_sync_data *tsd = (struct thread_sync_data *)arg;
|
---|
506 | struct thread_data *td = tsd->td;
|
---|
507 |
|
---|
508 | tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port);
|
---|
509 |
|
---|
510 | if(!tsd->res) {
|
---|
511 | tsd->sock_error = SOCKERRNO;
|
---|
512 | if(tsd->sock_error == 0)
|
---|
513 | tsd->sock_error = RESOLVER_ENOMEM;
|
---|
514 | }
|
---|
515 |
|
---|
516 | Curl_mutex_acquire(tsd->mtx);
|
---|
517 | if(tsd->done) {
|
---|
518 | /* too late, gotta clean up the mess */
|
---|
519 | Curl_mutex_release(tsd->mtx);
|
---|
520 | destroy_thread_sync_data(tsd);
|
---|
521 | free(td);
|
---|
522 | }
|
---|
523 | else {
|
---|
524 | tsd->done = 1;
|
---|
525 | Curl_mutex_release(tsd->mtx);
|
---|
526 | }
|
---|
527 |
|
---|
528 | return 0;
|
---|
529 | }
|
---|
530 |
|
---|
531 | #endif /* HAVE_GETADDRINFO */
|
---|
532 |
|
---|
533 | /*
|
---|
534 | * destroy_async_data() cleans up async resolver data and thread handle.
|
---|
535 | */
|
---|
536 | static void destroy_async_data(struct Curl_async *async)
|
---|
537 | {
|
---|
538 | if(async->tdata) {
|
---|
539 | struct thread_data *td = async->tdata;
|
---|
540 | int done;
|
---|
541 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
542 | curl_socket_t sock_rd = td->tsd.sock_pair[0];
|
---|
543 | struct Curl_easy *data = td->tsd.data;
|
---|
544 | #endif
|
---|
545 |
|
---|
546 | /*
|
---|
547 | * if the thread is still blocking in the resolve syscall, detach it and
|
---|
548 | * let the thread do the cleanup...
|
---|
549 | */
|
---|
550 | Curl_mutex_acquire(td->tsd.mtx);
|
---|
551 | done = td->tsd.done;
|
---|
552 | td->tsd.done = 1;
|
---|
553 | Curl_mutex_release(td->tsd.mtx);
|
---|
554 |
|
---|
555 | if(!done) {
|
---|
556 | #ifdef _WIN32
|
---|
557 | if(td->complete_ev)
|
---|
558 | CloseHandle(td->complete_ev);
|
---|
559 | else
|
---|
560 | #endif
|
---|
561 | Curl_thread_destroy(td->thread_hnd);
|
---|
562 | }
|
---|
563 | else {
|
---|
564 | #ifdef _WIN32
|
---|
565 | if(td->complete_ev) {
|
---|
566 | Curl_GetAddrInfoExCancel(&td->tsd.w8.cancel_ev);
|
---|
567 | WaitForSingleObject(td->complete_ev, INFINITE);
|
---|
568 | CloseHandle(td->complete_ev);
|
---|
569 | }
|
---|
570 | #endif
|
---|
571 | if(td->thread_hnd != curl_thread_t_null)
|
---|
572 | Curl_thread_join(&td->thread_hnd);
|
---|
573 |
|
---|
574 | destroy_thread_sync_data(&td->tsd);
|
---|
575 |
|
---|
576 | free(async->tdata);
|
---|
577 | }
|
---|
578 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
579 | /*
|
---|
580 | * ensure CURLMOPT_SOCKETFUNCTION fires CURL_POLL_REMOVE
|
---|
581 | * before the FD is invalidated to avoid EBADF on EPOLL_CTL_DEL
|
---|
582 | */
|
---|
583 | Curl_multi_closed(data, sock_rd);
|
---|
584 | wakeup_close(sock_rd);
|
---|
585 | #endif
|
---|
586 | }
|
---|
587 | async->tdata = NULL;
|
---|
588 |
|
---|
589 | free(async->hostname);
|
---|
590 | async->hostname = NULL;
|
---|
591 | }
|
---|
592 |
|
---|
593 | /*
|
---|
594 | * init_resolve_thread() starts a new thread that performs the actual
|
---|
595 | * resolve. This function returns before the resolve is done.
|
---|
596 | *
|
---|
597 | * Returns FALSE in case of failure, otherwise TRUE.
|
---|
598 | */
|
---|
599 | static bool init_resolve_thread(struct Curl_easy *data,
|
---|
600 | const char *hostname, int port,
|
---|
601 | const struct addrinfo *hints)
|
---|
602 | {
|
---|
603 | struct thread_data *td = calloc(1, sizeof(struct thread_data));
|
---|
604 | int err = ENOMEM;
|
---|
605 | struct Curl_async *asp = &data->state.async;
|
---|
606 |
|
---|
607 | data->state.async.tdata = td;
|
---|
608 | if(!td)
|
---|
609 | goto errno_exit;
|
---|
610 |
|
---|
611 | asp->port = port;
|
---|
612 | asp->done = FALSE;
|
---|
613 | asp->status = 0;
|
---|
614 | asp->dns = NULL;
|
---|
615 | td->thread_hnd = curl_thread_t_null;
|
---|
616 | #ifdef _WIN32
|
---|
617 | td->complete_ev = NULL;
|
---|
618 | #endif
|
---|
619 |
|
---|
620 | if(!init_thread_sync_data(td, hostname, port, hints)) {
|
---|
621 | asp->tdata = NULL;
|
---|
622 | free(td);
|
---|
623 | goto errno_exit;
|
---|
624 | }
|
---|
625 |
|
---|
626 | free(asp->hostname);
|
---|
627 | asp->hostname = strdup(hostname);
|
---|
628 | if(!asp->hostname)
|
---|
629 | goto err_exit;
|
---|
630 |
|
---|
631 | /* The thread will set this to 1 when complete. */
|
---|
632 | td->tsd.done = 0;
|
---|
633 |
|
---|
634 | #ifdef _WIN32
|
---|
635 | if(Curl_isWindows8OrGreater && Curl_FreeAddrInfoExW &&
|
---|
636 | Curl_GetAddrInfoExCancel && Curl_GetAddrInfoExW) {
|
---|
637 | #define MAX_NAME_LEN 256 /* max domain name is 253 chars */
|
---|
638 | #define MAX_PORT_LEN 8
|
---|
639 | WCHAR namebuf[MAX_NAME_LEN];
|
---|
640 | WCHAR portbuf[MAX_PORT_LEN];
|
---|
641 | /* calculate required length */
|
---|
642 | int w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, hostname,
|
---|
643 | -1, NULL, 0);
|
---|
644 | if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
|
---|
645 | /* do utf8 conversion */
|
---|
646 | w_len = MultiByteToWideChar(CP_UTF8, 0, hostname, -1, namebuf, w_len);
|
---|
647 | if((w_len > 0) && (w_len < MAX_NAME_LEN)) {
|
---|
648 | swprintf(portbuf, MAX_PORT_LEN, L"%d", port);
|
---|
649 | td->tsd.w8.hints.ai_family = hints->ai_family;
|
---|
650 | td->tsd.w8.hints.ai_socktype = hints->ai_socktype;
|
---|
651 | td->complete_ev = CreateEvent(NULL, TRUE, FALSE, NULL);
|
---|
652 | if(!td->complete_ev) {
|
---|
653 | /* failed to start, mark it as done here for proper cleanup. */
|
---|
654 | td->tsd.done = 1;
|
---|
655 | goto err_exit;
|
---|
656 | }
|
---|
657 | err = Curl_GetAddrInfoExW(namebuf, portbuf, NS_DNS,
|
---|
658 | NULL, &td->tsd.w8.hints, &td->tsd.w8.res,
|
---|
659 | NULL, &td->tsd.w8.overlapped,
|
---|
660 | &query_complete, &td->tsd.w8.cancel_ev);
|
---|
661 | if(err != WSA_IO_PENDING)
|
---|
662 | query_complete(err, 0, &td->tsd.w8.overlapped);
|
---|
663 | return TRUE;
|
---|
664 | }
|
---|
665 | }
|
---|
666 | }
|
---|
667 | #endif
|
---|
668 |
|
---|
669 | #ifdef HAVE_GETADDRINFO
|
---|
670 | td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
|
---|
671 | #else
|
---|
672 | td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
|
---|
673 | #endif
|
---|
674 |
|
---|
675 | if(!td->thread_hnd) {
|
---|
676 | /* The thread never started, so mark it as done here for proper cleanup. */
|
---|
677 | td->tsd.done = 1;
|
---|
678 | err = errno;
|
---|
679 | goto err_exit;
|
---|
680 | }
|
---|
681 |
|
---|
682 | return TRUE;
|
---|
683 |
|
---|
684 | err_exit:
|
---|
685 | destroy_async_data(asp);
|
---|
686 |
|
---|
687 | errno_exit:
|
---|
688 | errno = err;
|
---|
689 | return FALSE;
|
---|
690 | }
|
---|
691 |
|
---|
692 | /*
|
---|
693 | * 'entry' may be NULL and then no data is returned
|
---|
694 | */
|
---|
695 | static CURLcode thread_wait_resolv(struct Curl_easy *data,
|
---|
696 | struct Curl_dns_entry **entry,
|
---|
697 | bool report)
|
---|
698 | {
|
---|
699 | struct thread_data *td;
|
---|
700 | CURLcode result = CURLE_OK;
|
---|
701 |
|
---|
702 | DEBUGASSERT(data);
|
---|
703 | td = data->state.async.tdata;
|
---|
704 | DEBUGASSERT(td);
|
---|
705 | #ifdef _WIN32
|
---|
706 | DEBUGASSERT(td->complete_ev || td->thread_hnd != curl_thread_t_null);
|
---|
707 | #else
|
---|
708 | DEBUGASSERT(td->thread_hnd != curl_thread_t_null);
|
---|
709 | #endif
|
---|
710 |
|
---|
711 | /* wait for the thread to resolve the name */
|
---|
712 | #ifdef _WIN32
|
---|
713 | if(td->complete_ev) {
|
---|
714 | WaitForSingleObject(td->complete_ev, INFINITE);
|
---|
715 | CloseHandle(td->complete_ev);
|
---|
716 | if(entry)
|
---|
717 | result = getaddrinfo_complete(data);
|
---|
718 | }
|
---|
719 | else
|
---|
720 | #endif
|
---|
721 | if(Curl_thread_join(&td->thread_hnd)) {
|
---|
722 | if(entry)
|
---|
723 | result = getaddrinfo_complete(data);
|
---|
724 | }
|
---|
725 | else
|
---|
726 | DEBUGASSERT(0);
|
---|
727 |
|
---|
728 | data->state.async.done = TRUE;
|
---|
729 |
|
---|
730 | if(entry)
|
---|
731 | *entry = data->state.async.dns;
|
---|
732 |
|
---|
733 | if(!data->state.async.dns && report)
|
---|
734 | /* a name was not resolved, report error */
|
---|
735 | result = Curl_resolver_error(data);
|
---|
736 |
|
---|
737 | destroy_async_data(&data->state.async);
|
---|
738 |
|
---|
739 | if(!data->state.async.dns && report)
|
---|
740 | connclose(data->conn, "asynch resolve failed");
|
---|
741 |
|
---|
742 | return result;
|
---|
743 | }
|
---|
744 |
|
---|
745 |
|
---|
746 | /*
|
---|
747 | * Until we gain a way to signal the resolver threads to stop early, we must
|
---|
748 | * simply wait for them and ignore their results.
|
---|
749 | */
|
---|
750 | void Curl_resolver_kill(struct Curl_easy *data)
|
---|
751 | {
|
---|
752 | struct thread_data *td = data->state.async.tdata;
|
---|
753 |
|
---|
754 | /* If we're still resolving, we must wait for the threads to fully clean up,
|
---|
755 | unfortunately. Otherwise, we can simply cancel to clean up any resolver
|
---|
756 | data. */
|
---|
757 | if(td && td->thread_hnd != curl_thread_t_null
|
---|
758 | && (data->set.quick_exit != 1L))
|
---|
759 | (void)thread_wait_resolv(data, NULL, FALSE);
|
---|
760 | else
|
---|
761 | Curl_resolver_cancel(data);
|
---|
762 | }
|
---|
763 |
|
---|
764 | /*
|
---|
765 | * Curl_resolver_wait_resolv()
|
---|
766 | *
|
---|
767 | * Waits for a resolve to finish. This function should be avoided since using
|
---|
768 | * this risk getting the multi interface to "hang".
|
---|
769 | *
|
---|
770 | * If 'entry' is non-NULL, make it point to the resolved dns entry
|
---|
771 | *
|
---|
772 | * Returns CURLE_COULDNT_RESOLVE_HOST if the host was not resolved,
|
---|
773 | * CURLE_OPERATION_TIMEDOUT if a time-out occurred, or other errors.
|
---|
774 | *
|
---|
775 | * This is the version for resolves-in-a-thread.
|
---|
776 | */
|
---|
777 | CURLcode Curl_resolver_wait_resolv(struct Curl_easy *data,
|
---|
778 | struct Curl_dns_entry **entry)
|
---|
779 | {
|
---|
780 | return thread_wait_resolv(data, entry, TRUE);
|
---|
781 | }
|
---|
782 |
|
---|
783 | /*
|
---|
784 | * Curl_resolver_is_resolved() is called repeatedly to check if a previous
|
---|
785 | * name resolve request has completed. It should also make sure to time-out if
|
---|
786 | * the operation seems to take too long.
|
---|
787 | */
|
---|
788 | CURLcode Curl_resolver_is_resolved(struct Curl_easy *data,
|
---|
789 | struct Curl_dns_entry **entry)
|
---|
790 | {
|
---|
791 | struct thread_data *td = data->state.async.tdata;
|
---|
792 | int done = 0;
|
---|
793 |
|
---|
794 | DEBUGASSERT(entry);
|
---|
795 | *entry = NULL;
|
---|
796 |
|
---|
797 | if(!td) {
|
---|
798 | DEBUGASSERT(td);
|
---|
799 | return CURLE_COULDNT_RESOLVE_HOST;
|
---|
800 | }
|
---|
801 |
|
---|
802 | Curl_mutex_acquire(td->tsd.mtx);
|
---|
803 | done = td->tsd.done;
|
---|
804 | Curl_mutex_release(td->tsd.mtx);
|
---|
805 |
|
---|
806 | if(done) {
|
---|
807 | getaddrinfo_complete(data);
|
---|
808 |
|
---|
809 | if(!data->state.async.dns) {
|
---|
810 | CURLcode result = Curl_resolver_error(data);
|
---|
811 | destroy_async_data(&data->state.async);
|
---|
812 | return result;
|
---|
813 | }
|
---|
814 | destroy_async_data(&data->state.async);
|
---|
815 | *entry = data->state.async.dns;
|
---|
816 | }
|
---|
817 | else {
|
---|
818 | /* poll for name lookup done with exponential backoff up to 250ms */
|
---|
819 | /* should be fine even if this converts to 32 bit */
|
---|
820 | timediff_t elapsed = Curl_timediff(Curl_now(),
|
---|
821 | data->progress.t_startsingle);
|
---|
822 | if(elapsed < 0)
|
---|
823 | elapsed = 0;
|
---|
824 |
|
---|
825 | if(td->poll_interval == 0)
|
---|
826 | /* Start at 1ms poll interval */
|
---|
827 | td->poll_interval = 1;
|
---|
828 | else if(elapsed >= td->interval_end)
|
---|
829 | /* Back-off exponentially if last interval expired */
|
---|
830 | td->poll_interval *= 2;
|
---|
831 |
|
---|
832 | if(td->poll_interval > 250)
|
---|
833 | td->poll_interval = 250;
|
---|
834 |
|
---|
835 | td->interval_end = elapsed + td->poll_interval;
|
---|
836 | Curl_expire(data, td->poll_interval, EXPIRE_ASYNC_NAME);
|
---|
837 | }
|
---|
838 |
|
---|
839 | return CURLE_OK;
|
---|
840 | }
|
---|
841 |
|
---|
842 | int Curl_resolver_getsock(struct Curl_easy *data, curl_socket_t *socks)
|
---|
843 | {
|
---|
844 | int ret_val = 0;
|
---|
845 | timediff_t milli;
|
---|
846 | timediff_t ms;
|
---|
847 | struct resdata *reslv = (struct resdata *)data->state.async.resolver;
|
---|
848 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
849 | struct thread_data *td = data->state.async.tdata;
|
---|
850 | #else
|
---|
851 | (void)socks;
|
---|
852 | #endif
|
---|
853 |
|
---|
854 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
855 | if(td) {
|
---|
856 | /* return read fd to client for polling the DNS resolution status */
|
---|
857 | socks[0] = td->tsd.sock_pair[0];
|
---|
858 | td->tsd.data = data;
|
---|
859 | ret_val = GETSOCK_READSOCK(0);
|
---|
860 | }
|
---|
861 | else {
|
---|
862 | #endif
|
---|
863 | ms = Curl_timediff(Curl_now(), reslv->start);
|
---|
864 | if(ms < 3)
|
---|
865 | milli = 0;
|
---|
866 | else if(ms <= 50)
|
---|
867 | milli = ms/3;
|
---|
868 | else if(ms <= 250)
|
---|
869 | milli = 50;
|
---|
870 | else
|
---|
871 | milli = 200;
|
---|
872 | Curl_expire(data, milli, EXPIRE_ASYNC_NAME);
|
---|
873 | #ifndef CURL_DISABLE_SOCKETPAIR
|
---|
874 | }
|
---|
875 | #endif
|
---|
876 |
|
---|
877 |
|
---|
878 | return ret_val;
|
---|
879 | }
|
---|
880 |
|
---|
881 | #ifndef HAVE_GETADDRINFO
|
---|
882 | /*
|
---|
883 | * Curl_getaddrinfo() - for platforms without getaddrinfo
|
---|
884 | */
|
---|
885 | struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
---|
886 | const char *hostname,
|
---|
887 | int port,
|
---|
888 | int *waitp)
|
---|
889 | {
|
---|
890 | struct resdata *reslv = (struct resdata *)data->state.async.resolver;
|
---|
891 |
|
---|
892 | *waitp = 0; /* default to synchronous response */
|
---|
893 |
|
---|
894 | reslv->start = Curl_now();
|
---|
895 |
|
---|
896 | /* fire up a new resolver thread! */
|
---|
897 | if(init_resolve_thread(data, hostname, port, NULL)) {
|
---|
898 | *waitp = 1; /* expect asynchronous response */
|
---|
899 | return NULL;
|
---|
900 | }
|
---|
901 |
|
---|
902 | failf(data, "getaddrinfo() thread failed");
|
---|
903 |
|
---|
904 | return NULL;
|
---|
905 | }
|
---|
906 |
|
---|
907 | #else /* !HAVE_GETADDRINFO */
|
---|
908 |
|
---|
909 | /*
|
---|
910 | * Curl_resolver_getaddrinfo() - for getaddrinfo
|
---|
911 | */
|
---|
912 | struct Curl_addrinfo *Curl_resolver_getaddrinfo(struct Curl_easy *data,
|
---|
913 | const char *hostname,
|
---|
914 | int port,
|
---|
915 | int *waitp)
|
---|
916 | {
|
---|
917 | struct addrinfo hints;
|
---|
918 | int pf = PF_INET;
|
---|
919 | struct resdata *reslv = (struct resdata *)data->state.async.resolver;
|
---|
920 |
|
---|
921 | *waitp = 0; /* default to synchronous response */
|
---|
922 |
|
---|
923 | #ifdef CURLRES_IPV6
|
---|
924 | if((data->conn->ip_version != CURL_IPRESOLVE_V4) && Curl_ipv6works(data)) {
|
---|
925 | /* The stack seems to be IPv6-enabled */
|
---|
926 | if(data->conn->ip_version == CURL_IPRESOLVE_V6)
|
---|
927 | pf = PF_INET6;
|
---|
928 | else
|
---|
929 | pf = PF_UNSPEC;
|
---|
930 | }
|
---|
931 | #endif /* CURLRES_IPV6 */
|
---|
932 |
|
---|
933 | memset(&hints, 0, sizeof(hints));
|
---|
934 | hints.ai_family = pf;
|
---|
935 | hints.ai_socktype = (data->conn->transport == TRNSPRT_TCP)?
|
---|
936 | SOCK_STREAM : SOCK_DGRAM;
|
---|
937 |
|
---|
938 | reslv->start = Curl_now();
|
---|
939 | /* fire up a new resolver thread! */
|
---|
940 | if(init_resolve_thread(data, hostname, port, &hints)) {
|
---|
941 | *waitp = 1; /* expect asynchronous response */
|
---|
942 | return NULL;
|
---|
943 | }
|
---|
944 |
|
---|
945 | failf(data, "getaddrinfo() thread failed to start");
|
---|
946 | return NULL;
|
---|
947 |
|
---|
948 | }
|
---|
949 |
|
---|
950 | #endif /* !HAVE_GETADDRINFO */
|
---|
951 |
|
---|
952 | CURLcode Curl_set_dns_servers(struct Curl_easy *data,
|
---|
953 | char *servers)
|
---|
954 | {
|
---|
955 | (void)data;
|
---|
956 | (void)servers;
|
---|
957 | return CURLE_NOT_BUILT_IN;
|
---|
958 |
|
---|
959 | }
|
---|
960 |
|
---|
961 | CURLcode Curl_set_dns_interface(struct Curl_easy *data,
|
---|
962 | const char *interf)
|
---|
963 | {
|
---|
964 | (void)data;
|
---|
965 | (void)interf;
|
---|
966 | return CURLE_NOT_BUILT_IN;
|
---|
967 | }
|
---|
968 |
|
---|
969 | CURLcode Curl_set_dns_local_ip4(struct Curl_easy *data,
|
---|
970 | const char *local_ip4)
|
---|
971 | {
|
---|
972 | (void)data;
|
---|
973 | (void)local_ip4;
|
---|
974 | return CURLE_NOT_BUILT_IN;
|
---|
975 | }
|
---|
976 |
|
---|
977 | CURLcode Curl_set_dns_local_ip6(struct Curl_easy *data,
|
---|
978 | const char *local_ip6)
|
---|
979 | {
|
---|
980 | (void)data;
|
---|
981 | (void)local_ip6;
|
---|
982 | return CURLE_NOT_BUILT_IN;
|
---|
983 | }
|
---|
984 |
|
---|
985 | #endif /* CURLRES_THREADED */
|
---|