Пример #1
0
/*
 * 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;
}
Пример #2
0
/*
 * Curl_getaddrinfo() when built ipv6-enabled (non-threading and
 * non-ares version).
 *
 * Returns name information about the given hostname and port number. If
 * successful, the 'addrinfo' is returned and the forth argument will point to
 * memory we need to free after use. That memory *MUST* be freed with
 * Curl_freeaddrinfo(), nothing else.
 */
Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
                                const char *hostname,
                                int port,
                                int *waitp)
{
  struct addrinfo hints;
  Curl_addrinfo *res;
  int error;
  char sbuf[12];
  char *sbufptr = NULL;
  char addrbuf[128];
  int pf;
  struct SessionHandle *data = conn->data;

  *waitp = 0; /* synchronous response only */

  /*
   * Check if a limited name resolve has been requested.
   */
  switch(conn->ip_version) {
  case CURL_IPRESOLVE_V4:
    pf = PF_INET;
    break;
  case CURL_IPRESOLVE_V6:
    pf = PF_INET6;
    break;
  default:
    pf = PF_UNSPEC;
    break;
  }

  if((pf != PF_INET) && !Curl_ipv6works())
    /* the stack seems to be a non-ipv6 one */
    pf = PF_INET;

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = pf;
  hints.ai_socktype = conn->socktype;

  if((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
     (1 == Curl_inet_pton(AF_INET6, hostname, addrbuf))) {
    /* the given address is numerical only, prevent a reverse lookup */
    hints.ai_flags = AI_NUMERICHOST;
  }

  if(port) {
    snprintf(sbuf, sizeof(sbuf), "%d", port);
    sbufptr=sbuf;
  }
  error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res);
  if(error) {
    infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
    return NULL;
  }

  dump_addrinfo(conn, res);

  return res;
}
Пример #3
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;
	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 */
}
Пример #4
0
/*
 * 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;
  char   service [NI_MAXSERV];
  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;
  }

  Curl_mutex_acquire(tsd->mtx);
  tsd->done = 1;
  Curl_mutex_release(tsd->mtx);

  return 0;
}
Пример #5
0
/*
 * Curl_resolver_getaddrinfo() - for getaddrinfo
 */
Curl_addrinfo *Curl_resolver_getaddrinfo(struct connectdata *conn,
                                         const char *hostname,
                                         int port,
                                         int *waitp)
{
  struct addrinfo hints;
  struct in_addr in;
  Curl_addrinfo *res;
  int error;
  char sbuf[12];
  int pf = PF_INET;
#ifdef CURLRES_IPV6
  struct in6_addr in6;
#endif /* CURLRES_IPV6 */

  *waitp = 0; /* default to synchronous response */

#ifndef USE_RESOLVE_ON_IPS
  /* First check if this is an IPv4 address string */
  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
    /* This is a dotted IP address 123.123.123.123-style */
    return Curl_ip2addr(AF_INET, &in, hostname, port);

#ifdef CURLRES_IPV6
  /* check if this is an IPv6 address string */
  if(Curl_inet_pton(AF_INET6, hostname, &in6) > 0)
    /* This is an IPv6 address literal */
    return Curl_ip2addr(AF_INET6, &in6, hostname, port);
#endif /* CURLRES_IPV6 */
#endif /* !USE_RESOLVE_ON_IPS */

#ifdef CURLRES_IPV6
  /*
   * Check if a limited name resolve has been requested.
   */
  switch(conn->ip_version) {
  case CURL_IPRESOLVE_V4:
    pf = PF_INET;
    break;
  case CURL_IPRESOLVE_V6:
    pf = PF_INET6;
    break;
  default:
    pf = PF_UNSPEC;
    break;
  }

  if((pf != PF_INET) && !Curl_ipv6works())
    /* The stack seems to be a non-IPv6 one */
    pf = PF_INET;
#endif /* CURLRES_IPV6 */

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = pf;
  hints.ai_socktype = conn->socktype;

  snprintf(sbuf, sizeof(sbuf), "%d", port);

  /* fire up a new resolver thread! */
  if(init_resolve_thread(conn, hostname, port, &hints)) {
    *waitp = 1; /* expect asynchronous response */
    return NULL;
  }

  /* fall-back to blocking version */
  infof(conn->data, "init_resolve_thread() failed for %s; %s\n",
        hostname, Curl_strerror(conn, ERRNO));

  error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
  if(error) {
    infof(conn->data, "getaddrinfo() failed for %s:%d; %s\n",
          hostname, port, Curl_strerror(conn, SOCKERRNO));
    return NULL;
  }
  else {
    Curl_addrinfo_set_port(res, port);
  }

  return res;
}
Пример #6
0
/*
 * Curl_getaddrinfo() - for Windows threading IPv6 enabled
 */
Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
                                const char *hostname,
                                int port,
                                int *waitp)
{
  struct addrinfo hints;
  Curl_addrinfo *res;
  int error;
  char sbuf[NI_MAXSERV];
  int pf;
  struct SessionHandle *data = conn->data;

  *waitp = FALSE; /* default to synch response */

  /*
   * Check if a limited name resolve has been requested.
   */
  switch(data->set.ip_version) {
  case CURL_IPRESOLVE_V4:
    pf = PF_INET;
    break;
  case CURL_IPRESOLVE_V6:
    pf = PF_INET6;
    break;
  default:
    pf = PF_UNSPEC;
    break;
  }

  if (pf != PF_INET) {
    /* see if we have an IPv6 stack */
    curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
    if(s == CURL_SOCKET_BAD) {
      /* Some non-IPv6 stacks have been found to make very slow name resolves
       * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
       * the stack seems to be a non-ipv6 one. */

      pf = PF_INET;
    }
    else {
      /* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
       * possible checks. And close the socket again.
       */
      sclose(s);
    }
  }

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = pf;
  hints.ai_socktype = conn->socktype;
#if 0 /* removed nov 8 2005 before 7.15.1 */
  hints.ai_flags = AI_CANONNAME;
#endif
  itoa(port, sbuf, 10);

  /* fire up a new resolver thread! */
  if(init_resolve_thread(conn, hostname, port, &hints)) {
    *waitp = TRUE;  /* please wait for the response */
    return NULL;
  }

  /* fall-back to blocking version */
  infof(data, "init_resolve_thread() failed for %s; %s\n",
        hostname, Curl_strerror(conn, ERRNO));

  error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
  if(error) {
    infof(data, "getaddrinfo() failed for %s:%d; %s\n",
          hostname, port, Curl_strerror(conn, SOCKERRNO));
    return NULL;
  }
  return res;
}
Пример #7
0
/*
 * Curl_getaddrinfo() when built ipv6-enabled (non-threading and
 * non-ares version).
 *
 * Returns name information about the given hostname and port number. If
 * successful, the 'addrinfo' is returned and the forth argument will point to
 * memory we need to free after use. That memory *MUST* be freed with
 * Curl_freeaddrinfo(), nothing else.
 */
