1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2006, 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.2 2007-03-15 19:22:13 andy Exp $
22 ***************************************************************************/
32 #ifdef HAVE_SYS_TYPES_H
33 #include <sys/types.h>
35 #ifdef HAVE_SYS_SOCKET_H
36 #include <sys/socket.h>
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
44 #ifdef HAVE_ARPA_INET_H
45 #include <arpa/inet.h>
48 #include <stdlib.h> /* required for free() prototypes */
51 #include <unistd.h> /* for the close() proto */
67 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
69 #define in_addr_t unsigned long
81 #define _MPRINTF_REPLACE /* use our functions only */
82 #include <curl/mprintf.h>
84 #include "inet_ntop.h"
87 /* The last #include file should be: */
90 #if defined(_MSC_VER) && defined(CURL_NO__BEGINTHREADEX)
91 #pragma message ("No _beginthreadex() available in this RTL")
94 /***********************************************************************
95 * Only for Windows threaded name resolves builds
96 **********************************************************************/
97 #ifdef CURLRES_THREADED
99 /* This function is used to init a threaded resolve */
100 static bool init_resolve_thread(struct connectdata
*conn
,
101 const char *hostname
, int port
,
102 const Curl_addrinfo
*hints
);
105 #define THREAD_FUNC gethostbyname_thread
106 #define THREAD_NAME "gethostbyname_thread"
108 #define THREAD_FUNC getaddrinfo_thread
109 #define THREAD_NAME "getaddrinfo_thread"
112 #if defined(DEBUG_THREADING_GETHOSTBYNAME) || \
113 defined(DEBUG_THREADING_GETADDRINFO)
114 /* If this is defined, provide tracing */
115 #define TRACE(args) \
116 do { trace_it("%u: ", __LINE__); trace_it args; } while (0)
118 static void trace_it (const char *fmt
, ...)
120 static int do_trace
= -1;
123 if (do_trace
== -1) {
124 const char *env
= getenv("CURL_TRACE");
125 do_trace
= (env
&& atoi(env
) > 0);
129 va_start (args
, fmt
);
130 vfprintf (stderr
, fmt
, args
);
138 #ifdef DEBUG_THREADING_GETADDRINFO
139 static void dump_addrinfo (struct connectdata
*conn
, const struct addrinfo
*ai
)
141 TRACE(("dump_addrinfo:\n"));
142 for ( ; ai
; ai
= ai
->ai_next
) {
143 char buf
[INET6_ADDRSTRLEN
];
145 trace_it(" fam %2d, CNAME %s, ",
146 ai
->ai_family
, ai
->ai_canonname
? ai
->ai_canonname
: "<none>");
147 if (Curl_printable_address(ai
, buf
, sizeof(buf
)))
148 trace_it("%s\n", buf
);
150 trace_it("failed; %s\n", Curl_strerror(conn
,WSAGetLastError()));
159 curl_socket_t dummy_sock
; /* dummy for Curl_resolv_fdset() */
160 HANDLE mutex_waiting
; /* marks that we are still waiting for a resolve */
161 HANDLE event_resolved
; /* marks that the thread obtained the information */
162 HANDLE event_thread_started
; /* marks that the thread has initialized and
164 HANDLE mutex_terminate
; /* serializes access to flag_terminate */
165 HANDLE event_terminate
; /* flag for thread to terminate instead of calling
168 struct addrinfo hints
;
172 /* Data for synchronization between resolver thread and its parent */
173 struct thread_sync_data
{
174 HANDLE mutex_waiting
; /* thread_data.mutex_waiting duplicate */
175 HANDLE mutex_terminate
; /* thread_data.mutex_terminate duplicate */
176 HANDLE event_terminate
; /* thread_data.event_terminate duplicate */
177 char * hostname
; /* hostname to resolve, Curl_async.hostname
181 /* Destroy resolver thread synchronization data */
183 void destroy_thread_sync_data(struct thread_sync_data
* tsd
)
187 tsd
->hostname
= NULL
;
189 if (tsd
->event_terminate
) {
190 CloseHandle(tsd
->event_terminate
);
191 tsd
->event_terminate
= NULL
;
193 if (tsd
->mutex_terminate
) {
194 CloseHandle(tsd
->mutex_terminate
);
195 tsd
->mutex_terminate
= NULL
;
197 if (tsd
->mutex_waiting
) {
198 CloseHandle(tsd
->mutex_waiting
);
199 tsd
->mutex_waiting
= NULL
;
203 /* Initialize resolver thread synchronization data */
205 BOOL
init_thread_sync_data(struct thread_data
* td
,
207 struct thread_sync_data
* tsd
)
209 HANDLE curr_proc
= GetCurrentProcess();
211 memset(tsd
, 0, sizeof(*tsd
));
212 if (!DuplicateHandle(curr_proc
, td
->mutex_waiting
,
213 curr_proc
, &tsd
->mutex_waiting
, 0, FALSE
,
214 DUPLICATE_SAME_ACCESS
)) {
215 /* failed to duplicate the mutex, no point in continuing */
216 destroy_thread_sync_data(tsd
);
219 if (!DuplicateHandle(curr_proc
, td
->mutex_terminate
,
220 curr_proc
, &tsd
->mutex_terminate
, 0, FALSE
,
221 DUPLICATE_SAME_ACCESS
)) {
222 /* failed to duplicate the mutex, no point in continuing */
223 destroy_thread_sync_data(tsd
);
226 if (!DuplicateHandle(curr_proc
, td
->event_terminate
,
227 curr_proc
, &tsd
->event_terminate
, 0, FALSE
,
228 DUPLICATE_SAME_ACCESS
)) {
229 /* failed to duplicate the event, no point in continuing */
230 destroy_thread_sync_data(tsd
);
233 /* Copying hostname string because original can be destroyed by parent
234 * thread during gethostbyname execution.
236 tsd
->hostname
= strdup(hostname
);
237 if (!tsd
->hostname
) {
238 /* Memory allocation failed */
239 destroy_thread_sync_data(tsd
);
245 /* acquire resolver thread synchronization */
247 BOOL
acquire_thread_sync(struct thread_sync_data
* tsd
)
249 /* is the thread initiator still waiting for us ? */
250 if (WaitForSingleObject(tsd
->mutex_waiting
, 0) == WAIT_TIMEOUT
) {
253 /* Waiting access to event_terminate */
254 if (WaitForSingleObject(tsd
->mutex_terminate
, INFINITE
) != WAIT_OBJECT_0
) {
255 /* Something went wrong - now just ignoring */
258 if (WaitForSingleObject(tsd
->event_terminate
, 0) != WAIT_TIMEOUT
) {
259 /* Parent thread signaled us to terminate.
260 * This means that all data in conn->async is now destroyed
261 * and we cannot use it.
272 /* release resolver thread synchronization */
274 void release_thread_sync(struct thread_sync_data
* tsd
)
276 ReleaseMutex(tsd
->mutex_terminate
);
279 #if defined(CURLRES_IPV4)
281 * gethostbyname_thread() resolves a name, calls the Curl_addrinfo4_callback
284 * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on
287 static unsigned __stdcall
gethostbyname_thread (void *arg
)
289 struct connectdata
*conn
= (struct connectdata
*) arg
;
290 struct thread_data
*td
= (struct thread_data
*) conn
->async
.os_specific
;
294 /* Duplicate the passed mutex and event handles.
295 * This allows us to use it even after the container gets destroyed
296 * due to a resolver timeout.
298 struct thread_sync_data tsd
= { 0,0,0,NULL
};
299 if (!init_thread_sync_data(td
, conn
->async
.hostname
, &tsd
)) {
300 /* thread synchronization data initialization failed */
304 WSASetLastError (conn
->async
.status
= NO_DATA
); /* pending status */
306 /* Signaling that we have initialized all copies of data and handles we
308 SetEvent(td
->event_thread_started
);
310 he
= gethostbyname (tsd
.hostname
);
312 /* is parent thread waiting for us and are we able to access conn members? */
313 if (acquire_thread_sync(&tsd
)) {
314 /* Mark that we have obtained the information, and that we are calling
316 SetEvent(td
->event_resolved
);
318 rc
= Curl_addrinfo4_callback(conn
, CURL_ASYNC_SUCCESS
, he
);
321 rc
= Curl_addrinfo4_callback(conn
, (int)WSAGetLastError(), NULL
);
323 TRACE(("Winsock-error %d, addr %s\n", conn
->async
.status
,
324 he
? inet_ntoa(*(struct in_addr
*)he
->h_addr
) : "unknown"));
325 release_thread_sync(&tsd
);
329 destroy_thread_sync_data(&tsd
);
332 /* An implicit _endthreadex() here */
335 #elif defined(CURLRES_IPV6)
338 * getaddrinfo_thread() resolves a name, calls Curl_addrinfo6_callback and then
341 * For builds without ARES, but with ENABLE_IPV6, create a resolver thread
344 static unsigned __stdcall
getaddrinfo_thread (void *arg
)
346 struct connectdata
*conn
= (struct connectdata
*) arg
;
347 struct thread_data
*td
= (struct thread_data
*) conn
->async
.os_specific
;
348 struct addrinfo
*res
;
349 char service
[NI_MAXSERV
];
351 struct addrinfo hints
= td
->hints
;
353 /* Duplicate the passed mutex handle.
354 * This allows us to use it even after the container gets destroyed
355 * due to a resolver timeout.
357 struct thread_sync_data tsd
= { 0,0,0,NULL
};
358 if (!init_thread_sync_data(td
, conn
->async
.hostname
, &tsd
)) {
359 /* thread synchronization data initialization failed */
363 itoa(conn
->async
.port
, service
, 10);
365 WSASetLastError(conn
->async
.status
= NO_DATA
); /* pending status */
367 /* Signaling that we have initialized all copies of data and handles we
369 SetEvent(td
->event_thread_started
);
371 rc
= getaddrinfo(tsd
.hostname
, service
, &hints
, &res
);
373 /* is parent thread waiting for us and are we able to access conn members? */
374 if (acquire_thread_sync(&tsd
)) {
375 /* Mark that we have obtained the information, and that we are calling
377 SetEvent(td
->event_resolved
);
380 #ifdef DEBUG_THREADING_GETADDRINFO
381 dump_addrinfo (conn
, res
);
383 rc
= Curl_addrinfo6_callback(conn
, CURL_ASYNC_SUCCESS
, res
);
386 rc
= Curl_addrinfo6_callback(conn
, (int)WSAGetLastError(), NULL
);
387 TRACE(("Winsock-error %d, no address\n", conn
->async
.status
));
389 release_thread_sync(&tsd
);
393 destroy_thread_sync_data(&tsd
);
396 /* An implicit _endthreadex() here */
401 * Curl_destroy_thread_data() cleans up async resolver data and thread handle.
402 * Complementary of ares_destroy.
404 void Curl_destroy_thread_data (struct Curl_async
*async
)
407 free(async
->hostname
);
409 if (async
->os_specific
) {
410 struct thread_data
*td
= (struct thread_data
*) async
->os_specific
;
411 curl_socket_t sock
= td
->dummy_sock
;
413 if (td
->mutex_terminate
&& td
->event_terminate
) {
414 /* Signaling resolver thread to terminate */
415 if (WaitForSingleObject(td
->mutex_terminate
, INFINITE
) == WAIT_OBJECT_0
) {
416 SetEvent(td
->event_terminate
);
417 ReleaseMutex(td
->mutex_terminate
);
420 /* Something went wrong - just ignoring it */
424 if (td
->mutex_terminate
)
425 CloseHandle(td
->mutex_terminate
);
426 if (td
->event_terminate
)
427 CloseHandle(td
->event_terminate
);
428 if (td
->event_thread_started
)
429 CloseHandle(td
->event_thread_started
);
431 if (sock
!= CURL_SOCKET_BAD
)
434 /* destroy the synchronization objects */
435 if (td
->mutex_waiting
)
436 CloseHandle(td
->mutex_waiting
);
437 td
->mutex_waiting
= NULL
;
438 if (td
->event_resolved
)
439 CloseHandle(td
->event_resolved
);
442 CloseHandle(td
->thread_hnd
);
444 free(async
->os_specific
);
446 async
->hostname
= NULL
;
447 async
->os_specific
= NULL
;
451 * init_resolve_thread() starts a new thread that performs the actual
452 * resolve. This function returns before the resolve is done.
454 * Returns FALSE in case of failure, otherwise TRUE.
456 static bool init_resolve_thread (struct connectdata
*conn
,
457 const char *hostname
, int port
,
458 const Curl_addrinfo
*hints
)
460 struct thread_data
*td
= calloc(sizeof(*td
), 1);
461 HANDLE thread_and_event
[2] = {0};
464 SetLastError(ENOMEM
);
468 Curl_safefree(conn
->async
.hostname
);
469 conn
->async
.hostname
= strdup(hostname
);
470 if (!conn
->async
.hostname
) {
472 SetLastError(ENOMEM
);
476 conn
->async
.port
= port
;
477 conn
->async
.done
= FALSE
;
478 conn
->async
.status
= 0;
479 conn
->async
.dns
= NULL
;
480 conn
->async
.os_specific
= (void*) td
;
481 td
->dummy_sock
= CURL_SOCKET_BAD
;
483 /* Create the mutex used to inform the resolver thread that we're
484 * still waiting, and take initial ownership.
486 td
->mutex_waiting
= CreateMutex(NULL
, TRUE
, NULL
);
487 if (td
->mutex_waiting
== NULL
) {
488 Curl_destroy_thread_data(&conn
->async
);
489 SetLastError(EAGAIN
);
493 /* Create the event that the thread uses to inform us that it's
494 * done resolving. Do not signal it.
496 td
->event_resolved
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
497 if (td
->event_resolved
== NULL
) {
498 Curl_destroy_thread_data(&conn
->async
);
499 SetLastError(EAGAIN
);
502 /* Create the mutex used to serialize access to event_terminated
503 * between us and resolver thread.
505 td
->mutex_terminate
= CreateMutex(NULL
, FALSE
, NULL
);
506 if (td
->mutex_terminate
== NULL
) {
507 Curl_destroy_thread_data(&conn
->async
);
508 SetLastError(EAGAIN
);
511 /* Create the event used to signal thread that it should terminate.
513 td
->event_terminate
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
514 if (td
->event_terminate
== NULL
) {
515 Curl_destroy_thread_data(&conn
->async
);
516 SetLastError(EAGAIN
);
519 /* Create the event used by thread to inform it has initialized its own data.
521 td
->event_thread_started
= CreateEvent(NULL
, TRUE
, FALSE
, NULL
);
522 if (td
->event_thread_started
== NULL
) {
523 Curl_destroy_thread_data(&conn
->async
);
524 SetLastError(EAGAIN
);
529 td
->thread_hnd
= (HANDLE
) CreateThread(NULL
, 0,
530 (LPTHREAD_START_ROUTINE
) THREAD_FUNC
,
531 conn
, 0, &td
->thread_id
);
533 td
->thread_hnd
= (HANDLE
) _beginthreadex(NULL
, 0, THREAD_FUNC
,
534 conn
, 0, &td
->thread_id
);
544 if (!td
->thread_hnd
) {
546 TRACE(("CreateThread() failed; %s\n", Curl_strerror(conn
,GetLastError())));
549 TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn
,errno
)));
551 Curl_destroy_thread_data(&conn
->async
);
554 /* Waiting until the thread will initialize its data or it will exit due errors.
556 thread_and_event
[0] = td
->thread_hnd
;
557 thread_and_event
[1] = td
->event_thread_started
;
558 if (WaitForMultipleObjects(sizeof(thread_and_event
) /
559 sizeof(thread_and_event
[0]),
560 (const HANDLE
*)thread_and_event
, FALSE
,
561 INFINITE
) == WAIT_FAILED
) {
562 /* The resolver thread has been created,
563 * most probably it works now - ignoring this "minor" error
566 /* This socket is only to keep Curl_resolv_fdset() and select() happy;
567 * should never become signalled for read/write since it's unbound but
568 * Windows needs atleast 1 socket in select().
570 td
->dummy_sock
= socket(AF_INET
, SOCK_DGRAM
, 0);
576 * Curl_wait_for_resolv() waits for a resolve to finish. This function should
577 * be avoided since using this risk getting the multi interface to "hang".
579 * If 'entry' is non-NULL, make it point to the resolved dns entry
581 * This is the version for resolves-in-a-thread.
583 CURLcode
Curl_wait_for_resolv(struct connectdata
*conn
,
584 struct Curl_dns_entry
**entry
)
586 struct thread_data
*td
= (struct thread_data
*) conn
->async
.os_specific
;
587 struct SessionHandle
*data
= conn
->data
;
592 curlassert (conn
&& td
);
594 /* now, see if there's a connect timeout or a regular timeout to
595 use instead of the default one */
597 conn
->data
->set
.connecttimeout
? conn
->data
->set
.connecttimeout
:
598 conn
->data
->set
.timeout
? conn
->data
->set
.timeout
:
599 CURL_TIMEOUT_RESOLVE
; /* default name resolve timeout */
600 ticks
= GetTickCount();
602 /* wait for the thread to resolve the name */
603 status
= WaitForSingleObject(td
->event_resolved
, 1000UL*timeout
);
605 /* mark that we are now done waiting */
606 ReleaseMutex(td
->mutex_waiting
);
608 /* close our handle to the mutex, no point in hanging on to it */
609 CloseHandle(td
->mutex_waiting
);
610 td
->mutex_waiting
= NULL
;
612 /* close the event handle, it's useless now */
613 CloseHandle(td
->event_resolved
);
614 td
->event_resolved
= NULL
;
616 /* has the resolver thread succeeded in resolving our query ? */
617 if (status
== WAIT_OBJECT_0
) {
618 /* wait for the thread to exit, it's in the callback sequence */
619 if (WaitForSingleObject(td
->thread_hnd
, 5000) == WAIT_TIMEOUT
) {
620 TerminateThread(td
->thread_hnd
, 0);
621 conn
->async
.done
= TRUE
;
622 td
->thread_status
= (DWORD
)-1;
623 TRACE(("%s() thread stuck?!, ", THREAD_NAME
));
626 /* Thread finished before timeout; propagate Winsock error to this
627 * thread. 'conn->async.done = TRUE' is set in
628 * Curl_addrinfo4/6_callback().
630 WSASetLastError(conn
->async
.status
);
631 GetExitCodeThread(td
->thread_hnd
, &td
->thread_status
);
632 TRACE(("%s() status %lu, thread retval %lu, ",
633 THREAD_NAME
, status
, td
->thread_status
));
637 conn
->async
.done
= TRUE
;
638 td
->thread_status
= (DWORD
)-1;
639 TRACE(("%s() timeout, ", THREAD_NAME
));
642 TRACE(("elapsed %lu ms\n", GetTickCount()-ticks
));
645 *entry
= conn
->async
.dns
;
649 if (!conn
->async
.dns
) {
650 /* a name was not resolved */
651 if (td
->thread_status
== CURLE_OUT_OF_MEMORY
) {
652 rc
= CURLE_OUT_OF_MEMORY
;
653 failf(data
, "Could not resolve host: %s", curl_easy_strerror(rc
));
655 else if(conn
->async
.done
) {
656 if(conn
->bits
.httpproxy
) {
657 failf(data
, "Could not resolve proxy: %s; %s",
658 conn
->proxy
.dispname
, Curl_strerror(conn
, conn
->async
.status
));
659 rc
= CURLE_COULDNT_RESOLVE_PROXY
;
662 failf(data
, "Could not resolve host: %s; %s",
663 conn
->host
.name
, Curl_strerror(conn
, conn
->async
.status
));
664 rc
= CURLE_COULDNT_RESOLVE_HOST
;
667 else if (td
->thread_status
== (DWORD
)-1 || conn
->async
.status
== NO_DATA
) {
668 failf(data
, "Resolving host timed out: %s", conn
->host
.name
);
669 rc
= CURLE_OPERATION_TIMEDOUT
;
672 rc
= CURLE_OPERATION_TIMEDOUT
;
675 Curl_destroy_thread_data(&conn
->async
);
678 conn
->bits
.close
= TRUE
;
684 * Curl_is_resolved() is called repeatedly to check if a previous name resolve
685 * request has completed. It should also make sure to time-out if the
686 * operation seems to take too long.
688 CURLcode
Curl_is_resolved(struct connectdata
*conn
,
689 struct Curl_dns_entry
**entry
)
693 if (conn
->async
.done
) {
695 Curl_destroy_thread_data(&conn
->async
);
696 if (!conn
->async
.dns
) {
697 TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n"));
698 return CURLE_COULDNT_RESOLVE_HOST
;
700 *entry
= conn
->async
.dns
;
701 TRACE(("resolved okay, dns %p\n", *entry
));
706 int Curl_resolv_getsock(struct connectdata
*conn
,
707 curl_socket_t
*socks
,
710 const struct thread_data
*td
=
711 (const struct thread_data
*) conn
->async
.os_specific
;
713 if (td
&& td
->dummy_sock
!= CURL_SOCKET_BAD
) {
715 /* return one socket waiting for writable, even though this is just
717 socks
[0] = td
->dummy_sock
;
718 return GETSOCK_WRITESOCK(0);
726 * Curl_getaddrinfo() - for Windows threading without ENABLE_IPV6.
728 Curl_addrinfo
*Curl_getaddrinfo(struct connectdata
*conn
,
729 const char *hostname
,
733 struct hostent
*h
= NULL
;
734 struct SessionHandle
*data
= conn
->data
;
737 *waitp
= 0; /* don't wait, we act synchronously */
739 in
= inet_addr(hostname
);
740 if (in
!= CURL_INADDR_NONE
)
741 /* This is a dotted IP address 123.123.123.123-style */
742 return Curl_ip2addr(in
, hostname
, port
);
744 /* fire up a new resolver thread! */
745 if (init_resolve_thread(conn
, hostname
, port
, NULL
)) {
746 *waitp
= TRUE
; /* please wait for the response */
750 /* fall-back to blocking version */
751 infof(data
, "init_resolve_thread() failed for %s; %s\n",
752 hostname
, Curl_strerror(conn
,GetLastError()));
754 h
= gethostbyname(hostname
);
756 infof(data
, "gethostbyname(2) failed for %s:%d; %s\n",
757 hostname
, port
, Curl_strerror(conn
,WSAGetLastError()));
760 return Curl_he2ai(h
, port
);
762 #endif /* CURLRES_IPV4 */
766 * Curl_getaddrinfo() - for Windows threading IPv6 enabled
768 Curl_addrinfo
*Curl_getaddrinfo(struct connectdata
*conn
,
769 const char *hostname
,
773 struct addrinfo hints
, *res
;
775 char sbuf
[NI_MAXSERV
];
778 struct SessionHandle
*data
= conn
->data
;
780 *waitp
= FALSE
; /* default to synch response */
782 /* see if we have an IPv6 stack */
783 s
= socket(PF_INET6
, SOCK_DGRAM
, 0);
784 if (s
== CURL_SOCKET_BAD
) {
785 /* Some non-IPv6 stacks have been found to make very slow name resolves
786 * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
787 * the stack seems to be a non-ipv6 one. */
792 /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
793 * possible checks. And close the socket again.
798 * Check if a more limited name resolve has been requested.
800 switch(data
->set
.ip_version
) {
801 case CURL_IPRESOLVE_V4
:
804 case CURL_IPRESOLVE_V6
:
813 memset(&hints
, 0, sizeof(hints
));
814 hints
.ai_family
= pf
;
815 hints
.ai_socktype
= conn
->socktype
;
816 #if 0 /* removed nov 8 2005 before 7.15.1 */
817 hints
.ai_flags
= AI_CANONNAME
;
819 itoa(port
, sbuf
, 10);
821 /* fire up a new resolver thread! */
822 if (init_resolve_thread(conn
, hostname
, port
, &hints
)) {
823 *waitp
= TRUE
; /* please wait for the response */
827 /* fall-back to blocking version */
828 infof(data
, "init_resolve_thread() failed for %s; %s\n",
829 hostname
, Curl_strerror(conn
,GetLastError()));
831 error
= getaddrinfo(hostname
, sbuf
, &hints
, &res
);
833 infof(data
, "getaddrinfo() failed for %s:%d; %s\n",
834 hostname
, port
, Curl_strerror(conn
,WSAGetLastError()));
839 #endif /* CURLRES_IPV6 */
840 #endif /* CURLRES_THREADED */