Skip to content
Snippets Groups Projects
url.c 128 KiB
Newer Older
/***************************************************************************
 *                                  _   _ ____  _
 *  Project                     ___| | | |  _ \| |
 *                             / __| | | | |_) | |
 *                            | (__| |_| |  _ <| |___
Daniel Stenberg's avatar
Daniel Stenberg committed
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2006, Daniel Stenberg, <daniel@haxx.se>, et al.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
 * are also available at http://curl.haxx.se/docs/copyright.html.
Daniel Stenberg's avatar
Daniel Stenberg committed
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 * copies of the Software, and permit persons to whom the Software is
 * furnished to do so, under the terms of the COPYING file.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * $Id$
 ***************************************************************************/
Daniel Stenberg's avatar
Daniel Stenberg committed

/* -- WIN32 approved -- */
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_STAT_H
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <sys/stat.h>
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <errno.h>

#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <netinet/in.h>
Gisle Vanem's avatar
Gisle Vanem committed
#ifndef __WATCOMC__
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <sys/time.h>
Gisle Vanem's avatar
Gisle Vanem committed
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <netdb.h>
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef HAVE_NET_IF_H
#include <net/if.h>
#endif
#include <sys/ioctl.h>
#include <signal.h>

#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef VMS
Daniel Stenberg's avatar
Daniel Stenberg committed
#include <in.h>
#include <inet.h>
#endif

Daniel Stenberg's avatar
Daniel Stenberg committed
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif

#endif
Gisle Vanem's avatar
 
Gisle Vanem committed
#include <tld.h>
#ifdef HAVE_IDN_FREE_H
#include <idn-free.h>
#else
void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
                              libidn 0.4.5's make install! */
#ifndef HAVE_IDN_FREE
/* if idn_free() was not found in this version of libidn, use plain free()
   instead */
#define idn_free(x) (free)(x)
#endif
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "urldata.h"
#include "netrc.h"

#include "formdata.h"
#include "base64.h"
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "hostip.h"
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "sendf.h"
#include "progress.h"
#include "cookie.h"
#include "strequal.h"
#include "strtok.h"
#include "share.h"
#include "http_digest.h"
#include "select.h"
Daniel Stenberg's avatar
Daniel Stenberg committed

/* And now for the protocols */
#include "ftp.h"
#include "dict.h"
#include "telnet.h"
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "http.h"
#include "file.h"
#include "ldap.h"
#include "connect.h"
#include "http_ntlm.h"
#if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
#include "inet_ntoa_r.h"
#endif

Daniel Stenberg's avatar
Daniel Stenberg committed
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>

Daniel Stenberg's avatar
Daniel Stenberg committed
#endif
/* The last #include file should be: */
#include "memdebug.h"
Daniel Stenberg's avatar
Daniel Stenberg committed

/* Local static prototypes */
static long ConnectionKillOne(struct SessionHandle *data);
static bool ConnectionExists(struct SessionHandle *data,
                             struct connectdata *needle,
                             struct connectdata **usethis);
static long ConnectionStore(struct SessionHandle *data,
                            struct connectdata *conn);
Daniel Stenberg's avatar
Daniel Stenberg committed

#ifndef USE_ARES
/* not for Win32, unless it is cygwin
   not for ares builds */
#if !defined(WIN32) || defined(__CYGWIN__)
#ifndef RETSIGTYPE
#define RETSIGTYPE void
#endif
extern sigjmp_buf curl_jmpenv;
RETSIGTYPE alarmfunc(int sig)
{
  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
  (void)sig;
#ifdef HAVE_SIGSETJMP
  siglongjmp(curl_jmpenv, 1);
#endif
  return;
}
#endif

/*
 * This is the internal function curl_easy_cleanup() calls. This should
 * cleanup and free all resources associated with this sessionhandle.
 *
 * NOTE: if we ever add something that attempts to write to a socket or
 * similar here, we must ignore SIGPIPE first. It is currently only done
 * when curl_easy_perform() is invoked.
 */

CURLcode Curl_close(struct SessionHandle *data)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
    /* this handle is still part of a multi handle, take care of this first */
    Curl_multi_rmeasy(data->multi, data);
  }
  /* Loop through all open connections and kill them one by one */
  while(-1 != ConnectionKillOne(data))
    ; /* empty loop */
