/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "squid.h"
#include "acl/FilledChecklist.h"
#include "acl/Gadgets.h"
+#include "base/EnumIterator.h"
#include "client_side.h"
#include "client_side_request.h"
#include "comm/Connection.h"
#include "ConfigParser.h"
#include "fde.h"
#include "globals.h"
+#include "http/RegisteredHeaders.h"
+#include "http/Stream.h"
#include "HttpHdrContRange.h"
#include "HttpHeader.h"
#include "HttpHeaderFieldInfo.h"
#include <cerrno>
#include <string>
-static void httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs);
-
-HttpHeaderFieldInfo *
-httpHeaderBuildFieldsInfo(const HttpHeaderFieldAttrs * attrs, int count)
-{
- int i;
- HttpHeaderFieldInfo *table = NULL;
- assert(attrs && count);
-
- /* allocate space */
- table = new HttpHeaderFieldInfo[count];
-
- for (i = 0; i < count; ++i) {
- const http_hdr_type id = attrs[i].id;
- HttpHeaderFieldInfo *info = table + id;
- /* sanity checks */
- assert(id >= 0 && id < count);
- assert(attrs[i].name);
- assert(info->id == HDR_ACCEPT && info->type == ftInvalid); /* was not set before */
- /* copy and init fields */
- info->id = id;
- info->type = attrs[i].type;
- info->name = attrs[i].name;
- assert(info->name.size());
- }
-
- return table;
-}
-
-void
-httpHeaderDestroyFieldsInfo(HttpHeaderFieldInfo * table, int count)
-{
- int i;
-
- for (i = 0; i < count; ++i)
- table[i].name.clean();
-
- delete [] table;
-}
+static void httpHeaderPutStrvf(HttpHeader * hdr, Http::HdrType id, const char *fmt, va_list vargs);
+static void httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd);
void
httpHeaderMaskInit(HttpHeaderMask * mask, int value)
memset(mask, value, sizeof(*mask));
}
-/** calculates a bit mask of a given array; does not reset mask! */
-void
-httpHeaderCalcMask(HttpHeaderMask * mask, http_hdr_type http_hdr_type_enums[], size_t count)
-{
- size_t i;
- const int * enums = (const int *) http_hdr_type_enums;
- assert(mask && enums);
- assert(count < sizeof(*mask) * 8); /* check for overflow */
-
- for (i = 0; i < count; ++i) {
- assert(!CBIT_TEST(*mask, enums[i])); /* check for duplicates */
- CBIT_SET(*mask, enums[i]);
- }
-}
-
/* same as httpHeaderPutStr, but formats the string using snprintf first */
void
-httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...)
+httpHeaderPutStrf(HttpHeader * hdr, Http::HdrType id, const char *fmt,...)
{
va_list args;
va_start(args, fmt);
/* used by httpHeaderPutStrf */
static void
-httpHeaderPutStrvf(HttpHeader * hdr, http_hdr_type id, const char *fmt, va_list vargs)
+httpHeaderPutStrvf(HttpHeader * hdr, Http::HdrType id, const char *fmt, va_list vargs)
{
MemBuf mb;
mb.init();
assert(hdr && ent_len >= 0);
httpHdrContRangeSet(cr, spec, ent_len);
hdr->putContRange(cr);
- httpHdrContRangeDestroy(cr);
+ delete cr;
}
/**
- * return true if a given directive is found in at least one of
- * the "connection" header-fields note: if HDR_PROXY_CONNECTION is
- * present we ignore HDR_CONNECTION.
+ * \return true if a given directive is found in the Connection header field-value.
+ *
+ * \note if no Connection header exists we may check the Proxy-Connection header
*/
-int
-httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive)
+bool
+httpHeaderHasConnDir(const HttpHeader * hdr, const SBuf &directive)
{
String list;
- int res;
+
/* what type of header do we have? */
+ if (hdr->getList(Http::HdrType::CONNECTION, &list))
+ return strListIsMember(&list, directive, ',') != 0;
#if USE_HTTP_VIOLATIONS
- if (hdr->has(HDR_PROXY_CONNECTION))
- list = hdr->getList(HDR_PROXY_CONNECTION);
- else
+ if (hdr->getList(Http::HdrType::PROXY_CONNECTION, &list))
+ return strListIsMember(&list, directive, ',') != 0;
#endif
- if (hdr->has(HDR_CONNECTION))
- list = hdr->getList(HDR_CONNECTION);
- else
- return 0;
-
- res = strListIsMember(&list, directive, ',');
- list.clean();
-
- return res;
+ // else, no connection header for it to exist in
+ return false;
}
/** handy to printf prefixes of potentially very long buffers */
return 1;
}
-int
-httpHeaderParseOffset(const char *start, int64_t * value)
+bool
+httpHeaderParseOffset(const char *start, int64_t *value, char **endPtr)
{
+ char *end = nullptr;
errno = 0;
- int64_t res = strtoll(start, NULL, 10);
- if (!res && EINVAL == errno) /* maybe not portable? */
- return 0;
+ const int64_t res = strtoll(start, &end, 10);
+ if (errno && !res) {
+ debugs(66, 7, "failed to parse malformed offset in " << start);
+ return false;
+ }
+ if (errno == ERANGE && (res == LLONG_MIN || res == LLONG_MAX)) { // no overflow
+ debugs(66, 7, "failed to parse huge offset in " << start);
+ return false;
+ }
+ if (start == end) {
+ debugs(66, 7, "failed to parse empty offset");
+ return false;
+ }
*value = res;
- return 1;
+ if (endPtr)
+ *endPtr = end;
+ debugs(66, 7, "offset " << start << " parsed as " << res);
+ return true;
}
/**
* \retval 1 Header has no access controls to test
*/
static int
-httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep)
+httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, HeaderManglers *hms)
{
int retval;
- /* check with anonymizer tables */
- HeaderManglers *hms = NULL;
assert(e);
- if (ROR_REQUEST == req_or_rep) {
- hms = Config.request_header_access;
- } else if (ROR_REPLY == req_or_rep) {
- hms = Config.reply_header_access;
- } else {
- /* error. But let's call it "request". */
- hms = Config.request_header_access;
- }
-
- /* manglers are not configured for this message kind */
- if (!hms)
- return 1;
-
const headerMangler *hm = hms->find(*e);
/* mangler or checklist went away. default allow */
if (!hm || !hm->access_list) {
+ debugs(66, 7, "couldn't find mangler or access list. Allowing");
return 1;
}
ACLFilledChecklist checklist(hm->access_list, request, NULL);
- if (checklist.fastCheck() == ACCESS_ALLOWED) {
+ if (checklist.fastCheck().allowed()) {
/* aclCheckFast returns true for allow. */
+ debugs(66, 7, "checklist for mangler is positive. Mangle");
retval = 1;
} else if (NULL == hm->replacement) {
/* It was denied, and we don't have any replacement */
+ debugs(66, 7, "checklist denied, we have no replacement. Pass");
retval = 0;
} else {
/* It was denied, but we have a replacement. Replace the
* header on the fly, and return that the new header
* is allowed.
*/
+ debugs(66, 7, "checklist denied but we have replacement. Replace");
e->value = hm->replacement;
retval = 1;
}
/** Mangles headers for a list of headers. */
void
-httpHdrMangleList(HttpHeader * l, HttpRequest * request, int req_or_rep)
+httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
{
HttpHeaderEntry *e;
HttpHeaderPos p = HttpHeaderInitPos;
- int headers_deleted = 0;
- while ((e = l->getEntry(&p)))
- if (0 == httpHdrMangle(e, request, req_or_rep))
- l->delAt(p, headers_deleted);
+ /* check with anonymizer tables */
+ HeaderManglers *hms = nullptr;
+ HeaderWithAclList *headersAdd = nullptr;
+
+ switch (req_or_rep) {
+ case ROR_REQUEST:
+ hms = Config.request_header_access;
+ headersAdd = Config.request_header_add;
+ break;
+ case ROR_REPLY:
+ hms = Config.reply_header_access;
+ headersAdd = Config.reply_header_add;
+ break;
+ }
+
+ if (hms) {
+ int headers_deleted = 0;
+ while ((e = l->getEntry(&p))) {
+ if (0 == httpHdrMangle(e, request, hms))
+ l->delAt(p, headers_deleted);
+ }
- if (headers_deleted)
- l->refreshMask();
+ if (headers_deleted)
+ l->refreshMask();
+ }
+
+ if (headersAdd && !headersAdd->empty()) {
+ httpHdrAdd(l, request, al, *headersAdd);
+ }
}
static
HeaderManglers::~HeaderManglers()
{
- for (int i = 0; i < HDR_ENUM_END; ++i)
+ for (auto i : WholeEnum<Http::HdrType>())
header_mangler_clean(known[i]);
- typedef ManglersByName::iterator MBNI;
- for (MBNI i = custom.begin(); i != custom.end(); ++i)
- header_mangler_clean(i->second);
+ for (auto i : custom)
+ header_mangler_clean(i.second);
header_mangler_clean(all);
}
void
HeaderManglers::dumpAccess(StoreEntry * entry, const char *name) const
{
- for (int i = 0; i < HDR_ENUM_END; ++i) {
- header_mangler_dump_access(entry, name, known[i],
- httpHeaderNameById(i));
- }
+ for (auto id : WholeEnum<Http::HdrType>())
+ header_mangler_dump_access(entry, name, known[id], Http::HeaderLookupTable.lookup(id).name);
- typedef ManglersByName::const_iterator MBNCI;
- for (MBNCI i = custom.begin(); i != custom.end(); ++i)
- header_mangler_dump_access(entry, name, i->second, i->first.c_str());
+ for (auto i : custom)
+ header_mangler_dump_access(entry, name, i.second, i.first.c_str());
header_mangler_dump_access(entry, name, all, "All");
}
void
HeaderManglers::dumpReplacement(StoreEntry * entry, const char *name) const
{
- for (int i = 0; i < HDR_ENUM_END; ++i) {
- header_mangler_dump_replacement(entry, name, known[i],
- httpHeaderNameById(i));
+ for (auto id : WholeEnum<Http::HdrType>()) {
+ header_mangler_dump_replacement(entry, name, known[id], Http::HeaderLookupTable.lookup(id).name);
}
- typedef ManglersByName::const_iterator MBNCI;
- for (MBNCI i = custom.begin(); i != custom.end(); ++i) {
- header_mangler_dump_replacement(entry, name, i->second,
- i->first.c_str());
+ for (auto i: custom) {
+ header_mangler_dump_replacement(entry, name, i.second, i.first.c_str());
}
header_mangler_dump_replacement(entry, name, all, "All");
headerMangler *
HeaderManglers::track(const char *name)
{
- int id = httpHeaderIdByNameDef(name, strlen(name));
+ if (strcmp(name, "All") == 0)
+ return &all;
- if (id == HDR_BAD_HDR) { // special keyword or a custom header
- if (strcmp(name, "All") == 0)
- id = HDR_ENUM_END;
- else if (strcmp(name, "Other") == 0)
- id = HDR_OTHER;
- }
+ const Http::HdrType id = Http::HeaderLookupTable.lookup(SBuf(name)).id;
- headerMangler *m = NULL;
- if (id == HDR_ENUM_END) {
- m = &all;
- } else if (id == HDR_BAD_HDR) {
- m = &custom[name];
- } else {
- m = &known[id]; // including HDR_OTHER
- }
+ if (id != Http::HdrType::BAD_HDR)
+ return &known[id];
+
+ if (strcmp(name, "Other") == 0)
+ return &known[Http::HdrType::OTHER];
- assert(m);
- return m;
+ return &custom[name];
}
void
HeaderManglers::find(const HttpHeaderEntry &e) const
{
// a known header with a configured ACL list
- if (e.id != HDR_OTHER && 0 <= e.id && e.id < HDR_ENUM_END &&
+ if (e.id != Http::HdrType::OTHER && Http::any_HdrType_enum_value(e.id) &&
known[e.id].access_list)
return &known[e.id];
// a custom header
- if (e.id == HDR_OTHER) {
+ if (e.id == Http::HdrType::OTHER) {
// does it have an ACL list configured?
// Optimize: use a name type that we do not need to convert to here
- const ManglersByName::const_iterator i = custom.find(e.name.termedBuf());
+ SBuf tmp(e.name); // XXX: performance regression. c_str() reallocates
+ const ManglersByName::const_iterator i = custom.find(tmp.c_str());
if (i != custom.end())
return &i->second;
}
// Next-to-last resort: "Other" rules match any custom header
- if (e.id == HDR_OTHER && known[HDR_OTHER].access_list)
- return &known[HDR_OTHER];
+ if (e.id == Http::HdrType::OTHER && known[Http::HdrType::OTHER].access_list)
+ return &known[Http::HdrType::OTHER];
// Last resort: "All" rules match any header
if (all.access_list)
ACLFilledChecklist checklist(NULL, request, NULL);
for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) {
- if (!hwa->aclList || checklist.fastCheck(hwa->aclList) == ACCESS_ALLOWED) {
+ if (!hwa->aclList || checklist.fastCheck(hwa->aclList).allowed()) {
const char *fieldValue = NULL;
MemBuf mb;
if (hwa->quoted) {
if (!fieldValue || fieldValue[0] == '\0')
fieldValue = "-";
- HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, hwa->fieldName.c_str(),
- fieldValue);
+ HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, SBuf(hwa->fieldName), fieldValue);
heads->addEntry(e);
}
}