Skip to content
url.c 154 KiB
Newer Older
                       va_arg(param, char *));
  case CURLOPT_CRLFILE:
    /*
     * Set CRL file info for SSL connection. Specify file name of the CRL
     * to check certificates revocation
     */
    result = setstropt(&data->set.str[STRING_SSL_CRLFILE],
                       va_arg(param, char *));
    break;
  case CURLOPT_ISSUERCERT:
    /*
     * Set Issuer certificate file
     * to check certificates issuer
     */
    result = setstropt(&data->set.str[STRING_SSL_ISSUERCERT],
                       va_arg(param, char *));
    break;
  case CURLOPT_TELNETOPTIONS:
    /*
     * Set a linked list of telnet options
     */
    data->set.telnet_options = va_arg(param, struct curl_slist *);

  case CURLOPT_BUFFERSIZE:
    /*
     * The application kindly asks for a differently sized receive buffer.
     * If it seems reasonable, we'll use it.
     */
    data->set.buffer_size = va_arg(param, long);

    if((data->set.buffer_size> (BUFSIZE -1 )) ||
       (data->set.buffer_size < 1))
      data->set.buffer_size = 0; /* huge internal default */

    break;

  case CURLOPT_NOSIGNAL:
    /*
     * The application asks not to set any signal() or alarm() handlers,
     * even when using a timeout.
     */