Daniel Stenberg's avatar
Daniel Stenberg committed

  if ( ! (data->share && data->share->hostcache) ) {
    if ( !Curl_global_host_cache_use(data)) {
      Curl_hash_destroy(data->dns.hostcache);
  /* Close down all open SSL info and sessions */
  if(data->change.proxy_alloc)
    free(data->change.proxy);
  if(data->change.referer_alloc)
    free(data->change.referer);
  if(data->change.url_alloc)
    free(data->change.url);
  Curl_safefree(data->state.headerbuff);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
  Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
    if(data->change.cookielist) {
      /* If there is a list of cookie files to read, do it first so that
         we have all the told files read before we write the new jar */
      Curl_cookie_loadfiles(data);
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* we have a "destination" for all the cookies to get dumped to */
    if(Curl_cookie_output(data->cookies, data->set.cookiejar))
      infof(data, "WARNING: failed to save cookies in %s\n",
            data->set.cookiejar);
  else {
    if(data->change.cookielist)
      /* since nothing is written, we can just free the list of cookie file
         names */
      curl_slist_free_all(data->change.cookielist); /* clean up list */
  }
  if( !data->share || (data->cookies != data->share->cookies) ) {
    Curl_cookie_cleanup(data->cookies);
  }
  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH)
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* free the connection cache */
  Curl_safefree(data->info.contenttype);
#ifdef USE_ARES
  /* this destroys the channel and we cannot use it anymore after this */
  ares_destroy(data->state.areschannel);
#endif

#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
  /* close iconv conversion descriptors */
  if (data->inbound_cd != (iconv_t)-1) {
     iconv_close(data->inbound_cd);
  }
  if (data->outbound_cd != (iconv_t)-1) {
     iconv_close(data->outbound_cd);
  }
  if (data->utf8_cd != (iconv_t)-1) {
     iconv_close(data->utf8_cd);
  }
#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */

  /* No longer a dirty share, if it exists */
  if (data->share)
    data->share->dirty--;

  return CURLE_OK;
}
/**
 * Curl_open()
 *
 * @param curl is a pointer to a sessionhandle pointer that gets set by this
 * function.
 * @return CURLcode
 */

CURLcode Curl_open(struct SessionHandle **curl)
  /* Very simple start-up: alloc the struct, init it with zeroes and return */
  data = (struct SessionHandle *)calloc(1, sizeof(struct SessionHandle));
  if(!data)
    /* this is a very serious error */
    return CURLE_OUT_OF_MEMORY;
#ifdef USE_ARES
  if(ARES_SUCCESS != ares_init(&data->state.areschannel)) {
    free(data);
    return CURLE_FAILED_INIT;
  }
  /* make sure that all other returns from this function should destroy the
     ares channel before returning error! */
#endif

  /* We do some initial setup here, all those fields that can't be just 0 */

  data->state.headerbuff=(char*)malloc(HEADERSIZE);
  if(!data->state.headerbuff)
    res = CURLE_OUT_OF_MEMORY;
  else {
    data->state.headersize=HEADERSIZE;
    data->set.out = stdout; /* default output to stdout */
    data->set.in  = stdin;  /* default input from stdin */
    data->set.err  = stderr;  /* default stderr to stderr */
    /* use fwrite as default function to store output */
    data->set.fwrite = (curl_write_callback)fwrite;
    /* use fread as default function to read input */
    data->set.fread = (curl_read_callback)fread;
    /* conversion callbacks for non-ASCII hosts */
    data->set.convfromnetwork = (curl_conv_callback)NULL;
    data->set.convtonetwork   = (curl_conv_callback)NULL;
    data->set.convfromutf8    = (curl_conv_callback)NULL;

#if defined(CURL_DOES_CONVERSIONS) && defined(HAVE_ICONV)
    /* conversion descriptors for iconv calls */
    data->outbound_cd = (iconv_t)-1;
    data->inbound_cd  = (iconv_t)-1;
    data->utf8_cd     = (iconv_t)-1;
#endif /* CURL_DOES_CONVERSIONS && HAVE_ICONV */

    data->set.infilesize = -1; /* we don't know any size */
    data->set.maxredirs = -1; /* allow any amount by default */
    data->state.current_speed = -1; /* init to negative == impossible */
    data->set.httpreq = HTTPREQ_GET; /* Default HTTP request */
    data->set.ftp_use_epsv = TRUE;   /* FTP defaults to EPSV operations */
    data->set.ftp_use_eprt = TRUE;   /* FTP defaults to EPRT operations */
    data->set.ftp_filemethod = FTPFILE_MULTICWD;
    data->set.dns_cache_timeout = 60; /* Timeout every 60 seconds by default */
    /* make libcurl quiet by default: */
    data->set.hide_progress = TRUE;  /* CURLOPT_NOPROGRESS changes these */
    data->progress.flags |= PGRS_HIDE;
    /* Set the default size of the SSL session ID cache */
    data->set.ssl.numsessions = 5;
    data->set.proxyport = 1080;
    data->set.proxytype = CURLPROXY_HTTP; /* defaults to HTTP proxy */
    data->set.httpauth = CURLAUTH_BASIC;  /* defaults to basic */
    data->set.proxyauth = CURLAUTH_BASIC; /* defaults to basic */
    /* create an array with connection data struct pointers */
    data->state.numconnects = 5; /* hard-coded right now */
    data->state.connects = (struct connectdata **)
Gisle Vanem's avatar
Gisle Vanem committed
      calloc(sizeof(struct connectdata *) * data->state.numconnects, 1);
    if(!data->state.connects)
      res = CURLE_OUT_OF_MEMORY;
    /* most recent connection is not yet defined */
    data->state.lastconnect = -1;

    /*
     * libcurl 7.10 introduced SSL verification *by default*! This needs to be
     * switched off unless wanted.
     */
    data->set.ssl.verifypeer = TRUE;
    data->set.ssl.verifyhost = 2;
    /* This is our preferred CA cert bundle since install time */
    data->set.ssl.CAfile = (char *)CURL_CA_BUNDLE;
  if(res) {
#ifdef USE_ARES
    ares_destroy(data->state.areschannel);
#endif
    if(data->state.headerbuff)
      free(data->state.headerbuff);
    free(data);
    data = NULL;
  }
CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
                     va_list param)
  case CURLOPT_DNS_CACHE_TIMEOUT:
    data->set.dns_cache_timeout = va_arg(param, int);
    break;
  case CURLOPT_DNS_USE_GLOBAL_CACHE:
      int use_cache = va_arg(param, int);
      if (use_cache) {
        Curl_global_host_cache_init();
      }

      data->set.global_dns_cache = use_cache;
    }
    break;
  case CURLOPT_SSL_CIPHER_LIST:
    /* set a list of cipher we want to use in the SSL connection */
    data->set.ssl.cipher_list = va_arg(param, char *);
    break;

  case CURLOPT_RANDOM_FILE:
    /*
     * This is the path name to a file that contains random data to seed
     * the random SSL stuff with. The file is only used for reading.
     */
    data->set.ssl.random_file = va_arg(param, char *);
    break;
  case CURLOPT_EGDSOCKET:
    /*
     * The Entropy Gathering Daemon socket pathname
     */
    data->set.ssl.egdsocket = va_arg(param, char *);
  case CURLOPT_MAXCONNECTS:
    /*
     * Set the absolute number of maximum simultaneous alive connection that
     * libcurl is allowed to have.
     */
    {
      long newconnects= va_arg(param, long);
      struct connectdata **newptr;
      if(newconnects < data->state.numconnects) {
        /* Since this number is *decreased* from the existing number, we must
           close the possibly open connections that live on the indexes that
           are being removed! */
        for(i=newconnects; i< data->state.numconnects; i++)
          Curl_disconnect(data->state.connects[i]);

        /* If the most recent connection is no longer valid, mark it invalid. */
        if(data->state.lastconnect <= newconnects)
          data->state.lastconnect = -1;
      }
      if(newconnects) {
        newptr= (struct connectdata **)
                  sizeof(struct connectdata *) * newconnects);
        if(!newptr)
          /* we closed a few connections in vain, but so what? */
          return CURLE_OUT_OF_MEMORY;

        /* nullify the newly added pointers */
        for(i=data->state.numconnects; i<newconnects; i++) {
          newptr[i] = NULL;
        }

        data->state.connects = newptr;
        data->state.numconnects = newconnects;
      }
      else {
        /* zero makes NO cache at all */
        if(data->state.connects)
          free(data->state.connects);
        data->state.connects = NULL;
        data->state.numconnects = 0;
        data->state.lastconnect = -1;
      }
    }
    break;
  case CURLOPT_FORBID_REUSE:
    /*
     * When this transfer is done, it must not be left to be reused by a
     * subsequent transfer but shall be closed immediately.
     */
    data->set.reuse_forbid = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FRESH_CONNECT:
    /*
     * This transfer shall not use a previously cached connection but
     * should be made with a fresh new connect!
     */
    data->set.reuse_fresh = va_arg(param, long)?TRUE:FALSE;
  case CURLOPT_VERBOSE:
    /*
     * Verbose means infof() calls that give a lot of information about
     * the connection and transfer procedures as well as internal choices.
     */
    data->set.verbose = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_HEADER:
    /*
     * Set to include the header in the general data output stream.
     */
    data->set.include_header = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_NOPROGRESS:
    /*
     * Shut off the internal supported progress meter
     */
    data->set.hide_progress = va_arg(param, long)?TRUE:FALSE;
    if(data->set.hide_progress)
      data->progress.flags |= PGRS_HIDE;
    else
      data->progress.flags &= ~PGRS_HIDE;
    break;
  case CURLOPT_NOBODY:
    /*
     * Do not include the body part in the output data stream.
     */
    data->set.opt_no_body = va_arg(param, long)?TRUE:FALSE;
    if(data->set.opt_no_body)
      /* in HTTP lingo, this means using the HEAD request */
      data->set.httpreq = HTTPREQ_HEAD;
    break;
  case CURLOPT_FAILONERROR:
    /*
     * Don't output the >=300 error code HTML-page, but instead only
     * return error.
     */
    data->set.http_fail_on_error = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_UPLOAD:
     * We want to sent data to the remote host. If this is HTTP, that equals
     * using the PUT request.
    data->set.upload = va_arg(param, long)?TRUE:FALSE;
    if(data->set.upload)
      /* If this is HTTP, PUT is what's needed to "upload" */
