Skip to content
url.c 55.2 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
/*****************************************************************************
 *                                  _   _ ____  _     
 *  Project                     ___| | | |  _ \| |    
 *                             / __| | | | |_) | |    
 *                            | (__| |_| |  _ <| |___ 
 *                             \___|\___/|_| \_\_____|
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * Copyright (C) 2000, Daniel Stenberg, <daniel@haxx.se>, et al.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * In order to be useful for every potential user, curl and libcurl are
 * dual-licensed under the MPL and the MIT/X-derivate licenses.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
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 MPL or the MIT/X-derivate
 * licenses. You may pick one of these licenses.
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>
#include <sys/types.h>
#include <sys/stat.h>

#include <errno.h>

#if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
#include <winsock.h>
#include <time.h>
#include <io.h>
#else
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/resource.h>
#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

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif

#ifndef HAVE_SELECT
#error "We can't compile without select() support!"
#endif
#ifndef HAVE_SOCKET
#error "We can't compile without socket() support!"
#endif

#endif

#include "urldata.h"
#include "netrc.h"

#include "formdata.h"
#include "base64.h"
#include "ssluse.h"
#include "hostip.h"
#include "if2ip.h"
Daniel Stenberg's avatar
Daniel Stenberg committed
#include "sendf.h"
#include "getpass.h"
#include "progress.h"
#include "cookie.h"
#include "strequal.h"
Daniel Stenberg's avatar
Daniel Stenberg committed

/* And now for the protocols */
#include "ftp.h"
#include "dict.h"
#include "telnet.h"
#include "http.h"
#include "file.h"
#include "ldap.h"

#include <curl/types.h>
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
#ifdef KRB4
#include "security.h"
#endif
/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

/* Local static prototypes */
static int ConnectionKillOne(struct UrlData *data);
static bool ConnectionExists(struct UrlData *data,
                             struct connectdata *needle,
                             struct connectdata **usethis);
static unsigned int ConnectionStore(struct UrlData *data,
                                    struct connectdata *conn);