Yang Tse's avatar
Yang Tse committed
    data->set.no_signal = (bool)(0 != va_arg(param, long));
  case CURLOPT_SHARE:
  {
    struct Curl_share *set;
    set = va_arg(param, struct Curl_share *);
    /* disconnect from old share, if any */
    if(data->share) {
      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
      if(data->dns.hostcachetype == HCACHE_SHARED) {
        data->dns.hostcache = NULL;
        data->dns.hostcachetype = HCACHE_NONE;
      }
      if(data->share->cookies == data->cookies)
        data->cookies = NULL;
      data->share->dirty--;
      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
      data->share = NULL;
    }
    /* use new share if it set */
    data->share = set;
    if(data->share) {
      Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
      data->share->dirty++;
      if(data->share->hostcache) {
        /* use shared host cache, first free the private one if any */
        if(data->dns.hostcachetype == HCACHE_PRIVATE)
          Curl_hash_destroy(data->dns.hostcache);
        data->dns.hostcache = data->share->hostcache;
        data->dns.hostcachetype = HCACHE_SHARED;
      }
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
      if(data->share->cookies) {
        /* use shared cookie list, first free own one if any */
        if(data->cookies)
          Curl_cookie_cleanup(data->cookies);
        /* enable cookies since we now use a share that uses cookies! */
        data->cookies = data->share->cookies;
      }
      Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
    /* check for host cache not needed,
     * it will be done by curl_easy_perform */
  }
  break;
  case CURLOPT_PRIVATE:
    /*
     * Set private data pointer.
     */
    data->set.private_data = va_arg(param, void *);
  case CURLOPT_MAXFILESIZE:
    /*
     * Set the maximum size of a file to download.
     */
    data->set.max_filesize = va_arg(param, long);
    break;

     * Make transfers attempt to use SSL/TLS.
    data->set.ftp_ssl = (curl_usessl)va_arg(param, long);
  case CURLOPT_FTPSSLAUTH:
    /*
     * Set a specific auth for FTP-SSL transfers.
     */
    data->set.ftpsslauth = (curl_ftpauth)va_arg(param, long);
    break;

  case CURLOPT_IPRESOLVE:
    data->set.ip_version = va_arg(param, long);
    break;

  case CURLOPT_MAXFILESIZE_LARGE:
    /*
     * Set the maximum size of a file to download.
     */
    data->set.max_filesize = va_arg(param, curl_off_t);
  case CURLOPT_TCP_NODELAY:
    /*
     * Enable or disable TCP_NODELAY, which will disable/enable the Nagle
     * algorithm
     */
Yang Tse's avatar
Yang Tse committed
    data->set.tcp_nodelay = (bool)(0 != va_arg(param, long));
    result = setstropt(&data->set.str[STRING_FTP_ACCOUNT],
                       va_arg(param, char *));
Yang Tse's avatar
Yang Tse committed
    data->set.ignorecl = (bool)(0 != va_arg(param, long));
  case CURLOPT_CONNECT_ONLY:
    /*
     * No data transfer, set up connection and let application use the socket
     */
Yang Tse's avatar
Yang Tse committed
    data->set.connect_only = (bool)(0 != va_arg(param, long));
    result = setstropt(&data->set.str[STRING_FTP_ALTERNATIVE_TO_USER],
                       va_arg(param, char *));
  case CURLOPT_SOCKOPTFUNCTION:
    /*
     * socket callback function: called after socket() but before connect()
     */
    data->set.fsockopt = va_arg(param, curl_sockopt_callback);
    break;

  case CURLOPT_SOCKOPTDATA:
    /*
     * socket callback data pointer. Might be NULL.
     */
    data->set.sockopt_client = va_arg(param, void *);
    break;

  case CURLOPT_OPENSOCKETFUNCTION:
    /*
     * open/create socket callback function: called instead of socket(),
     * before connect()
     */
    data->set.fopensocket = va_arg(param, curl_opensocket_callback);
    break;

  case CURLOPT_OPENSOCKETDATA:
    /*
     * socket callback data pointer. Might be NULL.
     */
    data->set.opensocket_client = va_arg(param, void *);
    break;

Yang Tse's avatar
Yang Tse committed
    data->set.ssl.sessionid = (bool)(0 != va_arg(param, long));
#ifdef USE_LIBSSH2
    /* we only include SSH options if explicitly built to support SSH */
  case CURLOPT_SSH_AUTH_TYPES:
    data->set.ssh_auth_types = va_arg(param, long);
    break;

  case CURLOPT_SSH_PUBLIC_KEYFILE:
    /*
     * Use this file instead of the $HOME/.ssh/id_dsa.pub file
     */
    result = setstropt(&data->set.str[STRING_SSH_PUBLIC_KEY],
                       va_arg(param, char *));
    break;

  case CURLOPT_SSH_PRIVATE_KEYFILE:
    /*
     * Use this file instead of the $HOME/.ssh/id_dsa file
     */
    result = setstropt(&data->set.str[STRING_SSH_PRIVATE_KEY],
                       va_arg(param, char *));
  case CURLOPT_SSH_HOST_PUBLIC_KEY_MD5:
    /*
     * Option to allow for the MD5 of the host public key to be checked
    result = setstropt(&data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5],
                       va_arg(param, char *));
#ifdef HAVE_LIBSSH2_KNOWNHOST_API
  case CURLOPT_SSH_KNOWNHOSTS:
    /*
     * Store the file name to read known hosts from.
     */
    result = setstropt(&data->set.str[STRING_SSH_KNOWNHOSTS],
                       va_arg(param, char *));
    break;

  case CURLOPT_SSH_KEYFUNCTION:
    /* setting to NULL is fine since the ssh.c functions themselves will
       then rever to use the internal default */
    data->set.ssh_keyfunc = va_arg(param, curl_sshkeycallback);
    break;

  case CURLOPT_SSH_KEYDATA:
    /*
     * Custom client data to pass to the SSH keyfunc callback
     */
    data->set.ssh_keyfunc_userp = va_arg(param, void *);
    break;
#endif /* HAVE_LIBSSH2_KNOWNHOST_API */

#endif /* USE_LIBSSH2 */

  case CURLOPT_HTTP_TRANSFER_DECODING:
    /*
     * disable libcurl transfer encoding is used
     */
    data->set.http_te_skip = (bool)(0 == va_arg(param, long));
    break;

  case CURLOPT_HTTP_CONTENT_DECODING:
    /*
     * raw data passed to the application when content encoding is used
     */
    data->set.http_ce_skip = (bool)(0 == va_arg(param, long));
    break;

  case CURLOPT_NEW_FILE_PERMS:
    /*
     * Uses these permissions instead of 0644
     */
    data->set.new_file_perms = va_arg(param, long);
    break;

  case CURLOPT_NEW_DIRECTORY_PERMS:
    /*
     * Uses these permissions instead of 0755
     */
    data->set.new_directory_perms = va_arg(param, long);
    break;

  case CURLOPT_ADDRESS_SCOPE:
    /*
     * We always get longs when passed plain numericals, but for this value we
     * know that an unsigned int will always hold the value so we blindly
     * typecast to this type
     */
    data->set.scope = (unsigned int) va_arg(param, long);
    break;

  case CURLOPT_PROTOCOLS:
    /* set the bitmask for the protocols that are allowed to be used for the
       transfer, which thus helps the app which takes URLs from users or other
       external inputs and want to restrict what protocol(s) to deal
       with. Defaults to CURLPROTO_ALL. */
    data->set.allowed_protocols = va_arg(param, long) & PROT_EXTMASK;
    break;

  case CURLOPT_REDIR_PROTOCOLS:
    /* set the bitmask for the protocols that libcurl is allowed to follow to,
       as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs
       to be set in both bitmasks to be allowed to get redirected to. Defaults
       to all protocols except FILE and SCP. */
    data->set.redir_protocols = va_arg(param, long) & PROT_EXTMASK;
    break;

  case CURLOPT_MAIL_FROM:
    result = setstropt(&data->set.str[STRING_MAIL_FROM],
                       va_arg(param, char *));
    break;

  case CURLOPT_MAIL_RCPT:
    result = setstropt(&data->set.str[STRING_MAIL_RCPT],
                       va_arg(param, char *));
    break;

  default:
    /* unknown tag and its companion, just ignore: */
    result = CURLE_FAILED_INIT; /* correct this */
    break;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
static void conn_free(struct connectdata *conn)
{
  /* close the SSL stuff before we close any sockets since they will/may
     write to the sockets */
  Curl_ssl_close(conn, FIRSTSOCKET);
  Curl_ssl_close(conn, SECONDARYSOCKET);

  /* close possibly still open sockets */
  if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
    sclose(conn->sock[SECONDARYSOCKET]);
  if(CURL_SOCKET_BAD != conn->sock[FIRSTSOCKET])
    sclose(conn->sock[FIRSTSOCKET]);

  Curl_safefree(conn->user);
  Curl_safefree(conn->passwd);
  Curl_safefree(conn->proxyuser);
  Curl_safefree(conn->proxypasswd);
  Curl_safefree(conn->allocptr.proxyuserpwd);
  Curl_safefree(conn->allocptr.uagent);
  Curl_safefree(conn->allocptr.userpwd);
  Curl_safefree(conn->allocptr.accept_encoding);
  Curl_safefree(conn->allocptr.rangeline);
  Curl_safefree(conn->allocptr.ref);
  Curl_safefree(conn->allocptr.host);
  Curl_safefree(conn->allocptr.cookiehost);
  Curl_safefree(conn->trailer);
  Curl_safefree(conn->host.rawalloc); /* host name buffer */
  Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */

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

  /* possible left-overs from the async name resolvers */
#if defined(USE_ARES)
  Curl_safefree(conn->async.hostname);
  Curl_safefree(conn->async.os_specific);
#elif defined(CURLRES_THREADED)
  Curl_destroy_thread_data(&conn->async);
#endif

  Curl_free_ssl_config(&conn->ssl_config);

  free(conn); /* free all the connection oriented data */
}

CURLcode Curl_disconnect(struct connectdata *conn)
  struct SessionHandle *data;
  if(!conn)
    return CURLE_OK; /* this is closed and fine already */
  data = conn->data;
  if(!data) {
    DEBUGF(infof(data, "DISCONNECT without easy handle, ignoring\n"));
    return CURLE_OK;
  }

  if(conn->dns_entry != NULL) {
    Curl_resolv_unlock(data, conn->dns_entry);
    conn->dns_entry = NULL;
  }

#if defined(DEBUGBUILD) && defined(AGGRESIVE_TEST)
  /* scan for DNS cache entries still marked as in use */
  Curl_hash_apply(data->hostcache,
                  NULL, Curl_scan_cache_used);
#endif

  Curl_expire(data, 0); /* shut off timers */
  Curl_hostcache_prune(data); /* kill old DNS cache entries */

  {
    int has_host_ntlm = (conn->ntlm.state != NTLMSTATE_NONE);
    int has_proxy_ntlm = (conn->proxyntlm.state != NTLMSTATE_NONE);

    /* Authentication data is a mix of connection-related and sessionhandle-
       related stuff. NTLM is connection-related so when we close the shop
       we shall forget. */
    if (has_host_ntlm) {
      data->state.authhost.done = FALSE;
      data->state.authhost.picked =
    }

    if (has_proxy_ntlm) {
      data->state.authproxy.done = FALSE;
      data->state.authproxy.picked =
    if (has_host_ntlm || has_proxy_ntlm) {
      data->state.authproblem = FALSE;
  /* Cleanup possible redirect junk */
  if(data->req.newurl) {
    free(data->req.newurl);
    data->req.newurl = NULL;
  }

    /* This is set if protocol-specific cleanups should be made */
  if(-1 != conn->connectindex) {
    infof(data, "Closing connection #%ld\n", conn->connectindex);
    if(data->state.connc)
      /* only clear the table entry if we still know in which cache we
         used to be in */
      data->state.connc->connects[conn->connectindex] = NULL;
#ifdef USE_LIBIDN
    idn_free(conn->host.encalloc); /* encoded host name buffer, must be freed
                                      with idn_free() since this was allocated
                                      by libidn */
    idn_free(conn->proxy.encalloc); /* encoded proxy name buffer, must be
                                       freed with idn_free() since this was
                                       allocated by libidn */
  /* Indicate to all handles on the pipe that we're dead */
    signalPipeClose(conn->send_pipe, TRUE);
    signalPipeClose(conn->recv_pipe, TRUE);
    signalPipeClose(conn->pend_pipe, TRUE);
    signalPipeClose(conn->done_pipe, FALSE);
Daniel Stenberg's avatar
Daniel Stenberg committed
/*
 * This function should return TRUE if the socket is to be assumed to
 * be dead. Most commonly this happens when the server has closed the
 * connection due to inactivity.
 */
static bool SocketIsDead(curl_socket_t sock)
{
  int sval;
  bool ret_val = TRUE;
  sval = Curl_socket_ready(sock, CURL_SOCKET_BAD, 0);
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* timeout */
Daniel Stenberg's avatar
Daniel Stenberg committed
  return ret_val;
}

static bool IsPipeliningPossible(const struct SessionHandle *handle)
  if(handle->multi && Curl_multi_canPipeline(handle->multi) &&
      (handle->set.httpreq == HTTPREQ_GET ||
       handle->set.httpreq == HTTPREQ_HEAD) &&
      handle->set.httpversion != CURL_HTTP_VERSION_1_0)
    return TRUE;

  return FALSE;
}

bool Curl_isPipeliningEnabled(const struct SessionHandle *handle)
  if(handle->multi && Curl_multi_canPipeline(handle->multi))
CURLcode Curl_addHandleToPipeline(struct SessionHandle *data,
#ifdef DEBUGBUILD
  if(!IsPipeliningPossible(data)) {
    /* when not pipelined, there MUST be no handle in the list already */
      infof(data, "PIPE when no PIPE supposed!\n");
  }
#endif
  if(!Curl_llist_insert_next(pipeline, pipeline->tail, data))
    return CURLE_OUT_OF_MEMORY;
  return CURLE_OK;
int Curl_removeHandleFromPipeline(struct SessionHandle *handle,
      Curl_llist_remove(pipeline, curr, NULL);
#if 0 /* this code is saved here as it is useful for debugging purposes */
static void Curl_printPipeline(struct curl_llist *pipeline)
    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;
    infof(data, "Handle in pipeline: %s\n", data->state.path);
static struct SessionHandle* gethandleathead(struct curl_llist *pipeline)
  struct curl_llist_element *curr = pipeline->head;
/* remove the specified connection from all (possible) pipelines and related
   queues */
void Curl_getoff_all_pipelines(struct SessionHandle *data,
                               struct connectdata *conn)
{
Yang Tse's avatar
Yang Tse committed
  bool recv_head = (bool)(conn->readchannel_inuse &&
    (gethandleathead(conn->recv_pipe) == data));
Yang Tse's avatar
Yang Tse committed
  bool send_head = (bool)(conn->writechannel_inuse &&
    (gethandleathead(conn->send_pipe) == data));

  if(Curl_removeHandleFromPipeline(data, conn->recv_pipe) && recv_head)
    conn->readchannel_inuse = FALSE;
  if(Curl_removeHandleFromPipeline(data, conn->send_pipe) && send_head)
    conn->writechannel_inuse = FALSE;
  Curl_removeHandleFromPipeline(data, conn->pend_pipe);
  Curl_removeHandleFromPipeline(data, conn->done_pipe);
static void signalPipeClose(struct curl_llist *pipeline, bool pipe_broke)
    struct curl_llist_element *next = curr->next;
    struct SessionHandle *data = (struct SessionHandle *) curr->ptr;

#ifdef DEBUGBUILD /* debug-only code */
    if(data->magic != CURLEASY_MAGIC_NUMBER) {
      /* MAJOR BADNESS */
      infof(data, "signalPipeClose() found BAAD easy handle\n");
    if (pipe_broke)
       data->state.pipe_broke = TRUE;
    Curl_multi_handlePipeBreak(data);
    Curl_llist_remove(pipeline, curr, NULL);
 * 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.
ConnectionExists(struct SessionHandle *data,
                 struct connectdata *needle,
                 struct connectdata **usethis)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct connectdata *check;
  bool canPipeline = IsPipeliningPossible(data);
  for(i=0; i< data->state.connc->num; i++) {
Yang Tse's avatar
Yang Tse committed
    size_t pipeLen = 0;
    /*
     * Note that if we use a HTTP proxy, we check connections to that
     * proxy and not to the actual remote server.
     */
    check = data->state.connc->connects[i];
    if(!check)
      /* NULL pointer means not filled-in entry */
      continue;
    pipeLen = check->send_pipe->size + check->recv_pipe->size;

      check->connectindex = i; /* Set this appropriately since it might have
                                  been set to -1 when the easy was removed
                                  from the multi */
    }

    if(!pipeLen && !check->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 = SocketIsDead(check->sock[FIRSTSOCKET]);
      if(dead) {
        check->data = data;
        infof(data, "Connection #%d seems to be dead!\n", i);

        Curl_disconnect(check); /* disconnect resources */
        data->state.connc->connects[i]=NULL; /* nothing here */

        continue;
      }
    }

      /* Make sure the pipe has only GET requests */
      struct SessionHandle* sh = gethandleathead(check->send_pipe);
      struct SessionHandle* rh = gethandleathead(check->recv_pipe);
#ifdef DEBUGBUILD
      if(pipeLen > MAX_PIPELINE_LENGTH) {
        infof(data, "BAD! Connection #%ld has too big pipeline!\n",
              check->connectindex);
      }
#endif
    }
    else {
      if(pipeLen > 0) {
        /* can only happen within multi handles, and means that another easy
           handle is using this connection */
        continue;
      }

#ifdef CURLRES_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 hasn't finished name resolve, can't reuse\n",
              check->connectindex);
        continue;
      }
#endif

      if((check->sock[FIRSTSOCKET] == CURL_SOCKET_BAD) || check->bits.close) {
        /* 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->connectindex);
#ifdef DEBUGBUILD
        if(check->recv_pipe->size > 0) {
          infof(data, "BAD! Unconnected #%ld has a non-empty recv pipeline!\n",
                check->connectindex);
        }
#endif
        continue;
      }
    if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
      /* don't do mixed SSL and non-SSL connections */
      continue;

Yang Tse's avatar
 
Yang Tse committed
    if(needle->protocol&PROT_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 */
      continue;
    if(!canPipeline && check->inuse)
      /* this request can't be pipelined but the checked connection is already
         in use so we skip it */
      continue;

    if(!needle->bits.httpproxy || needle->protocol&PROT_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->protostr, check->protostr) &&
         Curl_raw_equal(needle->host.name, check->host.name) &&
         (needle->remote_port == check->remote_port) ) {
        if(needle->protocol & PROT_SSL) {
          /* This is SSL, 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->connectindex));
            continue;
          }
          else if(check->ssl[FIRSTSOCKET].state != ssl_connection_complete) {
            DEBUGF(infof(data,
                         "Connection #%ld has not started ssl connect, "
                         "can't reuse\n",
                         check->connectindex));
        if((needle->protocol & PROT_FTP) ||
           ((needle->protocol & PROT_HTTP) &&
            (data->state.authhost.want==CURLAUTH_NTLM))) {
          /* This is FTP or HTTP+NTLM, 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;
          }
        }
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! */
      check->inuse = TRUE; /* mark this as being in use so that no other
                              handle in a multi stack may nick it */

      *usethis = check;
      return TRUE; /* yes, we found one to use! */
  return FALSE; /* no matching connecting exists */
}

/*
 * This function frees/closes a connection in the connection cache. This
 * should take the previously set policy into account when deciding which
 * of the connections to kill.
 */
ConnectionKillOne(struct SessionHandle *data)
  struct connectdata *conn;
  long highscore=-1;
  long connindex=-1;
  long score;
  struct timeval now;

  now = Curl_tvnow();
  for(i=0; data->state.connc && (i< data->state.connc->num); i++) {
    conn = data->state.connc->connects[i];
    /* Set higher score for the age passed since the connection was used */
    score = Curl_tvdiff(now, conn->now);

    if(score > highscore) {
      highscore = score;
      connindex = i;
    }
  }
  if(connindex >= 0) {
    /* Set the connection's owner correctly */
    conn = data->state.connc->connects[connindex];
    conn->data = data;

    /* the winner gets the honour of being disconnected */

    /* clean the array entry */
    data->state.connc->connects[connindex] = NULL;
  }

  return connindex; /* return the available index or -1 */
}

/* this connection can now be marked 'idle' */
static void
ConnectionDone(struct connectdata *conn)
{
  conn->inuse = FALSE;
}

/*
 * The given input connection struct pointer is to be stored. If the "cache"
 * is already full, we must clean out the most suitable using the previously
 * set policy.
 *
 * The given connection should be unique. That must've been checked prior to
 * this call.
 */
ConnectionStore(struct SessionHandle *data,
                struct connectdata *conn)
{
  for(i=0; i< data->state.connc->num; i++) {
    if(!data->state.connc->connects[i])
    /* there was no room available, kill one */
    i = ConnectionKillOne(data);
      infof(data, "Connection (#%d) was killed to make room (holds %d)\n",
            i, data->state.connc->num);
    else
      infof(data, "This connection did not fit in the connection cache\n");
  conn->connectindex = i; /* Make the child know where the pointer to this
                             particular data is stored. But note that this -1
                             if this is not within the cache and this is
                             probably not checked for everywhere (yet). */
  conn->inuse = TRUE;
    /* Only do this if a true index was returned, if -1 was returned there
       is no room in the cache for an unknown reason and we cannot store
       this there.

       TODO: make sure we really can work with more handles than positions in
       the cache, or possibly we should (allow to automatically) resize the
       connection cache when we add more easy handles to a multi handle!
    */
    data->state.connc->connects[i] = conn; /* fill in this */
    conn->data = data;
/* after a TCP connection to the proxy has been verified, this function does
   the next magic step.

   Note: this function (and its sub-functions) calls failf()

*/
CURLcode Curl_connected_proxy(struct connectdata *conn)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;

  if(conn->bits.tcpconnect)
    /* allow this to get called again from the multi interface when TCP is
       found connected in the state machine, even though it has already been
       called if the connection happened "instantly" */
    return CURLE_OK;

  switch(data->set.proxytype) {
#ifndef CURL_DISABLE_PROXY
  case CURLPROXY_SOCKS5:
  case CURLPROXY_SOCKS5_HOSTNAME:
    result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd,
                         conn->host.name, conn->remote_port,
                         FIRSTSOCKET, conn);
    break;
  case CURLPROXY_SOCKS4:
    result = Curl_SOCKS4(conn->proxyuser, conn->host.name,
                         conn->remote_port, FIRSTSOCKET, conn, FALSE);
    break;
  case CURLPROXY_SOCKS4A:
    result = Curl_SOCKS4(conn->proxyuser, conn->host.name,
                         conn->remote_port, FIRSTSOCKET, conn, TRUE);
    break;
#endif /* CURL_DISABLE_PROXY */
  case CURLPROXY_HTTP:
  case CURLPROXY_HTTP_1_0:
    /* do nothing here. handled later. */
    break;
  default:
static CURLcode ConnectPlease(struct SessionHandle *data,
                              struct connectdata *conn,
  CURLcode result;
Yang Tse's avatar
Yang Tse committed
#ifndef CURL_DISABLE_VERBOSE_STRINGS
  char *hostname = conn->bits.proxy?conn->proxy.name:conn->host.name;
  infof(data, "About to connect() to %s%s port %d (#%d)\n",
        hostname, conn->port, conn->connectindex);
#else
  (void)data;
Yang Tse's avatar
Yang Tse committed
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

  /*************************************************************
   * Connect to server/proxy
   *************************************************************/
  result= Curl_connecthost(conn,
  if(CURLE_OK == result) {
    /* All is cool, we store the current information */
    if(*connected)
      result = Curl_connected_proxy(conn);
  }
    *connected = FALSE; /* mark it as not connected */
  return result;