]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
4223. [bug] Improve HTTP header processing on statschannel.
authorMark Andrews <marka@isc.org>
Wed, 2 Mar 2016 00:04:59 +0000 (11:04 +1100)
committerMark Andrews <marka@isc.org>
Wed, 2 Mar 2016 00:11:11 +0000 (11:11 +1100)
                        [RT #41674]

(cherry picked from commit ce7216c40ac973ed9ac5a90d75cd41b14b789725)
(cherry picked from commit 62e7d7533a7410e18a34861ab79aee14c4b4a513)

CHANGES
lib/isc/httpd.c

diff --git a/CHANGES b/CHANGES
index 0d966144279b4adb7a7f847a93cec41e047ccbd0..d9a454013b61df695dffe576acf4d6f18154bc32 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,6 @@
+4223.  [bug]           Improve HTTP header processing on statschannel.
+                       [RT #41674]
+
 4320.  [bug]           Insufficient memory allocation when handling
                        "none" ACL could cause an assertion failure in
                        named when parsing ACL configuration. [RT #41745]
index 7bb9b0998f86008511e557215d358b6a2e5d2963..2d9559d3e7e81582c66d78ae518da083fbd64ac8 100644 (file)
@@ -360,6 +360,72 @@ httpdmgr_destroy(isc_httpdmgr_t *httpdmgr) {
 #define LENGTHOK(s) (httpd->recvbuf - (s) < (int)httpd->recvlen)
 #define BUFLENOK(s) (httpd->recvbuf - (s) < HTTP_RECVLEN)
 
+/*
+ * Look for the given header in headers.
+ * If value is specified look for it terminated with a character in eov.
+ */
+static isc_boolean_t
+have_header(isc_httpd_t *httpd, const char *header, const char *value,
+           const char *eov)
+{
+       char *cr, *nl, *h;
+       size_t hlen, vlen;
+
+       h = httpd->headers;
+       hlen = strlen(header);
+       if (value != NULL) {
+               INSIST(eov != NULL);
+               vlen = strlen(value);
+       }
+
+       for (;;) {
+               if (strncasecmp(h, header, hlen) != 0) {
+                       /*
+                        * Skip to next line;
+                        */
+                       cr = strchr(h, '\r');
+                       if (cr != NULL && cr[1] == '\n')
+                               cr++;
+                       nl = strchr(h, '\n');
+                       /* last header? */
+                       if (cr == NULL && nl == NULL)
+                               return(ISC_FALSE);
+                       h = cr;
+                       if (h == NULL || nl < h)
+                               h = nl;
+                       h++;
+                       continue;
+               }
+
+               if (value == NULL)
+                       return (ISC_TRUE);
+
+               /*
+                * Skip optional leading white space.
+                */
+               h += hlen;
+               while (*h == ' ' || *h == '\t')
+                       h++;
+               /*
+                * Terminate token search on NULL or EOL.
+                */
+               while (*h != 0 && *h != '\r' && *h != '\n') {
+                       if (strncasecmp(h, value, vlen) == 0)
+                               if (strchr(eov, h[vlen]) != NULL)
+                                       return (ISC_TRUE);
+                       /*
+                        * Skip to next token.
+                        */
+                       h += strcspn(h, eov);
+                       if (h[0] == '\r' && h[1] == '\n')
+                               h++;
+                       if (h[0] != 0)
+                               h++;
+               }
+               return (ISC_FALSE);
+       }
+}
+
 static isc_result_t
 process_request(isc_httpd_t *httpd, int length) {
        char *s;
@@ -378,14 +444,19 @@ process_request(isc_httpd_t *httpd, int length) {
         * more data.
         */
        s = strstr(httpd->recvbuf, "\r\n\r\n");
-       delim = 1;
+       delim = 2;
        if (s == NULL) {
                s = strstr(httpd->recvbuf, "\n\n");
-               delim = 2;
+               delim = 1;
        }
        if (s == NULL)
                return (ISC_R_NOTFOUND);
 
+       /*
+        * NUL terminate request at the blank line.
+        */
+       s[delim] = 0;
+
        /*
         * Determine if this is a POST or GET method.  Any other values will
         * cause an error to be returned.
@@ -444,7 +515,7 @@ process_request(isc_httpd_t *httpd, int length) {
        }
 
        httpd->url = p;
-       p = s + delim;
+       p = s + 1;
        s = p;
 
        /*
@@ -459,7 +530,7 @@ process_request(isc_httpd_t *httpd, int length) {
 
        /*
         * Extract the HTTP/1.X protocol.  We will bounce on anything but
-        * HTTP/1.1 for now.
+        * HTTP/1.0 or HTTP/1.1 for now.
         */
        while (LENGTHOK(s) && BUFLENOK(s) &&
               (*s != '\n' && *s != '\r' && *s != '\0'))
@@ -468,24 +539,30 @@ process_request(isc_httpd_t *httpd, int length) {
                return (ISC_R_NOTFOUND);
        if (!BUFLENOK(s))
                return (ISC_R_NOMEMORY);
+       /*
+        * Check that we have the expected eol delimiter.
+        */
+       if (strncmp(s, delim == 1 ? "\n" : "\r\n", delim) != 0)
+               return (ISC_R_RANGE);
        *s = 0;
        if ((strncmp(p, "HTTP/1.0", 8) != 0)
            && (strncmp(p, "HTTP/1.1", 8) != 0))
                return (ISC_R_RANGE);
        httpd->protocol = p;
-       p = s + 1;
+       p = s + delim;  /* skip past eol */
        s = p;
 
        httpd->headers = s;
 
-       if (strstr(s, "Connection: close") != NULL)
+       if (have_header(httpd, "Connection:", "close", ", \t\r\n"))
                httpd->flags |= HTTPD_CLOSE;
 
-       if (strstr(s, "Host: ") != NULL)
+       if (have_header(httpd, "Host:", NULL, NULL))
                httpd->flags |= HTTPD_FOUNDHOST;
 
        if (strncmp(httpd->protocol, "HTTP/1.0", 8) == 0) {
-               if (strcasestr(s, "Connection: Keep-Alive") != NULL)
+               if (have_header(httpd, "Connection:", "Keep-Alive",
+                               ", \t\r\n"))
                        httpd->flags |= HTTPD_KEEPALIVE;
                else
                        httpd->flags |= HTTPD_CLOSE;