CURLcode Curl_close(CURL *curl)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  struct UrlData *data=(struct UrlData *)curl;
  
  /* Loop through all open connections and kill them one by one */
  while(-1 != ConnectionKillOne(data));
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(data->bits.proxystringalloc) {
    data->bits.proxystringalloc=FALSE;;
    free(data->proxy);
    data->proxy=NULL;

    /* Since we allocated the string the previous round, it means that we
       "discovered" the proxy in the environment variables and thus we must
       switch off that knowledge again... */
    data->bits.httpproxy=FALSE;
  if(data->bits.rangestringalloc) {
    free(data->range);
    data->range=NULL;
    data->bits.rangestringalloc=0; /* free now */
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* check for allocated [URL] memory to free: */
  if(data->freethis)
    free(data->freethis);
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(data->headerbuff)
    free(data->headerbuff);
  if(data->free_referer)
    free(data->referer);
  if(data->bits.urlstringalloc)
    /* the URL is allocated, free it! */
    free(data->url);
Daniel Stenberg's avatar
Daniel Stenberg committed

  Curl_cookie_cleanup(data->cookies);
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* free the connection cache */
  free(data->connects);
  return CURLE_OK;
}
int my_getpass(void *clientp, char *prompt, char* buffer, int buflen )
{
  char *retbuf;
  retbuf = getpass_r(prompt, buffer, buflen);
  if(NULL == retbuf)
    return 1;
  else
    return 0; /* success */
CURLcode Curl_open(CURL **curl, char *url)
{
  /* We don't yet support specifying the URL at this point */
  struct UrlData *data;

  /* Very simple start-up: alloc the struct, init it with zeroes and return */
  data = (struct UrlData *)malloc(sizeof(struct UrlData));
    memset(data, 0, sizeof(struct UrlData));

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

    data-> headerbuff=(char*)malloc(HEADERSIZE);
    if(!data->headerbuff) {
      free(data); /* free the memory again */
      return CURLE_OUT_OF_MEMORY;
    }


    data->out = stdout; /* default output to stdout */
    data->in  = stdin;  /* default input from stdin */
    data->err  = stderr;  /* default stderr to stderr */

    /* use fwrite as default function to store output */
    data->fwrite = (size_t (*)(char *, size_t, size_t, FILE *))fwrite;

    /* use fread as default function to read input */
    data->fread = (size_t (*)(char *, size_t, size_t, FILE *))fread;

    /* set the default passwd function */
    data->fpasswd = my_getpass;

    data->infilesize = -1; /* we don't know any size */

    data->current_speed = -1; /* init to negative == impossible */

    data->httpreq = HTTPREQ_GET; /* Default HTTP request */

    /* create an array with connection data struct pointers */
    data->numconnects = 5; /* hard-coded right now */
    data->connects = (struct connectdata **)
      malloc(sizeof(struct connectdata *) * data->numconnects);

    if(!data->connects) {
      free(data);
      return CURLE_OUT_OF_MEMORY;
    }

    memset(data->connects, 0, sizeof(struct connectdata *)*data->numconnects);

    return CURLE_OK;
  }

  /* this is a very serious error */
  return CURLE_OUT_OF_MEMORY;
CURLcode Curl_setopt(CURL *curl, CURLoption option, ...)
{
  struct UrlData *data = curl;
  va_list param;
  char *cookiefile;

  va_start(param, option);

  switch(option) {
  case CURLOPT_VERBOSE:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.verbose = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_HEADER:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_include_header = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_NOPROGRESS:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.hide_progress = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.hide_progress)
      data->progress.flags |= PGRS_HIDE;
    break;
  case CURLOPT_NOBODY:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.no_body = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FAILONERROR:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_fail_on_error = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_UPLOAD:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.upload = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.upload)
      /* If this is HTTP, PUT is what's needed to "upload" */
      data->httpreq = HTTPREQ_PUT;
Daniel Stenberg's avatar
Daniel Stenberg committed
  case CURLOPT_FILETIME:
    data->bits.get_filetime = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FTPLISTONLY:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.ftp_list_only = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FTPAPPEND:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.ftp_append = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_NETRC:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.use_netrc = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FOLLOWLOCATION:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_follow_location = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_FTPASCII:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.ftp_ascii = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_PUT:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.http_put = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.http_put)
      data->httpreq = HTTPREQ_PUT;
    break;
  case CURLOPT_MUTE:
Daniel Stenberg's avatar
Daniel Stenberg committed
    data->bits.mute = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_TIMECONDITION:
    data->timecondition = va_arg(param, long);
    break;
  case CURLOPT_TIMEVALUE:
    data->timevalue = va_arg(param, long);
    break;
  case CURLOPT_SSLVERSION:
    data->ssl.version = va_arg(param, long);
  case CURLOPT_COOKIEFILE:
    cookiefile = (char *)va_arg(param, void *);
    if(cookiefile) {
      data->cookies = Curl_cookie_init(cookiefile);
  case CURLOPT_WRITEHEADER:
    data->writeheader = (FILE *)va_arg(param, FILE *);
    break;
  case CURLOPT_COOKIE:
    data->cookie = va_arg(param, char *);
    break;
  case CURLOPT_ERRORBUFFER:
    data->errorbuffer = va_arg(param, char *);
    break;
  case CURLOPT_FILE:
    data->out = va_arg(param, FILE *);
    break;
  case CURLOPT_FTPPORT:
    data->ftpport = va_arg(param, char *);
    data->bits.ftp_use_port = data->ftpport?1:0;
  case CURLOPT_HTTPHEADER:
    data->headers = va_arg(param, struct curl_slist *);
  case CURLOPT_CUSTOMREQUEST:
    data->customrequest = va_arg(param, char *);
    if(data->customrequest)
      data->httpreq = HTTPREQ_CUSTOM;
  case CURLOPT_HTTPPOST:
    data->httppost = va_arg(param, struct HttpPost *);
    data->bits.http_formpost = data->httppost?1:0;
    if(data->bits.http_formpost)
      data->httpreq = HTTPREQ_POST_FORM;
  case CURLOPT_INFILE:
    data->in = va_arg(param, FILE *);
    break;
  case CURLOPT_INFILESIZE:
    data->infilesize = va_arg(param, long);
    break;
  case CURLOPT_LOW_SPEED_LIMIT:
    data->low_speed_limit=va_arg(param, long);
    break;
  case CURLOPT_LOW_SPEED_TIME:
    data->low_speed_time=va_arg(param, long);
    break;
  case CURLOPT_URL:
    data->url = va_arg(param, char *);
    break;
  case CURLOPT_PORT:
    data->use_port = va_arg(param, long);
  case CURLOPT_POST:
    /* Does this option serve a purpose anymore? */
    data->bits.http_post = va_arg(param, long)?TRUE:FALSE;
    if(data->bits.http_post)
      data->httpreq = HTTPREQ_POST;
    break;
  case CURLOPT_POSTFIELDS:
    data->postfields = va_arg(param, char *);
    data->bits.http_post = data->postfields?TRUE:FALSE;
    if(data->bits.http_post)
      data->httpreq = HTTPREQ_POST;
  case CURLOPT_POSTFIELDSIZE:
    data->postfieldsize = va_arg(param, long);
    break;
  case CURLOPT_REFERER:
    data->referer = va_arg(param, char *);
    data->bits.http_set_referer = (data->referer && *data->referer)?1:0;
  case CURLOPT_AUTOREFERER:
    data->bits.http_auto_referer = va_arg(param, long)?1:0;
    break;
  case CURLOPT_PROXY:
    data->proxy = va_arg(param, char *);
    data->bits.httpproxy = data->proxy?1:0;
  case CURLOPT_HTTPPROXYTUNNEL:
    data->bits.tunnel_thru_httpproxy = va_arg(param, long)?TRUE:FALSE;
    break;
  case CURLOPT_PROXYPORT:
    data->proxyport = va_arg(param, long);
  case CURLOPT_TIMEOUT:
    data->timeout = va_arg(param, long);
    break;
  case CURLOPT_MAXREDIRS:
    data->maxredirs = va_arg(param, long);
    break;
  case CURLOPT_USERAGENT:
    data->useragent = va_arg(param, char *);
    break;
  case CURLOPT_USERPWD:
    data->userpwd = va_arg(param, char *);
    data->bits.user_passwd = data->userpwd?1:0;
  case CURLOPT_POSTQUOTE:
    data->postquote = va_arg(param, struct curl_slist *);
    break;
  case CURLOPT_PROGRESSFUNCTION:
    data->fprogress = va_arg(param, curl_progress_callback);
    data->progress.callback = TRUE; /* no longer internal */
    break;
  case CURLOPT_PROGRESSDATA:
    data->progress_client = va_arg(param, void *);
    break;
  case CURLOPT_PASSWDFUNCTION:
    data->fpasswd = va_arg(param, curl_passwd_callback);
    break;
  case CURLOPT_PASSWDDATA:
    data->passwd_client = va_arg(param, void *);
    break;
  case CURLOPT_PROXYUSERPWD:
    data->proxyuserpwd = va_arg(param, char *);
    data->bits.proxy_user_passwd = data->proxyuserpwd?1:0;
  case CURLOPT_RANGE:
    data->range = va_arg(param, char *);
    data->bits.set_range = data->range?1:0;
  case CURLOPT_RESUME_FROM:
    data->resume_from = va_arg(param, long);
    break;
  case CURLOPT_STDERR:
    data->err = va_arg(param, FILE *);
    break;
  case CURLOPT_WRITEFUNCTION:
    data->fwrite = va_arg(param, curl_write_callback);
  case CURLOPT_READFUNCTION:
    data->fread = va_arg(param, curl_read_callback);
  case CURLOPT_SSLCERT:
    data->cert = va_arg(param, char *);
    break;
  case CURLOPT_SSLCERTPASSWD:
    data->cert_passwd = va_arg(param, char *);
    break;
  case CURLOPT_CRLF:
    data->crlf = va_arg(param, long);
    break;
  case CURLOPT_QUOTE:
    data->quote = va_arg(param, struct curl_slist *);
    break;
  case CURLOPT_INTERFACE:
    data->device = va_arg(param, char *);
    break;
Daniel Stenberg's avatar
Daniel Stenberg committed
  case CURLOPT_KRB4LEVEL:
    data->krb4_level = va_arg(param, char *);
    data->bits.krb4=data->krb4_level?TRUE:FALSE;
    break;
  case CURLOPT_SSL_VERIFYPEER:
    data->ssl.verifypeer = va_arg(param, long);
    break;
  case CURLOPT_CAINFO:
    data->ssl.CAfile = va_arg(param, char *);
    data->ssl.CApath = NULL; /*This does not work on windows.*/
    break;
  case CURLOPT_TELNETOPTIONS:
    data->telnet_options = va_arg(param, struct curl_slist *);
    break;
  default:
    /* unknown tag and its companion, just ignore: */
    return CURLE_READ_ERROR; /* correct this */
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  return CURLE_OK;
#if !defined(WIN32)||defined(__CYGWIN32__)
Daniel Stenberg's avatar
Daniel Stenberg committed
#ifndef RETSIGTYPE
#define RETSIGTYPE void
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
RETSIGTYPE alarmfunc(int signal)
{
  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
  (void)signal;
  return;
}
#endif

CURLcode Curl_disconnect(struct connectdata *conn)
  infof(conn->data, "Closing live connection (#%d)\n", conn->connectindex);

  if(-1 != conn->connectindex)
    /* unlink ourselves! */
    conn->data->connects[conn->connectindex] = NULL;

Daniel Stenberg's avatar
Daniel Stenberg committed
  if(conn->curl_disconnect)
    /* This is set if protocol-specific cleanups should be made */
    conn->curl_disconnect(conn);

  if(conn->proto.generic)
    free(conn->proto.generic);
#ifdef ENABLE_IPV6
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(conn->hp) /* host name info */
    freeaddrinfo(conn->hp);
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(conn->hostent_buf) /* host name info */
    free(conn->hostent_buf);
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(conn->path) /* the URL path part */
    free(conn->path);

#ifdef USE_SSLEAY
  if (conn->ssl.use) {
    if(conn->ssl.handle) {
      (void)SSL_shutdown(conn->ssl.handle);
      SSL_set_connect_state(conn->ssl.handle);
      SSL_free (conn->ssl.handle);
      conn->ssl.handle = NULL;
    }
    if(conn->ssl.ctx) {
      SSL_CTX_free (conn->ssl.ctx);
      conn->ssl.ctx = NULL;
    }
    conn->ssl.use = FALSE; /* get back to ordinary socket usage */
  }
#endif /* USE_SSLEAY */

  /* close possibly still open sockets */
  if(-1 != conn->secondarysocket)
    sclose(conn->secondarysocket);
  if(-1 != conn->firstsocket)
    sclose(conn->firstsocket);

  if(conn->allocptr.proxyuserpwd)
    free(conn->allocptr.proxyuserpwd);
  if(conn->allocptr.uagent)
    free(conn->allocptr.uagent);
  if(conn->allocptr.userpwd)
    free(conn->allocptr.userpwd);
  if(conn->allocptr.rangeline)
    free(conn->allocptr.rangeline);
  if(conn->allocptr.ref)
    free(conn->allocptr.ref);
  if(conn->allocptr.cookie)
    free(conn->allocptr.cookie);
  if(conn->allocptr.host)
    free(conn->allocptr.host);

  if(conn->proxyhost)
    free(conn->proxyhost);

  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(int sock) 
{ 
  int sval; 
  bool ret_val = TRUE; 
  fd_set check_set; 
  struct timeval to; 

  FD_ZERO(&check_set); 
  FD_SET(sock,&check_set); 

  to.tv_sec = 0; 
  to.tv_usec = 1; 

  sval = select(sock + 1, &check_set, 0, 0, &to);
  if(sval == 0) 
    /* timeout */
    ret_val = FALSE; 
  
  return ret_val;
}

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

  for(i=0; i< data->numconnects; i++) {
    /*
     * Note that if we use a HTTP proxy, we check connections to that
     * proxy and not to the actual remote server.
     */
    check = data->connects[i];
    if(!check)
      /* NULL pointer means not filled-in entry */
      continue;
    if(!needle->bits.httpproxy) {
      /* The requested connection does not use a HTTP proxy */

      if(strequal(needle->protostr, check->protostr) &&
         strequal(needle->name, check->name) &&
         (needle->port == check->port) ) {
Daniel Stenberg's avatar
Daniel Stenberg committed
        bool dead;
        if(strequal(needle->protostr, "FTP")) {
          /* This is FTP, verify that we're using the same name and
             password as well */
          if(!strequal(needle->data->user, check->proto.ftp->user) ||
             !strequal(needle->data->passwd, check->proto.ftp->passwd)) {
            /* one of them was different */
            continue;
          }
        }
Daniel Stenberg's avatar
Daniel Stenberg committed
        dead = SocketIsDead(check->firstsocket);
        if(dead) {
          infof(data, "Connection %d seems to be dead!\n", i);
          Curl_disconnect(check); /* disconnect resources */
Daniel Stenberg's avatar
Daniel Stenberg committed
          data->connects[i]=NULL; /* nothing here */
          continue; /* try another one now */
        }
      }
      *usethis = check;
      return TRUE; /* yes, we found one to use! */
    }
    else { /* The requested needle connection is using a proxy,
              is the checked one using the same? */
      if(check->bits.httpproxy &&
         strequal(needle->proxyhost, check->proxyhost) &&
         needle->port == check->port) {
        /* This is the same proxy connection, use it! */
        *usethis = check;
        return TRUE;
      }
    }
  }
  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.
 */
static int
ConnectionKillOne(struct UrlData *data)
{
  size_t i;
  struct connectdata *conn;
  int highscore=-1;
  int connindex=-1;
  int score;
  CURLcode result;

  for(i=0; i< data->numconnects; i++) {
    conn = data->connects[i];
    
    if(!conn)
      continue;

    /*
     * By using the set policy, we score each connection.
     */
    switch(data->closepolicy) {
    default:
      score = 1; /* not implemented yet */
      break;
    }

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

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

    /* clean the array entry */
    data->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.
 */
static unsigned int
ConnectionStore(struct UrlData *data,
                struct connectdata *conn)
{
  size_t i;
  for(i=0; i< data->numconnects; i++) {
    if(!data->connects[i])
      break;
  }
  if(i == data->numconnects) {
    /* there was no room available, kill one */
    i = ConnectionKillOne(data);
    infof(data, "Connection (#%d) was killed to make room\n", i);
  }

  data->connects[i] = conn; /* fill in this */
  conn->connectindex = i; /* make the child know where the pointer to this
                             particular data is stored */

  return i;
}

static CURLcode ConnectPlease(struct UrlData *data,
                              struct connectdata *conn)
{

#ifndef ENABLE_IPV6
  conn->firstsocket = socket(AF_INET, SOCK_STREAM, 0);

  memset((char *) &conn->serv_addr, '\0', sizeof(conn->serv_addr));
  memcpy((char *)&(conn->serv_addr.sin_addr),
         conn->hp->h_addr, conn->hp->h_length);
  conn->serv_addr.sin_family = conn->hp->h_addrtype;
  conn->serv_addr.sin_port = htons(conn->port);
Daniel Stenberg's avatar
Daniel Stenberg committed
#else
  /* IPv6-style */
  struct addrinfo *ai;

#if !defined(WIN32)||defined(__CYGWIN32__)
  /* We don't generally like checking for OS-versions, we should make this
     HAVE_XXXX based, although at the moment I don't have a decent test for
     this! */

#ifdef HAVE_INET_NTOA

#ifndef INADDR_NONE
#define INADDR_NONE (unsigned long) ~0
#ifndef ENABLE_IPV6
Daniel Stenberg's avatar
Daniel Stenberg committed
  /*************************************************************
   * Select device to bind socket to
Daniel Stenberg's avatar
Daniel Stenberg committed
   *************************************************************/
  if (data->device && (strlen(data->device)<255)) {
    struct sockaddr_in sa;
    struct hostent *h=NULL;
    char *hostdataptr=NULL;
    size_t size;
    char myhost[256] = "";
    unsigned long in;
    if(Curl_if2ip(data->device, myhost, sizeof(myhost))) {
      h = Curl_gethost(data, myhost, &hostdataptr);
    }
    else {
      if(strlen(data->device)>1) {
        h = Curl_gethost(data, data->device, &hostdataptr);
      }
      if(h) {
        /* we know data->device is shorter than the myhost array */
        strcpy(myhost, data->device);
      }
    }
    if(! *myhost) {
      /* need to fix this
         h=Curl_gethost(data,
         getmyhost(*myhost,sizeof(myhost)),
         hostent_buf,
         sizeof(hostent_buf));
      */
      printf("in here\n");
    }
    infof(data, "We connect from %s\n", myhost);
    if ( (in=inet_addr(myhost)) != INADDR_NONE ) {

      if ( h ) {
        memset((char *)&sa, 0, sizeof(sa));
        memcpy((char *)&sa.sin_addr,
               h->h_addr,
               h->h_length);
        sa.sin_family = AF_INET;
        sa.sin_addr.s_addr = in;
        sa.sin_port = 0; /* get any port */
	
        if( bind(conn->firstsocket, (struct sockaddr *)&sa, sizeof(sa)) >= 0) {
          /* we succeeded to bind */
          struct sockaddr_in add;
	
          size = sizeof(add);
          if(getsockname(conn->firstsocket, (struct sockaddr *) &add,
                         (socklen_t *)&size)<0) {
            failf(data, "getsockname() failed");
            return CURLE_HTTP_PORT_FAILED;
          }
        }
        else {
          switch(errno) {
          case EBADF:
            failf(data, "Invalid descriptor: %d", errno);
            break;
          case EINVAL:
            failf(data, "Invalid request: %d", errno);
            break;
          case EACCES:
            failf(data, "Address is protected, user not superuser: %d", errno);
            break;
          case ENOTSOCK:
            failf(data,
                  "Argument is a descriptor for a file, not a socket: %d",
                  errno);
            break;
          case EFAULT:
            failf(data, "Inaccessable memory error: %d", errno);
            break;
          case ENAMETOOLONG:
            failf(data, "Address too long: %d", errno);
            break;
          case ENOMEM:
            failf(data, "Insufficient kernel memory was available: %d", errno);
            break;
          default:
            failf(data,"errno %d\n");
          } /* end of switch */
	
          return CURLE_HTTP_PORT_FAILED;
        } /* end of else */
	
      } /* end of if  h */
      else {
	failf(data,"could't find my own IP address (%s)", myhost);
	return CURLE_HTTP_PORT_FAILED;
      }
    } /* end of inet_addr */
    else {
      failf(data, "could't find my own IP address (%s)", myhost);
      return CURLE_HTTP_PORT_FAILED;
    }
Daniel Stenberg's avatar
Daniel Stenberg committed

    if(hostdataptr)
      free(hostdataptr); /* allocated by Curl_gethost() */
  } /* end of device selection support */
#endif  /* end of HAVE_INET_NTOA */
#endif /* end of not WIN32 */
#endif /*ENABLE_IPV6*/
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed
  /*************************************************************
   * Connect to server/proxy
Daniel Stenberg's avatar
Daniel Stenberg committed
   *************************************************************/
#ifdef ENABLE_IPV6
Daniel Stenberg's avatar
Daniel Stenberg committed
  conn->firstsocket = -1;
  for (ai = conn->hp; ai; ai = ai->ai_next) {
    conn->firstsocket = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
    if (conn->firstsocket < 0)
Daniel Stenberg's avatar
Daniel Stenberg committed
    if (connect(conn->firstsocket, ai->ai_addr, ai->ai_addrlen) < 0) {
      close(conn->firstsocket);
      conn->firstsocket = -1;
Daniel Stenberg's avatar
Daniel Stenberg committed
  conn->ai = ai;
  if (conn->firstsocket < 0) {
    failf(data, strerror(errno));
    return CURLE_COULDNT_CONNECT;
  }
#else
  if (connect(conn->firstsocket,
              (struct sockaddr *) &(conn->serv_addr),
              sizeof(conn->serv_addr)
              ) < 0) {
    switch(errno) {
#ifdef ECONNREFUSED
      /* this should be made nicer */
    case ECONNREFUSED:
      failf(data, "Connection refused");
      break;
    case EFAULT:
      failf(data, "Invalid socket address: %d",errno);
      break;
    case EISCONN:
      failf(data, "Socket already connected: %d",errno);
      break;
    case ETIMEDOUT:
      failf(data, "Timeout while accepting connection, server busy: %d",errno);
      break;
    case ENETUNREACH:
      failf(data, "Network is unreachable: %d",errno);
      break;
    case EADDRINUSE:
      failf(data, "Local address already in use: %d",errno);
      break;
    case EINPROGRESS:
      failf(data, "Socket is nonblocking and connection can not be completed immediately: %d",errno);
      break;
    case EALREADY:
      failf(data, "Socket is nonblocking and a previous connection attempt not completed: %d",errno);
      break;
    case EAGAIN:
      failf(data, "No more free local ports: %d",errno);
      break;
    case EACCES:
    case EPERM:
      failf(data, "Attempt to connect to broadcast address without socket broadcast flag or local firewall rule violated: %d",errno);
      break;
#endif
    case EINTR:
      failf(data, "Connection timed out");
      break;
    default:
      failf(data, "Can't connect to server: %d", errno);
      break;
    }
    return CURLE_COULDNT_CONNECT;
  }
#endif

  return CURLE_OK;
}

static CURLcode Connect(struct UrlData *data,
                        struct connectdata **in_connect,
                        bool allow_port) /* allow data->use_port ? */
{
  char *tmp;
  char *buf;
  CURLcode result;
  char resumerange[40]="";
  struct connectdata *conn;
  struct connectdata *conn_temp;
  char endbracket;
  struct sigaction sigact;
  /*************************************************************
   * Check input data
   *************************************************************/

  if(!data->url)
    return CURLE_URL_MALFORMAT;

  /* First, split up the current URL in parts so that we can use the
     parts for checking against the already present connections. In order
     to not have to modify everything at once, we allocate a temporary
     connection data struct and fill in for comparison purposes. */

  conn = (struct connectdata *)malloc(sizeof(struct connectdata));
  if(!conn) {
    *in_connect = NULL; /* clear the pointer */
    return CURLE_OUT_OF_MEMORY;
  }
  /* We must set the return variable as soon as possible, so that our
     parent can cleanup any possible allocs we may have done before
     any failure */
  *in_connect = conn;

  /* we have to init the struct */
  memset(conn, 0, sizeof(struct connectdata));

  /* and we setup a few fields in case we end up actually using this struct */
  conn->data = data;           /* remember our daddy */
  conn->upload_bufsize = UPLOAD_BUFSIZE; /* default upload buffer size */
  conn->firstsocket = -1;     /* no file descriptor */
  conn->secondarysocket = -1; /* no file descriptor */
  conn->connectindex = -1;    /* no index */
  conn->bits.httpproxy = data->bits.httpproxy; /* proxy-or-not status */
Daniel Stenberg's avatar
Daniel Stenberg committed

  /* Default protocol-indepent behaveiour doesn't support persistant
     connections, so we set this to force-close. Protocols that support