void Curl_conncache_close_all_connections(struct conncache *connc) { struct connectdata *conn; conn = Curl_conncache_find_first_connection(connc); while(conn) { SIGPIPE_VARIABLE(pipe_st); conn->data = connc->closure_handle; sigpipe_ignore(conn->data, &pipe_st); conn->data->easy_conn = NULL; /* clear the easy handle's connection pointer */ /* This will remove the connection from the cache */ connclose(conn, "kill all"); (void)Curl_disconnect(connc->closure_handle, conn, FALSE); sigpipe_restore(&pipe_st); conn = Curl_conncache_find_first_connection(connc); } if(connc->closure_handle) { SIGPIPE_VARIABLE(pipe_st); sigpipe_ignore(connc->closure_handle, &pipe_st); Curl_hostcache_clean(connc->closure_handle, connc->closure_handle->dns.hostcache); Curl_close(connc->closure_handle); sigpipe_restore(&pipe_st); } }
/* called to check if the name is resolved now */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct SessionHandle *data = conn->data; long timeout; DWORD status, ticks; CURLcode rc; curlassert (conn && td); /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ timeout = conn->data->set.connecttimeout ? conn->data->set.connecttimeout : conn->data->set.timeout ? conn->data->set.timeout : 300; /* default name resolve timeout in seconds */ ticks = GetTickCount(); status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout); if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) { /* Thread finished before timeout; propagate Winsock error to this thread */ WSASetLastError(conn->async.status); GetExitCodeThread(td->thread_hnd, &td->thread_status); TRACE(("status %lu, thread-status %08lX\n", status, td->thread_status)); } else { conn->async.done = TRUE; TerminateThread(td->thread_hnd, (DWORD)-1); td->thread_status = (DWORD)-1; } TRACE(("gethostbyname_thread() retval %08lX, elapsed %lu ms\n", td->thread_status, GetTickCount()-ticks)); if(entry) *entry = conn->async.dns; rc = CURLE_OK; if (!conn->async.dns) { /* a name was not resolved */ if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) { failf(data, "Resolving host timed out: %s", conn->name); rc = CURLE_OPERATION_TIMEDOUT; } else if(conn->async.done) { failf(data, "Could not resolve host: %s (code %lu)", conn->name, conn->async.status); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; destroy_thread_data(conn); /* close the connection, since we can't return failure here without cleaning up this connection properly */ Curl_disconnect(conn); } return (rc); }
/* Call this function after Curl_connect() has returned async=TRUE and then a successful name resolve has been received. Note: this function disconnects and frees the conn data in case of resolve failure */ CURLcode Curl_async_resolved(struct connectdata *conn, bool *protocol_done) { CURLcode result; if(conn->async.dns) { conn->dns_entry = conn->async.dns; conn->async.dns = NULL; } result = Curl_setup_conn(conn, protocol_done); if(result) /* We're not allowed to return failure with memory left allocated in the connectdata struct, free those here */ Curl_disconnect(conn, FALSE); /* close the connection */ return result; }
/* * Give ownership of a connection back to the connection cache. Might * disconnect the oldest existing in there to make space. * * Return TRUE if stored, FALSE if closed. */ bool Curl_conncache_return_conn(struct connectdata *conn) { struct Curl_easy *data = conn->data; /* data->multi->maxconnects can be negative, deal with it. */ size_t maxconnects = (data->multi->maxconnects < 0) ? data->multi->num_easy * 4: data->multi->maxconnects; struct connectdata *conn_candidate = NULL; if(maxconnects > 0 && Curl_conncache_size(data) > maxconnects) { infof(data, "Connection cache is full, closing the oldest one.\n"); conn_candidate = Curl_conncache_extract_oldest(data); if(conn_candidate) { /* the winner gets the honour of being disconnected */ (void)Curl_disconnect(data, conn_candidate, /* dead_connection */ FALSE); } } return (conn_candidate == conn) ? FALSE : TRUE; }
/* This is a function that locks and waits until the name resolve operation has completed. If 'entry' is non-NULL, make it point to the resolved dns entry Return CURLE_COULDNT_RESOLVE_HOST if the host was not resolved, and CURLE_OPERATION_TIMEDOUT if a time-out occurred. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { CURLcode rc=CURLE_OK; struct SessionHandle *data = conn->data; struct timeval now = Curl_tvnow(); bool timedout = FALSE; long timeout = 300; /* default name resolve timeout in seconds */ long elapsed = 0; /* time taken so far */ /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ if(conn->data->set.connecttimeout) timeout = conn->data->set.connecttimeout; else if(conn->data->set.timeout) timeout = conn->data->set.timeout; /* Wait for the name resolve query to complete. */ while (1) { int nfds=0; fd_set read_fds, write_fds; struct timeval *tvp, tv, store; int count; store.tv_sec = (int)(timeout - elapsed); store.tv_usec = 0; FD_ZERO(&read_fds); FD_ZERO(&write_fds); nfds = ares_fds(data->state.areschannel, &read_fds, &write_fds); if (nfds == 0) break; tvp = ares_timeout(data->state.areschannel, &store, &tv); count = select(nfds, &read_fds, &write_fds, NULL, tvp); if (count < 0 && errno != EINVAL) break; else if(!count) { /* timeout */ timedout = TRUE; break; } ares_process(data->state.areschannel, &read_fds, &write_fds); elapsed = Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */ } /* Operation complete, if the lookup was successful we now have the entry in the cache. */ if(entry) *entry = conn->async.dns; if(!conn->async.dns) { /* a name was not resolved */ if(timedout || (conn->async.status == ARES_ETIMEOUT)) { failf(data, "Resolving host timed out: %s", conn->name); rc = CURLE_OPERATION_TIMEDOUT; } else if(conn->async.done) { failf(data, "Could not resolve host: %s (%s)", conn->name, ares_strerror(conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; /* close the connection, since we can't return failure here without cleaning up this connection properly */ Curl_disconnect(conn); } return rc; }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * This is the version for resolves-in-a-thread. */ CURLcode Curl_wait_for_resolv(struct connectdata *conn, struct Curl_dns_entry **entry) { struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct SessionHandle *data = conn->data; long timeout; DWORD status, ticks; CURLcode rc; curlassert (conn && td); /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ timeout = conn->data->set.connecttimeout ? conn->data->set.connecttimeout : conn->data->set.timeout ? conn->data->set.timeout : CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */ ticks = GetTickCount(); (void)ticks; status = WaitForSingleObject(td->thread_hnd, 1000UL*timeout); if (status == WAIT_OBJECT_0 || status == WAIT_ABANDONED) { /* Thread finished before timeout; propagate Winsock error to this thread. * 'conn->async.done = TRUE' is set in Curl_addrinfo4/6_callback(). */ WSASetLastError(conn->async.status); GetExitCodeThread(td->thread_hnd, &td->thread_status); TRACE(("%s() status %lu, thread retval %lu, ", THREAD_NAME, status, td->thread_status)); } else { conn->async.done = TRUE; td->thread_status = (DWORD)-1; TRACE(("%s() timeout, ", THREAD_NAME)); } TRACE(("elapsed %lu ms\n", GetTickCount()-ticks)); CloseHandle(td->thread_hnd); if(entry) *entry = conn->async.dns; rc = CURLE_OK; if (!conn->async.dns) { /* a name was not resolved */ if (td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA) { failf(data, "Resolving host timed out: %s", conn->host.name); rc = CURLE_OPERATION_TIMEDOUT; } else if(conn->async.done) { failf(data, "Could not resolve host: %s; %s", conn->host.name, Curl_strerror(conn,conn->async.status)); rc = CURLE_COULDNT_RESOLVE_HOST; } else rc = CURLE_OPERATION_TIMEDOUT; } destroy_thread_data(&conn->async); if(CURLE_OK != rc) /* close the connection, since we must not return failure from here without cleaning up this connection properly */ Curl_disconnect(conn); return (rc); }
CURLMcode curl_multi_perform(CURLM *multi_handle, int *running_handles) { struct Curl_multi *multi=(struct Curl_multi *)multi_handle; struct Curl_one_easy *easy; bool done; CURLMcode result=CURLM_OK; struct Curl_message *msg; bool connected; bool async; *running_handles = 0; /* bump this once for every living handle */ if(!GOOD_MULTI_HANDLE(multi)) return CURLM_BAD_HANDLE; easy=multi->easy.next; while(easy) { #if 0 fprintf(stderr, "HANDLE %p: State: %x\n", (char *)easy, easy->state); #endif do { if (CURLM_STATE_WAITCONNECT <= easy->state && easy->state <= CURLM_STATE_DO && easy->easy_handle->change.url_changed) { char *gotourl; Curl_posttransfer(easy->easy_handle); easy->result = Curl_done(&easy->easy_conn, CURLE_OK); if(CURLE_OK == easy->result) { gotourl = strdup(easy->easy_handle->change.url); if(gotourl) { easy->easy_handle->change.url_changed = FALSE; easy->result = Curl_follow(easy->easy_handle, gotourl); if(CURLE_OK == easy->result) easy->state = CURLM_STATE_CONNECT; else free(gotourl); } else { easy->result = CURLE_OUT_OF_MEMORY; easy->state = CURLM_STATE_COMPLETED; break; } } } easy->easy_handle->change.url_changed = FALSE; switch(easy->state) { case CURLM_STATE_INIT: /* init this transfer. */ easy->result=Curl_pretransfer(easy->easy_handle); if(CURLE_OK == easy->result) { /* after init, go CONNECT */ easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; easy->easy_handle->state.used_interface = Curl_if_multi; } break; case CURLM_STATE_CONNECT: /* Connect. We get a connection identifier filled in. */ Curl_pgrsTime(easy->easy_handle, TIMER_STARTSINGLE); easy->result = Curl_connect(easy->easy_handle, &easy->easy_conn, &async); if(CURLE_OK == easy->result) { if(async) /* We're now waiting for an asynchronous name lookup */ easy->state = CURLM_STATE_WAITRESOLVE; else { /* after the connect has been sent off, go WAITCONNECT */ easy->state = CURLM_STATE_WAITCONNECT; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_WAITRESOLVE: /* awaiting an asynch name resolve to complete */ { struct Curl_dns_entry *dns = NULL; /* check if we have the name resolved by now */ easy->result = Curl_is_resolved(easy->easy_conn, &dns); if(dns) { /* Perform the next step in the connection phase, and then move on to the WAITCONNECT state */ easy->result = Curl_async_resolved(easy->easy_conn); if(CURLE_OK != easy->result) /* if Curl_async_resolved() returns failure, the connection struct is already freed and gone */ easy->easy_conn = NULL; /* no more connection */ easy->state = CURLM_STATE_WAITCONNECT; } if(CURLE_OK != easy->result) { /* failure detected */ Curl_disconnect(easy->easy_conn); /* disconnect properly */ easy->easy_conn = NULL; /* no more connection */ break; } } break; case CURLM_STATE_WAITCONNECT: /* awaiting a completion of an asynch connect */ easy->result = Curl_is_connected(easy->easy_conn, FIRSTSOCKET, &connected); if(connected) easy->result = Curl_protocol_connect(easy->easy_conn); if(CURLE_OK != easy->result) { /* failure detected */ Curl_disconnect(easy->easy_conn); /* close the connection */ easy->easy_conn = NULL; /* no more connection */ break; } if(connected) { /* after the connect has completed, go DO */ easy->state = CURLM_STATE_DO; result = CURLM_CALL_MULTI_PERFORM; } break; case CURLM_STATE_DO: /* Do the fetch or put request */ easy->result = Curl_do(&easy->easy_conn); if(CURLE_OK == easy->result) { /* after do, go PERFORM... or DO_MORE */ if(easy->easy_conn->bits.do_more) { /* we're supposed to do more, but we need to sit down, relax and wait a little while first */ easy->state = CURLM_STATE_DO_MORE; result = CURLM_OK; } else { /* we're done with the DO, now PERFORM */ easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } } break; case CURLM_STATE_DO_MORE: /* * First, check if we really are ready to do more. */ easy->result = Curl_is_connected(easy->easy_conn, SECONDARYSOCKET, &connected); if(connected) { /* * When we are connected, DO MORE and then go PERFORM */ easy->result = Curl_do_more(easy->easy_conn); if(CURLE_OK == easy->result) easy->result = Curl_readwrite_init(easy->easy_conn); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_PERFORM; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_PERFORM: /* read/write data if it is ready to do so */ easy->result = Curl_readwrite(easy->easy_conn, &done); if(easy->result) { /* The transfer phase returned error, we mark the connection to get * closed to prevent being re-used. This is becasue we can't * possibly know if the connection is in a good shape or not now. */ easy->easy_conn->bits.close = TRUE; if(CURL_SOCKET_BAD != easy->easy_conn->sock[SECONDARYSOCKET]) { /* if we failed anywhere, we must clean up the secondary socket if it was used */ sclose(easy->easy_conn->sock[SECONDARYSOCKET]); easy->easy_conn->sock[SECONDARYSOCKET]=-1; } Curl_posttransfer(easy->easy_handle); Curl_done(&easy->easy_conn, easy->result); } /* after the transfer is done, go DONE */ else if(TRUE == done) { /* call this even if the readwrite function returned error */ Curl_posttransfer(easy->easy_handle); /* When we follow redirects, must to go back to the CONNECT state */ if(easy->easy_conn->newurl) { char *newurl = easy->easy_conn->newurl; easy->easy_conn->newurl = NULL; easy->result = Curl_done(&easy->easy_conn, CURLE_OK); if(easy->result == CURLE_OK) easy->result = Curl_follow(easy->easy_handle, newurl); if(CURLE_OK == easy->result) { easy->state = CURLM_STATE_CONNECT; result = CURLM_CALL_MULTI_PERFORM; } } else { easy->state = CURLM_STATE_DONE; result = CURLM_CALL_MULTI_PERFORM; } } break; case CURLM_STATE_DONE: /* post-transfer command */ easy->result = Curl_done(&easy->easy_conn, CURLE_OK); /* after we have DONE what we're supposed to do, go COMPLETED, and it doesn't matter what the Curl_done() returned! */ easy->state = CURLM_STATE_COMPLETED; break; case CURLM_STATE_COMPLETED: /* this is a completed transfer, it is likely to still be connected */ /* This node should be delinked from the list now and we should post an information message that we are complete. */ break; default: return CURLM_INTERNAL_ERROR; } if(CURLM_STATE_COMPLETED != easy->state) { if(CURLE_OK != easy->result) { /* * If an error was returned, and we aren't in completed state now, * then we go to completed and consider this transfer aborted. */ easy->state = CURLM_STATE_COMPLETED; } else /* this one still lives! */ (*running_handles)++; } } while (easy->easy_handle->change.url_changed); if ((CURLM_STATE_COMPLETED == easy->state) && !easy->msg) { /* clear out the usage of the shared DNS cache */ easy->easy_handle->hostcache = NULL; /* now add a node to the Curl_message linked list with this info */ msg = (struct Curl_message *)malloc(sizeof(struct Curl_message)); if(!msg) return CURLM_OUT_OF_MEMORY; msg->extmsg.msg = CURLMSG_DONE; msg->extmsg.easy_handle = easy->easy_handle; msg->extmsg.data.result = easy->result; msg->next=NULL; easy->msg = msg; easy->msg_num = 1; /* there is one unread message here */ multi->num_msgs++; /* increase message counter */ } easy = easy->next; /* operate on next handle */ } return result; }
/* * Curl_wait_for_resolv() waits for a resolve to finish. This function should * be avoided since using this risk getting the multi interface to "hang". * * If 'entry' is non-NULL, make it point to the resolved dns entry * * This is the version for resolves-in-a-thread. */ CURLcode Curl_wait_for_resolv( struct connectdata *conn, struct Curl_dns_entry **entry ) { struct thread_data *td = (struct thread_data*) conn->async.os_specific; struct SessionHandle *data = conn->data; long timeout; DWORD status, ticks; CURLcode rc; curlassert( conn && td ); /* now, see if there's a connect timeout or a regular timeout to use instead of the default one */ timeout = conn->data->set.connecttimeout ? conn->data->set.connecttimeout : conn->data->set.timeout ? conn->data->set.timeout : CURL_TIMEOUT_RESOLVE; /* default name resolve timeout */ ticks = GetTickCount(); /* wait for the thread to resolve the name */ status = WaitForSingleObject( td->event_resolved, 1000UL * timeout ); /* mark that we are now done waiting */ ReleaseMutex( td->mutex_waiting ); /* close our handle to the mutex, no point in hanging on to it */ CloseHandle( td->mutex_waiting ); td->mutex_waiting = NULL; /* close the event handle, it's useless now */ CloseHandle( td->event_resolved ); td->event_resolved = NULL; /* has the resolver thread succeeded in resolving our query ? */ if ( status == WAIT_OBJECT_0 ) { /* wait for the thread to exit, it's in the callback sequence */ if ( WaitForSingleObject( td->thread_hnd, 5000 ) == WAIT_TIMEOUT ) { TerminateThread( td->thread_hnd, 0 ); conn->async.done = TRUE; td->thread_status = (DWORD)-1; TRACE( ( "%s() thread stuck?!, ", THREAD_NAME ) ); } else { /* Thread finished before timeout; propagate Winsock error to this thread. * 'conn->async.done = TRUE' is set in Curl_addrinfo4/6_callback(). */ WSASetLastError( conn->async.status ); GetExitCodeThread( td->thread_hnd, &td->thread_status ); TRACE( ( "%s() status %lu, thread retval %lu, ", THREAD_NAME, status, td->thread_status ) ); } } else { conn->async.done = TRUE; td->thread_status = (DWORD)-1; TRACE( ( "%s() timeout, ", THREAD_NAME ) ); } TRACE( ( "elapsed %lu ms\n", GetTickCount() - ticks ) ); CloseHandle( td->thread_hnd ); if ( entry ) { *entry = conn->async.dns; } rc = CURLE_OK; if ( !conn->async.dns ) { /* a name was not resolved */ if ( td->thread_status == (DWORD)-1 || conn->async.status == NO_DATA ) { failf( data, "Resolving host timed out: %s", conn->host.name ); rc = CURLE_OPERATION_TIMEDOUT; } else if ( conn->async.done ) { failf( data, "Could not resolve host: %s; %s", conn->host.name, Curl_strerror( conn,conn->async.status ) ); rc = CURLE_COULDNT_RESOLVE_HOST; } else { rc = CURLE_OPERATION_TIMEDOUT; } } destroy_thread_data( &conn->async ); if ( CURLE_OK != rc ) { /* close the connection, since we must not return failure from here without cleaning up this connection properly */ Curl_disconnect( conn ); } return ( rc ); }