Skip to content
Snippets Groups Projects
url.c 111 KiB
Newer Older
  • Learn to ignore specific revisions
  •     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_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.
         */
    #ifdef HAVE_OPENSSL_ENGINE_H
        {
          const char *cpTemp = va_arg(param, char *);
          ENGINE     *e;
          if (cpTemp && cpTemp[0]) {
            e = ENGINE_by_id(cpTemp);
            if (e) {
              if (data->engine) {
                ENGINE_free(data->engine);
              }
              data->engine = e;
            }
            else {
              failf(data, "SSL Engine '%s' not found", cpTemp);
              return CURLE_SSL_ENGINE_NOTFOUND;
            }
          }
        }
    
    #else
        return CURLE_SSL_ENGINE_NOTFOUND;
    #endif
      case CURLOPT_SSLENGINE_DEFAULT:
        /*
         * flag to set engine as default.
         */
    #ifdef HAVE_OPENSSL_ENGINE_H
        if (data->engine) {
          if (ENGINE_set_default(data->engine, ENGINE_METHOD_ALL) > 0) {
    #ifdef DEBUG
            fprintf(stderr,"set default crypto engine\n");
    #endif
          }
          else {
    #ifdef DEBUG
            failf(data, "set default crypto engine failed");
    #endif
            return CURLE_SSL_ENGINE_SETFAILED;
          }
        }
    #endif
    
      case CURLOPT_CRLF:
    
         * Kludgy option to enable CRLF convertions. Subject for removal.
    
        data->set.crlf = va_arg(param, long)?TRUE:FALSE;
    
      case CURLOPT_INTERFACE:
    
        /*
         * Set what interface to bind to when performing an operation and thus
         * what from-IP your connection will use.
         */
    
        data->set.device = va_arg(param, char *);
    
    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->share->hostcache == data->hostcache)
              data->hostcache = NULL;
    
    
            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);
    
            if(data->share->hostcache) {
              /* use shared host cache, first free own one if any */
              if(data->hostcache)
                Curl_hash_destroy(data->hostcache);
    
              data->hostcache = data->share->hostcache;
            }
    
            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);
    
          /* 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 = va_arg(param, char *);
        break;
    
    
      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_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 ***********/
      case CURLOPT_SOURCE_HOST:
        /*
         * Use SOURCE HOST
         */
        data->set.source_host = va_arg(param, char *);
        data->set.printhost = (data->set.source_host != NULL);
        break;
    
      case CURLOPT_SOURCE_PORT:
        /*
         * Use SOURCE PORT
         */
        data->set.source_port = va_arg(param, char *);
        break;
    
      case CURLOPT_SOURCE_USERPWD:
        /*
         * Use SOURCE USER[:PASSWORD]
         */
        data->set.source_userpwd = va_arg(param, char *);
        break;
    
      case CURLOPT_SOURCE_PATH:
        /*
         * Use SOURCE PATH
         */
        data->set.source_path = va_arg(param, char *);
        break;
    
      case CURLOPT_PASV_HOST:
        /*
         * Indicates whether source or target host is passive
         */
        data->set.pasvHost = va_arg(param, long)?CURL_SOURCE_PASV:CURL_TARGET_PASV;
        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;
    
    
      default:
        /* unknown tag and its companion, just ignore: */
    
        return CURLE_FAILED_INIT; /* correct this */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
    
      return CURLE_OK;
    
    CURLcode Curl_disconnect(struct connectdata *conn)
    
      struct SessionHandle *data;
    
      if(!conn)
        return CURLE_OK; /* this is closed and fine already */
    
    
      /*
       * 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.authhost.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);
    
    #if defined(USE_ARES) || defined(USE_THREADING_GETHOSTBYNAME) || \
        defined(USE_THREADING_GETADDRINFO)
    
      /* possible left-overs from the async name resolve */
      Curl_safefree(conn->async.hostname);
    
      Curl_safefree(conn->async.os_specific);
    
      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;
      fd_set check_set;
      struct timeval to;
    
      FD_ZERO(&check_set);
    
      FD_SET(sock, &check_set);
    
      to.tv_sec = 0;
      to.tv_usec = 0;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      sval = select(sock + 1, &check_set, 0, 0, &to);
    
    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 SOCKS5 proxy and sends the specifies the final
     * desitination 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_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;
        }
    
    
        if ((socksreq[0] != 5) || /* version */
    
            (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) {
          if (proxy_name[0] == 0) {
            failf(conn->data,
                  "No authentication method was acceptable. (It is quite likely"
                  " that the SOCKS5 server wanted a username/password, since none"
                  " was supplied to the server on this connection.)");
          }
    
          else {
    
            failf(conn->data, "No authentication method was acceptable.");
          }
          return 1;
        }
        else {
          failf(conn->data,
                "Undocumented SOCKS5 mode attempted to be used by server.");
          return 1;
        }
      }
    
      /* Authentication is complete, now specify destination to the proxy */
      socksreq[0] = 5; /* version (SOCKS5) */
      socksreq[1] = 1; /* connect */
      socksreq[2] = 0; /* must be zero */
      socksreq[3] = 1; /* IPv4 = 1 */
    
        struct Curl_dns_entry *dns;
        Curl_addrinfo *hp=NULL;
    
        int rc = Curl_resolv(conn, conn->host.name, (int)conn->remote_port, &dns);
    
          /* 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 (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])) {
    
            socksreq[4] = (unsigned char)ip[0];
            socksreq[5] = (unsigned char)ip[1];
            socksreq[6] = (unsigned char)ip[2];
            socksreq[7] = (unsigned char)ip[3];
    
          Curl_resolv_unlock(conn->data, dns); /* not used anymore from now on */
    
          failf(conn->data, "Failed to resolve \"%s\" for SOCKS5 connect.",
    
          return 1;
        }
      }
    
      *((unsigned short*)&socksreq[8]) = htons(conn->remote_port);
    
      {
        const int packetsize = 10;
    
    
        code = Curl_write(conn, sock, (char *)socksreq, packetsize, &written);
        if ((code != CURLE_OK) || (written != packetsize)) {
    
          failf(conn->data, "Failed to send SOCKS5 connect request.");
          return 1;
        }
    
        result = Curl_read(conn, sock, (char *)socksreq, packetsize, &actualread);
        if ((result != CURLE_OK) || (actualread != packetsize)) {
          failf(conn->data, "Failed to receive SOCKS5 connect request ack.");
          return 1;
        }
    
        if (socksreq[0] != 5) { /* version */
          failf(conn->data,
                "SOCKS5 reply has wrong version, version should be 5.");
          return 1;
        }
        if (socksreq[1] != 0) { /* Anything besides 0 is an error */
            failf(conn->data,
                  "Can't complete SOCKS5 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! */
    }
    
    
    static CURLcode ConnectPlease(struct connectdata *conn,
    
      CURLcode result;
    
      struct SessionHandle *data = conn->data;
      char *hostname = data->change.proxy?conn->proxy.name:conn->host.name;
    
      infof(data, "About to connect() to %s port %d\n",
            hostname, conn->port);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      /*************************************************************
    
       * Connect to server/proxy
    
       *************************************************************/
    
      result= Curl_connecthost(conn,
    
      if(CURLE_OK == result) {
    
        /* All is cool, then we store the current information */
        conn->dns_entry = hostaddr;
        conn->ip_addr = addr;
    
    
        if (conn->data->set.proxytype == CURLPROXY_SOCKS5) {
    
          return handleSock5Proxy(conn->proxyuser,
                                  conn->proxypasswd,
    
            CURLE_COULDNT_CONNECT : CURLE_OK;
        }
        else if (conn->data->set.proxytype == CURLPROXY_HTTP) {
          /* do nothing here. handled later. */
        }
        else {
          failf(conn->data, "unknown proxytype option given");
    
          return CURLE_COULDNT_CONNECT;
    
      return result;
    
     * verboseconnect() displays verbose information after a connect
    
    static void verboseconnect(struct connectdata *conn)
    
    
      /* Get a printable version of the network address. */
    
      Curl_printable_address(conn->ip_addr, addrbuf, sizeof(addrbuf));
    
      infof(data, "Connected to %s (%s) port %d\n",
    
            conn->bits.httpproxy ? conn->proxy.dispname : conn->host.dispname,
            addrbuf[0] ? addrbuf : "??", conn->port);
    
    /*
     * We have discovered that the TCP connection has been successful, we can now
     * proceed with some action.