Daniel Stenberg's avatar
Daniel Stenberg committed
  case CURLOPT_FILETIME:
    /*
     * Try to get the file time of the remote document. The time will
     * later (possibly) become available using curl_easy_getinfo().
     */
    data->set.get_filetime = va_arg(param, long)?TRUE:FALSE;
Daniel Stenberg's avatar
Daniel Stenberg committed
    break;
  case CURLOPT_FTP_CREATE_MISSING_DIRS:
    /*
     * An FTP option that modifies an upload to create missing directories on
     * the server.
    data->set.ftp_create_missing_dirs = va_arg( param , long )?TRUE:FALSE;
    break;
  case CURLOPT_FTP_RESPONSE_TIMEOUT:
    /*
     * An FTP option that specifies how quickly an FTP response must be
     * obtained before it is considered failure.
     */
    data->set.ftp_response_timeout = va_arg( param , long );
    break;
  case CURLOPT_FTPLISTONLY:
    /*
     * An FTP option that changes the command to one that asks for a list
     * only, no file info details.
     */
    data->set.ftp_list_only = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FTPAPPEND:
    /*
     * We want to upload and append to an existing (FTP) file.
     */
    data->set.ftp_append = va_arg(param, long)?TRUE:FALSE;
  case CURLOPT_FTP_FILEMETHOD:
    /*
     * How do access files over FTP.
     */
    data->set.ftp_filemethod = (curl_ftpfile)va_arg(param, long);
  case CURLOPT_NETRC:
    /*
     * Parse the $HOME/.netrc file
     */
    data->set.use_netrc = (enum CURL_NETRC_OPTION)va_arg(param, long);
  case CURLOPT_NETRC_FILE:
    /*
     * Use this file instead of the $HOME/.netrc file
     */
    data->set.netrc_file = va_arg(param, char *);
    break;
     * This option was previously named 'FTPASCII'. Renamed to work with
     * more protocols than merely FTP.
     *
     * Transfer using ASCII (instead of BINARY).
    data->set.prefer_ascii = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_TIMECONDITION:
    /*
     * Set HTTP time condition. This must be one of the defines in the
     * curl/curl.h header file.
     */
    data->set.timecondition = (curl_TimeCond)va_arg(param, long);
  case CURLOPT_TIMEVALUE:
    /*
     * This is the value to compare with the remote document with the
     * method set with CURLOPT_TIMECONDITION
     */
    data->set.timevalue = (time_t)va_arg(param, long);
  case CURLOPT_SSLVERSION:
    /*
     * Set explicit SSL version to try to connect with, as some SSL
     * implementations are lame.
     */
    data->set.ssl.version = va_arg(param, long);
     * Switch on automatic referer that gets set if curl follows locations.
    data->set.http_auto_referer = va_arg(param, long)?1:0;
     * String to use at the value of Accept-Encoding header.
     *
     * If the encoding is set to "" we use an Accept-Encoding header that
     * encompasses all the encodings we support.
     * If the encoding is set to NULL we don't send an Accept-Encoding header
     * and ignore an received Content-Encoding header.
     *
    data->set.encoding = va_arg(param, char *);
    if(data->set.encoding && !*data->set.encoding)
      data->set.encoding = (char*)ALL_CONTENT_ENCODINGS;
    break;
     * Follow Location: header hints on a HTTP-server.
    data->set.http_follow_location = va_arg(param, long)?TRUE:FALSE;
Daniel Stenberg's avatar
Daniel Stenberg committed
    break;
     * Send authentication (user+password) when following locations, even when
     * hostname changed.
    data->set.http_disable_hostname_check_before_authentication =
      va_arg(param, long)?TRUE:FALSE;
     * The maximum amount of hops you allow curl to follow Location:
     * headers. This should mostly be used to detect never-ending loops.
    data->set.maxredirs = va_arg(param, long);

  case CURLOPT_POST:
    /* Does this option serve a purpose anymore? Yes it does, when
       CURLOPT_POSTFIELDS isn't used and the POST data is read off the
       callback! */
      data->set.opt_no_body = FALSE; /* this is implied */
    }
     * A string with POST data. Makes curl HTTP POST. Even if it is NULL.
    data->set.postfields = va_arg(param, char *);
     * The size of the POSTFIELD data to prevent libcurl to do strlen() to
     * figure it out. Enables binary posts.
    data->set.postfieldsize = va_arg(param, long);
     * The size of the POSTFIELD data to prevent libcurl to do strlen() to
     * figure it out. Enables binary posts.
    data->set.postfieldsize = va_arg(param, curl_off_t);
  case CURLOPT_HTTPPOST:
    /*
     * Set to make us do HTTP POST
     */
    data->set.httppost = va_arg(param, struct curl_httppost *);
    data->set.httpreq = HTTPREQ_POST_FORM;
    data->set.opt_no_body = FALSE; /* this is implied */
