]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/AccessCheck.cc
Renamed squid.h to squid-old.h and config.h to squid.h
[thirdparty/squid.git] / src / adaptation / AccessCheck.cc
1 #include "squid-old.h"
2 #include "structs.h"
3
4 #include "ConfigParser.h"
5 #include "HttpRequest.h"
6 #include "HttpReply.h"
7 #include "acl/FilledChecklist.h"
8 #include "adaptation/Initiator.h"
9 #include "adaptation/Service.h"
10 #include "adaptation/ServiceGroups.h"
11 #include "adaptation/AccessRule.h"
12 #include "adaptation/Config.h"
13 #include "adaptation/AccessCheck.h"
14 #include "base/TextException.h"
15
16 /** \cond AUTODOCS-IGNORE */
17 cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
18 /** \endcond */
19
20 bool
21 Adaptation::AccessCheck::Start(Method method, VectPoint vp,
22 HttpRequest *req, HttpReply *rep, Adaptation::Initiator *initiator)
23 {
24
25 if (Config::Enabled) {
26 // the new check will call the callback and delete self, eventually
27 AsyncJob::Start(new AccessCheck( // we do not store so not a CbcPointer
28 ServiceFilter(method, vp, req, rep), initiator));
29 return true;
30 }
31
32 debugs(83, 3, HERE << "adaptation off, skipping");
33 return false;
34 }
35
36 Adaptation::AccessCheck::AccessCheck(const ServiceFilter &aFilter,
37 Adaptation::Initiator *initiator):
38 AsyncJob("AccessCheck"), filter(aFilter),
39 theInitiator(initiator),
40 acl_checklist(NULL)
41 {
42 #if ICAP_CLIENT
43 Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
44 if (h != NULL)
45 h->start("ACL");
46 #endif
47
48 debugs(93, 5, HERE << "AccessCheck constructed for " <<
49 methodStr(filter.method) << " " << vectPointStr(filter.point));
50 }
51
52 Adaptation::AccessCheck::~AccessCheck()
53 {
54 #if ICAP_CLIENT
55 Adaptation::Icap::History::Pointer h = filter.request->icapHistory();
56 if (h != NULL)
57 h->stop("ACL");
58 #endif
59 }
60
61 void
62 Adaptation::AccessCheck::start()
63 {
64 AsyncJob::start();
65
66 if (!usedDynamicRules())
67 check();
68 }
69
70 /// returns true if previous services configured dynamic chaining "rules"
71 bool
72 Adaptation::AccessCheck::usedDynamicRules()
73 {
74 Adaptation::History::Pointer ah = filter.request->adaptHistory();
75 if (!ah)
76 return false; // dynamic rules not enabled or not triggered
77
78 DynamicGroupCfg services;
79 if (!ah->extractFutureServices(services)) { // clears history
80 debugs(85,9, HERE << "no service-proposed rules stored");
81 return false; // earlier service did not plan for the future
82 }
83
84 debugs(85,3, HERE << "using stored service-proposed rules: " << services);
85
86 ServiceGroupPointer g = new DynamicServiceChain(services, filter);
87 callBack(g);
88 Must(done());
89 return true;
90 }
91
92 /// Walk the access rules list to find rules with applicable service groups
93 void
94 Adaptation::AccessCheck::check()
95 {
96 debugs(93, 4, HERE << "start checking");
97
98 typedef AccessRules::iterator ARI;
99 for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
100 AccessRule *r = *i;
101 if (isCandidate(*r)) {
102 debugs(93, 5, HERE << "check: rule '" << r->id << "' is a candidate");
103 candidates += r->id;
104 }
105 }
106
107 checkCandidates();
108 }
109
110 // XXX: Here and everywhere we call FindRule(topCandidate()):
111 // Once we identified the candidate, we should not just ignore it
112 // if reconfigure changes rules. We should either lock the rule to
113 // prevent reconfigure from stealing it or restart the check with
114 // new rules. Throwing an exception may also be appropriate.
115 void
116 Adaptation::AccessCheck::checkCandidates()
117 {
118 debugs(93, 4, HERE << "has " << candidates.size() << " rules");
119
120 while (!candidates.empty()) {
121 if (AccessRule *r = FindRule(topCandidate())) {
122 /* BUG 2526: what to do when r->acl is empty?? */
123 // XXX: we do not have access to conn->rfc931 here.
124 acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str);
125 acl_checklist->reply = filter.reply ? HTTPMSGLOCK(filter.reply) : NULL;
126 acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
127 return;
128 }
129
130 candidates.shift(); // the rule apparently went away (reconfigure)
131 }
132
133 debugs(93, 4, HERE << "NO candidates left");
134 callBack(NULL);
135 Must(done());
136 }
137
138 void
139 Adaptation::AccessCheck::AccessCheckCallbackWrapper(allow_t answer, void *data)
140 {
141 debugs(93, 8, HERE << "callback answer=" << answer);
142 AccessCheck *ac = (AccessCheck*)data;
143
144 /** \todo AYJ 2008-06-12: If answer == ACCESS_AUTH_REQUIRED
145 * we should be kicking off an authentication before continuing
146 * with this request. see bug 2400 for details.
147 */
148
149 // convert to async call to get async call protections and features
150 typedef UnaryMemFunT<AccessCheck, allow_t> MyDialer;
151 AsyncCall::Pointer call =
152 asyncCall(93,7, "Adaptation::AccessCheck::noteAnswer",
153 MyDialer(ac, &Adaptation::AccessCheck::noteAnswer, answer));
154 ScheduleCallHere(call);
155
156 }
157
158 /// process the results of the ACL check
159 void
160 Adaptation::AccessCheck::noteAnswer(allow_t answer)
161 {
162 Must(!candidates.empty()); // the candidate we were checking must be there
163 debugs(93,5, HERE << topCandidate() << " answer=" << answer);
164
165 if (answer == ACCESS_ALLOWED) { // the rule matched
166 ServiceGroupPointer g = topGroup();
167 if (g != NULL) { // the corresponding group found
168 callBack(g);
169 Must(done());
170 return;
171 }
172 }
173
174 // no match or the group disappeared during reconfiguration
175 candidates.shift();
176 checkCandidates();
177 }
178
179 /// call back with a possibly nil group; the job ends here because all failures
180 /// at this point are fatal to the access check process
181 void
182 Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g)
183 {
184 debugs(93,3, HERE << g);
185 CallJobHere1(93, 5, theInitiator, Adaptation::Initiator,
186 noteAdaptationAclCheckDone, g);
187 mustStop("done"); // called back or will never be able to call back
188 }
189
190 Adaptation::ServiceGroupPointer
191 Adaptation::AccessCheck::topGroup() const
192 {
193 ServiceGroupPointer g;
194 if (candidates.size()) {
195 if (AccessRule *r = FindRule(topCandidate())) {
196 g = FindGroup(r->groupId);
197 debugs(93,5, HERE << "top group for " << r->id << " is " << g);
198 } else {
199 debugs(93,5, HERE << "no rule for " << topCandidate());
200 }
201 } else {
202 debugs(93,5, HERE << "no candidates"); // should not happen
203 }
204
205 return g;
206 }
207
208 /** Returns true iff the rule's service group will be used after ACL matches.
209 Used to detect rules worth ACl-checking. */
210 bool
211 Adaptation::AccessCheck::isCandidate(AccessRule &r)
212 {
213 debugs(93,7,HERE << "checking candidacy of " << r.id << ", group " <<
214 r.groupId);
215
216 ServiceGroupPointer g = FindGroup(r.groupId);
217
218 if (!g) {
219 debugs(93,7,HERE << "lost " << r.groupId << " group in rule" << r.id);
220 return false;
221 }
222
223 const bool wants = g->wants(filter);
224 debugs(93,7,HERE << r.groupId << (wants ? " wants" : " ignores"));
225 return wants;
226 }