From 9207fce4677a7f847581ff0e415041f5e12c8079 Mon Sep 17 00:00:00 2001 From: tanjiro Kamado Date: Sat, 7 Nov 2026 10:51:27 +0530 Subject: [PATCH] reject unrecognized Transfer-Encoding in httpGetLength --- cups/http.c | 13 ++++++++ cups/testhttp.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) diff --git a/cups/http.c b/cups/http.c index 88749a274..161a94b31 100644 --- a/cups/http.c +++ b/cups/http.c @@ -1066,6 +1066,13 @@ httpGetLength(http_t *http) // I - HTTP connection DEBUG_puts("4httpGetLength: chunked request!"); remaining = 0; } + else if (http->fields[HTTP_FIELD_TRANSFER_ENCODING] && http->fields[HTTP_FIELD_TRANSFER_ENCODING][0]) + { + // Don't fall back on Content-Length for an unsupported Transfer-Encoding + // (RFC 9112 section 6.1)... + DEBUG_printf("4httpGetLength: Unsupported Transfer-Encoding \"%s\".", http->fields[HTTP_FIELD_TRANSFER_ENCODING]); + remaining = -1; + } else { // The following is a hack for HTTP servers that don't send a Content-Length @@ -2639,10 +2646,16 @@ _httpUpdate(http_t *http, // I - HTTP connection else if ((value = strchr(http->line, ':')) != NULL) { // Got a value... + char *valend; // End of value + *value++ = '\0'; while (_cups_isspace(*value)) value ++; + // Strip trailing whitespace (RFC 9110 section 5.5)... + for (valend = value + strlen(value); valend > value && _cups_isspace(valend[-1]); valend --) + valend[-1] = '\0'; + DEBUG_printf("1_httpUpdate: Header %s: %s", http->line, value); // Be tolerants of servers that send unknown attribute fields... diff --git a/cups/testhttp.c b/cups/testhttp.c index cd11dfca4..53336a402 100644 --- a/cups/testhttp.c +++ b/cups/testhttp.c @@ -228,6 +228,7 @@ static const char * const base64_tests[][2] = // static bool test_date(time_t t); +static bool test_transfer_encoding(const char *te, bool chunked); // @@ -482,6 +483,16 @@ main(int argc, // I - Number of command-line arguments else testEndMessage(true, "%s", buffer); + // httpUpdate()/httpGetLength() with Transfer-Encoding + if (!test_transfer_encoding("chunked", true)) + failures ++; + if (!test_transfer_encoding("chunked ", true)) + failures ++; + if (!test_transfer_encoding("gzip", false)) + failures ++; + if (!test_transfer_encoding("chunked, chunked", false)) + failures ++; + return (failures); } else if (strstr(argv[1], "._tcp")) @@ -942,3 +953,74 @@ test_date(time_t t) // I - Time in seconds since Jan 1, 1970 return (false); } } + + +// +// 'test_transfer_encoding()' - Test request framing for a Transfer-Encoding value. +// + +static bool // O - `true` on success, `false` on failure +test_transfer_encoding( + const char *te, // I - Transfer-Encoding value + bool chunked) // I - `true` if chunked, `false` if the request must be rejected +{ + bool ret = false; // Return value + http_addrlist_t *addrlist; // Loopback address + socklen_t addrlen; // Length of address + int lfd = -1, // Listen socket + cfd = -1; // Client socket + http_t *http = NULL; // Server connection + char request[1024], // Request + resource[256]; // Resource path + http_status_t status; // HTTP status + off_t length; // Content length + + + testBegin("httpUpdate(Transfer-Encoding: \"%s\")", te); + + if ((addrlist = httpAddrGetList("127.0.0.1", AF_INET, "0")) == NULL) + { + testEndMessage(false, "httpAddrGetList: %s", cupsGetErrorString()); + return (false); + } + + addrlen = (socklen_t)sizeof(addrlist->addr); + + if ((lfd = httpAddrListen(&addrlist->addr, 0)) < 0 || getsockname(lfd, (struct sockaddr *)&addrlist->addr, &addrlen) || !httpAddrConnect(addrlist, &cfd, 10000, NULL)) + { + testEndMessage(false, "unable to connect: %s", cupsGetErrorString()); + goto done; + } + + snprintf(request, sizeof(request), "POST / HTTP/1.1\r\nHost: localhost\r\nTransfer-Encoding: %s\r\nContent-Length: 4\r\n\r\n", te); + if (send(cfd, request, strlen(request), 0) < 0 || (http = httpAcceptConnection(lfd, true)) == NULL || httpReadRequest(http, resource, sizeof(resource)) != HTTP_STATE_POST) + { + testEndMessage(false, "unable to send request: %s", cupsGetErrorString()); + goto done; + } + + while ((status = httpUpdate(http)) == HTTP_STATUS_CONTINUE); + + length = httpGetLength(http); + + if (chunked && (status != HTTP_STATUS_OK || length != 0)) + testEndMessage(false, "got status %d, length %ld, expected chunked", status, (long)length); + else if (!chunked && status != HTTP_STATUS_ERROR) + testEndMessage(false, "got status %d, length %ld, expected error", status, (long)length); + else + ret = true; + + if (ret) + testEnd(true); + + done: + + httpClose(http); + if (cfd >= 0) + httpAddrClose(NULL, cfd); + if (lfd >= 0) + httpAddrClose(NULL, lfd); + httpAddrFreeList(addrlist); + + return (ret); +}