Пример #1
0
/*
 * init_resolve_thread() starts a new thread that performs the actual
 * resolve. This function returns before the resolve is done.
 *
 * Returns FALSE in case of failure, otherwise TRUE.
 */
static bool init_resolve_thread (struct connectdata *conn,
                                 const char *hostname, int port,
                                 const struct addrinfo *hints)
{
  struct thread_data *td = calloc(1, sizeof(struct thread_data));
  int err = ENOMEM;

  conn->async.os_specific = (void*) td;
  if(!td) 
    goto err_exit;

  conn->async.port = port;
  conn->async.done = FALSE;
  conn->async.status = 0;
  conn->async.dns = NULL;
  td->dummy_sock = CURL_SOCKET_BAD;
  td->thread_hnd = curl_thread_t_null;

  if (!init_thread_sync_data(&td->tsd, hostname, port, hints)) 
    goto err_exit;

  Curl_safefree(conn->async.hostname);
  conn->async.hostname = strdup(hostname);
  if(!conn->async.hostname)
    goto err_exit;

#ifdef WIN32
  /* This socket is only to keep Curl_resolv_fdset() and select() happy;
   * should never become signalled for read since it's unbound but
   * Windows needs at least 1 socket in select().
   */
  td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (td->dummy_sock == CURL_SOCKET_BAD)
    goto err_exit;
#endif

#ifdef HAVE_GETADDRINFO
  td->thread_hnd = Curl_thread_create(getaddrinfo_thread, &td->tsd);
#else
  td->thread_hnd = Curl_thread_create(gethostbyname_thread, &td->tsd);
#endif

  if(!td->thread_hnd) {
#ifndef _WIN32_WCE
    err = errno;
#endif
    goto err_exit;
  }

  return TRUE;

 err_exit:
  Curl_destroy_thread_data(&conn->async);

  SET_ERRNO(err);

  return FALSE;
}
Пример #2
0
/*
 * Curl_is_resolved() is called repeatedly to check if a previous name resolve
 * request has completed. It should also make sure to time-out if the
 * operation seems to take too long.
 */
CURLcode Curl_is_resolved(struct connectdata *conn,
                          struct Curl_dns_entry **entry)
{
  struct SessionHandle *data = conn->data;
  struct thread_data   *td = (struct thread_data*) conn->async.os_specific;
  int done = 0;
 
  *entry = NULL;

  if (!td) {
    DEBUGASSERT(td);
    return CURLE_COULDNT_RESOLVE_HOST;
  }

  Curl_mutex_acquire(td->tsd.mtx);
  done = td->tsd.done;
  Curl_mutex_release(td->tsd.mtx);

  if (done) { 
    getaddrinfo_complete(conn);
    if (td->poll_interval != 0)
        Curl_expire(conn->data, 0);
    Curl_destroy_thread_data(&conn->async);

    if(!conn->async.dns) {
      failf(data, "Could not resolve host: %s; %s",
            conn->host.name, Curl_strerror(conn, conn->async.status));
      return CURLE_COULDNT_RESOLVE_HOST;
    }
    *entry = conn->async.dns;
  } else {
    /* poll for name lookup done with exponential backoff up to 250ms */
    int elapsed;

    elapsed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);
    if (elapsed < 0) {
      elapsed = 0;
    }

    if (td->poll_interval == 0) {
      /* Start at 1ms poll interval */
      td->poll_interval = 1;
    } else if (elapsed >= td->interval_end) {
      /* Back-off exponentially if last interval expired  */
      td->poll_interval *= 2;
    }

    if (td->poll_interval > 250)
      td->poll_interval = 250;

    td->interval_end = elapsed + td->poll_interval;

    Curl_expire(conn->data, td->poll_interval);
  }

  return CURLE_OK;
}
Пример #3
0
/*
 * Curl_is_resolved() is called repeatedly to check if a previous name resolve
 * request has completed. It should also make sure to time-out if the
 * operation seems to take too long.
 */
CURLcode Curl_is_resolved(struct connectdata *conn,
                          struct Curl_dns_entry **entry)
{
  *entry = NULL;

  if (conn->async.done) {
    /* we're done */
    Curl_destroy_thread_data(&conn->async);
    if (!conn->async.dns) {
      TRACE(("Curl_is_resolved(): CURLE_COULDNT_RESOLVE_HOST\n"));
      return CURLE_COULDNT_RESOLVE_HOST;
    }
    *entry = conn->async.dns;
    TRACE(("resolved okay, dns %p\n", *entry));
  }
  return CURLE_OK;
}
Пример #4
0
/*
 * Curl_is_resolved() is called repeatedly to check if a previous name resolve
 * request has completed. It should also make sure to time-out if the
 * operation seems to take too long.
 */
CURLcode Curl_is_resolved(struct connectdata *conn,
                          struct Curl_dns_entry **entry)
{
  struct SessionHandle *data = conn->data;

