From: Alex Rousskov Date: Mon, 26 Nov 2012 18:54:43 +0000 (-0700) Subject: Make it possible to match empty header field values using req_header and rep_header. X-Git-Tag: SQUID_3_4_0_1~473 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b2c447187eb0087fc63bfe5bd3b32d858ac3337f;p=thirdparty%2Fsquid.git Make it possible to match empty header field values using req_header and rep_header. Warning: Some req_header and rep_header ACLs that were [accidentally] not matching empty headers (e.g., "^$" or ".*") will now start matching them. A new HttpHeader::getByNameIfPresent() method is added to be able to detect presence of empty header fields while ACLHTTPHeaderData::match() is adjusted to convert undefined String values into empty c-strings ("") for ACLRegexData::match() to work. Prior to these changes, when trying to match an empty header value with a regex like "^$", ACLHTTPHeaderData::match() would return false because: * HttpHeader::getStrOrList() and getByName() return an undefined String. * String::termedBuf() returns NULL for undefined Strings; and * ACLRegexData::match() always fails on NULL c-strings. --- diff --git a/src/HttpHeader.cc b/src/HttpHeader.cc index 1333cf5ccb..4515303804 100644 --- a/src/HttpHeader.cc +++ b/src/HttpHeader.cc @@ -1020,10 +1020,19 @@ HttpHeader::getStrOrList(http_hdr_type id) const } /* - * Returns the value of the specified header. + * Returns the value of the specified header and/or an undefined String. */ String HttpHeader::getByName(const char *name) const +{ + String result; + // ignore presence: return undefined string if an empty header is present + (void)getByNameIfPresent(name, result); + return result; +} + +bool +HttpHeader::getByNameIfPresent(const char *name, String &result) const { http_hdr_type id; HttpHeaderPos pos = HttpHeaderInitPos; @@ -1034,19 +1043,23 @@ HttpHeader::getByName(const char *name) const /* First try the quick path */ id = httpHeaderIdByNameDef(name, strlen(name)); - if (id != -1) - return getStrOrList(id); - - String result; + if (id != -1) { + if (!has(id)) + return false; + result = getStrOrList(id); + return true; + } /* Sorry, an unknown header name. Do linear search */ + bool found = false; while ((e = getEntry(&pos))) { if (e->id == HDR_OTHER && e->name.caseCmp(name) == 0) { + found = true; strListAdd(&result, e->value.termedBuf(), ','); } } - return result; + return found; } /* diff --git a/src/HttpHeader.h b/src/HttpHeader.h index 4def174193..e93cf95d67 100644 --- a/src/HttpHeader.h +++ b/src/HttpHeader.h @@ -247,6 +247,8 @@ public: bool getList(http_hdr_type id, String *s) const; String getStrOrList(http_hdr_type id) const; String getByName(const char *name) const; + /// sets value and returns true iff a [possibly empty] named field is there + bool getByNameIfPresent(const char *name, String &value) const; String getByNameListMember(const char *name, const char *member, const char separator) const; String getListMember(http_hdr_type id, const char *member, const char separator) const; int has(http_hdr_type id) const; diff --git a/src/acl/HttpHeaderData.cc b/src/acl/HttpHeaderData.cc index 7ba80d9086..d84633df05 100644 --- a/src/acl/HttpHeaderData.cc +++ b/src/acl/HttpHeaderData.cc @@ -65,9 +65,23 @@ ACLHTTPHeaderData::match(HttpHeader* hdr) debugs(28, 3, "aclHeaderData::match: checking '" << hdrName << "'"); - String value = hdrId != HDR_BAD_HDR ? hdr->getStrOrList(hdrId) : hdr->getByName(hdrName.termedBuf()); + String value; + if (hdrId != HDR_BAD_HDR) { + if (!hdr->has(hdrId)) + return false; + value = hdr->getStrOrList(hdrId); + } else { + if (!hdr->getByNameIfPresent(hdrName.termedBuf(), value)) + return false; + } - return regex_rule->match(value.termedBuf()); + // By now, we know the header is present, but: + // HttpHeader::get*() return an undefined String for empty header values; + // String::termedBuf() returns NULL for undefined Strings; and + // ACLRegexData::match() always fails on NULL strings. + // This makes it possible to detect an empty header value using regex: + const char *cvalue = value.defined() ? value.termedBuf() : ""; + return regex_rule->match(cvalue); } wordlist *