Skip to content
url.c 197 KiB
Newer Older
  long score;
  struct timeval now;
  struct connectdata *conn_candidate = NULL;
  struct connectbundle *bundle;

  now = Curl_tvnow();

  Curl_hash_start_iterate(&bc->hash, &iter);

  he = Curl_hash_next_element(&iter);
  while(he) {
    struct connectdata *conn;

    bundle = he->ptr;

    curr = bundle->conn_list->head;
    while(curr) {
      conn = curr->ptr;

      if(!conn->inuse) {
        /* Set higher score for the age passed since the connection was used */
        score = Curl_tvdiff(now, conn->now);

        if(score > highscore) {
          highscore = score;
          conn_candidate = conn;
        }
      }
      curr = curr->next;
    }

    he = Curl_hash_next_element(&iter);
  }

  return conn_candidate;
}
/*
 * This function finds the connection in the connection
 * bundle that has been unused for the longest time.
 *
 * Returns the pointer to the oldest idle connection, or NULL if none was
 * found.
 */
static struct connectdata *
find_oldest_idle_connection_in_bundle(struct SessionHandle *data,
                                      struct connectbundle *bundle)
{
  struct curl_llist_element *curr;
  long highscore=-1;
  long score;
  struct timeval now;
  struct connectdata *conn_candidate = NULL;
  struct connectdata *conn;

  (void)data;

  now = Curl_tvnow();

  curr = bundle->conn_list->head;
  while(curr) {
    conn = curr->ptr;

    if(!conn->inuse) {
      /* Set higher score for the age passed since the connection was used */
      score = Curl_tvdiff(now, conn->now);

      if(score > highscore) {
        highscore = score;
        conn_candidate = conn;
      }
    }
    curr = curr->next;
  }

  return conn_candidate;
}

/*
 * This function checks if given connection is dead and disconnects if so.
 * (That also removes it from the connection cache.)
 *
 * Returns TRUE if the connection actually was dead and disconnected.
 */
static bool disconnect_if_dead(struct connectdata *conn,
                               struct SessionHandle *data)
{
  size_t pipeLen = conn->send_pipe->size + conn->recv_pipe->size;
  if(!pipeLen && !conn->inuse) {
    /* The check for a dead socket makes sense only if there are no
       handles in pipeline and the connection isn't already marked in
       use */
    bool dead;
    if(conn->handler->protocol & CURLPROTO_RTSP)
      /* RTSP is a special case due to RTP interleaving */
      dead = Curl_rtsp_connisdead(conn);
    else
      dead = SocketIsDead(conn->sock[FIRSTSOCKET]);

    if(dead) {
      conn->data = data;
      infof(data, "Connection %ld seems to be dead!\n", conn->connection_id);

      /* disconnect resources */
      Curl_disconnect(conn, /* dead_connection */TRUE);
      return TRUE;
    }
  }
  return FALSE;
}

/*
 * Wrapper to use disconnect_if_dead() function in Curl_conncache_foreach()
 *
 * Returns always 0.
 */
static int call_disconnect_if_dead(struct connectdata *conn,
                                      void *param)
{
  struct SessionHandle* data = (struct SessionHandle*)param;
  disconnect_if_dead(conn, data);
  return 0; /* continue iteration */
}

/*
 * This function scans the connection cache for half-open/dead connections,
 * closes and removes them.
 * The cleanup is done at most once per second.
 */
static void prune_dead_connections(struct SessionHandle *data)
{
  struct timeval now = Curl_tvnow();
  long elapsed = Curl_tvdiff(now, data->state.conn_cache->last_cleanup);

  if(elapsed >= 1000L) {
    Curl_conncache_foreach(data->state.conn_cache, data,
                           call_disconnect_if_dead);
    data->state.conn_cache->last_cleanup = now;
  }
}