  *entry = NULL;

  if(conn->async.done) {
    /* we're done */
    Curl_destroy_thread_data(&conn->async);
    if(!conn->async.dns) {
      failf(data, "Could not resolve host: %s; %s",
            conn->host.name, Curl_strerror(conn, conn->async.status));
      return CURLE_COULDNT_RESOLVE_HOST;
    }
    *entry = conn->async.dns;
  }
  return CURLE_OK;
}
Пример #5
0
/*
 * 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;
  CURLcode rc = CURLE_OK;

  DEBUGASSERT(conn && td);

  /* wait for the thread to resolve the name */
  if (Curl_thread_join(&td->thread_hnd)) {
    rc = getaddrinfo_complete(conn);
  } else {
    DEBUGASSERT(0);    
  }

  conn->async.done = TRUE;
    
  if(entry)
    *entry = conn->async.dns;

  if(!conn->async.dns) {
    /* a name was not resolved */
    if (conn->bits.httpproxy) {
      failf(data, "Could not resolve proxy: %s; %s",
            conn->async.hostname, Curl_strerror(conn, conn->async.status));
      rc = CURLE_COULDNT_RESOLVE_PROXY;
    } else {
      failf(data, "Could not resolve host: %s; %s",
            conn->async.hostname, Curl_strerror(conn, conn->async.status));
      rc = CURLE_COULDNT_RESOLVE_HOST;
    }
  }

  Curl_destroy_thread_data(&conn->async);

  if(!conn->async.dns)
    conn->bits.close = TRUE;

  return (rc);
}
Пример #6
0
/*
 * 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;

  DEBUGASSERT(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 * 1000; /* default name resolve timeout */
  ticks = GetTickCount();

  /* wait for the thread to resolve the name */
  status = WaitForSingleObject(td->event_resolved, 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().
       */
      SET_SOCKERRNO(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));

  if(entry)
    *entry = conn->async.dns;

  rc = CURLE_OK;

  if (!conn->async.dns) {
    /* a name was not resolved */
    if (td->thread_status == CURLE_OUT_OF_MEMORY) {
      rc = CURLE_OUT_OF_MEMORY;
      failf(data, "Could not resolve host: %s", curl_easy_strerror(rc));
    }
    else if(conn->async.done) {
      if(conn->bits.httpproxy) {
        failf(data, "Could not resolve proxy: %s; %s",
              conn->proxy.dispname, Curl_strerror(conn, conn->async.status));
        rc = CURLE_COULDNT_RESOLVE_PROXY;
      }
      else {
        failf(data, "Could not resolve host: %s; %s",
              conn->host.name, Curl_strerror(conn, conn->async.status));
        rc = CURLE_COULDNT_RESOLVE_HOST;
      }
    }
    else 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
      rc = CURLE_OPERATION_TIMEDOUT;
  }

  Curl_destroy_thread_data(&conn->async);

  if(!conn->async.dns)
    conn->bits.close = TRUE;

  return (rc);
}
Пример #7
0
/*
 * init_resolve_thread() starts a new thread that performs the actual
 * resolve. This function returns before the resolve is done.
 *
 * Returns FALSE in case of failure, otherwise TRUE.
 */
static bool init_resolve_thread (struct connectdata *conn,
                                 const char *hostname, int port,
                                 const Curl_addrinfo *hints)
{
  struct thread_data *td = calloc(sizeof(*td), 1);
  HANDLE thread_and_event[2] = {0};

  if (!td) {
    SET_ERRNO(ENOMEM);
    return FALSE;
  }

  Curl_safefree(conn->async.hostname);
  conn->async.hostname = strdup(hostname);
  if (!conn->async.hostname) {
    free(td);
    SET_ERRNO(ENOMEM);
    return FALSE;
  }

  conn->async.port = port;
  conn->async.done = FALSE;
  conn->async.status = 0;
  conn->async.dns = NULL;
  conn->async.os_specific = (void*) td;
  td->dummy_sock = CURL_SOCKET_BAD;

  /* Create the mutex used to inform the resolver thread that we're
   * still waiting, and take initial ownership.
   */
  td->mutex_waiting = CreateMutex(NULL, TRUE, NULL);
  if (td->mutex_waiting == NULL) {
    Curl_destroy_thread_data(&conn->async);
    SET_ERRNO(EAGAIN);
    return FALSE;
  }

  /* Create the event that the thread uses to inform us that it's
   * done resolving. Do not signal it.
   */
  td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (td->event_resolved == NULL) {
    Curl_destroy_thread_data(&conn->async);
    SET_ERRNO(EAGAIN);
    return FALSE;
  }
  /* Create the mutex used to serialize access to event_terminated
   * between us and resolver thread.
   */
  td->mutex_terminate = CreateMutex(NULL, FALSE, NULL);
  if (td->mutex_terminate == NULL) {
    Curl_destroy_thread_data(&conn->async);
    SET_ERRNO(EAGAIN);
    return FALSE;
  }
  /* Create the event used to signal thread that it should terminate.
   */
  td->event_terminate = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (td->event_terminate == NULL) {
    Curl_destroy_thread_data(&conn->async);
    SET_ERRNO(EAGAIN);
    return FALSE;
  }
  /* Create the event used by thread to inform it has initialized its own data.
   */
  td->event_thread_started = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (td->event_thread_started == NULL) {
    Curl_destroy_thread_data(&conn->async);
    SET_ERRNO(EAGAIN);
    return FALSE;
  }

#ifdef _WIN32_WCE
  td->thread_hnd = (HANDLE) CreateThread(NULL, 0,
                                         (LPTHREAD_START_ROUTINE) THREAD_FUNC,
                                         conn, 0, &td->thread_id);
#else
  td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC,
                                           conn, 0, &td->thread_id);
