1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2008, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: hostthre.c,v 1.1.1.1 2008-09-23 16:32:05 hoffman Exp $
22 ***************************************************************************/
32 #ifdef HAVE_SYS_SOCKET_H
33 #include <sys/socket.h>
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
41 #ifdef HAVE_ARPA_INET_H
42 #include <arpa/inet.h>
45 #include <stdlib.h> /* required for free() prototypes */
48 #include <unistd.h> /* for the close() proto */
64 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
66 #define in_addr_t unsigned long
78 #define _MPRINTF_REPLACE /* use our functions only */
79 #include <curl/mprintf.h>
81 #include "inet_ntop.h"
84 /* The last #include file should be: */
87 #if defined(_MSC_VER) && defined(CURL_NO__BEGINTHREADEX)
88 #pragma message ("No _beginthreadex() available in this RTL")
91 /***********************************************************************
92 * Only for Windows threaded name resolves builds
93 **********************************************************************/
94 #ifdef CURLRES_THREADED
96 /* This function is used to init a threaded resolve */
97 static bool init_resolve_thread(struct connectdata
*conn
,
98 const char *hostname
, int port
,
99 const Curl_addrinfo
*hints
);
102 #define THREAD_FUNC gethostbyname_thread
103 #define THREAD_NAME "gethostbyname_thread"
105 #define THREAD_FUNC getaddrinfo_thread
106 #define THREAD_NAME "getaddrinfo_thread"
113 curl_socket_t dummy_sock
; /* dummy for Curl_resolv_fdset() */
114 HANDLE mutex_waiting
; /* marks that we are still waiting for a resolve */
115 HANDLE event_resolved
; /* marks that the thread obtained the information */
116 HANDLE event_thread_started
; /* marks that the thread has initialized and
118 HANDLE mutex_terminate
; /* serializes access to flag_terminate */
119 HANDLE event_terminate
; /* flag for thread to terminate instead of calling
122 struct addrinfo hints
;
126 /* Data for synchronization between resolver thread and its parent */
127 struct thread_sync_data
{
128 HANDLE mutex_waiting
; /* thread_data.mutex_waiting duplicate */
129 HANDLE mutex_terminate
; /* thread_data.mutex_terminate duplicate */
130 HANDLE event_terminate
; /* thread_data.event_terminate duplicate */
131 char * hostname
; /* hostname to resolve, Curl_async.hostname
135 /* Destroy resolver thread synchronization data */
137 void destroy_thread_sync_data(struct thread_sync_data
* tsd
)
141 if(tsd
->event_terminate
)
142 CloseHandle(tsd
->event_terminate
);
143 if(tsd
->mutex_terminate
)
144 CloseHandle(tsd
->mutex_terminate
);
145 if(tsd
->mutex_waiting
)
146 CloseHandle(tsd
->mutex_waiting
);
147 memset(tsd
,0,sizeof(*tsd
));
150 /* Initialize resolver thread synchronization data */
152 BOOL
init_thread_sync_data(struct thread_data
* td
,
153 const char * hostname
,
154 struct thread_sync_data
* tsd
)
156 HANDLE curr_proc
= GetCurrentProcess();
158 memset(tsd
, 0, sizeof(*tsd
));
159 if(!DuplicateHandle(curr_proc
, td
->mutex_waiting
,
160 curr_proc
, &tsd
->mutex_waiting
, 0, FALSE
,
161 DUPLICATE_SAME_ACCESS
)) {
162 /* failed to duplicate the mutex, no point in continuing */
163 destroy_thread_sync_data(tsd
);
166 if(!DuplicateHandle(curr_proc
, td
->mutex_terminate
,
167 curr_proc
, &tsd
->mutex_terminate
, 0, FALSE
,
168 DUPLICATE_SAME_ACCESS
)) {
169 /* failed to duplicate the mutex, no point in continuing */
170 destroy_thread_sync_data(tsd
);
173 if(!DuplicateHandle(curr_proc
, td
->event_terminate
,
174 curr_proc
, &tsd
->event_terminate
, 0, FALSE
,
175 DUPLICATE_SAME_ACCESS
)) {
176 /* failed to duplicate the event, no point in continuing */
177 destroy_thread_sync_data(tsd
);
180 /* Copying hostname string because original can be destroyed by parent
181 * thread during gethostbyname execution.
183 tsd
->hostname
= strdup(hostname
);
185 /* Memory allocation failed */
186 destroy_thread_sync_data(tsd
);
192 /* acquire resolver thread synchronization */
194 BOOL
acquire_thread_sync(struct thread_sync_data
* tsd
)
196 /* is the thread initiator still waiting for us ? */
197 if(WaitForSingleObject(tsd
->mutex_waiting
, 0) == WAIT_TIMEOUT
) {
200 /* Waiting access to event_terminate */
201 if(WaitForSingleObject(tsd
->mutex_terminate
, INFINITE
) != WAIT_OBJECT_0
) {
202 /* Something went wrong - now just ignoring */
205 if(WaitForSingleObject(tsd
->event_terminate
, 0) != WAIT_TIMEOUT
) {
206 /* Parent thread signaled us to terminate.
207 * This means that all data in conn->async is now destroyed
208 * and we cannot use it.
219 /* release resolver thread synchronization */
221 void release_thread_sync(struct thread_sync_data
* tsd
)
223 ReleaseMutex(tsd
->mutex_terminate
);
226 #if defined(CURLRES_IPV4)
228 * gethostbyname_thread() resolves a name, calls the Curl_addrinfo4_callback
231 * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on
234 static unsigned __stdcall
gethostbyname_thread (void *arg
)
236 struct connectdata
*conn
= (struct connectdata
*) arg
;
237 struct thread_data
*td
= (struct thread_data
*) conn
->async
.os_specific
;
241 /* Duplicate the passed mutex and event handles.
242 * This allows us to use it even after the container gets destroyed
243 * due to a resolver timeout.
245 struct thread_sync_data tsd
= { 0,0,0,NULL
};
247 if(!init_thread_sync_data(td
, conn
->async
.hostname
, &tsd
)) {
248 /* thread synchronization data initialization failed */
252 conn
->async
.status
= NO_DATA
; /* pending status */
253 SET_SOCKERRNO(conn
->async
.status
);
255 /* Signaling that we have initialized all copies of data and handles we
257 SetEvent(td
->event_thread_started
);
259 he
= gethostbyname (tsd
.hostname
);
261 /* is parent thread waiting for us and are we able to access conn members? */
262 if(acquire_thread_sync(&tsd
)) {
263 /* Mark that we have obtained the information, and that we are calling
265 SetEvent(td
->event_resolved
);
267 rc
= Curl_addrinfo4_callback(conn
, CURL_ASYNC_SUCCESS
, he
);
270 rc
= Curl_addrinfo4_callback(conn
, SOCKERRNO
, NULL
);
272 release_thread_sync(&tsd
);
276 destroy_thread_sync_data(&tsd
);
279 /* An implicit _endthreadex() here */
282 #elif defined(CURLRES_IPV6)
285 * getaddrinfo_thread() resolves a name, calls Curl_addrinfo6_callback and then
288 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
291 static unsigned __stdcall
getaddrinfo_thread (void *arg
)
293 struct connectdata
*conn
= (struct connectdata
*) arg
;
294 struct thread_data
*td
= (struct thread_data
*) conn
->async
.os_specific
;
295 struct addrinfo
*res
;
296 char service
[NI_MAXSERV
];
298 struct addrinfo hints
= td
->hints
;
300 /* Duplicate the passed mutex handle.
301 * This allows us to use it even after the container gets destroyed
302 * due to a resolver timeout.
304 struct thread_sync_data tsd
= { 0,0,0,NULL
};
306 if(!init_thread_sync_data(td
, conn
->async
.hostname
, &tsd
)) {
307 /* thread synchronization data initialization failed */
311 itoa(conn
->async
.port
, service
, 10);
313 conn
->async
.status
= NO_DATA
; /* pending status */
314 SET_SOCKERRNO(conn
->async
.status
);
316 /* Signaling that we have initialized all copies of data and handles we
318 SetEvent(td
->event_thread_started
);
320 rc
= getaddrinfo(tsd
.hostname
, service
, &hints
, &res
);
322 /* is parent thread waiting for us and are we able to access conn members? */
323 if(acquire_thread_sync(&tsd
)) {
324 /* Mark that we have obtained the information, and that we are calling
326 SetEvent(td
->event_resolved
);
329 rc
= Curl_addrinfo6_callback(conn
, CURL_ASYNC_SUCCESS
, res
);
332 rc
= Curl_addrinfo6_callback(conn
, SOCKERRNO
, NULL
);
334 release_thread_sync(&tsd
);
338 destroy_thread_sync_data(&tsd
);
341 /* An implicit _endthreadex() here */
346 * Curl_destroy_thread_data() cleans up async resolver data and thread handle.
347 * Complementary of ares_destroy.
349 void Curl_destroy_thread_data (struct Curl_async
*async
)
352 free(async
->hostname
);
354 if(async
->os_specific
) {
355 struct thread_data
*td
= (struct thread_data
*) async
->os_specific
;
356 curl_socket_t sock
= td
->dummy_sock
;
358 if(td
->mutex_terminate
&& td
->event_terminate
) {
359 /* Signaling resolver thread to terminate */
360 if(WaitForSingleObject(td
->mutex_terminate
, INFINITE
) == WAIT_OBJECT_0
) {
361 SetEvent(td
->event_terminate
);
362 ReleaseMutex(td
->mutex_terminate
);
365 /* Something went wrong - just ignoring it */
369 if(td
->mutex_terminate
)
370 CloseHandle(td
->mutex_terminate
);
371 if(td
->event_terminate
)
372 CloseHandle(td
->event_terminate
);
373 if(td
->event_thread_started
)
374 CloseHandle(td
->event_thread_started
);
376 if(sock
!= CURL_SOCKET_BAD
)
379 /* destroy the synchronization objects */
380 if(td
->mutex_waiting
)
381 CloseHandle(td
->mutex_waiting
);
382 td
->mutex_waiting
= NULL
;
383 if(td
->event_resolved
)
384 CloseHandle(td
->event_resolved
);
387 CloseHandle(td
->thread_hnd
);
389 free(async
->os_specific
);
391 async
->hostname
= NULL
;
392 async
->os_specific
= NULL
;
396 * init_resolve_thread() starts a new thread that performs the actual
397 * resolve. This function returns before the resolve is done.
399 * Returns FALSE in case of failure, otherwise TRUE.
401 static bool init_resolve_thread (struct connectdata
*conn
,
402 const char *hostname
, int port
,
403 const Curl_addrinfo
*hints
)
405 struct thread_data
*td
= calloc(sizeof(*td
), 1);
406 HANDLE thread_and_event
[2] = {0};
413 Curl_safefree(conn
->async
.hostname
);
414 conn
->async
.hostname
= strdup(hostname
);
415 if(!conn
->async
.hostname
) {
421 conn
->async
.port
= port
;
422 conn
->async
.done
= FALSE
;
423 conn
->async
.status
= 0;
424 conn
->async
.dns
= NULL
;
425 conn
->async
.os_specific
= (void*) td
;
426 td
->dummy_sock
= CURL_SOCKET_BAD
;
428 /* Create the mutex used to inform the resolver thread that we're
429 * still waiting, and take initial ownership.
431 td
->mutex_waiting
= CreateMutex(NULL
, TRUE
, NULL
);
432 if(td
->mutex_waiting
== NULL
) {
433 Curl_destroy_thread_data(&conn
->async
);
438 /* Create the event that the thread uses to inform us that it's
439 * done resolving. Do not signal it.
441 td
->event_resolved
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
442 if(td
->event_resolved
== NULL
) {
443 Curl_destroy_thread_data(&conn
->async
);
447 /* Create the mutex used to serialize access to event_terminated
448 * between us and resolver thread.
450 td
->mutex_terminate
= CreateMutex(NULL
, FALSE
, NULL
);
451 if(td
->mutex_terminate
== NULL
) {
452 Curl_destroy_thread_data(&conn
->async
);
456 /* Create the event used to signal thread that it should terminate.
458 td
->event_terminate
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
459 if(td
->event_terminate
== NULL
) {
460 Curl_destroy_thread_data(&conn
->async
);
464 /* Create the event used by thread to inform it has initialized its own data.
466 td
->event_thread_started
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
467 if(td
->event_thread_started
== NULL
) {
468 Curl_destroy_thread_data(&conn
->async
);
474 td
->thread_hnd
= (HANDLE
) CreateThread(NULL
, 0,
475 (LPTHREAD_START_ROUTINE
) THREAD_FUNC
,
476 conn
, 0, &td
->thread_id
);
478 td
->thread_hnd
= (HANDLE
) _beginthreadex(NULL
, 0, THREAD_FUNC
,
479 conn
, 0, &td
->thread_id
);
489 if(!td
->thread_hnd
) {
493 Curl_destroy_thread_data(&conn
->async
);
496 /* Waiting until the thread will initialize its data or it will exit due errors.
498 thread_and_event
[0] = td
->thread_hnd
;
499 thread_and_event
[1] = td
->event_thread_started
;
500 if(WaitForMultipleObjects(sizeof(thread_and_event
) /
501 sizeof(thread_and_event
[0]),
502 (const HANDLE
*)thread_and_event
, FALSE
,
503 INFINITE
) == WAIT_FAILED
) {
504 /* The resolver thread has been created,
505 * most probably it works now - ignoring this "minor" error
508 /* This socket is only to keep Curl_resolv_fdset() and select() happy;
509 * should never become signalled for read/write since it's unbound but
510 * Windows needs atleast 1 socket in select().
512 td
->dummy_sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
518 * Curl_wait_for_resolv() waits for a resolve to finish. This function should
519 * be avoided since using this risk getting the multi interface to "hang".
521 * If 'entry' is non-NULL, make it point to the resolved dns entry
523 * This is the version for resolves-in-a-thread.
525 CURLcode
Curl_wait_for_resolv(struct connectdata
*conn
,
526 struct Curl_dns_entry
**entry
)
528 struct thread_data
*td
= (struct thread_data
*) conn
->async
.os_specific
;
529 struct SessionHandle
*data
= conn
->data
;
534 DEBUGASSERT(conn
&& td
);
536 /* now, see if there's a connect timeout or a regular timeout to
537 use instead of the default one */
539 conn
->data
->set
.connecttimeout
? conn
->data
->set
.connecttimeout
:
540 conn
->data
->set
.timeout
? conn
->data
->set
.timeout
:
541 CURL_TIMEOUT_RESOLVE
* 1000; /* default name resolve timeout */
543 /* wait for the thread to resolve the name */
544 status
= WaitForSingleObject(td
->event_resolved
, timeout
);
546 /* mark that we are now done waiting */
547 ReleaseMutex(td
->mutex_waiting
);
549 /* close our handle to the mutex, no point in hanging on to it */
550 CloseHandle(td
->mutex_waiting
);
551 td
->mutex_waiting
= NULL
;
553 /* close the event handle, it's useless now */
554 CloseHandle(td
->event_resolved
);
555 td
->event_resolved
= NULL
;
557 /* has the resolver thread succeeded in resolving our query ? */
558 if(status
== WAIT_OBJECT_0
) {
559 /* wait for the thread to exit, it's in the callback sequence */
560 if(WaitForSingleObject(td
->thread_hnd
, 5000) == WAIT_TIMEOUT
) {
561 TerminateThread(td
->thread_hnd
, 0);
562 conn
->async
.done
= TRUE
;
563 td
->thread_status
= (DWORD
)-1;
566 /* Thread finished before timeout; propagate Winsock error to this
567 * thread. 'conn->async.done = TRUE' is set in
568 * Curl_addrinfo4/6_callback().
570 SET_SOCKERRNO(conn
->async
.status
);
571 GetExitCodeThread(td
->thread_hnd
, &td
->thread_status
);
575 conn
->async
.done
= TRUE
;
576 td
->thread_status
= (DWORD
)-1;
580 *entry
= conn
->async
.dns
;
584 if(!conn
->async
.dns
) {
585 /* a name was not resolved */
586 if(td
->thread_status
== CURLE_OUT_OF_MEMORY
) {
587 rc
= CURLE_OUT_OF_MEMORY
;
588 failf(data
, "Could not resolve host: %s", curl_easy_strerror(rc
));
590 else if(conn
->async
.done
) {
591 if(conn
->bits
.httpproxy
) {
592 failf(data
, "Could not resolve proxy: %s; %s",
593 conn
->proxy
.dispname
, Curl_strerror(conn
, conn
->async
.status
));
594 rc
= CURLE_COULDNT_RESOLVE_PROXY
;
597 failf(data
, "Could not resolve host: %s; %s",
598 conn
->host
.name
, Curl_strerror(conn
, conn
->async
.status
));
599 rc
= CURLE_COULDNT_RESOLVE_HOST
;
602 else if(td
->thread_status
== (DWORD
)-1 || conn
->async
.status
== NO_DATA
) {
603 failf(data
, "Resolving host timed out: %s", conn
->host
.name
);
604 rc
= CURLE_OPERATION_TIMEDOUT
;
607 rc
= CURLE_OPERATION_TIMEDOUT
;
610 Curl_destroy_thread_data(&conn
->async
);
613 conn
->bits
.close
= TRUE
;
619 * Curl_is_resolved() is called repeatedly to check if a previous name resolve
620 * request has completed. It should also make sure to time-out if the
621 * operation seems to take too long.
623 CURLcode
Curl_is_resolved(struct connectdata
*conn
,
624 struct Curl_dns_entry
**entry
)
626 struct SessionHandle
*data
= conn
->data
;
630 if(conn
->async
.done
) {
632 Curl_destroy_thread_data(&conn
->async
);
633 if(!conn
->async
.dns
) {
634 failf(data
, "Could not resolve host: %s; %s",
635 conn
->host
.name
, Curl_strerror(conn
, conn
->async
.status
));
636 return CURLE_COULDNT_RESOLVE_HOST
;
638 *entry
= conn
->async
.dns
;
643 int Curl_resolv_getsock(struct connectdata
*conn
,
644 curl_socket_t
*socks
,
647 const struct thread_data
*td
=
648 (const struct thread_data
*) conn
->async
.os_specific
;
650 if(td
&& td
->dummy_sock
!= CURL_SOCKET_BAD
) {
652 /* return one socket waiting for writable, even though this is just
654 socks
[0] = td
->dummy_sock
;
655 return GETSOCK_WRITESOCK(0);
663 * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6.
665 Curl_addrinfo
*Curl_getaddrinfo(struct connectdata
*conn
,
666 const char *hostname
,
670 struct hostent
*h
= NULL
;
671 struct SessionHandle
*data
= conn
->data
;
674 *waitp
= 0; /* don't wait, we act synchronously */
676 in
= inet_addr(hostname
);
677 if(in
!= CURL_INADDR_NONE
)
678 /* This is a dotted IP address 123.123.123.123-style */
679 return Curl_ip2addr(in
, hostname
, port
);
681 /* fire up a new resolver thread! */
682 if(init_resolve_thread(conn
, hostname
, port
, NULL
)) {
683 *waitp
= TRUE
; /* please wait for the response */
687 /* fall-back to blocking version */
688 infof(data
, "init_resolve_thread() failed for %s; %s\n",
689 hostname
, Curl_strerror(conn
, ERRNO
));
691 h
= gethostbyname(hostname
);
693 infof(data
, "gethostbyname(2) failed for %s:%d; %s\n",
694 hostname
, port
, Curl_strerror(conn
, SOCKERRNO
));
697 return Curl_he2ai(h
, port
);
699 #endif /* CURLRES_IPV4 */
703 * Curl_getaddrinfo() - for Windows threading IPv6 enabled
705 Curl_addrinfo
*Curl_getaddrinfo(struct connectdata
*conn
,
706 const char *hostname
,
710 struct addrinfo hints
, *res
;
712 char sbuf
[NI_MAXSERV
];
714 struct SessionHandle
*data
= conn
->data
;
716 *waitp
= FALSE
; /* default to synch response */
719 * Check if a limited name resolve has been requested.
721 switch(data
->set
.ip_version
) {
722 case CURL_IPRESOLVE_V4
:
725 case CURL_IPRESOLVE_V6
:
734 /* see if we have an IPv6 stack */
735 curl_socket_t s
= socket(PF_INET6
, SOCK_DGRAM
, 0);
736 if(s
== CURL_SOCKET_BAD
) {
737 /* Some non-IPv6 stacks have been found to make very slow name resolves
738 * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
739 * the stack seems to be a non-ipv6 one. */
744 /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
745 * possible checks. And close the socket again.
751 memset(&hints
, 0, sizeof(hints
));
752 hints
.ai_family
= pf
;
753 hints
.ai_socktype
= conn
->socktype
;
754 #if 0 /* removed nov 8 2005 before 7.15.1 */
755 hints
.ai_flags
= AI_CANONNAME
;
757 itoa(port
, sbuf
, 10);
759 /* fire up a new resolver thread! */
760 if(init_resolve_thread(conn
, hostname
, port
, &hints
)) {
761 *waitp
= TRUE
; /* please wait for the response */
765 /* fall-back to blocking version */
766 infof(data
, "init_resolve_thread() failed for %s; %s\n",
767 hostname
, Curl_strerror(conn
, ERRNO
));
769 error
= getaddrinfo(hostname
, sbuf
, &hints
, &res
);
771 infof(data
, "getaddrinfo() failed for %s:%d; %s\n",
772 hostname
, port
, Curl_strerror(conn
, SOCKERRNO
));
777 #endif /* CURLRES_IPV6 */
778 #endif /* CURLRES_THREADED */