1 /*
2 * Copyright (C) 1996-2021 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 #include "squid.h"
10 #include "AccessLogEntry.h"
11 #include "acl/FilledChecklist.h"
12 #include "acl/Gadgets.h"
13 #include "client_side.h"
14 #include "ConfigParser.h"
15 #include "globals.h"
16 #include "http/Stream.h"
17 #include "HttpReply.h"
18 #include "HttpRequest.h"
19 #include "SquidConfig.h"
20 #include "Store.h"
21 #include "StrList.h"
22
23 #include <algorithm>
24 #include <string>
25
~Value()26 Note::Value::~Value()
27 {
28 aclDestroyAclList(&aclList);
29 }
30
31 Note::Value::Pointer
addValue(const String & value)32 Note::addValue(const String &value)
33 {
34 Value::Pointer v = new Value(value);
35 values.push_back(v);
36 return v;
37 }
38
39 const char *
match(HttpRequest * request,HttpReply * reply,const AccessLogEntry::Pointer & al)40 Note::match(HttpRequest *request, HttpReply *reply, const AccessLogEntry::Pointer &al)
41 {
42
43 typedef Values::iterator VLI;
44 ACLFilledChecklist ch(NULL, request, NULL);
45 ch.al = al;
46 ch.reply = reply;
47 ch.syncAle(request, nullptr);
48 if (reply)
49 HTTPMSGLOCK(ch.reply);
50
51 for (VLI i = values.begin(); i != values.end(); ++i ) {
52 const auto ret= ch.fastCheck((*i)->aclList);
53 debugs(93, 5, HERE << "Check for header name: " << key << ": " << (*i)->value
54 <<", HttpRequest: " << request << " HttpReply: " << reply << " matched: " << ret);
55 if (ret.allowed()) {
56 if (al != NULL && (*i)->valueFormat != NULL) {
57 static MemBuf mb;
58 mb.reset();
59 (*i)->valueFormat->assemble(mb, al, 0);
60 return mb.content();
61 } else
62 return (*i)->value.termedBuf();
63 }
64 }
65 return NULL;
66 }
67
68 Note::Pointer
add(const String & noteKey)69 Notes::add(const String ¬eKey)
70 {
71 typedef Notes::NotesList::iterator AMLI;
72 for (AMLI i = notes.begin(); i != notes.end(); ++i) {
73 if ((*i)->key == noteKey)
74 return (*i);
75 }
76
77 Note::Pointer note = new Note(noteKey);
78 notes.push_back(note);
79 return note;
80 }
81
82 Note::Pointer
parse(ConfigParser & parser)83 Notes::parse(ConfigParser &parser)
84 {
85 String key = ConfigParser::NextToken();
86 ConfigParser::EnableMacros();
87 String value = ConfigParser::NextQuotedToken();
88 ConfigParser::DisableMacros();
89 bool valueWasQuoted = ConfigParser::LastTokenWasQuoted();
90 Note::Pointer note = add(key);
91 Note::Value::Pointer noteValue = note->addValue(value);
92
93 String label(key);
94 label.append('=');
95 label.append(value);
96 aclParseAclList(parser, ¬eValue->aclList, label.termedBuf());
97 if (formattedValues && valueWasQuoted) {
98 noteValue->valueFormat = new Format::Format(descr ? descr : "Notes");
99 noteValue->valueFormat->parse(value.termedBuf());
100 }
101 if (blacklisted) {
102 for (int i = 0; blacklisted[i] != NULL; ++i) {
103 if (note->key.caseCmp(blacklisted[i]) == 0) {
104 fatalf("%s:%d: meta key \"%s\" is a reserved %s name",
105 cfg_filename, config_lineno, note->key.termedBuf(),
106 descr ? descr : "");
107 }
108 }
109 }
110
111 return note;
112 }
113
114 void
dump(StoreEntry * entry,const char * key)115 Notes::dump(StoreEntry *entry, const char *key)
116 {
117 typedef Notes::NotesList::iterator AMLI;
118 for (AMLI m = notes.begin(); m != notes.end(); ++m) {
119 typedef Note::Values::iterator VLI;
120 for (VLI v =(*m)->values.begin(); v != (*m)->values.end(); ++v ) {
121 storeAppendPrintf(entry, "%s " SQUIDSTRINGPH " %s",
122 key, SQUIDSTRINGPRINT((*m)->key), ConfigParser::QuoteString((*v)->value));
123 dump_acl_list(entry, (*v)->aclList);
124 storeAppendPrintf(entry, "\n");
125 }
126 }
127 }
128
129 void
clean()130 Notes::clean()
131 {
132 notes.clear();
133 }
134
~NotePairs()135 NotePairs::~NotePairs()
136 {
137 while (!entries.empty()) {
138 delete entries.back();
139 entries.pop_back();
140 }
141 }
142
143 const char *
find(const char * noteKey,const char * sep) const144 NotePairs::find(const char *noteKey, const char *sep) const
145 {
146 static String value;
147 value.clean();
148 for (std::vector<NotePairs::Entry *>::const_iterator i = entries.begin(); i != entries.end(); ++i) {
149 if ((*i)->name.cmp(noteKey) == 0) {
150 if (value.size())
151 value.append(sep);
152 value.append((*i)->value);
153 }
154 }
155 return value.size() ? value.termedBuf() : NULL;
156 }
157
158 const char *
toString(const char * sep) const159 NotePairs::toString(const char *sep) const
160 {
161 static String value;
162 value.clean();
163 for (std::vector<NotePairs::Entry *>::const_iterator i = entries.begin(); i != entries.end(); ++i) {
164 value.append((*i)->name);
165 value.append(": ");
166 value.append((*i)->value);
167 value.append(sep);
168 }
169 return value.size() ? value.termedBuf() : NULL;
170 }
171
172 const char *
findFirst(const char * noteKey) const173 NotePairs::findFirst(const char *noteKey) const
174 {
175 for (std::vector<NotePairs::Entry *>::const_iterator i = entries.begin(); i != entries.end(); ++i) {
176 if ((*i)->name.cmp(noteKey) == 0)
177 return (*i)->value.termedBuf();
178 }
179 return NULL;
180 }
181
182 void
add(const char * key,const char * note)183 NotePairs::add(const char *key, const char *note)
184 {
185 entries.push_back(new NotePairs::Entry(key, note));
186 }
187
188 void
remove(const char * key)189 NotePairs::remove(const char *key)
190 {
191 std::vector<NotePairs::Entry *>::iterator i = entries.begin();
192 while (i != entries.end()) {
193 if ((*i)->name.cmp(key) == 0) {
194 delete *i;
195 i = entries.erase(i);
196 } else {
197 ++i;
198 }
199 }
200 }
201
202 void
addStrList(const char * key,const char * values)203 NotePairs::addStrList(const char *key, const char *values)
204 {
205 String strValues(values);
206 const char *item;
207 const char *pos = NULL;
208 int ilen = 0;
209 while (strListGetItem(&strValues, ',', &item, &ilen, &pos)) {
210 String v;
211 v.append(item, ilen);
212 entries.push_back(new NotePairs::Entry(key, v.termedBuf()));
213 }
214 }
215
216 bool
hasPair(const char * key,const char * value) const217 NotePairs::hasPair(const char *key, const char *value) const
218 {
219 for (std::vector<NotePairs::Entry *>::const_iterator i = entries.begin(); i != entries.end(); ++i) {
220 if ((*i)->name.cmp(key) == 0 && (*i)->value.cmp(value) == 0)
221 return true;
222 }
223 return false;
224 }
225
226 void
append(const NotePairs * src)227 NotePairs::append(const NotePairs *src)
228 {
229 for (std::vector<NotePairs::Entry *>::const_iterator i = src->entries.begin(); i != src->entries.end(); ++i) {
230 entries.push_back(new NotePairs::Entry((*i)->name.termedBuf(), (*i)->value.termedBuf()));
231 }
232 }
233
234 void
appendNewOnly(const NotePairs * src)235 NotePairs::appendNewOnly(const NotePairs *src)
236 {
237 for (std::vector<NotePairs::Entry *>::const_iterator i = src->entries.begin(); i != src->entries.end(); ++i) {
238 if (!hasPair((*i)->name.termedBuf(), (*i)->value.termedBuf()))
239 entries.push_back(new NotePairs::Entry((*i)->name.termedBuf(), (*i)->value.termedBuf()));
240 }
241 }
242
243 void
replaceOrAdd(const NotePairs * src)244 NotePairs::replaceOrAdd(const NotePairs *src)
245 {
246 for (std::vector<NotePairs::Entry *>::const_iterator i = src->entries.begin(); i != src->entries.end(); ++i) {
247 remove((*i)->name.termedBuf());
248 }
249 append(src);
250 }
251
252 NotePairs &
SyncNotes(AccessLogEntry & ale,HttpRequest & request)253 SyncNotes(AccessLogEntry &ale, HttpRequest &request)
254 {
255 // XXX: auth code only has access to HttpRequest being authenticated
256 // so we must handle the case where HttpRequest is set without ALE being set.
257
258 if (!ale.notes) {
259 if (!request.notes)
260 request.notes = new NotePairs;
261 ale.notes = request.notes;
262 } else {
263 assert(ale.notes == request.notes);
264 }
265 return *ale.notes;
266 }
267
268 void
UpdateRequestNotes(ConnStateData * csd,HttpRequest & request,NotePairs const & helperNotes)269 UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes)
270 {
271 // Tag client connection if the helper responded with clt_conn_tag=tag.
272 if (const char *connTag = helperNotes.findFirst("clt_conn_tag")) {
273 if (csd)
274 csd->connectionTag(connTag);
275 }
276 if (!request.notes)
277 request.notes = new NotePairs;
278 request.notes->replaceOrAdd(&helperNotes);
279 }
280
281