]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
http_proxy: fix adding custom proxy headers
authorDaniel Stenberg <daniel@haxx.se>
Sat, 25 Oct 2025 16:48:36 +0000 (18:48 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 27 Oct 2025 08:52:00 +0000 (09:52 +0100)
Reported-by: Joshua Rogers
Fixes #19227
Closes #19239

lib/http_proxy.c

index 845ba2e8f8daf3be202ea75f38614ced07f0627d..f6e546b9fd960a9db95e7668842d6d7a8ecb8388 100644 (file)
 #include "curl_memory.h"
 #include "memdebug.h"
 
-static bool hd_name_eq(const char *n1, size_t n1len,
-                       const char *n2, size_t n2len)
-{
-  return (n1len == n2len) ? curl_strnequal(n1, n2, n1len) : FALSE;
-}
-
 static CURLcode dynhds_add_custom(struct Curl_easy *data,
                                   bool is_connect, int httpversion,
                                   struct dynhds *hds)
 {
   struct connectdata *conn = data->conn;
-  const char *ptr;
   struct curl_slist *h[2];
   struct curl_slist *headers;
   int numlists = 1; /* by default */
@@ -95,86 +88,76 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
   /* loop through one or two lists */
   for(i = 0; i < numlists; i++) {
     for(headers = h[i]; headers; headers = headers->next) {
-      const char *name, *value;
-      size_t namelen, valuelen;
+      struct Curl_str name;
+      const char *value = NULL;
+      size_t valuelen = 0;
+      const char *ptr = headers->data;
 
       /* There are 2 quirks in place for custom headers:
        * 1. setting only 'name:' to suppress a header from being sent
        * 2. setting only 'name;' to send an empty (illegal) header
        */
-      ptr = strchr(headers->data, ':');
-      if(ptr) {
-        name = headers->data;
-        namelen = ptr - headers->data;
-        ptr++; /* pass the colon */
-        curlx_str_passblanks(&ptr);
-        if(*ptr) {
-          value = ptr;
-          valuelen = strlen(value);
+      if(!curlx_str_cspn(&ptr, &name, ";:")) {
+        if(!curlx_str_single(&ptr, ':')) {
+          curlx_str_passblanks(&ptr);
+          if(*ptr) {
+            value = ptr;
+            valuelen = strlen(value);
+          }
+          else {
+            /* quirk #1, suppress this header */
+            continue;
+          }
         }
-        else {
-          /* quirk #1, suppress this header */
-          continue;
+        else if(!curlx_str_single(&ptr, ';')) {
+          curlx_str_passblanks(&ptr);
+          if(!*ptr) {
+            /* quirk #2, send an empty header */
+            value = "";
+            valuelen = 0;
+          }
+          else {
+            /* this may be used for something else in the future,
+             * ignore this for now */
+            continue;
+          }
         }
-      }
-      else {
-        ptr = strchr(headers->data, ';');
-
-        if(!ptr) {
-          /* neither : nor ; in provided header value. We seem
-           * to ignore this silently */
+        else
+          /* neither : nor ; in provided header value. We ignore this
+           * silently */
           continue;
-        }
-
-        name = headers->data;
-        namelen = ptr - headers->data;
-        ptr++; /* pass the semicolon */
-        curlx_str_passblanks(&ptr);
-        if(!*ptr) {
-          /* quirk #2, send an empty header */
-          value = "";
-          valuelen = 0;
-        }
-        else {
-          /* this may be used for something else in the future,
-           * ignore this for now */
-          continue;
-        }
       }
+      else
+        /* no name, move on */
+        continue;
 
-      DEBUGASSERT(name && value);
+      DEBUGASSERT(curlx_strlen(&name) && value);
       if(data->state.aptr.host &&
          /* a Host: header was sent already, do not pass on any custom Host:
             header as that will produce *two* in the same request! */
-         hd_name_eq(name, namelen, STRCONST("Host:")))
-        ;
+         curlx_str_casecompare(&name, "Host"));
       else if(data->state.httpreq == HTTPREQ_POST_FORM &&
               /* this header (extended by formdata.c) is sent later */
-              hd_name_eq(name, namelen, STRCONST("Content-Type:")))
-        ;
+              curlx_str_casecompare(&name, "Content-Type"));
       else if(data->state.httpreq == HTTPREQ_POST_MIME &&
               /* this header is sent later */
-              hd_name_eq(name, namelen, STRCONST("Content-Type:")))
-        ;
+              curlx_str_casecompare(&name, "Content-Type"));
       else if(data->req.authneg &&
               /* while doing auth neg, do not allow the custom length since
                  we will force length zero then */
-              hd_name_eq(name, namelen, STRCONST("Content-Length:")))
-        ;
+              curlx_str_casecompare(&name, "Content-Length"));
       else if((httpversion >= 20) &&
-              hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
-        /* HTTP/2 and HTTP/3 do not support chunked requests */
-        ;
-      else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) ||
-               hd_name_eq(name, namelen, STRCONST("Cookie:"))) &&
+              curlx_str_casecompare(&name, "Transfer-Encoding"));
+      /* HTTP/2 and HTTP/3 do not support chunked requests */
+      else if((curlx_str_casecompare(&name, "Authorization") ||
+               curlx_str_casecompare(&name, "Cookie")) &&
               /* be careful of sending this potentially sensitive header to
                  other hosts */
-              !Curl_auth_allowed_to_host(data))
-        ;
+              !Curl_auth_allowed_to_host(data));
       else {
-        CURLcode result;
-
-        result = Curl_dynhds_add(hds, name, namelen, value, valuelen);
+        CURLcode result =
+          Curl_dynhds_add(hds, curlx_str(&name), curlx_strlen(&name),
+                          value, valuelen);
         if(result)
           return result;
       }