Unverified Commit 8440616f authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

http: fix for tiny "HTTP/0.9" response

Deal with tiny "HTTP/0.9" (header-less) responses by checking the
status-line early, even before a full "HTTP/" is received to allow
detecting 0.9 properly.

Test 1266 and 1267 added to verify.

Fixes #2420
Closes #2872
parent 233908a5
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -801,6 +801,11 @@ endings either CRLF or LF so 't' is appropriate.
#define CURL_SA_FAMILY_T unsigned short
#endif

/* Some convenience macros to get the larger/smaller value out of two given.
   We prefix with CURL to prevent name collisions. */
#define CURLMAX(x,y) ((x)>(y)?(x):(y))
#define CURLMIN(x,y) ((x)<(y)?(x):(y))

/* Some versions of the Android SDK is missing the declaration */
#if defined(HAVE_GETPWUID_R) && defined(HAVE_DECL_GETPWUID_R_MISSING)
struct passwd;
+45 −23
Original line number Diff line number Diff line
@@ -2899,17 +2899,32 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
  return result;
}

typedef enum {
  STATUS_UNKNOWN, /* not enough data to tell yet */
  STATUS_DONE, /* a status line was read */
  STATUS_BAD /* not a status line */
} statusline;


/* Check a string for a prefix. Check no more than 'len' bytes */
static bool checkprefixmax(const char *prefix, const char *buffer, size_t len)
{
  size_t ch = CURLMIN(strlen(prefix), len);
  return curl_strnequal(prefix, buffer, ch);
}

/*
 * checkhttpprefix()
 *
 * Returns TRUE if member of the list matches prefix of string
 */
