Skip to content
url.c 126 KiB
Newer Older
    conn->protocol |= PROT_HTTP;
    conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
    conn->curl_done = Curl_http_done;
    conn->curl_connect = Curl_http_connect;
#else
    failf(data, LIBCURL_NAME
          " was built with HTTP disabled, http: not supported!");
    return CURLE_UNSUPPORTED_PROTOCOL;
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else if (strequal(conn->protostr, "HTTPS")) {
#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP)
    conn->protocol |= PROT_HTTP|PROT_HTTPS|PROT_SSL;
    conn->curl_do_more = (Curl_do_more_func)ZERO_NULL;
    conn->curl_done = Curl_http_done;
    conn->curl_connect = Curl_http_connect;
    conn->curl_connecting = Curl_https_connecting;
    conn->curl_proto_getsock = Curl_https_getsock;
    failf(data, LIBCURL_NAME
          " was built with SSL disabled, https: not supported!");
    return CURLE_UNSUPPORTED_PROTOCOL;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
Daniel Stenberg's avatar
Daniel Stenberg committed
  else if(strequal(conn->protostr, "FTP") ||
          strequal(conn->protostr, "FTPS")) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    char *type;
Daniel Stenberg's avatar
Daniel Stenberg committed

    if(strequal(conn->protostr, "FTPS")) {
      conn->protocol |= PROT_FTPS|PROT_SSL;
      conn->ssl[SECONDARYSOCKET].use = TRUE; /* send data securely */
Daniel Stenberg's avatar
Daniel Stenberg committed
#else
      failf(data, LIBCURL_NAME
            " was built with SSL disabled, ftps: not supported!");
Daniel Stenberg's avatar
Daniel Stenberg committed
      return CURLE_UNSUPPORTED_PROTOCOL;
Yang Tse's avatar
Yang Tse committed
    conn->remote_port = (unsigned short)port;
    conn->protocol |= PROT_FTP|PROT_CLOSEACTION;
Daniel Stenberg's avatar
Daniel Stenberg committed

      /* Unless we have asked to tunnel ftp operations through the proxy, we
         switch and use HTTP operations only */
      conn->curl_do = Curl_http;
      conn->curl_done = Curl_http_done;
      conn->protocol = PROT_HTTP; /* switch to HTTP */
#else
      failf(data, "FTP over http proxy requires HTTP support built-in!");
      return CURLE_UNSUPPORTED_PROTOCOL;
#endif
      conn->curl_do_more = Curl_ftp_nextconnect;
      conn->curl_done = Curl_ftp_done;
      conn->curl_connect = Curl_ftp_connect;
      conn->curl_connecting = Curl_ftp_multi_statemach;
      conn->curl_doing = Curl_ftp_doing;
      conn->curl_proto_getsock = Curl_ftp_getsock;
      conn->curl_doing_getsock = Curl_ftp_getsock;
Daniel Stenberg's avatar
Daniel Stenberg committed
      conn->curl_disconnect = Curl_ftp_disconnect;
    data->reqdata.path++; /* don't include the initial slash */
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* FTP URLs support an extension like ";type=<typecode>" that
Daniel Stenberg's avatar
Daniel Stenberg committed
     * we'll try to get now! */
    type=strstr(data->reqdata.path, ";type=");
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(!type) {
      type=strstr(conn->host.rawalloc, ";type=");
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    if(type) {
      char command;
      *type=0;                     /* it was in the middle of the hostname */
Yang Tse's avatar
Yang Tse committed
      command = (char)toupper((int)type[6]);
Daniel Stenberg's avatar
Daniel Stenberg committed
      switch(command) {
      case 'A': /* ASCII mode */
Daniel Stenberg's avatar
Daniel Stenberg committed
      case 'D': /* directory mode */
Daniel Stenberg's avatar
Daniel Stenberg committed
      case 'I': /* binary mode */
      default:
        /* switch off ASCII */
#else /* CURL_DISABLE_FTP */
    failf(data, LIBCURL_NAME
          " was built with FTP disabled, ftp/ftps: not supported!");
    return CURLE_UNSUPPORTED_PROTOCOL;
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else if(strequal(conn->protostr, "TELNET")) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* telnet testing factory */
    conn->protocol |= PROT_TELNET;

    conn->curl_do = Curl_telnet;
    conn->curl_done = Curl_telnet_done;
#else
    failf(data, LIBCURL_NAME
          " was built with TELNET disabled!");
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else if (strequal(conn->protostr, "DICT")) {
    conn->protocol |= PROT_DICT;
    /* no DICT-specific done */
    conn->curl_done = (Curl_done_func)ZERO_NULL;
#else
    failf(data, LIBCURL_NAME
          " was built with DICT disabled!");
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else if (strequal(conn->protostr, "LDAP")) {
    conn->protocol |= PROT_LDAP;
    /* no LDAP-specific done */
    conn->curl_done = (Curl_done_func)ZERO_NULL;
#else
    failf(data, LIBCURL_NAME
          " was built with LDAP disabled!");
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else if (strequal(conn->protostr, "FILE")) {
    conn->protocol |= PROT_FILE;
Daniel Stenberg's avatar
Daniel Stenberg committed

    conn->curl_done = Curl_file_done;
    /* anyway, this is supposed to be the connect function so we better
       at least check that the file is present here! */
    result = Curl_file_connect(conn);
    /* Setup a "faked" transfer that'll do nothing */
    if(CURLE_OK == result) {
      conn->bits.tcpconnect = TRUE; /* we are "connected */
      result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
                                   -1, NULL); /* no upload */
#else
    failf(data, LIBCURL_NAME
          " was built with FILE disabled!");
#endif
  }
  else if (strequal(conn->protostr, "TFTP")) {
#ifndef CURL_DISABLE_TFTP
    char *type;
    conn->socktype = SOCK_DGRAM; /* UDP datagram based */
    conn->protocol |= PROT_TFTP;
    conn->port = PORT_TFTP;
    conn->remote_port = PORT_TFTP;
    conn->curl_connect = Curl_tftp_connect;
    conn->curl_do = Curl_tftp;
    /* TFTP URLs support an extension like ";mode=<typecode>" that
     * we'll try to get now! */
    type=strstr(data->reqdata.path, ";mode=");
    if(!type) {
      type=strstr(conn->host.rawalloc, ";mode=");
    }
    if(type) {
      char command;
      *type=0;                     /* it was in the middle of the hostname */
Yang Tse's avatar
Yang Tse committed
      command = (char)toupper((int)type[6]);
      switch(command) {
      case 'A': /* ASCII mode */
      case 'N': /* NETASCII mode */
        break;
      case 'O': /* octet mode */
      case 'I': /* binary mode */
      default:
        /* switch off ASCII */
        break;
      }
    }
#else
    failf(data, LIBCURL_NAME
          " was built with TFTP disabled!");
Daniel Stenberg's avatar
Daniel Stenberg committed
  else {
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* We fell through all checks and thus we don't support the specified
       protocol */
    failf(data, "Unsupported protocol: %s", conn->protostr);
    return CURLE_UNSUPPORTED_PROTOCOL;
  if(data->change.proxy && *data->change.proxy) {
    /* If this is supposed to use a proxy, we need to figure out the proxy
       host name name, so that we can re-use an existing connection
       that may exist registered to the same proxy host. */

    char *prox_portno;
    char *endofprot;

    /* We need to make a duplicate of the proxy so that we can modify the
       string safely. */
    char *proxydup=strdup(data->change.proxy);

    /* We use 'proxyptr' to point to the proxy name from now on... */
    char *proxyptr=proxydup;

    if(NULL == proxydup) {
      failf(data, "memory shortage");
      return CURLE_OUT_OF_MEMORY;
    }

    /* We do the proxy host string parsing here. We want the host name and the
     * port name. Accept a protocol:// prefix, even though it should just be
     * ignored.
     */
    /* Skip the protocol part if present */
    endofprot=strstr(proxyptr, "://");
      proxyptr = endofprot+3;
    /* Is there a username and password given in this proxy url? */
    atsign = strchr(proxyptr, '@');
    if(atsign) {
      char proxyuser[MAX_CURL_USER_LENGTH];
      char proxypasswd[MAX_CURL_PASSWORD_LENGTH];
                     "%" MAX_CURL_USER_LENGTH_TXT"[^:]:"
                     "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
                     proxyuser, proxypasswd)) {
        CURLcode res = CURLE_OK;

        /* found user and password, rip them out.  note that we are
           unescaping them, as there is otherwise no way to have a
           username or password with reserved characters like ':' in
           them. */
        Curl_safefree(conn->proxyuser);
        conn->proxyuser = curl_easy_unescape(data, proxyuser, 0, NULL);

        if(!conn->proxyuser)
          res = CURLE_OUT_OF_MEMORY;
        else {
          Curl_safefree(conn->proxypasswd);
          conn->proxypasswd = curl_easy_unescape(data, proxypasswd, 0, NULL);

          if(!conn->proxypasswd)
            res = CURLE_OUT_OF_MEMORY;
        }

        if(CURLE_OK == res) {
          conn->bits.proxy_user_passwd = TRUE; /* enable it */
          atsign = strdup(atsign+1); /* the right side of the @-letter */

          if(atsign) {
            free(proxydup); /* free the former proxy string */
            proxydup = proxyptr = atsign; /* now use this instead */
          }
          else
            res = CURLE_OUT_OF_MEMORY;
        }

        if(res) {
          free(proxydup); /* free the allocated proxy string */
          return res;
        }
    /* start scanning for port number at this point */
    portptr = proxyptr;

    /* detect and extract RFC2732-style IPv6-addresses */
    if(*proxyptr == '[') {
      char *ptr = ++proxyptr; /* advance beyond the initial bracket */
      while(*ptr && (ISXDIGIT(*ptr) || (*ptr == ':')))
        ptr++;
      if(*ptr == ']') {
        /* yeps, it ended nicely with a bracket as well */
        *ptr = 0;
        portptr = ptr+1;
      }
      /* Note that if this didn't end with a bracket, we still advanced the
       * proxyptr first, but I can't see anything wrong with that as no host
       * name nor a numeric can legally start with a bracket.
       */
    /* Get port number off proxy.server.com:1080 */
    prox_portno = strchr(portptr, ':');
    if (prox_portno) {
      *prox_portno = 0x0; /* cut off number from host name */
      prox_portno ++;
      /* now set the local port number */
      conn->port = atoi(prox_portno);
    }
      /* None given in the proxy string, then get the default one if it is
         given */
    }

    /* now, clone the cleaned proxy host name */
    conn->proxy.rawalloc = strdup(proxyptr);
    conn->proxy.name = conn->proxy.rawalloc;

    free(proxydup); /* free the duplicate pointer and not the modified */
    if(!conn->proxy.rawalloc)
      return CURLE_OUT_OF_MEMORY;
  /*************************************************************
   * If the protcol is using SSL and HTTP proxy is used, we set
   * the tunnel_proxy bit.
   *************************************************************/
  if((conn->protocol&PROT_SSL) && conn->bits.httpproxy)
    conn->bits.tunnel_proxy = TRUE;

  /*************************************************************
   * Take care of user and password authentication stuff
   *************************************************************/

  /*
   * Inputs: data->set.userpwd   (CURLOPT_USERPWD)
   *         data->set.fpasswd   (CURLOPT_PASSWDFUNCTION)
   *         data->set.use_netrc (CURLOPT_NETRC)
   *         netrc file
   *         hard-coded defaults
   *
   * Outputs: (almost :- all currently undefined)
   *          conn->bits.user_passwd  - non-zero if non-default passwords exist
   *          conn->user              - non-zero length if defined
   *          conn->passwd            -   ditto
   *          conn->host.name          - remove user name and password
   */

  /* At this point, we're hoping all the other special cases have
   * been taken care of, so conn->host.name is at most
   *    [user[:password]]@]hostname
   *
   * We need somewhere to put the embedded details, so do that first.
   */

  user[0] =0;   /* to make everything well-defined */
  passwd[0]=0;
  if (conn->protocol & (PROT_FTP|PROT_HTTP)) {
    /* This is a FTP or HTTP URL, we will now try to extract the possible
     * user+password pair in a string like:
     * ftp://user:password@ftp.my.site:8021/README */
    char *ptr=strchr(conn->host.name, '@');
    char *userpass = conn->host.name;
    if(ptr != NULL) {
      /* there's a user+password given here, to the left of the @ */


      /* So the hostname is sane.  Only bother interpreting the
       * results if we could care.  It could still be wasted
       * work because it might be overtaken by the programmatically
       * set user/passwd, but doing that first adds more cases here :-(
       */

      if (data->set.use_netrc != CURL_NETRC_REQUIRED) {
        /* We could use the one in the URL */

        conn->bits.user_passwd = 1; /* enable user+password */

        if(*userpass != ':') {
          /* the name is given, get user+password */
          sscanf(userpass, "%" MAX_CURL_USER_LENGTH_TXT "[^:@]:"
                 "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]",
        }
        else
          /* no name given, get the password only */
          sscanf(userpass, ":%" MAX_CURL_PASSWORD_LENGTH_TXT "[^@]", passwd);
          char *newname=curl_easy_unescape(data, user, 0, NULL);
          if(!newname)
            return CURLE_OUT_OF_MEMORY;
          if(strlen(newname) < sizeof(user))
          /* if the new name is longer than accepted, then just use
             the unconverted name, it'll be wrong but what the heck */
          free(newname);
        }
          /* we have a password found in the URL, decode it! */
          char *newpasswd=curl_easy_unescape(data, passwd, 0, NULL);
          if(!newpasswd)
            return CURLE_OUT_OF_MEMORY;
          if(strlen(newpasswd) < sizeof(passwd))
  /*************************************************************
   * Figure out the remote port number
   *
   * No matter if we use a proxy or not, we have to figure out the remote
   * port number of various reasons.
   *
   * To be able to detect port number flawlessly, we must not confuse them
   * IPv6-specified addresses in the [0::1] style. (RFC2732)
   *
   * The conn->host.name is currently [user:passwd@]host[:port] where host
   * could be a hostname, IPv4 address or IPv6 address.
   *************************************************************/
  if((1 == sscanf(conn->host.name, "[%*39[0-9a-fA-F:.]%c", &endbracket)) &&
     (']' == endbracket)) {
    /* this is a RFC2732-style specified IP-address */
    conn->bits.ipv6_ip = TRUE;

    conn->host.name++; /* pass the starting bracket */
    tmp = strchr(conn->host.name, ']');
    *tmp = 0; /* zero terminate */
    tmp++; /* pass the ending bracket */
    if(':' != *tmp)
      tmp = NULL; /* no port number available */
  }
  else
    tmp = strrchr(conn->host.name, ':');

  if(data->set.use_port && data->state.allow_port) {
    /* if set, we use this and ignore the port possibly given in the URL */
    conn->remote_port = (unsigned short)data->set.use_port;
    if(tmp)
      *tmp = '\0'; /* cut off the name there anyway - if there was a port
                      number - since the port number is to be ignored! */
    if(conn->bits.httpproxy) {
      /* we need to create new URL with the new port number */
      char *url;

      url = aprintf("http://%s:%d%s", conn->host.name, conn->remote_port,
      if(!url)
        return CURLE_OUT_OF_MEMORY;

      if(data->change.url_alloc)
        free(data->change.url);

      data->change.url = url;
      data->change.url_alloc = TRUE;
    }
  }
  else if (tmp) {
    /* no CURLOPT_PORT given, extract the one from the URL */

    char *rest;
    unsigned long port;

    port=strtoul(tmp+1, &rest, 10);  /* Port number must be decimal */

    if (rest != (tmp+1) && *rest == '\0') {
      /* The colon really did have only digits after it,
       * so it is either a port number or a mistake */

      if (port > 0xffff) {   /* Single unix standard says port numbers are
                              * 16 bits long */
        failf(data, "Port number too large: %lu", port);
        return CURLE_URL_MALFORMAT;
      }

      *tmp = '\0'; /* cut off the name there */
      conn->remote_port = (unsigned short)port;
    }
  }

  /* Programmatically set password:
   *   - always applies, if available
   *   - takes precedence over the values we just set above
   * so scribble it over the top.
   * User-supplied passwords are assumed not to need unescaping.
   *
   * user_password is set in "inherit initial knowledge' above,
   * so it doesn't have to be set in this block
   */
  if (data->set.userpwd != NULL) {
    /* the name is given, get user+password */
    sscanf(data->set.userpwd,
           "%" MAX_CURL_USER_LENGTH_TXT "[^:]:"
           "%" MAX_CURL_PASSWORD_LENGTH_TXT "[^\n]",
  if (data->set.use_netrc != CURL_NETRC_IGNORED) {
                       user, passwd,
                       data->set.netrc_file)) {
      infof(data, "Couldn't find host %s in the " DOT_CHAR
            "netrc file, using defaults\n",
    else {
      /* set bits.netrc TRUE to remember that we got the name from a .netrc
         file, so that it is safe to use even if we followed a Location: to a
         different host or similar. */
      conn->bits.netrc = TRUE;

      conn->bits.user_passwd = 1; /* enable user+password */
  }

  /* If our protocol needs a password and we have none, use the defaults */
       !conn->bits.user_passwd) {
    conn->user = strdup(CURL_DEFAULT_USER);
    conn->passwd = strdup(CURL_DEFAULT_PASSWORD);
    /* This is the default password, so DON'T set conn->bits.user_passwd */
  }
    /* store user + password, zero-length if not set */
    conn->user = strdup(user);
    conn->passwd = strdup(passwd);
  if(!conn->user || !conn->passwd)
    return CURLE_OUT_OF_MEMORY;
  /*************************************************************
   * Check the current list of connections to see if we can
   * re-use an already existing one or if we have to create a
   * new one.
   *************************************************************/

  /* get a cloned copy of the SSL config situation stored in the
     connection struct */
  if(!Curl_clone_ssl_config(&data->set.ssl, &conn->ssl_config))
    return CURLE_OUT_OF_MEMORY;

  /* reuse_fresh is TRUE if we are told to use a new connection by force, but
     we only acknowledge this option if this is not a re-used connection
     already (which happens due to follow-location or during a HTTP
     authentication phase). */
  if(data->set.reuse_fresh && !data->state.this_is_a_follow)
    reuse = ConnectionExists(data, conn, &conn_temp);

  if(reuse) {
    /*
     * We already have a connection for this, we got the former connection
     * in the conn_temp variable and thus we need to cleanup the one we
     * just allocated before we can move along and use the previously
     * existing one.
     */
    if(old_conn->proxy.rawalloc)
      free(old_conn->proxy.rawalloc);
    /* free the SSL config struct from this connection struct as this was
       allocated in vain and is targeted for destruction */
    Curl_free_ssl_config(&conn->ssl_config);

    conn = conn_temp;        /* use this connection from now on */
    /* get the user+password information from the old_conn struct since it may
     * be new for this request even when we re-use an existing connection */
    conn->bits.user_passwd = old_conn->bits.user_passwd;
    if (conn->bits.user_passwd) {
      /* use the new user namd and password though */
      Curl_safefree(conn->user);
      Curl_safefree(conn->passwd);
      conn->user = old_conn->user;
      conn->passwd = old_conn->passwd;
      old_conn->user = NULL;
      old_conn->passwd = NULL;
    }

    conn->bits.proxy_user_passwd = old_conn->bits.proxy_user_passwd;
    if (conn->bits.proxy_user_passwd) {
      /* use the new proxy user name and proxy password though */
      Curl_safefree(conn->proxyuser);
      Curl_safefree(conn->proxypasswd);
      conn->proxyuser = old_conn->proxyuser;
      conn->proxypasswd = old_conn->proxypasswd;
      old_conn->proxyuser = NULL;
      old_conn->proxypasswd = NULL;
    }
    /* host can change, when doing keepalive with a proxy ! */
    if (conn->bits.httpproxy) {
      free(conn->host.rawalloc);
      conn->host=old_conn->host;
    }

    /* get the newly set value, not the old one */
    conn->bits.no_body = old_conn->bits.no_body;

    if (!conn->bits.httpproxy)
      free(old_conn->host.rawalloc); /* free the newly allocated name buffer */
Daniel Stenberg's avatar
Daniel Stenberg committed
    conn->bits.reuse = TRUE; /* yes, we're re-using here */
    conn->bits.chunk = FALSE; /* always assume not chunked unless told
                                 otherwise */
    Curl_safefree(old_conn->user);
    Curl_safefree(old_conn->passwd);
Daniel Stenberg's avatar
Daniel Stenberg committed
    Curl_safefree(old_conn->proxyuser);
    Curl_safefree(old_conn->proxypasswd);
    Curl_llist_destroy(old_conn->send_pipe, NULL);
    Curl_llist_destroy(old_conn->recv_pipe, NULL);
    free(old_conn);          /* we don't need this anymore */

    /*
     * If we're doing a resumed transfer, we need to setup our stuff
     * properly.
     */
    data->reqdata.resume_from = data->set.set_resume_from;
    if (data->reqdata.resume_from) {
      if (data->reqdata.rangestringalloc == TRUE)
        free(data->reqdata.range);
      data->reqdata.range = aprintf("%" FORMAT_OFF_T "-",
                                    data->reqdata.resume_from);
      if(!data->reqdata.range)

      /* tell ourselves to fetch this range */
      data->reqdata.use_range = TRUE;        /* enable range download */
      data->reqdata.rangestringalloc = TRUE; /* mark range string allocated */
      /* There is a range, but is not a resume, useful for random ftp access */
      data->reqdata.range = strdup(data->set.set_range);
      if(!data->reqdata.range)
      data->reqdata.rangestringalloc = TRUE; /* mark range string allocated */
      data->reqdata.use_range = TRUE;        /* enable range download */
      data->reqdata.use_range = FALSE; /* disable range download */
    *in_connect = conn;      /* return this instead! */

    infof(data, "Re-using existing connection! (#%ld) with host %s\n",
          conn->bits.httpproxy?conn->proxy.dispname:conn->host.dispname);
  }
  else {
    /*
     * This is a brand new connection, so let's store it in the connection
     * cache of ours!
     */
    ConnectionStore(data, conn);
  }

  /* Continue connectdata initialization here. */

  /*
   * Inherit the proper values from the urldata struct AFTER we have arranged
   * the persistent connection stuff */
  conn->fread = data->set.fread;
  conn->fread_in = data->set.in;

Yang Tse's avatar
Yang Tse committed
  if ((conn->protocol&PROT_HTTP) &&
      data->set.upload &&
      (data->set.infilesize == -1) &&
      (data->set.httpversion != CURL_HTTP_VERSION_1_0)) {
    /* HTTP, upload, unknown file size and not HTTP 1.0 */
Yang Tse's avatar
Yang Tse committed
    conn->bits.upload_chunky = TRUE;
  }
  else {
    /* else, no chunky upload */
    conn->bits.upload_chunky = FALSE;
  }
  /*************************************************************
   * Set timeout if that is being used, and we're not using an asynchronous
   * name resolve.
   *************************************************************/
  if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
    /*************************************************************
     * Set signal handler to catch SIGALRM
     * Store the old value to be able to set it back later!
     *************************************************************/

#ifdef HAVE_ALARM
    long shortest;
#endif
#ifdef HAVE_SIGACTION
    struct sigaction sigact;
    sigaction(SIGALRM, NULL, &sigact);
    keep_sigact = sigact;
    keep_copysig = TRUE; /* yes, we have a copy */
    sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
    /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
    sigact.sa_flags &= ~SA_RESTART;
#endif
    /* now set the new struct */
    sigaction(SIGALRM, &sigact, NULL);
#else /* HAVE_SIGACTION */
    /* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
    keep_sigact = signal(SIGALRM, alarmfunc);
#endif /* HAVE_SIGACTION */
    /* We set the timeout on the name resolving phase first, separately from
     * the download/upload part to allow a maximum time on everything. This is
     * a signal-based timeout, why it won't work and shouldn't be used in
     * multi-threaded environments. */

    shortest = data->set.timeout; /* default to this timeout value */
    if(shortest && data->set.connecttimeout &&
       (data->set.connecttimeout < shortest))
      /* if both are set, pick the shortest */
      shortest = data->set.connecttimeout;
    else if(!shortest)
      /* if timeout is not set, use the connect timeout */
      shortest = data->set.connecttimeout;

    /* alarm() makes a signal get sent when the timeout fires off, and that
    prev_alarm = alarm((unsigned int) shortest);
    /* We can expect the conn->created time to be "now", as that was just
       recently set in the beginning of this function and nothing slow
       has been done since then until now. */
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed
  /*************************************************************
   * Resolve the name of the server or proxy
   *************************************************************/
  if(conn->bits.reuse) {
    /* re-used connection, no resolving is necessary */
    hostaddr = NULL;
    /* we'll need to clear conn->dns_entry later in Curl_disconnect() */
      fix_hostname(data, conn, &conn->host);
    /* set a pointer to the hostname we display */
    fix_hostname(data, conn, &conn->host);

    if(!data->change.proxy || !*data->change.proxy) {
      /* If not connecting via a proxy, extract the port from the URL, if it is
       * there, thus overriding any defaults that might have been set above. */
      conn->port =  conn->remote_port; /* it is the same port */

      rc = Curl_resolv(conn, conn->host.name, (int)conn->port, &hostaddr);
      if(rc == CURLRESOLV_PENDING)
        *async = TRUE;
      else if(!hostaddr) {
        failf(data, "Couldn't resolve host '%s'", conn->host.dispname);
        result =  CURLE_COULDNT_RESOLVE_HOST;
        /* don't return yet, we need to clean up the timeout first */
      }
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    else {
      /* This is a proxy that hasn't been resolved yet. */
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed
      /* IDN-fix the proxy name */
      fix_hostname(data, conn, &conn->proxy);
      rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &hostaddr);
      if(rc == CURLRESOLV_PENDING)
        *async = TRUE;

      else if(!hostaddr) {
        failf(data, "Couldn't resolve proxy '%s'", conn->proxy.dispname);
        result = CURLE_COULDNT_RESOLVE_PROXY;
        /* don't return yet, we need to clean up the timeout first */
      }
#if defined(HAVE_ALARM) && defined(SIGALRM) && !defined(USE_ARES)
  if((data->set.timeout || data->set.connecttimeout) && !data->set.no_signal) {
#ifdef HAVE_SIGACTION
    if(keep_copysig) {
      /* we got a struct as it looked before, now put that one back nice
         and clean */
      sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
    }
#else
#ifdef HAVE_SIGNAL
    /* restore the previous SIGALRM handler */
    signal(SIGALRM, keep_sigact);
#endif
    /* switch back the alarm() to either zero or to what it was before minus
       the time we spent until now! */
    if(prev_alarm) {
      /* there was an alarm() set before us, now put it back */
      unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
      unsigned long alarm_set;

      /* the alarm period is counted in even number of seconds */
      alarm_set = prev_alarm - elapsed_ms/1000;
      if(!alarm_set ||
         ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
        /* if the alarm time-left reached zero or turned "negative" (counted
           with unsigned values), we should fire off a SIGALRM here, but we
           won't, and zero would be to switch it off so we never set it to
           less than 1! */
        alarm(1);
        result = CURLE_OPERATION_TIMEOUTED;
        failf(data, "Previous alarm fired off!");
      }
      else
        alarm((unsigned int)alarm_set);
/* SetupConnection() is called after the name resolve initiated in
 *
 * NOTE: the argument 'hostaddr' is NULL when this function is called for a
 * re-used connection.
 *
 * conn->data MUST already have been setup fine (in CreateConnection)
static CURLcode SetupConnection(struct connectdata *conn,
                                struct Curl_dns_entry *hostaddr,
                                bool *protocol_done)
  struct SessionHandle *data = conn->data;
    /* There's nothing in this function to setup if we're only doing
       a file:// transfer */
  }
  *protocol_done = FALSE; /* default to not done */
Daniel Stenberg's avatar
Daniel Stenberg committed

Daniel Stenberg's avatar
Daniel Stenberg committed
  /*************************************************************
   * Send user-agent to HTTP proxies even if the target protocol
   * isn't HTTP.
   *************************************************************/
  if((conn->protocol&PROT_HTTP) ||
     (data->change.proxy && *data->change.proxy)) {
        aprintf("User-Agent: %s\015\012", data->set.useragent);
      if(!conn->allocptr.uagent)
        return CURLE_OUT_OF_MEMORY;
#ifdef CURL_DO_LINEEND_CONV
  data->state.crlf_conversions = 0; /* reset CRLF conversion counter */
#endif /* CURL_DO_LINEEND_CONV */
  if(CURL_SOCKET_BAD == conn->sock[FIRSTSOCKET]) {
Daniel Stenberg's avatar
Daniel Stenberg committed
    /* Connect only if not already connected! */
    result = ConnectPlease(data, conn, hostaddr, &connected);
      result = Curl_protocol_connect(conn, protocol_done);
      if(CURLE_OK == result)
        conn->bits.tcpconnect = TRUE;
    }
    else
      conn->bits.tcpconnect = FALSE;

Daniel Stenberg's avatar
Daniel Stenberg committed
    if(CURLE_OK != result)
      return result;
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else {
    Curl_pgrsTime(data, TIMER_CONNECT); /* we're connected already */
Daniel Stenberg's avatar
Daniel Stenberg committed
  }

  conn->now = Curl_tvnow(); /* time this *after* the connect is done, we
                               set this here perhaps a second time */
Daniel Stenberg's avatar
Daniel Stenberg committed

#ifdef __EMX__
  /* 20000330 mgs
   * the check is quite a hack...
   * we're calling _fsetmode to fix the problem with fwrite converting newline
   * characters (you get mangled text files, and corrupted binary files when
   * you download to stdout and redirect it to a file). */

  if ((data->set.out)->_handle == NULL) {
    _fsetmode(stdout, "b");
  }
#endif

CURLcode Curl_connect(struct SessionHandle *data,
{
  CURLcode code;
  *asyncp = FALSE; /* assume synchronous resolves by default */
  /* call the stuff that needs to be called */
  code = CreateConnection(data, in_connect, &dns, asyncp);

  if(CURLE_OK == code) {
    /* no error */
    if(dns || !*asyncp)
      /* If an address is available it means that we already have the name
         resolved, OR it isn't async. if this is a re-used connection 'dns'
         will be NULL here. Continue connecting from here */
      code = SetupConnection(*in_connect, dns, protocol_done);
Daniel Stenberg's avatar
Daniel Stenberg committed
       response will be received and treated async wise */
  if(CURLE_OK != code) {
    /* We're not allowed to return failure with memory left allocated
       in the connectdata struct, free those here */
    if(*in_connect) {
      Curl_disconnect(*in_connect); /* close the connection */
      *in_connect = NULL;           /* return a NULL */
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else {
    if ((*in_connect)->is_in_pipeline)