#endif

#ifdef CURLRES_IPV6
  DEBUGASSERT(hints);
  td->hints = *hints;
#else
  (void) hints;
#endif

  if (!td->thread_hnd) {
#ifdef _WIN32_WCE
     TRACE(("CreateThread() failed; %s\n", Curl_strerror(conn, ERRNO)));
#else
     SET_ERRNO(errno);
     TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn, ERRNO)));
#endif
     Curl_destroy_thread_data(&conn->async);
     return FALSE;
  }
  /* Waiting until the thread will initialize its data or it will exit due errors.
   */
  thread_and_event[0] = td->thread_hnd;
  thread_and_event[1] = td->event_thread_started;
  if (WaitForMultipleObjects(sizeof(thread_and_event) /
                             sizeof(thread_and_event[0]),
                             (const HANDLE*)thread_and_event, FALSE,
                             INFINITE) == WAIT_FAILED) {
    /* The resolver thread has been created,
     * most probably it works now - ignoring this "minor" error
     */
  }
  /* This socket is only to keep Curl_resolv_fdset() and select() happy;
   * should never become signalled for read/write since it's unbound but
   * Windows needs atleast 1 socket in select().
   */
  td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
  return TRUE;
}
Пример #8
0
/*
 * init_resolve_thread() starts a new thread that performs the actual
 * resolve. This function returns before the resolve is done.
 *
 * Returns FALSE in case of failure, otherwise TRUE.
 */
static bool init_resolve_thread (struct connectdata *conn,
                                 const char *hostname, int port,
                                 const Curl_addrinfo *hints)
{
  struct thread_data *td = calloc(sizeof(*td), 1);

  if (!td) {
    SetLastError(ENOMEM);
    return FALSE;
  }

  Curl_safefree(conn->async.hostname);
  conn->async.hostname = strdup(hostname);
  if (!conn->async.hostname) {
    free(td);
    SetLastError(ENOMEM);
    return FALSE;
  }

  conn->async.port = port;
  conn->async.done = FALSE;
  conn->async.status = 0;
  conn->async.dns = NULL;
  conn->async.os_specific = (void*) td;
  td->dummy_sock = CURL_SOCKET_BAD;

  /* Create the mutex used to inform the resolver thread that we're
   * still waiting, and take initial ownership.
   */
  td->mutex_waiting = CreateMutex(NULL, TRUE, NULL);
  if (td->mutex_waiting == NULL) {
    Curl_destroy_thread_data(&conn->async);
    SetLastError(EAGAIN);
    return FALSE;
  }

  /* Create the event that the thread uses to inform us that it's
   * done resolving. Do not signal it.
   */
  td->event_resolved = CreateEvent(NULL, TRUE, FALSE, NULL);
  if (td->event_resolved == NULL) {
    Curl_destroy_thread_data(&conn->async);
    SetLastError(EAGAIN);
    return FALSE;
  }

  td->stderr_file = stderr;

#ifdef _WIN32_WCE
  td->thread_hnd = (HANDLE) CreateThread(NULL, 0,
                                         (LPTHREAD_START_ROUTINE) THREAD_FUNC,
                                         conn, 0, &td->thread_id);
#else
  td->thread_hnd = (HANDLE) _beginthreadex(NULL, 0, THREAD_FUNC,
                                           conn, 0, &td->thread_id);
#endif

#ifdef CURLRES_IPV6
  curlassert(hints);
  td->hints = *hints;
#else
  (void) hints;
#endif

  if (!td->thread_hnd) {
     SetLastError(errno);
     TRACE(("_beginthreadex() failed; %s\n", Curl_strerror(conn,errno)));
     Curl_destroy_thread_data(&conn->async);
     return FALSE;
  }
  /* This socket is only to keep Curl_resolv_fdset() and select() happy;
   * should never become signalled for read/write since it's unbound but
   * Windows needs atleast 1 socket in select().
   */
  td->dummy_sock = socket(AF_INET, SOCK_DGRAM, 0);
  return TRUE;
}