Unverified Commit 8123560d authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

HTTP: allow "header;" to replace an internal header with a blank one

Reported-by: Michael Kaufmann
Fixes #2357
Closes #2362
parent 019aa722
Loading
Loading
Loading
Loading
+70 −64
Original line number Diff line number Diff line
@@ -177,9 +177,9 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn)
 * if proxy headers are not available, then it will lookup into http header
 * link list
 *
 * It takes a connectdata struct as input instead of the Curl_easy simply
 * to know if this is a proxy request or not, as it then might check a
 * different header list.
 * It takes a connectdata struct as input instead of the Curl_easy simply to
 * know if this is a proxy request or not, as it then might check a different
 * header list. Provide the header prefix without colon!.
 */
char *Curl_checkProxyheaders(const struct connectdata *conn,
                             const char *thisheader)
@@ -191,7 +191,8 @@ char *Curl_checkProxyheaders(const struct connectdata *conn,
  for(head = (conn->bits.proxy && data->set.sep_headers) ?
        data->set.proxyheaders : data->set.headers;
      head; head = head->next) {
    if(strncasecompare(head->data, thisheader, thislen))
    if(strncasecompare(head->data, thisheader, thislen) &&
       Curl_headersep(head->data[thislen]))
      return head->data;
  }

@@ -614,9 +615,9 @@ output_auth_headers(struct connectdata *conn,
  if(authstatus->picked == CURLAUTH_BASIC) {
    /* Basic */
    if((proxy && conn->bits.proxy_user_passwd &&
        !Curl_checkProxyheaders(conn, "Proxy-authorization:")) ||
        !Curl_checkProxyheaders(conn, "Proxy-authorization")) ||
       (!proxy && conn->bits.user_passwd &&
        !Curl_checkheaders(conn, "Authorization:"))) {
        !Curl_checkheaders(conn, "Authorization"))) {
      auth = "Basic";
      result = http_output_basic(conn, proxy);
      if(result)
@@ -1533,7 +1534,7 @@ static CURLcode expect100(struct Curl_easy *data,
    /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
       Expect: 100-continue to the headers which actually speeds up post
       operations (as there is one packet coming back from the web server) */
    ptr = Curl_checkheaders(conn, "Expect:");
    ptr = Curl_checkheaders(conn, "Expect");
    if(ptr) {
      data->state.expect100header =
        Curl_compareheader(ptr, "Expect:", "100-continue");
@@ -1598,7 +1599,32 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
    headers = h[i];

    while(headers) {
      char *semicolonp = NULL;
      ptr = strchr(headers->data, ':');
      if(!ptr) {
        char *optr;
        /* no colon, semicolon? */
        ptr = strchr(headers->data, ';');
        if(ptr) {
          optr = ptr;
          ptr++; /* pass the semicolon */
          while(*ptr && ISSPACE(*ptr))
            ptr++;

          if(*ptr) {
            /* this may be used for something else in the future */
            optr = NULL;
          }
          else {
            if(*(--ptr) == ';') {
              /* send no-value custom header if terminated by semicolon */
              *ptr = ':';
              semicolonp = ptr;
            }
          }
          ptr = optr;
        }
      }
      if(ptr) {
        /* we require a colon for this to be a true header */

@@ -1606,8 +1632,9 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
        while(*ptr && ISSPACE(*ptr))
          ptr++;

        if(*ptr) {
          /* only send this if the contents was non-blank */
        if(*ptr || semicolonp) {
          /* only send this if the contents was non-blank or done special */
          CURLcode result = CURLE_OK;

          if(conn->allocptr.host &&
             /* a Host: header was sent already, don't pass on any custom Host:
@@ -1645,42 +1672,14 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn,
                   !strcasecompare(data->state.first_host, conn->host.name)))
            ;
          else {
            CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
                                               headers->data);
            if(result)
              return result;
            result = Curl_add_bufferf(req_buffer, "%s\r\n", headers->data);
          }
        }
      }
      else {
        ptr = strchr(headers->data, ';');
        if(ptr) {

          ptr++; /* pass the semicolon */
          while(*ptr && ISSPACE(*ptr))
            ptr++;

          if(*ptr) {
            /* this may be used for something else in the future */
          }
          else {
            if(*(--ptr) == ';') {
              CURLcode result;

              /* send no-value custom header if terminated by semicolon */
              *ptr = ':';
              result = Curl_add_bufferf(req_buffer, "%s\r\n",
                                        headers->data);

              /* restore the previous value */
              *ptr = ';';

          if(semicolonp)
            *semicolonp = ';'; /* put back the semicolon */
          if(result)
            return result;
        }
      }
        }
      }
      headers = headers->next;
    }
  }
@@ -1869,7 +1868,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     it might have been used in the proxy connect, but if we have got a header
     with the user-agent string specified, we erase the previously made string
     here. */
  if(Curl_checkheaders(conn, "User-Agent:")) {
  if(Curl_checkheaders(conn, "User-Agent")) {
    free(conn->allocptr.uagent);
    conn->allocptr.uagent = NULL;
  }
@@ -1890,7 +1889,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    conn->bits.authneg = FALSE;

  Curl_safefree(conn->allocptr.ref);
  if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) {
  if(data->change.referer && !Curl_checkheaders(conn, "Referer")) {
    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
    if(!conn->allocptr.ref)
      return CURLE_OUT_OF_MEMORY;
@@ -1899,11 +1898,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    conn->allocptr.ref = NULL;

#if !defined(CURL_DISABLE_COOKIES)
  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:"))
  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie"))
    addcookies = data->set.str[STRING_COOKIE];
#endif

  if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
  if(!Curl_checkheaders(conn, "Accept-Encoding") &&
     data->set.str[STRING_ENCODING]) {
    Curl_safefree(conn->allocptr.accept_encoding);
    conn->allocptr.accept_encoding =
@@ -1919,22 +1918,29 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
#ifdef HAVE_LIBZ
  /* we only consider transfer-encoding magic if libz support is built-in */

  if(!Curl_checkheaders(conn, "TE:") &&
  if(!Curl_checkheaders(conn, "TE") &&
     data->set.http_transfer_encoding) {
    /* When we are to insert a TE: header in the request, we must also insert
       TE in a Connection: header, so we need to merge the custom provided
       Connection: header and prevent the original to get sent. Note that if
       the user has inserted his/hers own TE: header we don't do this magic
       but then assume that the user will handle it all! */
    char *cptr = Curl_checkheaders(conn, "Connection:");
    char *cptr = Curl_checkheaders(conn, "Connection");
#define TE_HEADER "TE: gzip\r\n"

    Curl_safefree(conn->allocptr.te);

    if(cptr) {
      cptr = Curl_copy_header_value(cptr);
      if(!cptr)
        return CURLE_OUT_OF_MEMORY;
    }

    /* Create the (updated) Connection: header */
    conn->allocptr.te = cptr? aprintf("%s, TE\r\n" TE_HEADER, cptr):
      strdup("Connection: TE\r\n" TE_HEADER);
    conn->allocptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER,
                                cptr ? cptr : "", (cptr && *cptr) ? ", ":"");

    free(cptr);
    if(!conn->allocptr.te)
      return CURLE_OUT_OF_MEMORY;
  }
@@ -1958,7 +1964,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
  }

  if(http->sendit) {
    const char *cthdr = Curl_checkheaders(conn, "Content-Type:");
    const char *cthdr = Curl_checkheaders(conn, "Content-Type");

    /* Read and seek body only. */
    http->sendit->flags |= MIME_BODY_ONLY;
@@ -1982,7 +1988,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    http->postsize = Curl_mime_size(http->sendit);
  }

  ptr = Curl_checkheaders(conn, "Transfer-Encoding:");
  ptr = Curl_checkheaders(conn, "Transfer-Encoding");
  if(ptr) {
    /* Some kind of TE is requested, check if 'chunked' is chosen */
    data->req.upload_chunky =
@@ -2016,7 +2022,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)

  Curl_safefree(conn->allocptr.host);

  ptr = Curl_checkheaders(conn, "Host:");
  ptr = Curl_checkheaders(conn, "Host");
  if(ptr && (!data->state.this_is_a_follow ||
             strcasecompare(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
@@ -2055,7 +2061,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
#endif

    if(strcmp("Host:", ptr)) {
      conn->allocptr.host = aprintf("%s\r\n", ptr);
      conn->allocptr.host = aprintf("Host:%s\r\n", &ptr[5]);
      if(!conn->allocptr.host)
        return CURLE_OUT_OF_MEMORY;
    }
@@ -2164,7 +2170,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
  }
#endif /* CURL_DISABLE_PROXY */

  http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n";
  http->p_accept = Curl_checkheaders(conn, "Accept")?NULL:"Accept: */*\r\n";

  if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) &&
     data->state.resume_from) {
@@ -2245,14 +2251,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
     * ones if any such are specified.
     */
    if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
       !Curl_checkheaders(conn, "Range:")) {
       !Curl_checkheaders(conn, "Range")) {
      /* if a line like this was already allocated, free the previous one */
      free(conn->allocptr.rangeline);
      conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
                                         data->state.range);
    }
    else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
            !Curl_checkheaders(conn, "Content-Range:")) {
            !Curl_checkheaders(conn, "Content-Range")) {

      /* if a line like this was already allocated, free the previous one */
      free(conn->allocptr.rangeline);
@@ -2354,7 +2360,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
                     conn->allocptr.ref:"" /* Referer: <data> */,
                     (conn->bits.httpproxy &&
                      !conn->bits.tunnel_proxy &&
                      !Curl_checkProxyheaders(conn, "Proxy-Connection:"))?
                      !Curl_checkProxyheaders(conn, "Proxy-Connection"))?
                     "Proxy-Connection: Keep-Alive\r\n":"",
                     te
      );
@@ -2455,7 +2461,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
      postsize = data->state.infilesize;

    if((postsize != -1) && !data->req.upload_chunky &&
       (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length:"))) {
       (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
      /* only add Content-Length if not uploading chunked */
      result = Curl_add_bufferf(req_buffer,
                                "Content-Length: %" CURL_FORMAT_CURL_OFF_T
@@ -2517,7 +2523,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       we don't upload data chunked, as RFC2616 forbids us to set both
       kinds of headers (Transfer-Encoding: chunked and Content-Length) */
    if(postsize != -1 && !data->req.upload_chunky &&
       (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length:"))) {
       (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
      /* we allow replacing this header if not during auth negotiation,
         although it isn't very wise to actually set your own */
      result = Curl_add_bufferf(req_buffer,
@@ -2542,7 +2548,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       the somewhat bigger ones we allow the app to disable it. Just make
       sure that the expect100header is always set to the preferred value
       here. */
    ptr = Curl_checkheaders(conn, "Expect:");
    ptr = Curl_checkheaders(conn, "Expect");
    if(ptr) {
      data->state.expect100header =
        Curl_compareheader(ptr, "Expect:", "100-continue");
@@ -2596,7 +2602,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       we don't upload data chunked, as RFC2616 forbids us to set both
       kinds of headers (Transfer-Encoding: chunked and Content-Length) */
    if((postsize != -1) && !data->req.upload_chunky &&
       (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length:"))) {
       (conn->bits.authneg || !Curl_checkheaders(conn, "Content-Length"))) {
      /* we allow replacing this header if not during auth negotiation,
         although it isn't very wise to actually set your own */
      result = Curl_add_bufferf(req_buffer,
@@ -2606,7 +2612,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
        return result;
    }

    if(!Curl_checkheaders(conn, "Content-Type:")) {
    if(!Curl_checkheaders(conn, "Content-Type")) {
      result = Curl_add_bufferf(req_buffer,
                                "Content-Type: application/"
                                "x-www-form-urlencoded\r\n");
@@ -2618,7 +2624,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
       the somewhat bigger ones we allow the app to disable it. Just make
       sure that the expect100header is always set to the preferred value
       here. */
    ptr = Curl_checkheaders(conn, "Expect:");
    ptr = Curl_checkheaders(conn, "Expect");
    if(ptr) {
      data->state.expect100header =
        Curl_compareheader(ptr, "Expect:", "100-continue");
+4 −4
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -252,7 +252,7 @@ static CURLcode CONNECT(struct connectdata *conn,
          return CURLE_OUT_OF_MEMORY;
        }

        if(!Curl_checkProxyheaders(conn, "Host:")) {
        if(!Curl_checkProxyheaders(conn, "Host")) {
          host = aprintf("Host: %s\r\n", hostheader);
          if(!host) {
            free(hostheader);
@@ -260,10 +260,10 @@ static CURLcode CONNECT(struct connectdata *conn,
            return CURLE_OUT_OF_MEMORY;
          }
        }
        if(!Curl_checkProxyheaders(conn, "Proxy-Connection:"))
        if(!Curl_checkProxyheaders(conn, "Proxy-Connection"))
          proxyconn = "Proxy-Connection: Keep-Alive\r\n";

        if(!Curl_checkProxyheaders(conn, "User-Agent:") &&
        if(!Curl_checkProxyheaders(conn, "User-Agent") &&
           data->set.str[STRING_USERAGENT])
          useragent = conn->allocptr.uagent;

+13 −13
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -357,7 +357,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
  }

  /* Transport Header for SETUP requests */
  p_transport = Curl_checkheaders(conn, "Transport:");
  p_transport = Curl_checkheaders(conn, "Transport");
  if(rtspreq == RTSPREQ_SETUP && !p_transport) {
    /* New Transport: setting? */
    if(data->set.str[STRING_RTSP_TRANSPORT]) {
@@ -381,11 +381,11 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
  /* Accept Headers for DESCRIBE requests */
  if(rtspreq == RTSPREQ_DESCRIBE) {
    /* Accept Header */
    p_accept = Curl_checkheaders(conn, "Accept:")?
    p_accept = Curl_checkheaders(conn, "Accept")?
      NULL:"Accept: application/sdp\r\n";

    /* Accept-Encoding header */
    if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
    if(!Curl_checkheaders(conn, "Accept-Encoding") &&
       data->set.str[STRING_ENCODING]) {
      Curl_safefree(conn->allocptr.accept_encoding);
      conn->allocptr.accept_encoding =
@@ -402,11 +402,11 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
     it might have been used in the proxy connect, but if we have got a header
     with the user-agent string specified, we erase the previously made string
     here. */
  if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
  if(Curl_checkheaders(conn, "User-Agent") && conn->allocptr.uagent) {
    Curl_safefree(conn->allocptr.uagent);
    conn->allocptr.uagent = NULL;
  }
  else if(!Curl_checkheaders(conn, "User-Agent:") &&
  else if(!Curl_checkheaders(conn, "User-Agent") &&
          data->set.str[STRING_USERAGENT]) {
    p_uagent = conn->allocptr.uagent;
  }
@@ -421,7 +421,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)

  /* Referrer */
  Curl_safefree(conn->allocptr.ref);
  if(data->change.referer && !Curl_checkheaders(conn, "Referer:"))
  if(data->change.referer && !Curl_checkheaders(conn, "Referer"))
    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
  else
    conn->allocptr.ref = NULL;
@@ -438,7 +438,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
     (rtspreq  & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) {

    /* Check to see if there is a range set in the custom headers */
    if(!Curl_checkheaders(conn, "Range:") && data->state.range) {
    if(!Curl_checkheaders(conn, "Range") && data->state.range) {
      Curl_safefree(conn->allocptr.rangeline);
      conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range);
      p_range = conn->allocptr.rangeline;
@@ -448,11 +448,11 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
  /*
   * Sanity check the custom headers
   */
  if(Curl_checkheaders(conn, "CSeq:")) {
  if(Curl_checkheaders(conn, "CSeq")) {
    failf(data, "CSeq cannot be set as a custom header.");
    return CURLE_RTSP_CSEQ_ERROR;
  }
  if(Curl_checkheaders(conn, "Session:")) {
  if(Curl_checkheaders(conn, "Session")) {
    failf(data, "Session ID cannot be set as a custom header.");
    return CURLE_BAD_FUNCTION_ARGUMENT;
  }
@@ -542,7 +542,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
    if(putsize > 0 || postsize > 0) {
      /* As stated in the http comments, it is probably not wise to
       * actually set a custom Content-Length in the headers */
      if(!Curl_checkheaders(conn, "Content-Length:")) {
      if(!Curl_checkheaders(conn, "Content-Length")) {
        result = Curl_add_bufferf(req_buffer,
            "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n",
            (data->set.upload ? putsize : postsize));
@@ -552,7 +552,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)

      if(rtspreq == RTSPREQ_SET_PARAMETER ||
         rtspreq == RTSPREQ_GET_PARAMETER) {
        if(!Curl_checkheaders(conn, "Content-Type:")) {
        if(!Curl_checkheaders(conn, "Content-Type")) {
          result = Curl_add_bufferf(req_buffer,
              "Content-Type: text/parameters\r\n");
          if(result)
@@ -561,7 +561,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done)
      }

      if(rtspreq == RTSPREQ_ANNOUNCE) {
        if(!Curl_checkheaders(conn, "Content-Type:")) {
        if(!Curl_checkheaders(conn, "Content-Type")) {
          result = Curl_add_bufferf(req_buffer,
              "Content-Type: application/sdp\r\n");
          if(result)
+4 −3
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -85,7 +85,7 @@
    !defined(CURL_DISABLE_IMAP)
/*
 * checkheaders() checks the linked list of custom headers for a
 * particular header (prefix).
 * particular header (prefix). Provide the prefix without colon!
 *
 * Returns a pointer to the first matching header or NULL if none matched.
 */
@@ -97,7 +97,8 @@ char *Curl_checkheaders(const struct connectdata *conn,
  struct Curl_easy *data = conn->data;

  for(head = data->set.headers; head; head = head->next) {
    if(strncasecompare(head->data, thisheader, thislen))
    if(strncasecompare(head->data, thisheader, thislen) &&
       Curl_headersep(head->data[thislen]) )
      return head->data;
  }

+2 −1
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@
 *                            | (__| |_| |  _ <| |___
 *                             \___|\___/|_| \_\_____|
 *
 * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
 * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution. The terms
@@ -22,6 +22,7 @@
 *
 ***************************************************************************/

#define Curl_headersep(x) ((((x)==':') || ((x)==';')))
char *Curl_checkheaders(const struct connectdata *conn,
                        const char *thisheader);

Loading