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 &noteKey)
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, &noteValue->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