Author: Alex Rousskov [request|reply]_header_* manglers fixes to handle custom headers This patch fix the [request|reply]_header_[access|replace] configuration parameters to support custom headers. Currently the user is able to remove/ replace/allow all custom headers using the "Other" as header name. This is a Measurement Factory project. === modified file 'src/HttpHeaderTools.cc' --- src/HttpHeaderTools.cc 2012-01-20 18:55:04 +0000 +++ src/HttpHeaderTools.cc 2012-06-29 16:36:58 +0000 @@ -17,44 +17,48 @@ * sources; see the CREDITS file for full details. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #include "squid-old.h" #include "acl/FilledChecklist.h" +#include "acl/Gadgets.h" #include "compat/strtoll.h" #include "HttpHdrContRange.h" #include "HttpHeader.h" +#include "HttpHeaderTools.h" +#include "HttpRequest.h" #include "MemBuf.h" +#include "Store.h" 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); @@ -397,52 +401,58 @@ /* Make sure it's defined even if empty "" */ if (!val->defined()) val->limitInit("", 0); return 1; } /** * Checks the anonymizer (header_access) configuration. * * \retval 0 Header is explicitly blocked for removal * \retval 1 Header is explicitly allowed * \retval 1 Header has been replaced, the current version can be used. * \retval 1 Header has no access controls to test */ static int httpHdrMangle(HttpHeaderEntry * e, HttpRequest * request, int req_or_rep) { int retval; /* check with anonymizer tables */ - header_mangler *hm; + HeaderManglers *hms = NULL; assert(e); if (ROR_REQUEST == req_or_rep) { - hm = &Config.request_header_access[e->id]; + hms = Config.request_header_access; } else if (ROR_REPLY == req_or_rep) { - hm = &Config.reply_header_access[e->id]; + hms = Config.reply_header_access; } else { /* error. But let's call it "request". */ - hm = &Config.request_header_access[e->id]; + hms = Config.request_header_access; } + /* manglers are not configured for this message kind */ + if (!hms) + return 1; + + const header_mangler *hm = hms->find(*e); + /* mangler or checklist went away. default allow */ if (!hm || !hm->access_list) { return 1; } ACLFilledChecklist checklist(hm->access_list, request, NULL); if (checklist.fastCheck() == ACCESS_ALLOWED) { /* aclCheckFast returns true for allow. */ retval = 1; } else if (NULL == hm->replacement) { /* It was denied, and we don't have any replacement */ 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. */ e->value = hm->replacement; retval = 1; @@ -450,34 +460,160 @@ return retval; } /** Mangles headers for a list of headers. */ void httpHdrMangleList(HttpHeader * l, HttpRequest * request, int 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); if (headers_deleted) l->refreshMask(); } -/** - * return 1 if manglers are configured. Used to set a flag - * for optimization during request forwarding. - */ -int -httpReqHdrManglersConfigured() +static +void header_mangler_clean(header_mangler &m) +{ + aclDestroyAccessList(&m.access_list); + safe_free(m.replacement); +} + +static +void header_mangler_dump_access(StoreEntry * entry, const char *option, + const header_mangler &m, const char *name) +{ + if (m.access_list != NULL) { + storeAppendPrintf(entry, "%s ", option); + dump_acl_access(entry, name, m.access_list); + } +} + +static +void header_mangler_dump_replacement(StoreEntry * entry, const char *option, + const header_mangler &m, const char *name) +{ + if (m.replacement) + storeAppendPrintf(entry, "%s %s %s\n", option, name, m.replacement); +} + +HeaderManglers::HeaderManglers() +{ + memset(known, 0, sizeof(known)); + memset(&all, 0, sizeof(all)); +} + +HeaderManglers::~HeaderManglers() +{ + for (int i = 0; i < HDR_ENUM_END; i++) + header_mangler_clean(known[i]); + + typedef ManglersByName::iterator MBNI; + for (MBNI i = custom.begin(); i != custom.end(); ++i) + 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++) { - if (NULL != Config.request_header_access[i].access_list) - return 1; + header_mangler_dump_access(entry, name, known[i], + httpHeaderNameById(i)); } - return 0; + 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()); + + 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)); + } + + 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()); + } + + header_mangler_dump_replacement(entry, name, all, "All"); +} + +header_mangler * +HeaderManglers::track(const char *name) +{ + int id = httpHeaderIdByNameDef(name, strlen(name)); + + 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; + } + + header_mangler *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 + } + + assert(m); + return m; +} + +void +HeaderManglers::setReplacement(const char *name, const char *value) +{ + // for backword compatibility, we allow replacements to be configured + // for headers w/o access rules, but such replacements are ignored + header_mangler *m = track(name); + + safe_free(m->replacement); // overwrite old value if any + m->replacement = xstrdup(value); } + +const header_mangler * +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 && + known[e.id].access_list) + return &known[e.id]; + + // a custom header + if (e.id == HDR_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()); + 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]; + + // Last resort: "All" rules match any header + if (all.access_list) + return &all; + + return NULL; +} + === added file 'src/HttpHeaderTools.h' --- src/HttpHeaderTools.h 1970-01-01 00:00:00 +0000 +++ src/HttpHeaderTools.h 2012-06-29 16:36:58 +0000 @@ -0,0 +1,58 @@ +#ifndef SQUID_HTTPHEADERTOOLS_H +#define SQUID_HTTPHEADERTOOLS_H + +#if HAVE_MAP +#include +#endif +#if HAVE_STRING +#include +#endif + +class acl_access; +struct _header_mangler { + acl_access *access_list; + char *replacement; +}; +typedef struct _header_mangler header_mangler; + +class StoreEntry; + +/// A collection of header_mangler objects for a given message kind. +class HeaderManglers { +public: + HeaderManglers(); + ~HeaderManglers(); + + /// returns a header mangler for field e or nil if none was specified + const header_mangler *find(const HttpHeaderEntry &e) const; + + /// returns a mangler for the named header (known or custom) + header_mangler *track(const char *name); + + /// updates mangler for the named header with a replacement value + void setReplacement(const char *name, const char *replacementValue); + + /// report the *_header_access part of the configuration + void dumpAccess(StoreEntry *entry, const char *optionName) const; + /// report the *_header_replace part of the configuration + void dumpReplacement(StoreEntry *entry, const char *optionName) const; + +private: + /// a name:mangler map; optimize: use unordered map or some such + typedef std::map ManglersByName; + + /// one mangler for each known header + header_mangler known[HDR_ENUM_END]; + + /// one mangler for each custom header + ManglersByName custom; + + /// configured if some mangling ACL applies to all header names + header_mangler all; + +private: + /* not implemented */ + HeaderManglers(const HeaderManglers &); + HeaderManglers &operator =(const HeaderManglers &); +}; +#endif === modified file 'src/Makefile.am' --- src/Makefile.am 2012-05-30 10:25:42 +0000 +++ src/Makefile.am 2012-06-29 16:36:58 +0000 @@ -346,40 +346,41 @@ http.cc \ http.h \ HttpStatusCode.h \ HttpStatusLine.cc \ HttpStatusLine.h \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrRange.cc \ HttpHdrSc.cc \ HttpHdrSc.h \ HttpHdrScTarget.cc \ HttpHdrScTarget.h \ HttpHdrContRange.cc \ HttpHdrContRange.h \ HttpHeaderStat.h \ HttpHeader.cc \ HttpHeader.h \ HttpHeaderMask.h \ HttpHeaderRange.h \ + HttpHeaderTools.h \ HttpHeaderTools.cc \ HttpBody.h \ HttpBody.cc \ HttpControlMsg.h \ HttpMsg.cc \ HttpMsg.h \ HttpParser.cc \ HttpParser.h \ HttpReply.cc \ HttpReply.h \ HttpRequest.cc \ HttpRequest.h \ HttpRequestMethod.cc \ HttpRequestMethod.h \ HttpVersion.h \ ICP.h \ icp_opcode.h \ icp_v2.cc \ icp_v3.cc \ int.cc \ @@ -1038,106 +1039,115 @@ ##NP: (TESTSOURCES) defines stub debugs() and new/delete for testing ## #tests_testX_SOURCES=\ # tests/testX.h \ # tests/testX.cc \ # tests/testMain.cc \ # X.h \ # X.cc #nodist_tests_testX_SOURCES=\ # $(TESTSOURCES) #tests_testX_LDFLAGS = $(LIBADD_DL) #tests_testX_LDADD=\ # $(SQUID_CPPUNIT_LIBS) \ # $(SQUID_CPPUNIT_LA) \ # $(COMPAT_LIB) \ #tests_testX_DEPENDENCIES= $(SQUID_CPPUNIT_LA) # - add other component .(h|cc) files needed to link and run tests tests_testHttpReply_SOURCES=\ + anyp/ProtocolType.cc \ cbdata.cc \ cbdata.h \ ETag.cc \ HttpBody.h \ HttpBody.cc \ HttpHdrCc.h \ HttpHdrCc.cc \ HttpHdrCc.cci \ HttpHdrContRange.cc \ HttpHdrContRange.h \ HttpHdrRange.cc \ HttpHdrSc.cc \ HttpHdrSc.h \ HttpHdrScTarget.cc \ HttpHdrScTarget.h \ HttpHeader.cc \ HttpHeader.h \ HttpHeaderMask.h \ HttpHeaderTools.cc \ HttpControlMsg.h \ HttpMsg.cc \ HttpMsg.h \ HttpReply.cc \ HttpReply.h \ HttpStatusCode.h \ HttpStatusLine.cc \ HttpStatusLine.h \ mem.cc \ MemBuf.cc \ MemBuf.h \ mime_header.cc \ Packer.cc \ Packer.h \ SquidString.h \ SquidTime.h \ String.cc \ + tests/stub_access_log.cc \ tests/stub_cache_cf.cc \ tests/stub_cache_manager.cc \ tests/stub_debug.cc \ + tests/stub_errorpage.cc \ tests/stub_HelperChildConfig.cc \ StatCounters.h \ StatCounters.cc \ StatHist.h \ tests/stub_StatHist.cc \ tests/stub_store.cc \ tests/stub_store_stats.cc \ tests/stub_tools.cc \ + tests/stub_HttpRequest.cc \ tests/testHttpReply.cc \ tests/testHttpReply.h \ tests/testMain.cc \ time.cc \ + url.cc \ + URLScheme.cc \ wordlist.cc nodist_tests_testHttpReply_SOURCES=\ $(TESTSOURCES) tests_testHttpReply_LDFLAGS = $(LIBADD_DL) tests_testHttpReply_LDADD=\ + acl/libacls.la \ acl/libapi.la \ acl/libstate.la \ $(AUTH_LIBS) \ ip/libip.la \ base/libbase.la \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(SQUID_CPPUNIT_LIBS) \ $(SQUID_CPPUNIT_LA) \ + $(SSLLIB) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testHttpReply_DEPENDENCIES= $(SQUID_CPPUNIT_LA) tests_testACLMaxUserIP_SOURCES= \ cbdata.cc \ ClientInfo.h \ ConfigOption.cc \ ConfigParser.cc \ DiskIO/ReadRequest.cc \ DiskIO/WriteRequest.cc \ ETag.cc \ event.cc \ FileMap.h \ filemap.cc \ HelperChildConfig.h \ HelperChildConfig.cc \ HttpHeader.cc \ HttpHeaderTools.cc \ @@ -1165,76 +1175,78 @@ StoreIOState.cc \ StoreMeta.cc \ StoreMetaMD5.cc \ StoreMetaSTD.cc \ StoreMetaSTDLFS.cc \ StoreMetaUnpacker.cc \ StoreMetaURL.cc \ StoreMetaVary.cc \ StoreSwapLogData.cc \ store_key_md5.cc \ swap_log_op.cc \ swap_log_op.h \ SwapDir.cc \ SwapDir.h \ tests/stub_access_log.cc \ tests/stub_cache_cf.cc \ tests/stub_comm.cc \ tests/stub_debug.cc \ tests/stub_DelayId.cc \ tests/stub_DiskIOModule.cc \ + tests/stub_errorpage.cc \ tests/stub_fd.cc \ tests/stub_HttpRequest.cc \ tests/stub_MemObject.cc \ tests/stub_MemStore.cc \ tests/stub_mime.cc \ tests/stub_store.cc \ tests/stub_store_rebuild.cc \ tests/stub_store_stats.cc \ tests/stub_store_swapout.cc \ tests/stub_tools.cc \ tests/stub_cache_manager.cc \ tests/testACLMaxUserIP.cc \ tests/testACLMaxUserIP.h \ tests/testMain.cc \ time.cc \ url.cc \ URL.h \ URLScheme.cc \ URLScheme.h \ mem.cc \ MemBuf.cc \ wordlist.cc nodist_tests_testACLMaxUserIP_SOURCES= \ $(TESTSOURCES) tests_testACLMaxUserIP_LDADD= \ $(AUTH_ACL_LIBS) \ ident/libident.la \ acl/libacls.la \ eui/libeui.la \ acl/libstate.la \ $(AUTH_LIBS) \ acl/libapi.la \ anyp/libanyp.la \ base/libbase.la \ libsquid.la \ ip/libip.la \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(DISK_OS_LIBS) \ $(REGEXLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SSLLIB) \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testACLMaxUserIP_LDFLAGS = $(LIBADD_DL) tests_testACLMaxUserIP_DEPENDENCIES = \ $(SQUID_CPPUNIT_LA) ## a demonstration test that does nothing but shows the salient points ## involved in writing tests. tests_testBoilerplate_SOURCES = \ tests/testBoilerplate.cc \ tests/testMain.cc \ tests/testBoilerplate.h \ time.cc @@ -2482,40 +2494,41 @@ $(TESTSOURCES) \ SquidMath.cc \ SquidMath.h \ swap_log_op.cc tests_testStore_LDADD= \ $(AUTH_ACL_LIBS) \ ident/libident.la \ acl/libacls.la \ eui/libeui.la \ acl/libstate.la \ $(AUTH_LIBS) \ acl/libapi.la \ base/libbase.la \ libsquid.la \ ip/libip.la \ fs/libfs.la \ mgr/libmgr.la \ ipc/libipc.la \ anyp/libanyp.la \ + $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ $(REGEXLIB) \ $(SQUID_CPPUNIT_LIBS) \ $(SSLLIB) \ CommCalls.o \ DnsLookupDetails.o \ $(COMPAT_LIB) \ $(XTRA_LIBS) tests_testStore_LDFLAGS = $(LIBADD_DL) tests_testStore_DEPENDENCIES = \ $(SQUID_CPPUNIT_LA) ## string needs mem.cc. ## mem.cc needs ClientInfo.h ## libsquid pulls in SquidConfig and children. stub them. tests_testString_SOURCES = \ ClientInfo.h \ mem.cc \ === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2012-06-22 03:49:38 +0000 +++ src/cache_cf.cc 2012-06-29 16:36:58 +0000 @@ -152,46 +152,47 @@ static uint64_t parseTimeUnits(const char *unit, bool allowMsec); static void parseTimeLine(time_msec_t * tptr, const char *units, bool allowMsec); static void parse_u_short(unsigned short * var); static void parse_string(char **); static void default_all(void); static void defaults_if_none(void); static int parse_line(char *); static void parse_obsolete(const char *); static void parseBytesLine(size_t * bptr, const char *units); #if USE_SSL static void parseBytesOptionValue(size_t * bptr, const char *units, char const * value); #endif #if !USE_DNSHELPER static void parseBytesLineSigned(ssize_t * bptr, const char *units); #endif static size_t parseBytesUnits(const char *unit); static void free_all(void); void requirePathnameExists(const char *name, const char *path); static OBJH dump_config; #if USE_HTTP_VIOLATIONS -static void dump_http_header_access(StoreEntry * entry, const char *name, header_mangler header[]); -static void parse_http_header_access(header_mangler header[]); -static void free_http_header_access(header_mangler header[]); -static void dump_http_header_replace(StoreEntry * entry, const char *name, header_mangler header[]); -static void parse_http_header_replace(header_mangler * header); -static void free_http_header_replace(header_mangler * header); +static void free_HeaderManglers(HeaderManglers **pm); +static void dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers); +static void parse_http_header_access(HeaderManglers **manglers); +#define free_http_header_access free_HeaderManglers +static void dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers); +static void parse_http_header_replace(HeaderManglers **manglers); +#define free_http_header_replace free_HeaderManglers #endif static void parse_denyinfo(acl_deny_info_list ** var); static void dump_denyinfo(StoreEntry * entry, const char *name, acl_deny_info_list * var); static void free_denyinfo(acl_deny_info_list ** var); #if USE_WCCPv2 static void parse_IpAddress_list(Ip::Address_list **); static void dump_IpAddress_list(StoreEntry *, const char *, const Ip::Address_list *); static void free_IpAddress_list(Ip::Address_list **); #if CURRENTLY_UNUSED static int check_null_IpAddress_list(const Ip::Address_list *); #endif /* CURRENTLY_UNUSED */ #endif /* USE_WCCPv2 */ static void parsePortCfg(AnyP::PortCfg **, const char *protocol); #define parse_PortCfg(l) parsePortCfg((l), token) static void dump_PortCfg(StoreEntry *, const char *, const AnyP::PortCfg *); static void free_PortCfg(AnyP::PortCfg **); static void parse_b_size_t(size_t * var); @@ -793,41 +794,41 @@ debugs(22, 1, "WARNING: use of 'ignore-auth' in 'refresh_pattern' violates HTTP"); break; } } #endif #if !USE_HTTP_VIOLATIONS Config.onoff.via = 1; #else if (!Config.onoff.via) debugs(22, 1, "WARNING: HTTP requires the use of Via"); #endif // we enable runtime PURGE checks if there is at least one PURGE method ACL // TODO: replace with a dedicated "purge" ACL option? Config2.onoff.enable_purge = (ACLMethodData::ThePurgeCount > 0); - Config2.onoff.mangle_request_headers = httpReqHdrManglersConfigured(); + Config2.onoff.mangle_request_headers = (Config.request_header_access != NULL); if (geteuid() == 0) { if (NULL != Config.effectiveUser) { struct passwd *pwd = getpwnam(Config.effectiveUser); if (NULL == pwd) { /* * Andres Kroonmaa : * Some getpwnam() implementations (Solaris?) require * an available FD < 256 for opening a FILE* to the * passwd file. * DW: * This should be safe at startup, but might still fail * during reconfigure. */ fatalf("getpwnam failed to find userid for effective user '%s'", Config.effectiveUser); return; } @@ -1658,169 +1659,99 @@ parse_client_delay_pool_count(ClientDelayConfig * cfg) { cfg->parsePoolCount(); } static void parse_client_delay_pool_rates(ClientDelayConfig * cfg) { cfg->parsePoolRates(); } static void parse_client_delay_pool_access(ClientDelayConfig * cfg) { cfg->parsePoolAccess(LegacyParser); } #endif #if USE_HTTP_VIOLATIONS static void -dump_http_header_access(StoreEntry * entry, const char *name, header_mangler header[]) +dump_http_header_access(StoreEntry * entry, const char *name, const HeaderManglers *manglers) { - int i; - - for (i = 0; i < HDR_ENUM_END; i++) { - if (header[i].access_list != NULL) { - storeAppendPrintf(entry, "%s ", name); - dump_acl_access(entry, httpHeaderNameById(i), - header[i].access_list); - } - } + if (manglers) + manglers->dumpAccess(entry, name); } static void -parse_http_header_access(header_mangler header[]) +parse_http_header_access(HeaderManglers **pm) { - int id, i; char *t = NULL; if ((t = strtok(NULL, w_space)) == NULL) { debugs(3, 0, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(3, 0, "parse_http_header_access: missing header name."); return; } - /* Now lookup index of header. */ - id = httpHeaderIdByNameDef(t, strlen(t)); - - if (strcmp(t, "All") == 0) - id = HDR_ENUM_END; - else if (strcmp(t, "Other") == 0) - id = HDR_OTHER; - else if (id == -1) { - debugs(3, 0, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); - debugs(3, 0, "parse_http_header_access: unknown header name '" << t << "'"); - return; - } - - if (id != HDR_ENUM_END) { - parse_acl_access(&header[id].access_list); - } else { - char *next_string = t + strlen(t) - 1; - *next_string = 'A'; - *(next_string + 1) = ' '; - - for (i = 0; i < HDR_ENUM_END; i++) { - char *new_string = xstrdup(next_string); - strtok(new_string, w_space); - parse_acl_access(&header[i].access_list); - safe_free(new_string); - } - } + if (!*pm) + *pm = new HeaderManglers; + HeaderManglers *manglers = *pm; + header_mangler *mangler = manglers->track(t); + assert(mangler); + parse_acl_access(&mangler->access_list); } static void -free_http_header_access(header_mangler header[]) +free_HeaderManglers(HeaderManglers **pm) { - int i; - - for (i = 0; i < HDR_ENUM_END; i++) { - free_acl_access(&header[i].access_list); + // we delete the entire http_header_* mangler configuration at once + if (const HeaderManglers *manglers = *pm) { + delete manglers; + *pm = NULL; } } static void -dump_http_header_replace(StoreEntry * entry, const char *name, header_mangler - header[]) +dump_http_header_replace(StoreEntry * entry, const char *name, const HeaderManglers *manglers) { - int i; - - for (i = 0; i < HDR_ENUM_END; i++) { - if (NULL == header[i].replacement) - continue; - - storeAppendPrintf(entry, "%s %s %s\n", name, httpHeaderNameById(i), - header[i].replacement); - } + if (manglers) + manglers->dumpReplacement(entry, name); } static void -parse_http_header_replace(header_mangler header[]) +parse_http_header_replace(HeaderManglers **pm) { - int id, i; char *t = NULL; if ((t = strtok(NULL, w_space)) == NULL) { debugs(3, 0, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(3, 0, "parse_http_header_replace: missing header name."); return; } - /* Now lookup index of header. */ - id = httpHeaderIdByNameDef(t, strlen(t)); - - if (strcmp(t, "All") == 0) - id = HDR_ENUM_END; - else if (strcmp(t, "Other") == 0) - id = HDR_OTHER; - else if (id == -1) { - debugs(3, 0, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); - debugs(3, 0, "parse_http_header_replace: unknown header name " << t << "."); - - return; - } - - if (id != HDR_ENUM_END) { - if (header[id].replacement != NULL) - safe_free(header[id].replacement); - - header[id].replacement = xstrdup(t + strlen(t) + 1); - } else { - for (i = 0; i < HDR_ENUM_END; i++) { - if (header[i].replacement != NULL) - safe_free(header[i].replacement); - - header[i].replacement = xstrdup(t + strlen(t) + 1); - } - } -} - -static void -free_http_header_replace(header_mangler header[]) -{ - int i; + const char *value = t + strlen(t) + 1; - for (i = 0; i < HDR_ENUM_END; i++) { - if (header[i].replacement != NULL) - safe_free(header[i].replacement); - } + if (!*pm) + *pm = new HeaderManglers; + HeaderManglers *manglers = *pm; + manglers->setReplacement(t, value); } #endif static void dump_cachedir(StoreEntry * entry, const char *name, SquidConfig::_cacheSwap swap) { SwapDir *s; int i; assert (entry); for (i = 0; i < swap.n_configured; i++) { s = dynamic_cast(swap.swapDirs[i].getRaw()); if (!s) continue; storeAppendPrintf(entry, "%s %s %s", name, s->type(), s->path); s->dump(*entry); storeAppendPrintf(entry, "\n"); } } === modified file 'src/cf.data.pre' --- src/cf.data.pre 2012-05-06 01:29:22 +0000 +++ src/cf.data.pre 2012-06-29 16:36:58 +0000 @@ -4375,62 +4375,76 @@ DOC_END NAME: request_entities TYPE: onoff LOC: Config.onoff.request_entities DEFAULT: off DOC_START Squid defaults to deny GET and HEAD requests with request entities, as the meaning of such requests are undefined in the HTTP standard even if not explicitly forbidden. Set this directive to on if you have clients which insists on sending request entities in GET or HEAD requests. But be warned that there is server software (both proxies and web servers) which can fail to properly process this kind of request which may make you vulnerable to cache pollution attacks if enabled. DOC_END NAME: request_header_access IFDEF: USE_HTTP_VIOLATIONS -TYPE: http_header_access[] +TYPE: http_header_access LOC: Config.request_header_access DEFAULT: none DOC_START Usage: request_header_access header_name allow|deny [!]aclname ... WARNING: Doing this VIOLATES the HTTP standard. Enabling this feature could make you liable for problems which it causes. This option replaces the old 'anonymize_headers' and the older 'http_anonymizer' option with something that is much - more configurable. This new method creates a list of ACLs - for each header, allowing you very fine-tuned header - mangling. - - This option only applies to request headers, i.e., from the - client to the server. - - You can only specify known headers for the header name. - Other headers are reclassified as 'Other'. You can also - refer to all the headers with 'All'. + more configurable. A list of ACLs for each header name allows + removal of specific header fields under specific conditions. + + This option only applies to outgoing HTTP request headers (i.e., + headers sent by Squid to the next HTTP hop such as a cache peer + or an origin server). The option has no effect during cache hit + detection. The equivalent adaptation vectoring point in ICAP + terminology is post-cache REQMOD. + + The option is applied to individual outgoing request header + fields. For each request header field F, Squid uses the first + qualifying sets of request_header_access rules: + + 1. Rules with header_name equal to F's name. + 2. Rules with header_name 'Other', provided F's name is not + on the hard-coded list of commonly used HTTP header names. + 3. Rules with header_name 'All'. + + Within that qualifying rule set, rule ACLs are checked as usual. + If ACLs of an "allow" rule match, the header field is allowed to + go through as is. If ACLs of a "deny" rule match, the header is + removed and request_header_replace is then checked to identify + if the removed header has a replacement. If no rules within the + set have matching ACLs, the header field is left as is. For example, to achieve the same behavior as the old 'http_anonymizer standard' option, you should use: request_header_access From deny all request_header_access Referer deny all request_header_access Server deny all request_header_access User-Agent deny all request_header_access WWW-Authenticate deny all request_header_access Link deny all Or, to reproduce the old 'http_anonymizer paranoid' feature you should use: request_header_access Allow allow all request_header_access Authorization allow all request_header_access WWW-Authenticate allow all request_header_access Proxy-Authorization allow all request_header_access Proxy-Authenticate allow all request_header_access Cache-Control allow all @@ -4447,65 +4461,56 @@ request_header_access Accept allow all request_header_access Accept-Charset allow all request_header_access Accept-Encoding allow all request_header_access Accept-Language allow all request_header_access Content-Language allow all request_header_access Mime-Version allow all request_header_access Retry-After allow all request_header_access Title allow all request_header_access Connection allow all request_header_access All deny all although many of those are HTTP reply headers, and so should be controlled with the reply_header_access directive. By default, all headers are allowed (no anonymizing is performed). DOC_END NAME: reply_header_access IFDEF: USE_HTTP_VIOLATIONS -TYPE: http_header_access[] +TYPE: http_header_access LOC: Config.reply_header_access DEFAULT: none DOC_START Usage: reply_header_access header_name allow|deny [!]aclname ... WARNING: Doing this VIOLATES the HTTP standard. Enabling this feature could make you liable for problems which it causes. This option only applies to reply headers, i.e., from the server to the client. This is the same as request_header_access, but in the other - direction. - - This option replaces the old 'anonymize_headers' and the - older 'http_anonymizer' option with something that is much - more configurable. This new method creates a list of ACLs - for each header, allowing you very fine-tuned header - mangling. - - You can only specify known headers for the header name. - Other headers are reclassified as 'Other'. You can also - refer to all the headers with 'All'. + direction. Please see request_header_access for detailed + documentation. For example, to achieve the same behavior as the old 'http_anonymizer standard' option, you should use: reply_header_access From deny all reply_header_access Referer deny all reply_header_access Server deny all reply_header_access User-Agent deny all reply_header_access WWW-Authenticate deny all reply_header_access Link deny all Or, to reproduce the old 'http_anonymizer paranoid' feature you should use: reply_header_access Allow allow all reply_header_access Authorization allow all reply_header_access WWW-Authenticate allow all reply_header_access Proxy-Authorization allow all reply_header_access Proxy-Authenticate allow all reply_header_access Cache-Control allow all @@ -4522,60 +4527,60 @@ reply_header_access Accept allow all reply_header_access Accept-Charset allow all reply_header_access Accept-Encoding allow all reply_header_access Accept-Language allow all reply_header_access Content-Language allow all reply_header_access Mime-Version allow all reply_header_access Retry-After allow all reply_header_access Title allow all reply_header_access Connection allow all reply_header_access All deny all although the HTTP request headers won't be usefully controlled by this directive -- see request_header_access for details. By default, all headers are allowed (no anonymizing is performed). DOC_END NAME: request_header_replace header_replace IFDEF: USE_HTTP_VIOLATIONS -TYPE: http_header_replace[] +TYPE: http_header_replace LOC: Config.request_header_access DEFAULT: none DOC_START Usage: request_header_replace header_name message Example: request_header_replace User-Agent Nutscrape/1.0 (CP/M; 8-bit) This option allows you to change the contents of headers denied with request_header_access above, by replacing them with some fixed string. This replaces the old fake_user_agent option. This only applies to request headers, not reply headers. By default, headers are removed if denied. DOC_END NAME: reply_header_replace IFDEF: USE_HTTP_VIOLATIONS -TYPE: http_header_replace[] +TYPE: http_header_replace LOC: Config.reply_header_access DEFAULT: none DOC_START Usage: reply_header_replace header_name message Example: reply_header_replace Server Foo/1.0 This option allows you to change the contents of headers denied with reply_header_access above, by replacing them with some fixed string. This only applies to reply headers, not request headers. By default, headers are removed if denied. DOC_END NAME: relaxed_header_parser COMMENT: on|off|warn TYPE: tristate LOC: Config.onoff.relaxed_header_parser DEFAULT: on === modified file 'src/protos.h' --- src/protos.h 2012-04-25 05:29:20 +0000 +++ src/protos.h 2012-06-29 16:36:58 +0000 @@ -217,41 +217,40 @@ SQUIDCEXTERN const char *httpHeaderNameById(int id); SQUIDCEXTERN int httpHeaderHasConnDir(const HttpHeader * hdr, const char *directive); SQUIDCEXTERN void strListAdd(String * str, const char *item, char del); SQUIDCEXTERN int strListIsMember(const String * str, const char *item, char del); SQUIDCEXTERN int strListIsSubstr(const String * list, const char *s, char del); SQUIDCEXTERN int strListGetItem(const String * str, char del, const char **item, int *ilen, const char **pos); SQUIDCEXTERN const char *getStringPrefix(const char *str, const char *end); SQUIDCEXTERN int httpHeaderParseInt(const char *start, int *val); SQUIDCEXTERN int httpHeaderParseOffset(const char *start, int64_t * off); SQUIDCEXTERN void httpHeaderPutStrf(HttpHeader * hdr, http_hdr_type id, const char *fmt,...) PRINTF_FORMAT_ARG3; /* Http Header */ SQUIDCEXTERN void httpHeaderInitModule(void); SQUIDCEXTERN void httpHeaderCleanModule(void); /* store report about current header usage and other stats */ void httpHeaderStoreReport(StoreEntry * e); SQUIDCEXTERN void httpHdrMangleList(HttpHeader *, HttpRequest *, int req_or_rep); -SQUIDCEXTERN int httpReqHdrManglersConfigured(); #if SQUID_SNMP SQUIDCEXTERN PF snmpHandleUdp; SQUIDCEXTERN void snmpInit(void); SQUIDCEXTERN void snmpOpenPorts(void); SQUIDCEXTERN void snmpClosePorts(void); SQUIDCEXTERN const char * snmpDebugOid(oid * Name, snint Len, MemBuf &outbuf); SQUIDCEXTERN void addr2oid(Ip::Address &addr, oid *Dest); SQUIDCEXTERN void oid2addr(oid *Dest, Ip::Address &addr, u_int code); SQUIDCEXTERN Ip::Address *client_entry(Ip::Address *current); extern variable_list *snmp_basicFn(variable_list *, snint *); extern variable_list *snmp_confFn(variable_list *, snint *); extern variable_list *snmp_sysFn(variable_list *, snint *); extern variable_list *snmp_prfSysFn(variable_list *, snint *); extern variable_list *snmp_prfProtoFn(variable_list *, snint *); extern variable_list *snmp_prfPeerFn(variable_list *, snint *); extern variable_list *snmp_netIpFn(variable_list *, snint *); extern variable_list *snmp_netFqdnFn(variable_list *, snint *); === modified file 'src/structs.h' --- src/structs.h 2012-04-26 01:04:17 +0000 +++ src/structs.h 2012-06-29 16:36:58 +0000 @@ -19,70 +19,63 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. * */ #ifndef SQUID_STRUCTS_H #define SQUID_STRUCTS_H #include "RefCount.h" #include "cbdata.h" #include "dlink.h" #include "err_type.h" /* needed for the global config */ #include "HttpHeader.h" +#include "HttpHeaderTools.h" /* for ICP_END */ #include "icp_opcode.h" #if USE_SSL #include #endif #define PEER_MULTICAST_SIBLINGS 1 struct acl_name_list { char name[ACL_NAME_SZ]; acl_name_list *next; }; struct acl_deny_info_list { err_type err_page_id; char *err_page_name; acl_name_list *acl_list; acl_deny_info_list *next; }; - -class acl_access; - -struct _header_mangler { - acl_access *access_list; - char *replacement; -}; - class ACLChecklist; #if SQUID_SNMP struct _snmp_request_t { u_char *buf; u_char *outbuf; int len; int sock; long reqid; int outlen; Ip::Address from; struct snmp_pdu *PDU; ACLChecklist *acl_checklist; u_char *community; struct snmp_session session; }; @@ -560,44 +553,44 @@ struct { struct { int average; int min_poll; } dns, udp, tcp; } comm_incoming; int max_open_disk_fds; int uri_whitespace; acl_size_t *rangeOffsetLimit; #if MULTICAST_MISS_STREAM struct { Ip::Address addr; int ttl; unsigned short port; char *encode_key; } mcast_miss; #endif - /* one access list per header type we know of */ - header_mangler request_header_access[HDR_ENUM_END]; - /* one access list per header type we know of */ - header_mangler reply_header_access[HDR_ENUM_END]; + /// request_header_access and request_header_replace + HeaderManglers *request_header_access; + /// reply_header_access and reply_header_replace + HeaderManglers *reply_header_access; char *coredump_dir; char *chroot_dir; #if USE_CACHE_DIGESTS struct { int bits_per_entry; time_t rebuild_period; time_t rewrite_period; size_t swapout_chunk_size; int rebuild_chunk_percentage; } digest; #endif #if USE_SSL struct { int unclean_shutdown; char *ssl_engine; } SSL; #endif