Commit 8e92600d authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

David Byron made CURLOPT_FAILONERROR work with authentications such as NTLM

or Digest.
parent 5e75c310
Loading
Loading
Loading
Loading
+101 −28
Original line number Diff line number Diff line
@@ -188,23 +188,23 @@ void Curl_http_auth_act(struct connectdata *conn)
  }
}

/*
/**
 * Setup the authentication headers for the host/proxy and the correct
 * authentication method.
 * authentication method.  @p conn->data->state.authdone set to TRUE
 * when authentication is done.
 *
 * @param conn all information about the current connection
 */

static CURLcode http_auth_headers(struct connectdata *conn,
                                  char *request,
                                  char *path,
                                  bool *ready) /* set TRUE when the auth phase
                                           is done and ready to do the *actual*
                                           request */
                                  char *path)
{
  CURLcode result = CURLE_OK;
  struct SessionHandle *data = conn->data;
  char *auth=NULL;

  *ready = FALSE; /* default is no */
  curlassert(data);
  data->state.authdone = FALSE; /* default is no */

  if(!data->state.authstage) {
    if(conn->bits.httpproxy && conn->bits.proxy_user_passwd)
@@ -212,7 +212,7 @@ static CURLcode http_auth_headers(struct connectdata *conn,
    else if(conn->bits.user_passwd)
      Curl_http_auth_stage(data, 401);
    else {
      *ready = TRUE;
      data->state.authdone = TRUE;
      return CURLE_OK; /* no authentication with no user or password */
    }
  }
@@ -229,7 +229,7 @@ static CURLcode http_auth_headers(struct connectdata *conn,
#ifdef USE_SSLEAY
      if(data->state.authwant == CURLAUTH_NTLM) {
        auth=(char *)"NTLM";
        result = Curl_output_ntlm(conn, TRUE, ready);
        result = Curl_output_ntlm(conn, TRUE);
        if(result)
          return result;
      }
@@ -244,7 +244,7 @@ static CURLcode http_auth_headers(struct connectdata *conn,
          if(result)
            return result;
        }
        *ready = TRUE;
        data->state.authdone = TRUE;
        /* Switch to web authentication after proxy authentication is done */
        Curl_http_auth_stage(data, 401);
      }
@@ -262,14 +262,14 @@ static CURLcode http_auth_headers(struct connectdata *conn,
        result = Curl_output_negotiate(conn);
        if (result)
          return result;
        *ready = TRUE;
        data->state.authdone = TRUE;
      }
      else
#endif
#ifdef USE_SSLEAY
      if(data->state.authwant == CURLAUTH_NTLM) {
        auth=(char *)"NTLM";
        result = Curl_output_ntlm(conn, FALSE, ready);
        result = Curl_output_ntlm(conn, FALSE);
        if(result)
          return result;
      }
@@ -284,7 +284,7 @@ static CURLcode http_auth_headers(struct connectdata *conn,
                                      (unsigned char *)path);
          if(result)
            return result;
          *ready = TRUE;
          data->state.authdone = TRUE;
        }
        else if(data->state.authwant == CURLAUTH_BASIC) {/* Basic */
          if(conn->bits.user_passwd &&
@@ -295,7 +295,7 @@ static CURLcode http_auth_headers(struct connectdata *conn,
              return result;
          }
          /* basic is always ready */
          *ready = TRUE;
          data->state.authdone = TRUE;
        }
      }
      if(auth)
@@ -304,7 +304,7 @@ static CURLcode http_auth_headers(struct connectdata *conn,
    }
  }
  else
    *ready = TRUE;
    data->state.authdone = TRUE;

  return result;
}
@@ -438,6 +438,83 @@ CURLcode Curl_http_auth(struct connectdata *conn,
  return CURLE_OK;
}

/**
 * determine whether an http response has gotten us into an
 * error state or not.
 *
 * @param conn all information about the current connection
 *
 * @retval 0 communications should continue
 *
 * @retval 1 communications should not continue
 */
int Curl_http_should_fail(struct connectdata *conn)
{
  struct SessionHandle *data;
  struct Curl_transfer_keeper *k;

  curlassert(conn);
  data = conn->data;
  curlassert(data);

  /*
  ** For readability
  */
  k = &conn->keep;

  /*
  ** If we haven't been asked to fail on error,
  ** don't fail.
  */
  if (!data->set.http_fail_on_error)
    return 0;

  /*
  ** Any code < 400 is never terminal.
  */
  if (k->httpcode < 400)
    return 0;

  /*
  ** Any code >= 400 that's not 401 or 407 is always
  ** a terminal error
  */
  if ((k->httpcode != 401) &&
      (k->httpcode != 407))
    return 1;

  /*
  ** All we have left to deal with is 401 and 407
  */
  curlassert((k->httpcode == 401) || (k->httpcode == 407));

  /*
  ** Examine the current authentication state to see if this
  ** is an error.  The idea is for this function to get
  ** called after processing all the headers in a response
  ** message.  So, if we've been to asked to authenticate a
  ** particular stage, and we've done it, we're OK.  But, if
  ** we're already completely authenticated, it's not OK to
  ** get another 401 or 407.
  **
  ** It is possible for authentication to go stale such that
  ** the client needs to reauthenticate.  Once that info is
  ** available, use it here.
  */
  infof(data,"%s: authstage = %d\n",__FUNCTION__,data->state.authstage);
  infof(data,"%s: httpcode = %d\n",__FUNCTION__,k->httpcode);
  infof(data,"%s: authdone = %d\n",__FUNCTION__,data->state.authdone);

  if (data->state.authstage &&
      (data->state.authstage == k->httpcode))
    return data->state.authdone;

  /*
  ** Either we're not authenticating, or we're supposed to
  ** be authenticating something else.  This is an error.
  */
  return 1;
}