static bool
static statusline
checkhttpprefix(struct Curl_easy *data,
                const char *s)
                const char *s, size_t len)
{
  struct curl_slist *head = data->set.http200aliases;
  bool rc = FALSE;
  statusline rc = STATUS_BAD;
  statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN;
#ifdef CURL_DOES_CONVERSIONS
  /* convert from the network encoding using a scratch area */
  char *scratch = strdup(s);
@@ -2926,15 +2941,15 @@ checkhttpprefix(struct Curl_easy *data,
#endif /* CURL_DOES_CONVERSIONS */

  while(head) {
    if(checkprefix(head->data, s)) {
      rc = TRUE;
    if(checkprefixmax(head->data, s, len)) {
      rc = onmatch;
      break;
    }
    head = head->next;
  }

  if(!rc && (checkprefix("HTTP/", s)))
    rc = TRUE;
  if((rc != STATUS_DONE) && (checkprefixmax("HTTP/", s, len)))
    rc = onmatch;

#ifdef CURL_DOES_CONVERSIONS
  free(scratch);
@@ -2943,11 +2958,12 @@ checkhttpprefix(struct Curl_easy *data,
}

#ifndef CURL_DISABLE_RTSP
static bool
static statusline
checkrtspprefix(struct Curl_easy *data,
                const char *s)
                const char *s, size_t len)
{
  bool result = FALSE;
  statusline result = STATUS_BAD;
  statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN;

#ifdef CURL_DOES_CONVERSIONS
  /* convert from the network encoding using a scratch area */
@@ -2960,30 +2976,31 @@ checkrtspprefix(struct Curl_easy *data,
    /* Curl_convert_from_network calls failf if unsuccessful */
    result = FALSE; /* can't return CURLE_foobar so return FALSE */
  }
  else
    result = checkprefix("RTSP/", scratch)? TRUE: FALSE;
  else if(checkprefixmax("RTSP/", scratch, len))
    result = onmatch;
  free(scratch);
#else
  (void)data; /* unused */
  result = checkprefix("RTSP/", s)? TRUE: FALSE;
  if(checkprefixmax("RTSP/", s, len))
    result = onmatch;
#endif /* CURL_DOES_CONVERSIONS */

  return result;
}
#endif /* CURL_DISABLE_RTSP */

static bool
static statusline
checkprotoprefix(struct Curl_easy *data, struct connectdata *conn,
                 const char *s)
                 const char *s, size_t len)
{
#ifndef CURL_DISABLE_RTSP
  if(conn->handler->protocol & CURLPROTO_RTSP)
    return checkrtspprefix(data, s);
    return checkrtspprefix(data, s, len);
#else
  (void)conn;
#endif /* CURL_DISABLE_RTSP */

  return checkhttpprefix(data, s);
  return checkhttpprefix(data, s, len);
}

/*
@@ -3097,12 +3114,15 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
      if(result)
        return result;

      if(!k->headerline && (k->hbuflen>5)) {
        /* make a first check that this looks like a protocol header */
        if(!checkprotoprefix(data, conn, data->state.headerbuff)) {
      if(!k->headerline) {
        /* check if this looks like a protocol header */
        statusline st = checkprotoprefix(data, conn, data->state.headerbuff,
                                         k->hbuflen);
        if(st == STATUS_BAD) {
          /* this is not the beginning of a protocol first header line */
          k->header = FALSE;
          k->badheader = HEADER_ALLBAD;
          streamclose(conn, "bad HTTP: No end-of-message indicator");
          break;
        }
      }
@@ -3131,8 +3151,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,

    if(!k->headerline) {
      /* the first read header */
      if((k->hbuflen>5) &&
         !checkprotoprefix(data, conn, data->state.headerbuff)) {
      statusline st = checkprotoprefix(data, conn, data->state.headerbuff,
                                       k->hbuflen);
      if(st == STATUS_BAD) {
        streamclose(conn, "bad HTTP: No end-of-message indicator");
        /* this is not the beginning of a protocol first header line */
        k->header = FALSE;
        if(*nread)
@@ -3501,7 +3523,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
             compare header line against list of aliases
          */
          if(!nc) {
            if(checkhttpprefix(data, k->p)) {
            if(checkhttpprefix(data, k->p, k->hbuflen) == STATUS_DONE) {
              nc = 1;
              k->httpcode = 200;
              conn->httpversion = 10;
+5 −6
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@
#include "memdebug.h"

#define H2_BUFSIZE 32768
#define MIN(x,y) ((x)<(y)?(x):(y))

#if (NGHTTP2_VERSION_NUM < 0x010000)
#error too old nghttp2 version, upgrade!
@@ -667,7 +666,7 @@ static int on_frame_recv(nghttp2_session *session, const nghttp2_frame *frame,
    Curl_add_buffer(stream->header_recvbuf, "\r\n", 2);

    left = stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
    ncopy = MIN(stream->len, left);
    ncopy = CURLMIN(stream->len, left);

    memcpy(&stream->mem[stream->memlen],
           stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
@@ -753,7 +752,7 @@ static int on_data_chunk_recv(nghttp2_session *session, uint8_t flags,
  if(!stream)
    return NGHTTP2_ERR_CALLBACK_FAILURE;

  nread = MIN(stream->len, len);
  nread = CURLMIN(stream->len, len);
  memcpy(&stream->mem[stream->memlen], data, nread);

  stream->len -= nread;
@@ -1076,7 +1075,7 @@ static ssize_t data_source_read_callback(nghttp2_session *session,
  else
    return NGHTTP2_ERR_INVALID_ARGUMENT;

  nread = MIN(stream->upload_len, length);
  nread = CURLMIN(stream->upload_len, length);
  if(nread > 0) {
    memcpy(buf, stream->upload_mem, nread);
    stream->upload_mem += nread;
@@ -1534,7 +1533,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
    /* If there is body data pending for this stream to return, do that */
    size_t left =
      stream->header_recvbuf->size_used - stream->nread_header_recvbuf;
    size_t ncopy = MIN(len, left);
    size_t ncopy = CURLMIN(len, left);
    memcpy(mem, stream->header_recvbuf->buffer + stream->nread_header_recvbuf,
           ncopy);
    stream->nread_header_recvbuf += ncopy;
@@ -1570,7 +1569,7 @@ static ssize_t http2_recv(struct connectdata *conn, int sockindex,
  }
  else if(stream->pausedata) {
    DEBUGASSERT(httpc->pause_stream_id == stream->stream_id);
    nread = MIN(len, stream->pauselen);
    nread = CURLMIN(len, stream->pauselen);
    memcpy(mem, stream->pausedata, nread);

    stream->pausedata += nread;
+2 −5
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2016, Florin Petriuc, <petriuc.florin@gmail.com>
 * Copyright (C) 1998 - 2018, Florin Petriuc, <petriuc.florin@gmail.com>
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -123,9 +123,6 @@ static const unsigned long K[64] = {
#define Sigma1(x)   (S(x, 6) ^ S(x, 11) ^ S(x, 25))
#define Gamma0(x)   (S(x, 7) ^ S(x, 18) ^ R(x, 3))
#define Gamma1(x)   (S(x, 17) ^ S(x, 19) ^ R(x, 10))
#ifndef MIN
#define MIN(x, y)   (((x) < (y)) ? (x) : (y))
#endif
/* compress 512-bits */
static int sha256_compress(struct sha256_state *md,
                           unsigned char *buf)
@@ -200,7 +197,7 @@ static int SHA256_Update(struct sha256_state *md,
      inlen -= block_size;
    }
    else {
      n = MIN(inlen, (block_size - md->curlen));
      n = CURLMIN(inlen, (block_size - md->curlen));
      memcpy(md->buf + md->curlen, in, n);
      md->curlen += n;
      in += n;
+0 −5
Original line number Diff line number Diff line
@@ -157,11 +157,6 @@ typedef ssize_t (Curl_recv)(struct connectdata *conn, /* connection data */
#define GOOD_EASY_HANDLE(x) \
  ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER))

/* Some convenience macros to get the larger/smaller value out of two given.
   We prefix with CURL to prevent name collisions. */
#define CURLMAX(x,y) ((x)>(y)?(x):(y))
#define CURLMIN(x,y) ((x)<(y)?(x):(y))

#ifdef HAVE_GSSAPI
/* Types needed for krb5-ftp connections */
struct krb5buffer {
Loading