Daniel Stenberg's avatar
Daniel Stenberg committed
    /*
     * String to set in the HTTP Referer: field.
Daniel Stenberg's avatar
Daniel Stenberg committed
     */
    if(data->change.referer_alloc) {
      free(data->change.referer);
      data->change.referer_alloc = FALSE;
    data->set.set_referer = va_arg(param, char *);
    data->change.referer = data->set.set_referer;
Daniel Stenberg's avatar
Daniel Stenberg committed
    break;

     * String to use in the HTTP User-Agent field
    data->set.useragent = va_arg(param, char *);
     * Set a list with HTTP headers to use (or replace internals with)
    data->set.headers = va_arg(param, struct curl_slist *);
     * Set a list of aliases for HTTP 200 in response header
    data->set.http200aliases = va_arg(param, struct curl_slist *);
#if !defined(CURL_DISABLE_COOKIES)
     * Cookie string to send to the remote server in the request.
    data->set.cookie = va_arg(param, char *);
     * Set cookie file to read and parse. Can be used multiple times.
    argptr = (char *)va_arg(param, void *);
    if(argptr) {
      struct curl_slist *cl;
      /* append the cookie file name to the list of file names, and deal with
         them later */
      cl = curl_slist_append(data->change.cookielist, argptr);

      if(!cl)
        return CURLE_OUT_OF_MEMORY;

      data->change.cookielist = cl;
    }
     * Set cookie file name to dump all cookies to when we're done.
    data->set.cookiejar = (char *)va_arg(param, void *);

     * Activate the cookie parser. This may or may not already
     * have been made.
    data->cookies = Curl_cookie_init(data, NULL, data->cookies,
                                     data->set.cookiesession);
     * Set this option to TRUE to start a new "cookie session". It will
     * prevent the forthcoming read-cookies-from-file actions to accept
     * cookies that are marked as being session cookies, as they belong to a
     * previous session.
     *
     * In the original Netscape cookie spec, "session cookies" are cookies
     * with no expire date set. RFC2109 describes the same action if no
     * 'Max-Age' is set and RFC2965 includes the RFC2109 description and adds
     * a 'Discard' action that can enforce the discard even for cookies that
     * have a Max-Age.
     *
     * We run mostly with the original cookie spec, as hardly anyone implements
     * anything else.
    data->set.cookiesession = (bool)va_arg(param, long);

  case CURLOPT_COOKIELIST:
    argptr = va_arg(param, char *);

      /* clear all cookies */
      Curl_cookie_clearall(data->cookies);
      break;
    }
    else if(strequal(argptr, "SESS")) {
      /* clear session cookies */
      Curl_cookie_clearsess(data->cookies);
      /* if cookie engine was not running, activate it */
      data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE);

    argptr = strdup(argptr);
    if(!argptr) {
      result = CURLE_OUT_OF_MEMORY;
      break;
    }

    if(checkprefix("Set-Cookie:", argptr))
      /* HTTP Header format line */
      Curl_cookie_add(data, data->cookies, TRUE, argptr + 11, NULL, NULL);

    else
      /* Netscape format line */
      Curl_cookie_add(data, data->cookies, FALSE, argptr, NULL, NULL);
    if(va_arg(param, long)) {
      data->set.httpreq = HTTPREQ_GET;
      data->set.upload = FALSE; /* switch off upload */
      data->set.opt_no_body = FALSE; /* this is implied */
     * This sets a requested HTTP version to be used. The value is one of
     * the listed enums in curl/curl.h.
    data->set.httpversion = va_arg(param, long);
     * Tunnel operations through the proxy instead of normal proxy use
    data->set.tunnel_thru_httpproxy = va_arg(param, long)?TRUE:FALSE;
     * Set a custom string to use as request
    data->set.customrequest = va_arg(param, char *);

    /* we don't set
       data->set.httpreq = HTTPREQ_CUSTOM;
       here, we continue as if we were using the already set type
       and this just changes the actual request keyword */
  case CURLOPT_PROXYPORT:
    /*
     * Explicitly set HTTP proxy port number.
     */
    data->set.proxyport = va_arg(param, long);
     * Set HTTP Authentication type BITMASK.
    long auth = va_arg(param, long);
    /* switch off bits we can't support */
    auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */
    if(!auth)
      return CURLE_FAILED_INIT; /* no supported types left! */

    data->set.httpauth = auth;
  case CURLOPT_PROXYAUTH:
    /*
     * Set HTTP Authentication type BITMASK.
     */
  {
    long auth = va_arg(param, long);
    /* switch off bits we can't support */
    auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
#endif
    auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */
#endif
    if(!auth)
      return CURLE_FAILED_INIT; /* no supported types left! */

    data->set.proxyauth = auth;
  }
  break;
  case CURLOPT_PROXY:
    /*
     * Set proxy server:port to use as HTTP proxy.
     *
     * If the proxy is set to "" we explicitly say that we don't want to use a
     * proxy (even though there might be environment variables saying so).
     *
     * Setting it to NULL, means no proxy but allows the environment variables
     * to decide for us.
     */
    if(data->change.proxy_alloc) {
      /*
       * The already set string is allocated, free that first
       */
      data->change.proxy_alloc=FALSE;;
      free(data->change.proxy);
    }
    data->set.set_proxy = va_arg(param, char *);
    data->change.proxy = data->set.set_proxy;
    break;

  case CURLOPT_WRITEHEADER:
    /*
     * Custom pointer to pass the header write callback function
     */
    data->set.writeheader = (void *)va_arg(param, void *);
    break;
  case CURLOPT_ERRORBUFFER:
    /*
     * Error buffer provided by the caller to get the human readable
     * error string in.
     */
    data->set.errorbuffer = va_arg(param, char *);
    break;
  case CURLOPT_FILE:
    /*
     * FILE pointer to write to or include in the data write callback
     */
    data->set.out = va_arg(param, FILE *);
    break;
  case CURLOPT_FTPPORT:
    /*
     * Use FTP PORT, this also specifies which IP address to use
     */
    data->set.ftpport = va_arg(param, char *);
    data->set.ftp_use_port = data->set.ftpport?1:0;
    break;

  case CURLOPT_FTP_USE_EPRT:
    data->set.ftp_use_eprt = va_arg(param, long)?TRUE:FALSE;
    break;

  case CURLOPT_FTP_USE_EPSV:
    data->set.ftp_use_epsv = va_arg(param, long)?TRUE:FALSE;
    break;

  case CURLOPT_FTP_SKIP_PASV_IP:
    /*
     * Enable or disable FTP_SKIP_PASV_IP, which will disable/enable the
     * bypass of the IP address in PASV responses.
     */
    data->set.ftp_skip_ip = (bool)va_arg(param, long);
    break;

  case CURLOPT_INFILE:
    /*
     * FILE pointer to read the file to be uploaded from. Or possibly
     * used as argument to the read callback.
     */
    data->set.in = va_arg(param, FILE *);
    break;
  case CURLOPT_INFILESIZE:
    /*
     * If known, this should inform curl about the file size of the
     * to-be-uploaded file.
     */
    data->set.infilesize = va_arg(param, long);
    break;
  case CURLOPT_INFILESIZE_LARGE:
    /*
     * If known, this should inform curl about the file size of the
     * to-be-uploaded file.
     */
    data->set.infilesize = va_arg(param, curl_off_t);
    break;
  case CURLOPT_LOW_SPEED_LIMIT:
    /*
     * The low speed limit that if transfers are below this for
     * CURLOPT_LOW_SPEED_TIME, the transfer is aborted.
     */
    data->set.low_speed_limit=va_arg(param, long);
    break;
  case CURLOPT_MAX_SEND_SPEED_LARGE:
    /*
     * The max speed limit that sends transfer more than
     * CURLOPT_MAX_SEND_PER_SECOND bytes per second the transfer is
     * throttled..
     */
    data->set.max_send_speed=va_arg(param, curl_off_t);
    break;
  case CURLOPT_MAX_RECV_SPEED_LARGE:
    /*
     * The max speed limit that sends transfer more than
     * CURLOPT_MAX_RECV_PER_SECOND bytes per second the transfer is
     * throttled..
     */
    data->set.max_recv_speed=va_arg(param, curl_off_t);
    break;
  case CURLOPT_LOW_SPEED_TIME:
    /*
     * The low speed time that if transfers are below the set
     * CURLOPT_LOW_SPEED_LIMIT during this time, the transfer is aborted.
     */
    data->set.low_speed_time=va_arg(param, long);
    break;
  case CURLOPT_URL:
    /*
     * The URL to fetch.
     */
    if(data->change.url_alloc) {
      /* the already set URL is allocated, free it first! */
      free(data->change.url);
      data->change.url_alloc=FALSE;
    }
    data->set.set_url = va_arg(param, char *);
    data->change.url = data->set.set_url;
    data->change.url_changed = TRUE;
    break;
  case CURLOPT_PORT:
    /*
     * The port number to use when getting the URL
     */
    data->set.use_port = va_arg(param, long);
    break;
  case CURLOPT_TIMEOUT:
    /*
     * The maximum time you allow curl to use for a single transfer
     * operation.
     */
    data->set.timeout = va_arg(param, long);
    break;
  case CURLOPT_CONNECTTIMEOUT:
    /*
     * The maximum time you allow curl to use to connect.
     */
    data->set.connecttimeout = va_arg(param, long);
    break;
  case CURLOPT_USERPWD:
    /*
     * user:password to use in the operation
     */
    data->set.userpwd = va_arg(param, char *);
  case CURLOPT_POSTQUOTE:
     * List of RAW FTP commands to use after a transfer
    data->set.postquote = va_arg(param, struct curl_slist *);
  case CURLOPT_PREQUOTE:
    /*
     * List of RAW FTP commands to use prior to RETR (Wesley Laxton)
     */
    data->set.prequote = va_arg(param, struct curl_slist *);
    break;
     * List of RAW FTP commands to use before a transfer
    data->set.quote = va_arg(param, struct curl_slist *);
    data->set.fprogress = va_arg(param, curl_progress_callback);
    if(data->set.fprogress)
      data->progress.callback = TRUE; /* no longer internal */
    else
      data->progress.callback = FALSE; /* NULL enforces internal */

    /*
     * Custom client data to pass to the progress callback
     */
    data->set.progress_client = va_arg(param, void *);
  case CURLOPT_PROXYUSERPWD:
    /*
     * user:password needed to use the proxy
     */
    data->set.proxyuserpwd = va_arg(param, char *);
  case CURLOPT_RANGE:
    /*
     * What range of the file you want to transfer
     */
    data->set.set_range = va_arg(param, char *);
  case CURLOPT_RESUME_FROM:
    /*
     * Resume transfer at the give file position
     */
    data->set.set_resume_from = va_arg(param, long);
  case CURLOPT_RESUME_FROM_LARGE:
    /*
     * Resume transfer at the give file position
     */
    data->set.set_resume_from = va_arg(param, curl_off_t);
  case CURLOPT_DEBUGFUNCTION:
    /*
     * stderr write callback.
     */
    data->set.fdebug = va_arg(param, curl_debug_callback);
    /*
     * if the callback provided is NULL, it'll use the default callback
     */
    break;
  case CURLOPT_DEBUGDATA:
    /*
     * Set to a void * that should receive all error writes. This
     * defaults to CURLOPT_STDERR for normal operations.
     */
    data->set.debugdata = va_arg(param, void *);
  case CURLOPT_STDERR:
    /*
     * Set to a FILE * that should receive all error writes. This
     * defaults to stderr for normal operations.
     */
    data->set.err = va_arg(param, FILE *);
    if(!data->set.err)
      data->set.err = stderr;
  case CURLOPT_HEADERFUNCTION:
    /*
     * Set header write callback
     */
    data->set.fwrite_header = va_arg(param, curl_write_callback);
  case CURLOPT_WRITEFUNCTION:
    data->set.fwrite = va_arg(param, curl_write_callback);
    if(!data->set.fwrite)
      /* When set to NULL, reset to our internal default function */
      data->set.fwrite = (curl_write_callback)fwrite;
  case CURLOPT_READFUNCTION:
    data->set.fread = va_arg(param, curl_read_callback);
    if(!data->set.fread)
      /* When set to NULL, reset to our internal default function */
      data->set.fread = (curl_read_callback)fread;
  case CURLOPT_CONV_FROM_NETWORK_FUNCTION:
    /*
     * "Convert from network encoding" callback
     */
    data->set.convfromnetwork = va_arg(param, curl_conv_callback);
    break;
  case CURLOPT_CONV_TO_NETWORK_FUNCTION:
    /*
     * "Convert to network encoding" callback
     */
    data->set.convtonetwork = va_arg(param, curl_conv_callback);
    break;
  case CURLOPT_CONV_FROM_UTF8_FUNCTION:
    /*
     * "Convert from UTF-8 encoding" callback
     */
    data->set.convfromutf8 = va_arg(param, curl_conv_callback);
    break;
  case CURLOPT_IOCTLFUNCTION:
    /*
     * I/O control callback. Might be NULL.
     */
    data->set.ioctl = va_arg(param, curl_ioctl_callback);
    break;
  case CURLOPT_IOCTLDATA:
    /*
     * I/O control data pointer. Might be NULL.
     */
    data->set.ioctl_client = va_arg(param, void *);
  case CURLOPT_SSLCERT:
    /*
     * String that holds file name of the SSL certificate to use
     */
    data->set.cert = va_arg(param, char *);
  case CURLOPT_SSLCERTTYPE:
     * String that holds file type of the SSL certificate to use
    data->set.cert_type = va_arg(param, char *);
    break;
  case CURLOPT_SSLKEY:
    /*
     * String that holds file name of the SSL certificate to use
     */
    data->set.key = va_arg(param, char *);
    break;
  case CURLOPT_SSLKEYTYPE:
    /*
     * String that holds file type of the SSL certificate to use
     */
    data->set.key_type = va_arg(param, char *);
    break;
  case CURLOPT_SSLKEYPASSWD:
    /*
     * String that holds the SSL private key password.
     */
    data->set.key_passwd = va_arg(param, char *);
    break;
  case CURLOPT_SSLENGINE:
    /*
     * String that holds the SSL crypto engine.
     */
    if (argptr && argptr[0])
       result = Curl_ssl_set_engine(data, argptr);
  case CURLOPT_SSLENGINE_DEFAULT:
    /*
     * flag to set engine as default.
     */
    result = Curl_ssl_set_engine_default(data);
  case CURLOPT_CRLF:
     * Kludgy option to enable CRLF conversions. Subject for removal.
    data->set.crlf = va_arg(param, long)?TRUE:FALSE;
  case CURLOPT_INTERFACE:
     * Set what interface or address/hostname to bind the socket to when
     * performing an operation and thus what from-IP your connection will use.
    data->set.device = va_arg(param, char *);
  case CURLOPT_LOCALPORT:
    /*
     * Set what local port to bind the socket to when performing an operation.
     */
    data->set.localport = (unsigned short) va_arg(param, long);
    break;
  case CURLOPT_LOCALPORTRANGE:
    /*
     * Set number of local ports to try, starting with CURLOPT_LOCALPORT.
     */
    data->set.localportrange = (int) va_arg(param, long);
    break;
Daniel Stenberg's avatar
Daniel Stenberg committed
  case CURLOPT_KRB4LEVEL:
    /*
     * A string that defines the krb4 security level.
     */
    data->set.krb4_level = va_arg(param, char *);
    data->set.krb4=data->set.krb4_level?TRUE:FALSE;
Daniel Stenberg's avatar
Daniel Stenberg committed
    break;
    data->set.ssl.verifypeer = va_arg(param, long);
  case CURLOPT_SSL_VERIFYHOST:
    /*
     * Enable verification of the CN contained in the peer certificate
     */
    data->set.ssl.verifyhost = va_arg(param, long);
  case CURLOPT_SSL_CTX_FUNCTION:
    /*
     * Set a SSL_CTX callback
     */
    data->set.ssl.fsslctx = va_arg(param, curl_ssl_ctx_callback);
    break;
  case CURLOPT_SSL_CTX_DATA:
    /*
     * Set a SSL_CTX callback parameter pointer
     */
    data->set.ssl.fsslctxp = va_arg(param, void *);
    break;
    /*
     * Set CA info for SSL connection. Specify file name of the CA certificate
     */
    data->set.ssl.CAfile = va_arg(param, char *);
    break;
  case CURLOPT_CAPATH:
    /*
     * Set CA path info for SSL connection. Specify directory name of the CA
     * certificates which have been prepared using openssl c_rehash utility.
    /* This does not work on windows. */
    data->set.ssl.CApath = va_arg(param, char *);
  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.
     */
    data->set.no_signal = va_arg(param, long) ? TRUE : FALSE;
    break;

      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 = set;
      if(data->share) {

        Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE);
          /* 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);
          data->cookies = data->share->cookies;
        }
        Curl_share_unlock(data, CURL_LOCK_DATA_SHARE);
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
      /* check cookie list is set */
      if(!data->cookies)
        data->cookies = Curl_cookie_init(data, NULL, NULL, TRUE );
       * it will be done by curl_easy_perform */
  case CURLOPT_PROXYTYPE:
    /*
     * Set proxy type. HTTP/SOCKS4/SOCKS5
     */
    data->set.proxytype = (curl_proxytype)va_arg(param, long);
  case CURLOPT_PRIVATE:
    /*
     * Set private data pointer.
     */
    data->set.private_data = va_arg(param, char *);
  case CURLOPT_MAXFILESIZE:
    /*
     * Set the maximum size of a file to download.
     */
    data->set.max_filesize = va_arg(param, long);
    break;

  case CURLOPT_FTP_SSL:
    /*
     * Make FTP transfers attempt to use SSL/TLS.
     */
    data->set.ftp_ssl = (curl_ftpssl)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
     */
    data->set.tcp_nodelay = (bool)va_arg(param, long);
  /*********** 3rd party transfer options ***********/
    data->set.source_url = va_arg(param, char *);
    data->set.printhost = (data->set.source_url != NULL);
    break;

  case CURLOPT_SOURCE_USERPWD:
    /*
     * Use SOURCE USER[:PASSWORD]
     */
    data->set.source_userpwd = va_arg(param, char *);
    break;

     * List of RAW FTP commands to use after a connect
    data->set.source_quote = va_arg(param, struct curl_slist *);
    break;

  case CURLOPT_SOURCE_PREQUOTE:
    /*
     * List of RAW FTP commands to use before a transfer on the source host
     */
    data->set.source_prequote = va_arg(param, struct curl_slist *);
    break;

  case CURLOPT_SOURCE_POSTQUOTE:
    /*
     * List of RAW FTP commands to use after a transfer on the source host
     */
    data->set.source_postquote = va_arg(param, struct curl_slist *);
    break;

  case CURLOPT_FTP_ACCOUNT:
    data->set.ftp_account = va_arg(param, char *);
    break;

  case CURLOPT_IGNORE_CONTENT_LENGTH:
    data->set.ignorecl = va_arg(param, long)?TRUE:FALSE;
    break;

  case CURLOPT_CONNECT_ONLY:
    /*
     * No data transfer, set up connection and let application use the socket
     */
    data->set.connect_only = va_arg(param, long)?TRUE:FALSE;
    break;

  case CURLOPT_FTP_ALTERNATIVE_TO_USER:
    data->set.ftp_alternative_to_user = va_arg(param, char *);
    break;

  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;

  default:
    /* unknown tag and its companion, just ignore: */
    result = CURLE_FAILED_INIT; /* correct this */
    break;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
CURLcode Curl_disconnect(struct connectdata *conn)
  struct SessionHandle *data;
  if(!conn)
    return CURLE_OK; /* this is closed and fine already */

#if defined(CURLDEBUG) && 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 */

  /*
   * The range string is usually freed in curl_done(), but we might
   * get here *instead* if we fail prematurely. Thus we need to be able
   * to free this resource here as well.
   */
  if(conn->bits.rangestringalloc) {
    free(conn->range);
    conn->bits.rangestringalloc = FALSE;
  }

  if((conn->ntlm.state != NTLMSTATE_NONE) ||
     (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. */
    data->state.authhost.done = FALSE;
    data->state.authhost.picked =
      data->state.authhost.want;

    data->state.authproxy.done = FALSE;
    data->state.authproxy.picked =
      data->state.authproxy.want;

    data->state.authproblem = FALSE;
  if(conn->curl_disconnect)
    /* This is set if protocol-specific cleanups should be made */
    conn->curl_disconnect(conn);

  if(-1 != conn->connectindex) {
    infof(data, "Closing connection #%ld\n", conn->connectindex);
    data->state.connects[conn->connectindex] = NULL;
  Curl_safefree(conn->proto.generic);
  Curl_safefree(conn->newurl);
  Curl_safefree(conn->pathbuffer); /* the URL path buffer */

  Curl_safefree(conn->host.rawalloc); /* host name buffer */
  Curl_safefree(conn->proxy.rawalloc); /* proxy name buffer */
#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 */

  /* close possibly still open sockets */
  if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
  if(CURL_SOCKET_BAD != 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);
  /* possible left-overs from the async name resolvers */
#if defined(USE_ARES)
  Curl_safefree(conn->async.os_specific);
#elif defined(CURLRES_THREADED)
  Curl_destroy_thread_data(&conn->async);
  Curl_free_ssl_config(&conn->ssl_config);
  free(conn); /* free all the connection oriented data */
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_select(sock, CURL_SOCKET_BAD, 0);
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* timeout */
Daniel Stenberg's avatar
Daniel Stenberg committed
  return ret_val;
}

 * Given one filled in connection struct (named needle), this function should
 * detect if there already is one that have all the significant details
 * exactly the same and thus should be used instead.
ConnectionExists(struct SessionHandle *data,
                 struct connectdata *needle,
                 struct connectdata **usethis)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct connectdata *check;

  for(i=0; i< data->state.numconnects; i++) {
    /*
     * Note that if we use a HTTP proxy, we check connections to that
     * proxy and not to the actual remote server.
     */
    if(!check)
      /* NULL pointer means not filled-in entry */
      continue;

    if((needle->protocol&PROT_SSL) != (check->protocol&PROT_SSL))
      /* don't do mixed SSL and non-SSL connections */
      continue;

    if(!needle->bits.httpproxy || needle->protocol&PROT_SSL) {
      /* The requested connection does not use a HTTP proxy or it
         uses SSL. */

      if(!(needle->protocol&PROT_SSL) && check->bits.httpproxy)
        /* we don't do SSL but the cached connection has a proxy,
           then don't match this */
        continue;

      if(strequal(needle->protostr, check->protostr) &&
         strequal(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)) {
        if((needle->protocol & PROT_FTP) ||
           ((needle->protocol & PROT_HTTP) &&
            (needle->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? */
      if(check->bits.httpproxy &&
         strequal(needle->proxy.name, check->proxy.name) &&
         needle->port == check->port) {
        /* This is the same proxy connection, use it! */
      bool dead = SocketIsDead(check->sock[FIRSTSOCKET]);
      if(dead) {
        /*
         */
        infof(data, "Connection %d seems to be dead!\n", i);
        Curl_disconnect(check); /* disconnect resources */
        data->state.connects[i]=NULL; /* nothing here */

        /* There's no need to continue searching, because we only store
           one connection for each unique set of identifiers */
        return FALSE;

      *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; i< data->state.numconnects; i++) {
    conn = data->state.connects[i];
    if(!conn)
      continue;

    /*
     * By using the set policy, we score each connection.
     */
    case CURLCLOSEPOLICY_LEAST_RECENTLY_USED:
      /*
       * Set higher score for the age passed since the connection
       * was used.
       */
      score = Curl_tvdiff(now, conn->now);
      break;
    case CURLCLOSEPOLICY_OLDEST:
      /*
       * Set higher score for the age passed since the connection
       * was created.
       */
      score = Curl_tvdiff(now, conn->created);
      break;
    }

    if(score > highscore) {
      highscore = score;
      connindex = i;
    }
  }
  if(connindex >= 0) {

    /* the winner gets the honour of being disconnected */
    (void) Curl_disconnect(data->state.connects[connindex]);

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

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

/*
 * 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.numconnects; i++) {
    if(!data->state.connects[i])
    /* there was no room available, kill one */
    i = ConnectionKillOne(data);
    infof(data, "Connection (#%d) was killed to make room\n", i);
  }
  if(-1 != i) {
    /* 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. */
    data->state.connects[i] = conn; /* fill in this */
    conn->connectindex = i; /* make the child know where the pointer to this
                               particular data is stored */
  }
* This function logs in to a SOCKS4 proxy and sends the specifics to the final
* destination server.
*
* Reference :
*   http://socks.permeo.com/protocol/socks4.protocol
*
* Note :
*   Nonsupport "SOCKS 4A (Simple Extension to SOCKS 4 Protocol)"
*   Nonsupport "Identification Protocol (RFC1413)"
*/
static int handleSock4Proxy(const char *proxy_name, struct connectdata *conn)
  unsigned char socksreq[262]; /* room for SOCKS4 request incl. user id */
  int result;
  CURLcode code;
  curl_socket_t sock = conn->sock[FIRSTSOCKET];
  struct SessionHandle *data = conn->data;

  Curl_nonblock(sock, FALSE);

  /*
  * Compose socks4 request
  *
  * Request format
  *
  *     +----+----+----+----+----+----+----+----+----+----+....+----+
  *     | VN | CD | DSTPORT |      DSTIP        | USERID       |NULL|
  *     +----+----+----+----+----+----+----+----+----+----+....+----+
  * # of bytes:  1    1      2              4           variable       1
  */

  socksreq[0] = 4; /* version (SOCKS4) */
  socksreq[1] = 1; /* connect */
  *((unsigned short*)&socksreq[2]) = htons(conn->remote_port);

  /* DNS resolve */
  {
    struct Curl_dns_entry *dns;
    Curl_addrinfo *hp=NULL;
    int rc;

    rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);

    if(rc == CURLRESOLV_ERROR)
      return 1;

    if(rc == CURLRESOLV_PENDING)
      /* this requires that we're in "wait for resolve" state */
      rc = Curl_wait_for_resolv(conn, &dns);

    /*
    * We cannot use 'hostent' as a struct that Curl_resolv() returns.  It
    * returns a Curl_addrinfo pointer that may not always look the same.
    */
    if(dns)
      hp=dns->addr;
    if (hp) {
      char buf[64];
      unsigned short ip[4];
      Curl_printable_address(hp, buf, sizeof(buf));

      if(4 == sscanf( buf, "%hu.%hu.%hu.%hu",
        &ip[0], &ip[1], &ip[2], &ip[3])) {
          /* Set DSTIP */
          socksreq[4] = (unsigned char)ip[0];
          socksreq[5] = (unsigned char)ip[1];
          socksreq[6] = (unsigned char)ip[2];
          socksreq[7] = (unsigned char)ip[3];
        }
      else
        hp = NULL; /* fail! */

      Curl_resolv_unlock(conn->data, dns); /* not used anymore from now on */

    }
    if(!hp) {
      failf(conn->data, "Failed to resolve \"%s\" for SOCKS4 connect.",
Daniel Stenberg's avatar
Daniel Stenberg committed
   * This is currently not supporting "Identification Protocol (RFC1413)".
   */
  socksreq[8] = 0; /* ensure empty userid is NUL-terminated */
  if (proxy_name)
    strlcat((char*)socksreq + 8, proxy_name, sizeof(socksreq) - 8);
Daniel Stenberg's avatar
Daniel Stenberg committed

  /*
   * Make connection
   */
    int packetsize = 9 +
      (int)strlen((char*)socksreq + 8); /* size including NUL */

    /* Send request */
    code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
    if ((code != CURLE_OK) || (written != packetsize)) {
      failf(conn->data, "Failed to send SOCKS4 connect request.");
      return 1;
    }

    packetsize = 8; /* receive data size */

    /* Receive response */
    result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread);
    if ((result != CURLE_OK) || (actualread != packetsize)) {
      failf(conn->data, "Failed to receive SOCKS4 connect request ack.");
      return 1;
    }

    /*
    * Response format
    *
    *     +----+----+----+----+----+----+----+----+
    *     | VN | CD | DSTPORT |      DSTIP        |
    *     +----+----+----+----+----+----+----+----+
    * # of bytes:  1    1      2              4
    *
    * VN is the version of the reply code and should be 0. CD is the result
    * code with one of the following values:
    *
    * 90: request granted
    * 91: request rejected or failed
    * 92: request rejected because SOCKS server cannot connect to
    *     identd on the client
    * 93: request rejected because the client program and identd
    *     report different user-ids
    */

    /* wrong version ? */
    if (socksreq[0] != 0) {
      failf(conn->data,
        "SOCKS4 reply has wrong version, version should be 4.");
      return 1;
    }

    /* Result */
    switch(socksreq[1])
    {
      case 90:
        infof(data, "SOCKS4 request granted.\n");
        break;
      case 91:
        failf(conn->data,
          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
          (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
          socksreq[1]);
        return 1;
      case 92:
        failf(conn->data,
          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
          ", request rejected because SOCKS server cannot connect to "
          "identd on the client.",
          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
          (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
          socksreq[1]);
        return 1;
      case 93:
        failf(conn->data,
          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
          ", request rejected because the client program and identd "
          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
          (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
          socksreq[1]);
        return 1;
      default :
        failf(conn->data,
          "Can't complete SOCKS4 connection to %d.%d.%d.%d:%d. (%d)"
          (unsigned char)socksreq[4], (unsigned char)socksreq[5],
          (unsigned char)socksreq[6], (unsigned char)socksreq[7],
          (unsigned int)ntohs(*(unsigned short*)(&socksreq[8])),
          socksreq[1]);
        return 1;
    }
  }

  Curl_nonblock(sock, TRUE);

  return 0; /* Proxy was successful! */
}

 * This function logs in to a SOCKS5 proxy and sends the specifics to the final
 * destination server.
static int handleSock5Proxy(const char *proxy_name,
                            const char *proxy_password,
                            struct connectdata *conn)
  /*
    According to the RFC1928, section "6.  Replies". This is what a SOCK5
    replies:

        +----+-----+-------+------+----------+----------+
        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1   | Variable |    2     |
        +----+-----+-------+------+----------+----------+

    Where:

    o  VER    protocol version: X'05'
    o  REP    Reply field:
    o  X'00' succeeded
  */

  unsigned char socksreq[600]; /* room for large user/pw (255 max each) */
  curl_socket_t sock = conn->sock[FIRSTSOCKET];

  Curl_nonblock(sock, FALSE);

  socksreq[0] = 5; /* version */
  socksreq[1] = (char)(proxy_name ? 2 : 1); /* number of methods (below) */
  socksreq[2] = 0; /* no authentication */
  socksreq[3] = 2; /* username/password */

  code = Curl_write(conn, sock, (char *)socksreq, (2 + (int)socksreq[1]),
  if ((code != CURLE_OK) || (written != (2 + (int)socksreq[1]))) {
    failf(conn->data, "Unable to send initial SOCKS5 request.");
    return 1;
  }

  result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
  if ((result != CURLE_OK) || (actualread != 2)) {
    failf(conn->data, "Unable to receive initial SOCKS5 response.");
    return 1;
  }

  if (socksreq[0] != 5) {
    failf(conn->data, "Received invalid version in initial SOCKS5 response.");
    return 1;
  }
  if (socksreq[1] == 0) {
    /* Nothing to do, no authentication needed */
    ;
  }
  else if (socksreq[1] == 2) {
    /* Needs user name and password */
    int userlen, pwlen, len;

    userlen = (int)strlen(proxy_name);
    pwlen = proxy_password?(int)strlen(proxy_password):0;

    /*   username/password request looks like
     * +----+------+----------+------+----------+
     * |VER | ULEN |  UNAME   | PLEN |  PASSWD  |
     * +----+------+----------+------+----------+
     * | 1  |  1   | 1 to 255 |  1   | 1 to 255 |
     * +----+------+----------+------+----------+
     */
    len = 0;
    socksreq[len++] = 1;    /* username/pw subnegotiation version */
    socksreq[len++] = (char) userlen;
    memcpy(socksreq + len, proxy_name, (int) userlen);
    len += userlen;
    socksreq[len++] = (char) pwlen;
    memcpy(socksreq + len, proxy_password, (int) pwlen);
    len += pwlen;

    code = Curl_write(conn, sock, (char *)socksreq, len, &written);
    if ((code != CURLE_OK) || (len != written)) {
      failf(conn->data, "Failed to send SOCKS5 sub-negotiation request.");
      return 1;
    }

    result=Curl_read(conn, sock, (char *)socksreq, 2, &actualread);
    if ((result != CURLE_OK) || (actualread != 2)) {
      failf(conn->data, "Unable to receive SOCKS5 sub-negotiation response.");
      return 1;
    }

    /* ignore the first (VER) byte */
    if (socksreq[1] != 0) { /* status */
      failf(conn->data, "User was rejected by the SOCKS5 server (%d %d).",
            socksreq[0], socksreq[1]);
      return 1;
    }

    /* Everything is good so far, user was authenticated! */
  }
  else {
    /* error */
    if (socksreq[1] == 1) {
      failf(conn->data,
            "SOCKS5 GSSAPI per-message authentication is not supported.");
      return 1;
    }
    else if (socksreq[1] == 255) {
Loading
Loading full blame...