Curl_addrinfo *Curl_getaddrinfo(struct connectdata *conn,
								const char *hostname,
								int port,
								int *waitp)
{
	struct addrinfo hints;
	Curl_addrinfo *res;
	int error;
	char sbuf[NI_MAXSERV];
	char *sbufptr = NULL;
	char addrbuf[128];
	int pf;
	struct SessionHandle *data = conn->data;

	*waitp = 0; /* don't wait, we have the response now */

	/*
	 * Check if a limited name resolve has been requested.
	 */
	switch (data->set.ip_version)
	{
	case CURL_IPRESOLVE_V4:
		pf = PF_INET;
		break;
	case CURL_IPRESOLVE_V6:
		pf = PF_INET6;
		break;
	default:
		pf = PF_UNSPEC;
		break;
	}

	if (pf != PF_INET)
	{
		/* see if we have an IPv6 stack */
		curl_socket_t s = socket(PF_INET6, SOCK_DGRAM, 0);
		if (s == CURL_SOCKET_BAD)
		{
			/* Some non-IPv6 stacks have been found to make very slow name resolves
			 * when PF_UNSPEC is used, so thus we switch to a mere PF_INET lookup if
			 * the stack seems to be a non-ipv6 one. */

			pf = PF_INET;
		}
		else
		{
			/* This seems to be an IPv6-capable stack, use PF_UNSPEC for the widest
			 * possible checks. And close the socket again.
			 */
			sclose(s);
		}
	}

	MEMSET(&hints, 0, sizeof(hints));
	hints.ai_family = pf;
	hints.ai_socktype = conn->socktype;

	if ((1 == Curl_inet_pton(AF_INET, hostname, addrbuf)) ||
			(1 == Curl_inet_pton(AF_INET6, hostname, addrbuf)))
	{
		/* the given address is numerical only, prevent a reverse lookup */
		hints.ai_flags = AI_NUMERICHOST;
	}
#ifdef HAVE_GSSAPI
	if (conn->data->set.krb)
		/* if krb is used, we (might) need the canonical host name */
		hints.ai_flags |= AI_CANONNAME;
#endif

	if (port)
	{
		snprintf(sbuf, sizeof(sbuf), "%d", port);
		sbufptr = sbuf;
	}
	error = Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &res);
	if (error)
	{
		infof(data, "getaddrinfo(3) failed for %s:%d\n", hostname, port);
		return NULL;
	}

	dump_addrinfo(conn, res);

	return res;
}
Пример #8
0
/*
 * Curl_ipv4_resolve_r() - ipv4 threadsafe resolver function.
 *
 * This is used for both synchronous and asynchronous resolver builds,
 * implying that only threadsafe code and function calls may be used.
 *
 */
