/* * gethostbyname_thread() resolves a name and then exits. */ static unsigned int CURL_STDCALL gethostbyname_thread (void *arg) { struct thread_sync_data *tsd = (struct thread_sync_data *)arg; struct thread_data *td = tsd->td; tsd->res = Curl_ipv4_resolve_r(tsd->hostname, tsd->port); if(!tsd->res) { tsd->sock_error = SOCKERRNO; if(tsd->sock_error == 0) tsd->sock_error = RESOLVER_ENOMEM; } Curl_mutex_acquire(tsd->mtx); if(tsd->done) { /* too late, gotta clean up the mess */ Curl_mutex_release(tsd->mtx); destroy_thread_sync_data(tsd); free(td); } else { tsd->done = 1; Curl_mutex_release(tsd->mtx); } return 0; }
/* * destroy_async_data() cleans up async resolver data and thread handle. */ static void destroy_async_data (struct Curl_async *async) { if(async->os_specific) { struct thread_data *td = (struct thread_data*) async->os_specific; int done; /* * if the thread is still blocking in the resolve syscall, detach it and * let the thread do the cleanup... */ Curl_mutex_acquire(td->tsd.mtx); done = td->tsd.done; td->tsd.done = 1; Curl_mutex_release(td->tsd.mtx); if(!done) { Curl_thread_destroy(td->thread_hnd); } else { if(td->thread_hnd != curl_thread_t_null) Curl_thread_join(&td->thread_hnd); destroy_thread_sync_data(&td->tsd); free(async->os_specific); } } async->os_specific = NULL; free(async->hostname); async->hostname = NULL; }
/* * getaddrinfo_thread() resolves a name and then exits. * * For builds without ARES, but with ENABLE_IPV6, create a resolver thread * and wait on it. */ static unsigned int CURL_STDCALL getaddrinfo_thread (void *arg) { struct thread_sync_data *tsd = (struct thread_sync_data*)arg; struct thread_data *td = tsd->td; char service[12]; int rc; snprintf(service, sizeof(service), "%d", tsd->port); rc = Curl_getaddrinfo_ex(tsd->hostname, service, &tsd->hints, &tsd->res); if(rc != 0) { tsd->sock_error = SOCKERRNO?SOCKERRNO:rc; if(tsd->sock_error == 0) tsd->sock_error = RESOLVER_ENOMEM; } else { Curl_addrinfo_set_port(tsd->res, tsd->port); } Curl_mutex_acquire(tsd->mtx); if(tsd->done) { /* too late, gotta clean up the mess */ Curl_mutex_release(tsd->mtx); destroy_thread_sync_data(tsd); free(td); } else { tsd->done = 1; Curl_mutex_release(tsd->mtx); } return 0; }
/* * getaddrinfo_thread() resolves a name, calls Curl_addrinfo6_callback and then * exits. * * For builds without ARES, but with ENABLE_IPV6, create a resolver thread * and wait on it. */ static unsigned __stdcall getaddrinfo_thread (void *arg) { struct connectdata *conn = (struct connectdata*) arg; struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct addrinfo *res; char service [NI_MAXSERV]; int rc; struct addrinfo hints = td->hints; /* Duplicate the passed mutex handle. * This allows us to use it even after the container gets destroyed * due to a resolver timeout. */ struct thread_sync_data tsd = { 0,0,0,NULL }; if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) { /* thread synchronization data initialization failed */ return -1; } #ifndef _WIN32_WCE *stderr = *td->stderr_file; #endif itoa(conn->async.port, service, 10); WSASetLastError(conn->async.status = NO_DATA); /* pending status */ /* Signaling that we have initialized all copies of data and handles we need */ SetEvent(td->event_thread_started); rc = getaddrinfo(tsd.hostname, service, &hints, &res); /* is parent thread waiting for us and are we able to access conn members? */ if (acquire_thread_sync(&tsd)) { /* Mark that we have obtained the information, and that we are calling back with it. */ SetEvent(td->event_resolved); if (rc == 0) { #ifdef DEBUG_THREADING_GETADDRINFO dump_addrinfo (conn, res); #endif rc = Curl_addrinfo6_callback(conn, CURL_ASYNC_SUCCESS, res); } else { rc = Curl_addrinfo6_callback(conn, (int)WSAGetLastError(), NULL); TRACE(("Winsock-error %d, no address\n", conn->async.status)); } release_thread_sync(&tsd); } /* clean up */ destroy_thread_sync_data(&tsd); return (rc); /* An implicit _endthreadex() here */ }
/* Initialize resolver thread synchronization data */ static BOOL init_thread_sync_data(struct thread_data * td, const char * hostname, struct thread_sync_data * tsd) { HANDLE curr_proc = GetCurrentProcess(); MEMSET(tsd, 0, sizeof(*tsd)); if (!DuplicateHandle(curr_proc, td->mutex_waiting, curr_proc, &tsd->mutex_waiting, 0, FALSE, DUPLICATE_SAME_ACCESS)) { /* failed to duplicate the mutex, no point in continuing */ destroy_thread_sync_data(tsd); return FALSE; } if (!DuplicateHandle(curr_proc, td->mutex_terminate, curr_proc, &tsd->mutex_terminate, 0, FALSE, DUPLICATE_SAME_ACCESS)) { /* failed to duplicate the mutex, no point in continuing */ destroy_thread_sync_data(tsd); return FALSE; } if (!DuplicateHandle(curr_proc, td->event_terminate, curr_proc, &tsd->event_terminate, 0, FALSE, DUPLICATE_SAME_ACCESS)) { /* failed to duplicate the event, no point in continuing */ destroy_thread_sync_data(tsd); return FALSE; } /* Copying hostname string because original can be destroyed by parent * thread during gethostbyname execution. */ tsd->hostname = strdup(hostname); if (!tsd->hostname) { /* Memory allocation failed */ destroy_thread_sync_data(tsd); return FALSE; } return TRUE; }
/* * getaddrinfo_thread() resolves a name, calls Curl_addrinfo6_callback and then * exits. * * For builds without ARES, but with ENABLE_IPV6, create a resolver thread * and wait on it. */ static unsigned __stdcall getaddrinfo_thread (void *arg) { struct connectdata *conn = (struct connectdata*) arg; struct thread_data *td = (struct thread_data*) conn->async.os_specific; Curl_addrinfo *res; char service [NI_MAXSERV]; int rc; struct addrinfo hints = td->hints; /* Duplicate the passed mutex handle. * This allows us to use it even after the container gets destroyed * due to a resolver timeout. */ struct thread_sync_data tsd = { 0, 0, 0, NULL }; if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) { /* thread synchronization data initialization failed */ return -1; } itoa(conn->async.port, service, 10); conn->async.status = NO_DATA; /* pending status */ SET_SOCKERRNO(conn->async.status); /* Signaling that we have initialized all copies of data and handles we need */ SetEvent(td->event_thread_started); rc = Curl_getaddrinfo_ex(tsd.hostname, service, &hints, &res); /* is parent thread waiting for us and are we able to access conn members? */ if (acquire_thread_sync(&tsd)) { /* Mark that we have obtained the information, and that we are calling back with it. */ SetEvent(td->event_resolved); if (rc == 0) { rc = Curl_addrinfo6_callback(conn, CURL_ASYNC_SUCCESS, res); } else { rc = Curl_addrinfo6_callback(conn, SOCKERRNO, NULL); } release_thread_sync(&tsd); } /* clean up */ destroy_thread_sync_data(&tsd); return (rc); /* An implicit _endthreadex() here */ }
/* * gethostbyname_thread() resolves a name, calls the Curl_addrinfo4_callback * and then exits. * * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on * it. */ static unsigned __stdcall gethostbyname_thread (void *arg) { struct connectdata *conn = (struct connectdata*) arg; struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct hostent *he; int rc = 0; /* Duplicate the passed mutex and event handles. * This allows us to use it even after the container gets destroyed * due to a resolver timeout. */ struct thread_sync_data tsd = { 0,0,0,NULL }; if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) { /* thread synchronization data initialization failed */ return -1; } /* Sharing the same _iob[] element with our parent thread should * hopefully make printouts synchronised. I'm not sure it works * with a static runtime lib (MSVC's libc.lib). */ #ifndef _WIN32_WCE *stderr = *td->stderr_file; #endif WSASetLastError (conn->async.status = NO_DATA); /* pending status */ /* Signaling that we have initialized all copies of data and handles we need */ SetEvent(td->event_thread_started); he = gethostbyname (tsd.hostname); /* is parent thread waiting for us and are we able to access conn members? */ if (acquire_thread_sync(&tsd)) { /* Mark that we have obtained the information, and that we are calling * back with it. */ SetEvent(td->event_resolved); if (he) { rc = Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he); } else { rc = Curl_addrinfo4_callback(conn, (int)WSAGetLastError(), NULL); } TRACE(("Winsock-error %d, addr %s\n", conn->async.status, he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown")); release_thread_sync(&tsd); } /* clean up */ destroy_thread_sync_data(&tsd); return (rc); /* An implicit _endthreadex() here */ }
/* * gethostbyname_thread() resolves a name, calls the Curl_addrinfo4_callback * and then exits. * * For builds without ARES/ENABLE_IPV6, create a resolver thread and wait on * it. */ static unsigned __stdcall gethostbyname_thread (void *arg) { struct connectdata *conn = (struct connectdata*) arg; struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct hostent *he; int rc = 0; /* Duplicate the passed mutex and event handles. * This allows us to use it even after the container gets destroyed * due to a resolver timeout. */ struct thread_sync_data tsd = { 0,0,0,NULL }; if (!init_thread_sync_data(td, conn->async.hostname, &tsd)) { /* thread synchronization data initialization failed */ return (unsigned)-1; } conn->async.status = NO_DATA; /* pending status */ SET_SOCKERRNO(conn->async.status); /* Signaling that we have initialized all copies of data and handles we need */ SetEvent(td->event_thread_started); he = gethostbyname (tsd.hostname); /* is parent thread waiting for us and are we able to access conn members? */ if (acquire_thread_sync(&tsd)) { /* Mark that we have obtained the information, and that we are calling * back with it. */ SetEvent(td->event_resolved); if (he) { rc = Curl_addrinfo4_callback(conn, CURL_ASYNC_SUCCESS, he); } else { rc = Curl_addrinfo4_callback(conn, SOCKERRNO, NULL); } TRACE(("Winsock-error %d, addr %s\n", conn->async.status, he ? inet_ntoa(*(struct in_addr*)he->h_addr) : "unknown")); release_thread_sync(&tsd); } /* clean up */ destroy_thread_sync_data(&tsd); return (rc); /* An implicit _endthreadex() here */ }
/* Initialize resolver thread synchronization data */ static int init_thread_sync_data(struct thread_data * td, const char *hostname, int port, const struct addrinfo *hints) { struct thread_sync_data *tsd = &td->tsd; memset(tsd, 0, sizeof(*tsd)); tsd->td = td; tsd->port = port; /* Treat the request as done until the thread actually starts so any early * cleanup gets done properly. */ tsd->done = 1; #ifdef HAVE_GETADDRINFO DEBUGASSERT(hints); tsd->hints = *hints; #else (void) hints; #endif tsd->mtx = malloc(sizeof(curl_mutex_t)); if(tsd->mtx == NULL) goto err_exit; Curl_mutex_init(tsd->mtx); tsd->sock_error = CURL_ASYNC_SUCCESS; /* Copying hostname string because original can be destroyed by parent * thread during gethostbyname execution. */ tsd->hostname = strdup(hostname); if(!tsd->hostname) goto err_exit; return 1; err_exit: /* Memory allocation failed */ destroy_thread_sync_data(tsd); return 0; }
/* * destroy_async_data() cleans up async resolver data and thread handle. */ static void destroy_async_data (struct Curl_async *async) { if(async->hostname) free(async->hostname); if(async->os_specific) { struct thread_data *td = (struct thread_data*) async->os_specific; if(td->thread_hnd != curl_thread_t_null) Curl_thread_join(&td->thread_hnd); destroy_thread_sync_data(&td->tsd); free(async->os_specific); } async->hostname = NULL; async->os_specific = NULL; }
/* * Curl_destroy_thread_data() cleans up async resolver data and thread handle. * Complementary of ares_destroy. */ void Curl_destroy_thread_data (struct Curl_async *async) { if(async->hostname) free(async->hostname); if(async->os_specific) { struct thread_data *td = (struct thread_data*) async->os_specific; if (td->dummy_sock != CURL_SOCKET_BAD) sclose(td->dummy_sock); if (td->thread_hnd != curl_thread_t_null) Curl_thread_join(&td->thread_hnd); destroy_thread_sync_data(&td->tsd); free(async->os_specific); } async->hostname = NULL; async->os_specific = NULL; }
/* Initialize resolver thread synchronization data */ static int init_thread_sync_data(struct thread_sync_data * tsd, const char * hostname, int port, const struct addrinfo *hints) { memset(tsd, 0, sizeof(*tsd)); tsd->port = port; #ifdef CURLRES_IPV6 DEBUGASSERT(hints); tsd->hints = *hints; #else (void) hints; #endif tsd->mtx = malloc(sizeof(curl_mutex_t)); if(tsd->mtx == NULL) goto err_exit; Curl_mutex_init(tsd->mtx); tsd->sock_error = CURL_ASYNC_SUCCESS; /* Copying hostname string because original can be destroyed by parent * thread during gethostbyname execution. */ tsd->hostname = strdup(hostname); if(!tsd->hostname) goto err_exit; return 1; err_exit: /* Memory allocation failed */ destroy_thread_sync_data(tsd); return 0; }