/* fread() emulation to provide POST and/or request data */
static size_t readmoredata(char *buffer,
@@ -760,9 +837,6 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
  infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);

  do {
    bool auth; /* we don't really have to know when the auth phase is done,
                  but this variable will be set to true then */

    if(conn->newurl) {
      /* This only happens if we've looped here due to authentication reasons,
         and we don't really use the newly cloned URL here then. Just free()
@@ -776,7 +850,7 @@ CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
      return CURLE_OUT_OF_MEMORY;

    /* Setup the proxy-authorization header, if any */
    result = http_auth_headers(conn, (char *)"CONNECT", host_port, &auth);
    result = http_auth_headers(conn, (char *)"CONNECT", host_port);
    if(CURLE_OK == result) {

      /* OK, now send the connect request to the proxy */
@@ -1066,7 +1140,6 @@ CURLcode Curl_http(struct connectdata *conn)
  const char *te = ""; /* tranfer-encoding */
  char *ptr;
  char *request;
  bool authdone=TRUE; /* if the authentication phase is done */

  if(!conn->proto.http) {
    /* Only allocate this struct if we don't already have it! */
@@ -1105,7 +1178,7 @@ CURLcode Curl_http(struct connectdata *conn)
  }

  /* setup the authentication headers */
  result = http_auth_headers(conn, request, ppath, &authdone);
  result = http_auth_headers(conn, request, ppath);
  if(result)
    return result;

@@ -1535,8 +1608,8 @@ CURLcode Curl_http(struct connectdata *conn)
        /* setup variables for the upcoming transfer */
        result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
                               &http->readbytecount,
                               authdone?FIRSTSOCKET:-1,
                               authdone?&http->writebytecount:NULL);
                               data->state.authdone?FIRSTSOCKET:-1,
                               data->state.authdone?&http->writebytecount:NULL);
      if(result) {
        Curl_formclean(http->sendit); /* free that whole lot */
        return result;
@@ -1574,8 +1647,8 @@ CURLcode Curl_http(struct connectdata *conn)
        /* prepare for transfer */
        result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
                               &http->readbytecount,
                               authdone?FIRSTSOCKET:-1,
                               authdone?&http->writebytecount:NULL);
                               data->state.authdone?FIRSTSOCKET:-1,
                               data->state.authdone?&http->writebytecount:NULL);
      if(result)
        return result;
      break;
@@ -1606,7 +1679,7 @@ CURLcode Curl_http(struct connectdata *conn)

      if(data->set.postfields) {

        if(authdone && (postsize < (100*1024))) {
        if(data->state.authdone && (postsize < (100*1024))) {
          /* If we're not done with the authentication phase, we don't expect
             to actually send off any data yet. Hence, we delay the sending of
             the body until we receive that friendly 100-continue response */
@@ -1642,7 +1715,7 @@ CURLcode Curl_http(struct connectdata *conn)
          /* set the upload size to the progress meter */
          Curl_pgrsSetUploadSize(data, http->postsize);

          if(!authdone && !checkheaders(data, "Expect:")) {
          if(!data->state.authdone && !checkheaders(data, "Expect:")) {
            /* if not disabled explicitly we add a Expect: 100-continue to the
               headers which actually speeds up post operations (as there is
               one packet coming back from the web server) */
+4 −0
Original line number Diff line number Diff line
@@ -42,9 +42,13 @@ CURLcode Curl_http_connect(struct connectdata *conn);
void Curl_httpchunk_init(struct connectdata *conn);
CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap,
                              ssize_t length, ssize_t *wrote);

/* These functions are in http.c */
void Curl_http_auth_stage(struct SessionHandle *data, int stage);
CURLcode Curl_http_auth(struct connectdata *conn,
                        int httpcode, char *header);
void Curl_http_auth_act(struct connectdata *conn);

int Curl_http_should_fail(struct connectdata *conn);
#endif
#endif
+24 −2
Original line number Diff line number Diff line
@@ -441,6 +441,16 @@ CURLcode Curl_readwrite(struct connectdata *conn,
                FD_ZERO(&k->wkeepfd);
              }

              /*
              ** Now that all of the headers have been parsed, see
              ** if we should give up and return an error.
              */
              if (Curl_http_should_fail(conn)) {
                failf (data, "The requested URL returned error: %d",
                       k->httpcode);
                return CURLE_HTTP_RETURNED_ERROR;
              }

              /* now, only output this if the header AND body are requested:
               */
              writetype = CLIENTWRITE_HEADER;
@@ -576,9 +586,21 @@ CURLcode Curl_readwrite(struct connectdata *conn,
                data->info.httpcode = k->httpcode;
                data->info.httpversion = k->httpversion;

                /* 404 -> URL not found! */
                /*
                ** This code executes as part of processing
                ** the header.  As a result, it's not
                ** totally clear how to interpret the
                ** response code yet as that depends on what
                ** other headers may be present.  401 and
                ** 407 may be errors, but may be OK
                ** depending on how authentication is
                ** working.  Other codes are definitely
                ** errors, so give up here.
                */
                if (data->set.http_fail_on_error &&
                    (k->httpcode >= 400)) {
                    (k->httpcode >= 400) &&
                    (k->httpcode != 401) &&
                    (k->httpcode != 407)) {
                  /* If we have been told to fail hard on HTTP-errors,
                     here is the check for that: */
                  /* serious error, go home! */