Curl_addrinfo *Curl_ipv4_resolve_r(const char *hostname,
                                   int port)
{
#if !defined(HAVE_GETADDRINFO_THREADSAFE) && defined(HAVE_GETHOSTBYNAME_R_3)
  int res;
#endif
  Curl_addrinfo *ai = NULL;
  struct hostent *h = NULL;
  struct in_addr in;
  struct hostent *buf = NULL;

  if(Curl_inet_pton(AF_INET, hostname, &in) > 0)
    /* This is a dotted IP address 123.123.123.123-style */
    return Curl_ip2addr(AF_INET, &in, hostname, port);

#if defined(HAVE_GETADDRINFO_THREADSAFE)
  else {
    struct addrinfo hints;
    char sbuf[NI_MAXSERV];
    char *sbufptr = NULL;

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = PF_INET;
    hints.ai_socktype = SOCK_STREAM;
    if(port) {
      snprintf(sbuf, sizeof(sbuf), "%d", port);
      sbufptr = sbuf;
    }

    (void)Curl_getaddrinfo_ex(hostname, sbufptr, &hints, &ai);

#elif defined(HAVE_GETHOSTBYNAME_R)
  /*
   * gethostbyname_r() is the preferred resolve function for many platforms.
   * Since there are three different versions of it, the following code is
   * somewhat #ifdef-ridden.
   */
  else {
    int h_errnop;

    buf = calloc(1, CURL_HOSTENT_SIZE);
    if(!buf)
      return NULL; /* major failure */
    /*
     * The clearing of the buffer is a workaround for a gethostbyname_r bug in
     * qnx nto and it is also _required_ for some of these functions on some
     * platforms.
     */

#if defined(HAVE_GETHOSTBYNAME_R_5)
    /* Solaris, IRIX and more */
    h = gethostbyname_r(hostname,
                        (struct hostent *)buf,
                        (char *)buf + sizeof(struct hostent),
                        CURL_HOSTENT_SIZE - sizeof(struct hostent),
                        &h_errnop);

    /* If the buffer is too small, it returns NULL and sets errno to
     * ERANGE. The errno is thread safe if this is compiled with
     * -D_REENTRANT as then the 'errno' variable is a macro defined to get
     * used properly for threads.
     */

    if(h) {
      ;
    }
    else
#elif defined(HAVE_GETHOSTBYNAME_R_6)
    /* Linux */

    (void)gethostbyname_r(hostname,
                        (struct hostent *)buf,
                        (char *)buf + sizeof(struct hostent),
                        CURL_HOSTENT_SIZE - sizeof(struct hostent),
                        &h, /* DIFFERENCE */
                        &h_errnop);
    /* Redhat 8, using glibc 2.2.93 changed the behavior. Now all of a
     * sudden this function returns EAGAIN if the given buffer size is too
     * small. Previous versions are known to return ERANGE for the same
     * problem.
     *
     * This wouldn't be such a big problem if older versions wouldn't
     * sometimes return EAGAIN on a common failure case. Alas, we can't
     * assume that EAGAIN *or* ERANGE means ERANGE for any given version of
     * glibc.
     *
     * For now, we do that and thus we may call the function repeatedly and
     * fail for older glibc versions that return EAGAIN, until we run out of
     * buffer size (step_size grows beyond CURL_HOSTENT_SIZE).
     *
     * If anyone has a better fix, please tell us!
     *
     * -------------------------------------------------------------------
     *
     * On October 23rd 2003, Dan C dug up more details on the mysteries of
     * gethostbyname_r() in glibc:
     *
     * In glibc 2.2.5 the interface is different (this has also been
     * discovered in glibc 2.1.1-6 as shipped by Redhat 6). What I can't
     * explain, is that tests performed on glibc 2.2.4-34 and 2.2.4-32
     * (shipped/upgraded by Redhat 7.2) don't show this behavior!
     *
     * In this "buggy" version, the return code is -1 on error and 'errno'
     * is set to the ERANGE or EAGAIN code. Note that 'errno' is not a
     * thread-safe variable.
     */

    if(!h) /* failure */
#elif defined(HAVE_GETHOSTBYNAME_R_3)
    /* AIX, Digital Unix/Tru64, HPUX 10, more? */

    /* For AIX 4.3 or later, we don't use gethostbyname_r() at all, because of
     * the plain fact that it does not return unique full buffers on each
     * call, but instead several of the pointers in the hostent structs will
     * point to the same actual data! This have the unfortunate down-side that
     * our caching system breaks down horribly. Luckily for us though, AIX 4.3
     * and more recent versions have a "completely thread-safe"[*] libc where
     * all the data is stored in thread-specific memory areas making calls to
     * the plain old gethostbyname() work fine even for multi-threaded
     * programs.
     *
     * This AIX 4.3 or later detection is all made in the configure script.
     *
     * Troels Walsted Hansen helped us work this out on March 3rd, 2003.
     *
     * [*] = much later we've found out that it isn't at all "completely
     * thread-safe", but at least the gethostbyname() function is.
     */

    if(CURL_HOSTENT_SIZE >=
       (sizeof(struct hostent)+sizeof(struct hostent_data))) {

      /* August 22nd, 2000: Albert Chin-A-Young brought an updated version
       * that should work! September 20: Richard Prescott worked on the buffer
       * size dilemma.
       */

      res = gethostbyname_r(hostname,
                            (struct hostent *)buf,
                            (struct hostent_data *)((char *)buf +
                                                    sizeof(struct hostent)));
      h_errnop = SOCKERRNO; /* we don't deal with this, but set it anyway */
    }
    else
      res = -1; /* failure, too smallish buffer size */

    if(!res) { /* success */

      h = buf; /* result expected in h */

      /* This is the worst kind of the different gethostbyname_r() interfaces.
       * Since we don't know how big buffer this particular lookup required,
       * we can't realloc down the huge alloc without doing closer analysis of
       * the returned data. Thus, we always use CURL_HOSTENT_SIZE for every
       * name lookup. Fixing this would require an extra malloc() and then
       * calling Curl_addrinfo_copy() that subsequent realloc()s down the new
       * memory area to the actually used amount.
       */
    }
    else
#endif /* HAVE_...BYNAME_R_5 || HAVE_...BYNAME_R_6 || HAVE_...BYNAME_R_3 */
    {
      h = NULL; /* set return code to NULL */
      free(buf);
    }
#else /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */
    /*
     * Here is code for platforms that don't have a thread safe
     * getaddrinfo() nor gethostbyname_r() function or for which
     * gethostbyname() is the preferred one.
     */
  else {
    h = gethostbyname((void*)hostname);
#endif /* HAVE_GETADDRINFO_THREADSAFE || HAVE_GETHOSTBYNAME_R */
  }

  if(h) {
    ai = Curl_he2ai(h, port);

    if(buf) /* used a *_r() function */
      free(buf);
  }

  return ai;
}
Пример #9
0
/*
 * Curl_resolver_getaddrinfo() - for getaddrinfo
 */
Curl_addrinfo *Curl_resolver_getaddrinfo(CURL *data,
                                         const char *hostname,
                                         int port,
                                         int *waitp)
{
  struct connectdata *conn = data->easy_conn;
  struct addrinfo hints;
  Curl_addrinfo *res;
  int error;
  char sbuf[12];
  int pf = PF_INET;

  *waitp = 0; /* default to synchronous response */
#ifndef USE_RESOLVE_ON_IPS
  res = Curl_str2addr(hostname, port);
  if(res)
    return res;
#endif

#ifdef CURLRES_IPV6
  /*
   * Check if a limited name resolve has been requested.
   */
  switch(conn->ip_version) {
  case CURL_IPRESOLVE_V4:
    pf = PF_INET;
    break;
  case CURL_IPRESOLVE_V6:
    pf = PF_INET6;
    break;
  default:
    pf = PF_UNSPEC;
    break;
  }

  if((pf != PF_INET) && !Curl_ipv6works())
    /* The stack seems to be a non-IPv6 one */
    pf = PF_INET;
#endif /* CURLRES_IPV6 */

  memset(&hints, 0, sizeof(hints));
  hints.ai_family = pf;
  hints.ai_socktype = conn->socktype;

  snprintf(sbuf, sizeof(sbuf), "%d", port);

  /* fire up a new resolver thread! */
  if(init_resolve_thread(conn, hostname, port, &hints)) {
    *waitp = 1; /* expect asynchronous response */
    return NULL;
  }

  /* fall-back to blocking version */
  infof(data, "init_resolve_thread() failed for %s; %s\n",
        hostname, Curl_strerror(conn, errno));

  error = Curl_getaddrinfo_ex(hostname, sbuf, &hints, &res);
  if(error) {
    infof(data, "getaddrinfo() failed for %s:%d; %s\n",
          hostname, port, Curl_strerror(conn, SOCKERRNO));
    return NULL;
  }
  else {
    Curl_addrinfo_set_port(res, port);
  }

  return res;
}