HeaderMangling.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 66 HTTP Header Tools */
10 
11 #include "squid.h"
12 #include "acl/FilledChecklist.h"
13 #include "acl/Gadgets.h"
14 #include "base/EnumIterator.h"
15 #include "fde.h"
16 #include "globals.h"
17 #include "http/RegisteredHeaders.h"
18 #include "HttpHeader.h"
19 #include "HttpRequest.h"
20 #include "MemBuf.h"
21 #include "sbuf/Stream.h"
22 #include "sbuf/StringConvert.h"
23 #include "SquidConfig.h"
24 #include "Store.h"
25 
26 static void httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd);
27 
36 static int
38 {
39  int retval;
40 
41  assert(e);
42 
43  const headerMangler *hm = hms->find(*e);
44 
45  /* mangler or checklist went away. default allow */
46  if (!hm || !hm->access_list) {
47  debugs(66, 7, "couldn't find mangler or access list. Allowing");
48  return 1;
49  }
50 
51  ACLFilledChecklist checklist(hm->access_list, request);
52  checklist.updateAle(al);
53 
54  // XXX: The two "It was denied" clauses below mishandle cases with no
55  // matching rules, violating the "If no rules within the set have matching
56  // ACLs, the header field is left as is" promise in squid.conf.
57  // TODO: Use Acl::Answer::implicit. See HttpStateData::forwardUpgrade().
58  if (checklist.fastCheck().allowed()) {
59  /* aclCheckFast returns true for allow. */
60  debugs(66, 7, "checklist for mangler is positive. Mangle");
61  retval = 1;
62  } else if (nullptr == hm->replacement) {
63  /* It was denied, and we don't have any replacement */
64  debugs(66, 7, "checklist denied, we have no replacement. Pass");
65  // XXX: We said "Pass", but the caller will delete on zero retval.
66  retval = 0;
67  } else {
68  /* It was denied, but we have a replacement. Replace the
69  * header on the fly, and return that the new header
70  * is allowed.
71  */
72  debugs(66, 7, "checklist denied but we have replacement. Replace");
73  e->value = hm->replacement;
74  retval = 1;
75  }
76 
77  return retval;
78 }
79 
81 void
83 {
84  HttpHeaderEntry *e;
86 
87  /* check with anonymizer tables */
88  HeaderManglers *hms = nullptr;
89  HeaderWithAclList *headersAdd = nullptr;
90 
91  switch (req_or_rep) {
92  case ROR_REQUEST:
94  headersAdd = Config.request_header_add;
95  break;
96  case ROR_REPLY:
98  headersAdd = Config.reply_header_add;
99  break;
100  }
101 
102  if (hms) {
103  int headers_deleted = 0;
104  while ((e = l->getEntry(&p))) {
105  if (httpHdrMangle(e, request, hms, al) == 0)
106  l->delAt(p, headers_deleted);
107  }
108 
109  if (headers_deleted)
110  l->refreshMask();
111  }
112 
113  if (headersAdd && !headersAdd->empty()) {
114  httpHdrAdd(l, request, al, *headersAdd);
115  }
116 }
117 
118 static
120 {
123 }
124 
125 static
126 void header_mangler_dump_access(StoreEntry * entry, const char *option,
127  const headerMangler &m, const char *name)
128 {
129  if (m.access_list != nullptr) {
130  storeAppendPrintf(entry, "%s ", option);
131  dump_acl_access(entry, name, m.access_list);
132  }
133 }
134 
135 static
136 void header_mangler_dump_replacement(StoreEntry * entry, const char *option,
137  const headerMangler &m, const char *name)
138 {
139  if (m.replacement)
140  storeAppendPrintf(entry, "%s %s %s\n", option, name, m.replacement);
141 }
142 
144 {
145  memset(known, 0, sizeof(known));
146  memset(&all, 0, sizeof(all));
147 }
148 
150 {
151  for (auto i : WholeEnum<Http::HdrType>())
153 
154  for (auto i : custom)
155  header_mangler_clean(i.second);
156 
158 }
159 
160 void
161 HeaderManglers::dumpAccess(StoreEntry * entry, const char *name) const
162 {
163  for (auto id : WholeEnum<Http::HdrType>())
165 
166  for (auto i : custom)
167  header_mangler_dump_access(entry, name, i.second, i.first.c_str());
168 
169  header_mangler_dump_access(entry, name, all, "All");
170 }
171 
172 void
174 {
175  for (auto id : WholeEnum<Http::HdrType>()) {
177  }
178 
179  for (auto i: custom) {
180  header_mangler_dump_replacement(entry, name, i.second, i.first.c_str());
181  }
182 
183  header_mangler_dump_replacement(entry, name, all, "All");
184 }
185 
188 {
189  if (strcmp(name, "All") == 0)
190  return &all;
191 
193 
194  if (id != Http::HdrType::BAD_HDR)
195  return &known[id];
196 
197  if (strcmp(name, "Other") == 0)
198  return &known[Http::HdrType::OTHER];
199 
200  return &custom[name];
201 }
202 
203 void
204 HeaderManglers::setReplacement(const char *name, const char *value)
205 {
206  // for backword compatibility, we allow replacements to be configured
207  // for headers w/o access rules, but such replacements are ignored
208  headerMangler *m = track(name);
209 
210  safe_free(m->replacement); // overwrite old value if any
211  m->replacement = xstrdup(value);
212 }
213 
214 const headerMangler *
216 {
217  // a known header with a configured ACL list
219  known[e.id].access_list)
220  return &known[e.id];
221 
222  // a custom header
223  if (e.id == Http::HdrType::OTHER) {
224  // does it have an ACL list configured?
225  // Optimize: use a name type that we do not need to convert to here
226  SBuf tmp(e.name); // XXX: performance regression. c_str() reallocates
227  const ManglersByName::const_iterator i = custom.find(tmp.c_str());
228  if (i != custom.end())
229  return &i->second;
230  }
231 
232  // Next-to-last resort: "Other" rules match any custom header
234  return &known[Http::HdrType::OTHER];
235 
236  // Last resort: "All" rules match any header
237  if (all.access_list)
238  return &all;
239 
240  return nullptr;
241 }
242 
243 void
245 {
246  ACLFilledChecklist checklist(nullptr, request);
247  checklist.updateAle(al);
248 
249  for (HeaderWithAclList::const_iterator hwa = headersAdd.begin(); hwa != headersAdd.end(); ++hwa) {
250  if (!hwa->aclList || checklist.fastCheck(hwa->aclList).allowed()) {
251  const char *fieldValue = nullptr;
252  MemBuf mb;
253  if (hwa->quoted) {
254  if (al != nullptr) {
255  mb.init();
256  hwa->valueFormat->assemble(mb, al, 0);
257  fieldValue = mb.content();
258  }
259  } else {
260  fieldValue = hwa->fieldValue.c_str();
261  }
262 
263  if (!fieldValue || fieldValue[0] == '\0')
264  fieldValue = "-";
265 
266  HttpHeaderEntry *e = new HttpHeaderEntry(hwa->fieldId, SBuf(hwa->fieldName), fieldValue);
267  heads->addEntry(e);
268  }
269  }
270 }
271 
HeaderWithAclList * reply_header_add
reply_header_add access list
Definition: SquidConfig.h:467
acl_access * access_list
void refreshMask()
Definition: HttpHeader.cc:855
void httpHdrMangleList(HttpHeader *l, HttpRequest *request, const AccessLogEntryPointer &al, req_or_rep_t req_or_rep)
HeaderWithAclList * request_header_add
request_header_add access list
Definition: SquidConfig.h:465
std::list< HeaderWithAcl > HeaderWithAclList
ssize_t HttpHeaderPos
Definition: HttpHeader.h:45
#define HttpHeaderInitPos
Definition: HttpHeader.h:48
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:855
ManglersByName custom
one mangler for each custom header
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:93
Definition: SBuf.h:93
#define xstrdup
const headerMangler * find(const HttpHeaderEntry &e) const
returns a header mangler for field e or nil if none was specified
headerMangler known[static_cast< int >(Http::HdrType::enumEnd_)]
one mangler for each known header
headerMangler * track(const char *name)
returns a mangler for the named header (known or custom)
bool any_HdrType_enum_value(const Http::HdrType id)
match any known header type, including OTHER and BAD
headerMangler all
configured if some mangling ACL applies to all header names
static void header_mangler_clean(headerMangler &m)
@ ROR_REPLY
HeaderManglers * reply_header_access
reply_header_access and reply_header_replace
Definition: SquidConfig.h:463
void dump_acl_access(StoreEntry *entry, const char *name, acl_access *head)
Definition: cache_cf.cc:1510
const Acl::Answer & fastCheck()
Definition: Checklist.cc:298
Definition: MemBuf.h:23
const char *const name
waiting event name, for debugging
A collection of headerMangler objects for a given message kind.
static int httpHdrMangle(HttpHeaderEntry *e, HttpRequest *request, HeaderManglers *hms, const AccessLogEntryPointer &al)
Http::HdrType id
Definition: HttpHeader.h:66
#define safe_free(x)
Definition: xalloc.h:73
void addEntry(HttpHeaderEntry *e)
Definition: HttpHeader.cc:869
#define assert(EX)
Definition: assert.h:17
const HeaderTableRecord & lookup(const char *buf, const std::size_t len) const
look record type up by name (C-string and length)
static void header_mangler_dump_access(StoreEntry *entry, const char *option, const headerMangler &m, const char *name)
const char * c_str()
Definition: SBuf.cc:516
char * replacement
void dumpReplacement(StoreEntry *entry, const char *optionName) const
report the *_header_replace part of the configuration
const HeaderLookupTable_t HeaderLookupTable
void aclDestroyAccessList(acl_access **list)
Definition: Gadgets.cc:223
@ ROR_REQUEST
void dumpAccess(StoreEntry *entry, const char *optionName) const
report the *_header_access part of the configuration
bool allowed() const
Definition: Acl.h:82
req_or_rep_t
char * content()
start of the added data
Definition: MemBuf.h:41
void delAt(HttpHeaderPos pos, int &headers_deleted)
Definition: HttpHeader.cc:827
static void header_mangler_dump_replacement(StoreEntry *entry, const char *option, const headerMangler &m, const char *name)
HttpHeaderEntry * getEntry(HttpHeaderPos *pos) const
Definition: HttpHeader.cc:716
HeaderManglers * request_header_access
request_header_access and request_header_replace
Definition: SquidConfig.h:461
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Stream.h:192
void updateAle(const AccessLogEntry::Pointer &)
static void httpHdrAdd(HttpHeader *heads, HttpRequest *request, const AccessLogEntryPointer &al, HeaderWithAclList &headersAdd)
void setReplacement(const char *name, const char *replacementValue)
updates mangler for the named header with a replacement value
class SquidConfig Config
Definition: SquidConfig.cc:12

 

Introduction

Documentation

Support

Miscellaneous