static size_t max_pipeline_length(struct Curl_multi *multi)
{
  return multi ? multi->max_pipeline_length : 0;
}


 * Given one filled in connection struct (named needle), this function should
 * detect if there already is one that has all the significant details
 * exactly the same and thus should be used instead.
 *
 * If there is a match, this function returns TRUE - and has marked the
 * connection as 'in-use'. It must later be called with ConnectionDone() to
 * return back to 'idle' (unused) state.
 *
 * The force_reuse flag is set if the connection must be used, even if
 * the pipelining strategy wants to open a new connection instead of reusing.
ConnectionExists(struct SessionHandle *data,
                 struct connectdata *needle,
                 struct connectdata **usethis,
Daniel Stenberg's avatar
Daniel Stenberg committed
                 bool *force_reuse,
                 bool *waitpipe)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct connectdata *check;
  struct connectdata *chosen = 0;
  bool foundPendingCandidate = FALSE;
  bool canPipeline = IsPipeliningPossible(data, needle);
  struct connectbundle *bundle;

#ifdef USE_NTLM
  bool wantNTLMhttp = ((data->state.authhost.want &
                      (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
                      (needle->handler->protocol & PROTO_FAMILY_HTTP));
  bool wantProxyNTLMhttp = (needle->bits.proxy_user_passwd &&
                           ((data->state.authproxy.want &
                           (CURLAUTH_NTLM | CURLAUTH_NTLM_WB)) &&
                           (needle->handler->protocol & PROTO_FAMILY_HTTP)));
Daniel Stenberg's avatar
Daniel Stenberg committed
  *waitpipe = FALSE;

  /* We can't pipe if the site is blacklisted */
  if(canPipeline && Curl_pipeline_site_blacklisted(data, needle)) {
    canPipeline = FALSE;
  }

  /* Look up the bundle with all the connections to this
     particular host */
  bundle = Curl_conncache_find_bundle(needle, data->state.conn_cache);
    /* Max pipe length is zero (unlimited) for multiplexed connections */
    size_t max_pipe_len = (bundle->multiuse != BUNDLE_MULTIPLEX)?
      max_pipeline_length(data->multi):0;
    size_t best_pipe_len = max_pipe_len;
    const char *hostname;

    if(needle->bits.conn_to_host)
      hostname = needle->conn_to_host.name;
    else
      hostname = needle->host.name;
    infof(data, "Found bundle for host %s: %p [%s]\n",
          hostname, (void *)bundle,
          (bundle->multiuse== BUNDLE_PIPELINING?
           "can pipeline":
           (bundle->multiuse== BUNDLE_MULTIPLEX?
            "can multiplex":"serially")));
    /* We can't pipe if we don't know anything about the server */
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(canPipeline) {
      if(bundle->multiuse <= BUNDLE_UNKNOWN) {
        if((bundle->multiuse == BUNDLE_UNKNOWN) && data->set.pipewait) {
          infof(data, "Server doesn't support multi-use yet, wait\n");
          *waitpipe = TRUE;
          return FALSE; /* no re-use */
        }

        infof(data, "Server doesn't support multi-use (yet)\n");
        canPipeline = FALSE;
      }
      if((bundle->multiuse == BUNDLE_PIPELINING) &&
         !Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1)) {
        /* not asked for, switch off */
        infof(data, "Could pipeline, but not asked to!\n");
        canPipeline = FALSE;
      }
      else if((bundle->multiuse == BUNDLE_MULTIPLEX) &&
              !Curl_pipeline_wanted(data->multi, CURLPIPE_MULTIPLEX)) {
        infof(data, "Could multiplex, but not asked to!\n");
        canPipeline = FALSE;
      }
    curr = bundle->conn_list->head;
    while(curr) {
      bool match = FALSE;
      size_t pipeLen;
      /*
       * Note that if we use a HTTP proxy, we check connections to that
       * proxy and not to the actual remote server.
       */
      check = curr->ptr;
      curr = curr->next;

      if(disconnect_if_dead(check, data))
        continue;
      pipeLen = check->send_pipe->size + check->recv_pipe->size;

        if(!check->bits.multiplex) {
          /* If not multiplexing, make sure the pipe has only GET requests */
          struct SessionHandle* sh = gethandleathead(check->send_pipe);
          struct SessionHandle* rh = gethandleathead(check->recv_pipe);
          if(sh) {
            if(!IsPipeliningPossible(sh, check))
              continue;
          }
          else if(rh) {
            if(!IsPipeliningPossible(rh, check))
              continue;
          }
      else {
        if(pipeLen > 0) {
          /* can only happen within multi handles, and means that another easy
             handle is using this connection */
        if(Curl_resolver_asynch()) {
          /* ip_addr_str[0] is NUL only if the resolving of the name hasn't
             completed yet and until then we don't re-use this connection */
          if(!check->ip_addr_str[0]) {
            infof(data,
                  "Connection #%ld is still name resolving, can't reuse\n",
                  check->connection_id);
            continue;
          }

        if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) ||
           check->bits.close) {
          if(!check->bits.close)
            foundPendingCandidate = TRUE;
          /* Don't pick a connection that hasn't connected yet or that is going
             to get closed. */
          infof(data, "Connection #%ld isn't open enough, can't reuse\n",
                check->connection_id);
#ifdef DEBUGBUILD
          if(check->recv_pipe->size > 0) {
            infof(data,
                  "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
                  check->connection_id);
          }
      if((needle->handler->flags&PROTOPT_SSL) !=
         (check->handler->flags&PROTOPT_SSL))
        /* don't do mixed SSL and non-SSL connections */
        if(get_protocol_family(check->handler->protocol) !=
           needle->handler->protocol || !check->tls_upgraded)
          /* except protocols that have been upgraded via TLS */
          continue;
      if(needle->handler->flags&PROTOPT_SSL) {
        if((data->set.ssl.verifypeer != check->verifypeer) ||
           (data->set.ssl.verifyhost != check->verifyhost))
          continue;
      }

      if(needle->bits.proxy != check->bits.proxy)
        /* don't do mixed proxy and non-proxy connections */
Yang Tse's avatar
 
Yang Tse committed
        continue;

      if(needle->bits.conn_to_host != check->bits.conn_to_host)
        /* don't mix connections that use the "connect to host" feature and
         * connections that don't use this feature */
        continue;

      if(needle->bits.conn_to_port != check->bits.conn_to_port)
        /* don't mix connections that use the "connect to port" feature and
         * connections that don't use this feature */
        continue;

      if(!canPipeline && check->inuse)
        /* this request can't be pipelined but the checked connection is
           already in use so we skip it */
      if(needle->localdev || needle->localport) {
        /* If we are bound to a specific local end (IP+port), we must not
           re-use a random other one, although if we didn't ask for a
           particular one we can reuse one that was bound.

           This comparison is a bit rough and too strict. Since the input
           parameters can be specified in numerous ways and still end up the
           same it would take a lot of processing to make it really accurate.
           Instead, this matching will assume that re-uses of bound connections
           will most likely also re-use the exact same binding parameters and
           missing out a few edge cases shouldn't hurt anyone very much.
        */
        if((check->localport != needle->localport) ||
           (check->localportrange != needle->localportrange) ||
           !check->localdev ||
           !needle->localdev ||
           strcmp(check->localdev, needle->localdev))
          continue;
      }

      if(!(needle->handler->flags & PROTOPT_CREDSPERREQUEST)) {
        /* This protocol requires credentials per connection,
           so verify that we're using the same name and password as well */
        if(!strequal(needle->user, check->user) ||
           !strequal(needle->passwd, check->passwd)) {
          /* one of them was different */
          continue;
        }
      if(!needle->bits.httpproxy || (needle->handler->flags&PROTOPT_SSL) ||
         (needle->bits.httpproxy && check->bits.httpproxy &&
          needle->bits.tunnel_proxy && check->bits.tunnel_proxy &&
          Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
          (needle->port == check->port))) {
        /* The requested connection does not use a HTTP proxy or it uses SSL or
           it is a non-SSL protocol tunneled over the same HTTP proxy name and
           port number */
        if((Curl_raw_equal(needle->handler->scheme, check->handler->scheme) ||
            (get_protocol_family(check->handler->protocol) ==
             needle->handler->protocol && check->tls_upgraded)) &&
           (!needle->bits.conn_to_host || Curl_raw_equal(
            needle->conn_to_host.name, check->conn_to_host.name)) &&
           (!needle->bits.conn_to_port ||
             needle->conn_to_port == check->conn_to_port) &&
           Curl_raw_equal(needle->host.name, check->host.name) &&
           needle->remote_port == check->remote_port) {
          /* The schemes match or the the protocol family is the same and the
             previous connection was TLS upgraded, and the hostname and host
             port match */
          if(needle->handler->flags & PROTOPT_SSL) {
            /* This is a SSL connection so verify that we're using the same
               SSL options as well */
            if(!Curl_ssl_config_matches(&needle->ssl_config,
                                        &check->ssl_config)) {
              DEBUGF(infof(data,
                           "Connection #%ld has different SSL parameters, "
                           "can't reuse\n",
                           check->connection_id));
              continue;
            }
            else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
              foundPendingCandidate = TRUE;
              DEBUGF(infof(data,
                           "Connection #%ld has not started SSL connect, "
                           "can't reuse\n",
                           check->connection_id));
              continue;
            }
Daniel Stenberg's avatar
Daniel Stenberg committed
      }
      else { /* The requested needle connection is using a proxy,
                is the checked one using the same host, port and type? */
        if(check->bits.proxy &&
           (needle->proxytype == check->proxytype) &&
           (needle->bits.tunnel_proxy == check->bits.tunnel_proxy) &&
           Curl_raw_equal(needle->proxy.name, check->proxy.name) &&
           needle->port == check->port) {
          /* This is the same proxy connection, use it! */
          match = TRUE;
        }
        /* If we are looking for an HTTP+NTLM connection, check if this is
           already authenticating with the right credentials. If not, keep
           looking so that we can reuse NTLM connections if
           possible. (Especially we must not reuse the same connection if
           partway through a handshake!) */
        if(wantNTLMhttp) {
          if(!strequal(needle->user, check->user) ||
             !strequal(needle->passwd, check->passwd))
            continue;
        }
        else if(check->ntlm.state != NTLMSTATE_NONE) {
          /* Connection is using NTLM auth but we don't want NTLM */
          continue;
        }
        /* Same for Proxy NTLM authentication */
        if(wantProxyNTLMhttp) {
          /* Both check->proxyuser and check->proxypasswd could be NULL */
          if(check->proxyuser == NULL || check->proxypasswd == NULL)
            continue;

          if(!strequal(needle->proxyuser, check->proxyuser) ||
             !strequal(needle->proxypasswd, check->proxypasswd))
            continue;
        }
        else if(check->proxyntlm.state != NTLMSTATE_NONE) {
          /* Proxy connection is using NTLM auth but we don't want NTLM */
          continue;
        }

        if(wantNTLMhttp || wantProxyNTLMhttp) {
          /* Credentials are already checked, we can use this connection */
          chosen = check;

          if((wantNTLMhttp &&
             (check->ntlm.state != NTLMSTATE_NONE)) ||
              (wantProxyNTLMhttp &&
               (check->proxyntlm.state != NTLMSTATE_NONE))) {
            /* We must use this connection, no other */
            *force_reuse = TRUE;
            break;
          }

          /* Continue look up for a better connection */
        if(canPipeline) {
          /* We can pipeline if we want to. Let's continue looking for
             the optimal connection to use, i.e the shortest pipe that is not
             blacklisted. */
          if(pipeLen == 0) {
            /* We have the optimal connection. Let's stop looking. */
            chosen = check;
            break;
          }

          /* We can't use the connection if the pipe is full */
          if(max_pipe_len && (pipeLen >= max_pipe_len)) {
            infof(data, "Pipe is full, skip (%zu)\n", pipeLen);
#ifdef USE_NGHTTP2
          /* If multiplexed, make sure we don't go over concurrency limit */
          if(check->bits.multiplex) {
            /* Multiplexed connections can only be HTTP/2 for now */
            struct http_conn *httpc = &check->proto.httpc;
            if(pipeLen >= httpc->settings.max_concurrent_streams) {
              infof(data, "MAX_CONCURRENT_STREAMS reached, skip (%zu)\n",
                    pipeLen);
              continue;
            }
          }
          /* We can't use the connection if the pipe is penalized */
          if(Curl_pipeline_penalized(data, check)) {
            infof(data, "Penalized, skip\n");
          if(max_pipe_len) {
            if(pipeLen < best_pipe_len) {
              /* This connection has a shorter pipe so far. We'll pick this
                 and continue searching */
              chosen = check;
              best_pipe_len = pipeLen;
              continue;
            }
          }
          else {
            /* When not pipelining (== multiplexed), we have a match here! */
            infof(data, "Multiplexed connection found!\n");
            break;
          }
        }
        else {
          /* We have found a connection. Let's stop searching. */
          chosen = check;
  if(chosen) {
    *usethis = chosen;
    return TRUE; /* yes, we found one to use! */
  }

  if(foundPendingCandidate && data->set.pipewait) {
    infof(data,
          "Found pending candidate for reuse and CURLOPT_PIPEWAIT is set\n");
  return FALSE; /* no matching connecting exists */
}

/* after a TCP connection to the proxy has been verified, this function does
   the next magic step.

   Note: this function's sub-functions call failf()
CURLcode Curl_connected_proxy(struct connectdata *conn,
                              int sockindex)
  if(!conn->bits.proxy || sockindex)
    /* this magic only works for the primary socket as the secondary is used
       for FTP only and it has FTP specific magic in ftp.c */
    return CURLE_OK;

  switch(conn->proxytype) {
#ifndef CURL_DISABLE_PROXY
  case CURLPROXY_SOCKS5:
  case CURLPROXY_SOCKS5_HOSTNAME:
    return Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
                       conn->bits.conn_to_host ? conn->conn_to_host.name :
                       conn->host.name,
                       conn->bits.conn_to_port ? conn->conn_to_port :
                       conn->remote_port,
    return Curl_SOCKS4(conn->proxyuser,
                       conn->bits.conn_to_host ? conn->conn_to_host.name :
                       conn->host.name,
                       conn->bits.conn_to_port ? conn->conn_to_port :
                       conn->remote_port,
                       FIRSTSOCKET, conn, FALSE);
  case CURLPROXY_SOCKS4A:
    return Curl_SOCKS4(conn->proxyuser,
                       conn->bits.conn_to_host ? conn->conn_to_host.name :
                       conn->host.name,
                       conn->bits.conn_to_port ? conn->conn_to_port :
                       conn->remote_port,
                       FIRSTSOCKET, conn, TRUE);
#endif /* CURL_DISABLE_PROXY */
  case CURLPROXY_HTTP:
  case CURLPROXY_HTTP_1_0:
    /* do nothing here. handled later. */
    break;
  default:
 * verboseconnect() displays verbose information after a connect
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
void Curl_verboseconnect(struct connectdata *conn)
  if(conn->data->set.verbose)
    infof(conn->data, "Connected to %s (%s) port %ld (#%ld)\n",
          conn->bits.proxy ? conn->proxy.dispname : conn->host.dispname,
          conn->ip_addr_str, conn->port, conn->connection_id);
Yang Tse's avatar
Yang Tse committed
#endif
int Curl_protocol_getsock(struct connectdata *conn,
                          curl_socket_t *socks,
                          int numsocks)
  if(conn->handler->proto_getsock)
    return conn->handler->proto_getsock(conn, socks, numsocks);
int Curl_doing_getsock(struct connectdata *conn,
                       curl_socket_t *socks,
                       int numsocks)
  if(conn && conn->handler->doing_getsock)
    return conn->handler->doing_getsock(conn, socks, numsocks);
}

/*
 * We are doing protocol-specific connecting and this is being called over and
 * over from the multi interface until the connection phase is done on
 * protocol layer.
 */

CURLcode Curl_protocol_connecting(struct connectdata *conn,
                                  bool *done)
  if(conn && conn->handler->connecting) {
    result = conn->handler->connecting(conn, done);
  }
  else
    *done = TRUE;

  return result;
}

/*
 * We are DOING this is being called over and over from the multi interface
 * until the DOING phase is done on protocol layer.
 */

CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done)
{
  CURLcode result=CURLE_OK;

    result = conn->handler->doing(conn, done);
/*
 * We have discovered that the TCP connection has been successful, we can now
 * proceed with some action.
 *
 */
CURLcode Curl_protocol_connect(struct connectdata *conn,
                               bool *protocol_done)
  if(conn->bits.tcpconnect[FIRSTSOCKET] && conn->bits.protoconnstart) {
    /* We already are connected, get back. This may happen when the connect
       worked fine in the first call, like when we connect to a local server
       or proxy. Note that we don't know if the protocol is actually done.

       Unless this protocol doesn't have any protocol-connect callback, as
       then we know we're done. */
    result = Curl_proxy_connect(conn);
    if(result)
      return result;
    if(conn->bits.tunnel_proxy && conn->bits.httpproxy &&
       (conn->tunnel_state[FIRSTSOCKET] != TUNNEL_COMPLETE))
      /* when using an HTTP tunnel proxy, await complete tunnel establishment
         before proceeding further. Return CURLE_OK so we'll be called again */
      return CURLE_OK;

      /* is there a protocol-specific connect() procedure? */
      /* Call the protocol-specific connect function */
      result = conn->handler->connect_it(conn, protocol_done);
    }
    else
      *protocol_done = TRUE;

    /* it has started, possibly even completed but that knowledge isn't stored
       in this bit! */
static bool is_ASCII_name(const char *hostname)
{
  const unsigned char *ch = (const unsigned char*)hostname;

Gisle Vanem's avatar
 
Gisle Vanem committed

#ifdef USE_LIBIDN
Gisle Vanem's avatar
 
Gisle Vanem committed
/*
 * Check if characters in hostname is allowed in Top Level Domain.
 */
static bool tld_check_name(struct SessionHandle *data,
                           const char *ace_hostname)
Gisle Vanem's avatar
 
Gisle Vanem committed
{
  size_t err_pos;
  char *uc_name = NULL;
  int rc;
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
  const char *tld_errmsg = "<no msg>";
Yang Tse's avatar
Yang Tse committed
#else
  (void)data;
#endif
Gisle Vanem's avatar
 
Gisle Vanem committed

  /* Convert (and downcase) ACE-name back into locale's character set */
  rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0);
Yang Tse's avatar
Yang Tse committed
    return FALSE;
Gisle Vanem's avatar
 
Gisle Vanem committed

  /* Warning: err_pos receives "the decoded character offset rather than the
     byte position in the string." And as of libidn 1.32 that character offset
     is for UTF-8, even if the passed in string is another locale. */
Gisle Vanem's avatar
 
Gisle Vanem committed
  rc = tld_check_lz(uc_name, &err_pos, NULL);
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
    tld_errmsg = tld_strerror((Tld_rc)rc);
Yang Tse's avatar
Yang Tse committed
    infof(data, "WARNING: TLD check for %s failed; %s\n",
          uc_name, tld_errmsg);
#endif /* CURL_DISABLE_VERBOSE_STRINGS */
Gisle Vanem's avatar
 
Gisle Vanem committed
     idn_free(uc_name);
Yang Tse's avatar
Yang Tse committed
  if(rc != TLD_SUCCESS)
    return FALSE;

  return TRUE;
Gisle Vanem's avatar
 
Gisle Vanem committed
}
/*
 * Perform any necessary IDN conversion of hostname
 */
static void fix_hostname(struct SessionHandle *data,
                         struct connectdata *conn, struct hostname *host)
Yang Tse's avatar
Yang Tse committed
#ifndef USE_LIBIDN
  (void)data;
  (void)conn;
#elif defined(CURL_DISABLE_VERBOSE_STRINGS)
  (void)conn;
#endif

  /* set the name we use to display the host name */
Daniel Stenberg's avatar
Daniel Stenberg committed
  host->dispname = host->name;
  if(len && (host->name[len-1] == '.'))
    /* strip off a single trailing dot if present, primarily for SNI but
       there's no use for it */
    host->name[len-1]=0;

  /* Check name for non-ASCII and convert hostname to ACE form if we can */
  if(!is_ASCII_name(host->name)) {
    if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
      char *ace_hostname = NULL;

      int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0);
      infof(data, "Input domain encoded as `%s'\n",
            stringprep_locale_charset());
      if(rc == IDNA_SUCCESS) {
        /* tld_check_name() displays a warning if the host name contains
           "illegal" characters for this TLD */
        (void)tld_check_name(data, ace_hostname);

        host->encalloc = ace_hostname;
        /* change the name pointer to point to the encoded hostname */
        host->name = host->encalloc;
      }
      else
        infof(data, "Failed to convert %s to ACE; %s\n", host->name,
              Curl_idn_strerror(conn, rc));
#elif defined(USE_WIN32_IDN)
    char *ace_hostname = NULL;

    if(curl_win32_idn_to_ascii(host->name, &ace_hostname)) {
      host->encalloc = ace_hostname;
      /* change the name pointer to point to the encoded hostname */
      host->name = host->encalloc;
    }
    else
      infof(data, "Failed to convert %s to ACE;\n", host->name);
#else
    infof(data, "IDN support not present, can't parse Unicode domains\n");
Gisle Vanem's avatar
Gisle Vanem committed
#endif
/*
 * Frees data allocated by fix_hostname()
 */
static void free_fixed_hostname(struct hostname *host)
{
#if defined(USE_LIBIDN)
  if(host->encalloc) {
    idn_free(host->encalloc); /* must be freed with idn_free() since this was
                                 allocated by libidn */
    host->encalloc = NULL;
  }
#elif defined(USE_WIN32_IDN)
  free(host->encalloc); /* must be freed withidn_free() since this was
                           allocated by curl_win32_idn_to_ascii */
  host->encalloc = NULL;
static void llist_dtor(void *user, void *element)
{
  (void)user;
  (void)element;
  /* Do nothing */
}

/*
 * Allocate and initialize a new connectdata object.
 */
static struct connectdata *allocate_conn(struct SessionHandle *data)
  struct connectdata *conn = calloc(1, sizeof(struct connectdata));
  if(!conn)
    return NULL;

  conn->handler = &Curl_handler_dummy;  /* Be sure we have a handler defined
                                           already from start to avoid NULL
                                           situations and checks */

  /* and we setup a few fields in case we end up actually using this struct */

  conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;     /* no file descriptor */
  conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD; /* no file descriptor */
  conn->tempsock[0] = CURL_SOCKET_BAD; /* no file descriptor */
  conn->tempsock[1] = CURL_SOCKET_BAD; /* no file descriptor */
  conn->connection_id = -1;    /* no ID */
  conn->port = -1; /* unknown at this point */
  conn->remote_port = -1; /* unknown */
#if defined(USE_RECV_BEFORE_SEND_WORKAROUND) && defined(DEBUGBUILD)
  conn->postponed[0].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
  conn->postponed[1].bindsock = CURL_SOCKET_BAD; /* no file descriptor */
#endif /* USE_RECV_BEFORE_SEND_WORKAROUND && DEBUGBUILD */

  /* Default protocol-independent behavior doesn't support persistent
     connections, so we set this to force-close. Protocols that support
     this need to set this to FALSE in their "curl_do" functions. */
  connclose(conn, "Default to force-close");

  /* Store creation time to help future close decision making */
  conn->created = Curl_tvnow();

  conn->data = data; /* Setup the association between this connection
                        and the SessionHandle */

  conn->proxytype = data->set.proxytype; /* type */

#ifdef CURL_DISABLE_PROXY

  conn->bits.proxy = FALSE;
  conn->bits.httpproxy = FALSE;
  conn->bits.proxy_user_passwd = FALSE;
  conn->bits.tunnel_proxy = FALSE;

#else /* CURL_DISABLE_PROXY */

  /* note that these two proxy bits are now just on what looks to be
     requested, they may be altered down the road */
  conn->bits.proxy = (data->set.str[STRING_PROXY] &&
                      *data->set.str[STRING_PROXY]) ? TRUE : FALSE;
  conn->bits.httpproxy = (conn->bits.proxy &&
                          (conn->proxytype == CURLPROXY_HTTP ||
                           conn->proxytype == CURLPROXY_HTTP_1_0)) ?
                          TRUE : FALSE;
  conn->bits.proxy_user_passwd = (data->set.str[STRING_PROXYUSERNAME]) ?
                                 TRUE : FALSE;
  conn->bits.tunnel_proxy = data->set.tunnel_thru_httpproxy;

#endif /* CURL_DISABLE_PROXY */

  conn->bits.user_passwd = (data->set.str[STRING_USERNAME]) ? TRUE : FALSE;
  conn->bits.ftp_use_epsv = data->set.ftp_use_epsv;
  conn->bits.ftp_use_eprt = data->set.ftp_use_eprt;

  conn->verifypeer = data->set.ssl.verifypeer;
  conn->verifyhost = data->set.ssl.verifyhost;

  conn->ip_version = data->set.ipver;

#if !defined(CURL_DISABLE_HTTP) && defined(USE_NTLM) && \
    defined(NTLM_WB_ENABLED)
  conn->ntlm_auth_hlpr_socket = CURL_SOCKET_BAD;
  conn->ntlm_auth_hlpr_pid = 0;
  conn->challenge_header = NULL;
  conn->response_header = NULL;
#endif

  if(Curl_pipeline_wanted(data->multi, CURLPIPE_HTTP1) &&
     !conn->master_buffer) {
    /* Allocate master_buffer to be used for HTTP/1 pipelining */
    conn->master_buffer = calloc(BUFSIZE, sizeof (char));
    if(!conn->master_buffer)
      goto error;
  }

  /* Initialize the pipeline lists */
  conn->send_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
  conn->recv_pipe = Curl_llist_alloc((curl_llist_dtor) llist_dtor);
  if(!conn->send_pipe || !conn->recv_pipe)
#ifdef HAVE_GSSAPI
  conn->data_prot = PROT_CLEAR;
  /* Store the local bind parameters that will be used for this connection */
  if(data->set.str[STRING_DEVICE]) {
    conn->localdev = strdup(data->set.str[STRING_DEVICE]);
    if(!conn->localdev)
      goto error;
  }
  conn->localportrange = data->set.localportrange;
  conn->localport = data->set.localport;

  /* the close socket stuff needs to be copied to the connection struct as
     it may live on without (this specific) SessionHandle */
  conn->fclosesocket = data->set.fclosesocket;
  conn->closesocket_client = data->set.closesocket_client;

  Curl_llist_destroy(conn->send_pipe, NULL);
  Curl_llist_destroy(conn->recv_pipe, NULL);

  conn->send_pipe = NULL;
  conn->recv_pipe = NULL;

  free(conn->master_buffer);
  free(conn->localdev);
  free(conn);