]> git.ipfire.org Git - thirdparty/squid.git/blame - src/adaptation/AccessCheck.cc
Update squidclient manual and usage documentation.
[thirdparty/squid.git] / src / adaptation / AccessCheck.cc
CommitLineData
62c7f90e
AR
1#include "squid.h"
2#include "structs.h"
3
4#include "ConfigParser.h"
62c7f90e
AR
5#include "HttpRequest.h"
6#include "HttpReply.h"
127dce76 7#include "acl/FilledChecklist.h"
62c7f90e
AR
8#include "adaptation/Service.h"
9#include "adaptation/ServiceGroups.h"
10#include "adaptation/AccessRule.h"
abd4b611 11#include "adaptation/Config.h"
62c7f90e
AR
12#include "adaptation/AccessCheck.h"
13
14
d85b8894 15/** \cond AUTODOCS-IGNORE */
62c7f90e 16cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
d85b8894 17/** \endcond */
62c7f90e 18
abd4b611
AR
19bool
20Adaptation::AccessCheck::Start(Method method, VectPoint vp,
26ac0430
AJ
21 HttpRequest *req, HttpReply *rep, AccessCheckCallback *cb, void *cbdata)
22{
abd4b611
AR
23
24 if (Config::Enabled) {
25 // the new check will call the callback and delete self, eventually
26 AccessCheck *check = new AccessCheck(method, vp, req, rep, cb, cbdata);
27 check->check();
28 return true;
26ac0430 29 }
abd4b611
AR
30
31 debugs(83, 3, HERE << "adaptation off, skipping");
32 return false;
33}
34
62c7f90e 35Adaptation::AccessCheck::AccessCheck(Method aMethod,
26ac0430
AJ
36 VectPoint aPoint,
37 HttpRequest *aReq,
38 HttpReply *aRep,
39 AccessCheckCallback *aCallback,
40 void *aCallbackData): AsyncJob("AccessCheck"), done(FALSE)
62c7f90e
AR
41{
42 // TODO: assign these at creation time
43
44 method = aMethod;
45 point = aPoint;
46
47 req = HTTPMSGLOCK(aReq);
48 rep = aRep ? HTTPMSGLOCK(aRep) : NULL;
49
50 callback = aCallback;
51
52 callback_data = cbdataReference(aCallbackData);
53
54 acl_checklist = NULL;
55
192378eb 56 debugs(93, 5, HERE << "AccessCheck constructed for " << methodStr(method) << " " << vectPointStr(point));
62c7f90e
AR
57}
58
59Adaptation::AccessCheck::~AccessCheck()
60{
61 HTTPMSGUNLOCK(req);
62 HTTPMSGUNLOCK(rep);
7e81de2e
AR
63 if (callback_data)
64 cbdataReferenceDone(callback_data);
62c7f90e
AR
65}
66
67/*
68 * Walk the access rules list and find all classes that have at least
69 * one service with matching method and vectoring point.
70 */
71void
72Adaptation::AccessCheck::check()
73{
192378eb 74 debugs(93, 4, HERE << "start checking");
62c7f90e
AR
75
76 typedef AccessRules::iterator ARI;
77 for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
78
79 /*
80 * We only find the first matching service because we only need
81 * one matching service to justify ACL-checking a class. We might
82 * use other services belonging to the class if the first service
83 * turns out to be unusable for some reason.
84 */
85 AccessRule *r = *i;
86 ServicePointer service = findBestService(*r, false);
87 if (service != NULL) {
192378eb 88 debugs(93, 5, HERE << "check: rule '" << r->id << "' has candidate service '" << service->cfg().key << "'");
62c7f90e
AR
89 candidates += r->id;
90 }
91 }
92
93 checkCandidates();
94}
95
96// XXX: Here and everywhere we call FindRule(topCandidate()):
97// Once we identified the candidate, we should not just ignore it
98// if reconfigure changes rules. We should either lock the rule to
99// prevent reconfigure from stealing it or restart the check with
100// new rules. Throwing an exception may also be appropriate.
101void
102Adaptation::AccessCheck::checkCandidates()
103{
192378eb 104 debugs(93, 4, HERE << "has " << candidates.size() << " rules");
62c7f90e
AR
105
106 while (!candidates.empty()) {
107 if (AccessRule *r = FindRule(topCandidate())) {
b50e327b 108 /* BUG 2526: what to do when r->acl is empty?? */
62c7f90e 109 // XXX: we do not have access to conn->rfc931 here.
127dce76 110 acl_checklist = new ACLFilledChecklist(r->acl, req, dash_str);
1c906324 111 acl_checklist->reply = rep ? HTTPMSGLOCK(rep) : NULL;
62c7f90e
AR
112 acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
113 return;
114 }
115
116 candidates.shift(); // the rule apparently went away (reconfigure)
117 }
118
119 // when there are no canidates, fake answer 1
192378eb 120 debugs(93, 4, HERE << "NO candidates left");
62c7f90e
AR
121 noteAnswer(1);
122}
123
124void
125Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer, void *data)
126{
192378eb 127 debugs(93, 8, HERE << "callback answer=" << answer);
62c7f90e 128 AccessCheck *ac = (AccessCheck*)data;
f9cf4ba9
AJ
129
130 /** \todo AYJ 2008-06-12: If answer == ACCESS_REQ_PROXY_AUTH
131 * we should be kicking off an authentication before continuing
132 * with this request. see bug 2400 for details.
133 */
83181157 134 ac->noteAnswer(answer==ACCESS_ALLOWED);
62c7f90e
AR
135}
136
137void
138Adaptation::AccessCheck::noteAnswer(int answer)
139{
140 debugs(93, 5, HERE << "AccessCheck::noteAnswer " << answer);
141 if (candidates.size())
1c906324 142 debugs(93, 5, HERE << "was checking rule" << topCandidate());
62c7f90e
AR
143
144 if (!answer) {
1c906324 145 candidates.shift(); // the rule did not match
62c7f90e
AR
146 checkCandidates();
147 return;
148 }
149
150 /*
151 * We use an event here to break deep function call sequences
152 */
153 // XXX: use AsyncCall for callback and remove
154 CallJobHere(93, 5, this, Adaptation::AccessCheck::do_callback);
155}
156
157void
158Adaptation::AccessCheck::do_callback()
159{
192378eb 160 debugs(93, 3, HERE);
62c7f90e
AR
161
162 if (candidates.size())
163 debugs(93, 3, HERE << "was checking rule" << topCandidate());
164
165 void *validated_cbdata;
166 if (!cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
167 debugs(93,3,HERE << "do_callback: callback_data became invalid, skipping");
168 return;
169 }
170
171 ServicePointer service = NULL;
172 if (candidates.size()) {
173 if (AccessRule *r = FindRule(topCandidate())) {
174 service = findBestService(*r, true);
175 if (service != NULL)
176 debugs(93,3,HERE << "do_callback: with service " << service->cfg().uri);
177 else
178 debugs(93,3,HERE << "do_callback: no service for rule" << r->id);
179 } else {
180 debugs(93,3,HERE << "do_callback: no rule" << topCandidate());
181 }
182 candidates.shift(); // done with topCandidate()
26ac0430 183 } else {
62c7f90e 184 debugs(93,3,HERE << "do_callback: no candidate rules");
26ac0430 185 }
62c7f90e
AR
186
187 callback(service, validated_cbdata);
188 done = TRUE;
189}
190
191Adaptation::ServicePointer
26ac0430
AJ
192Adaptation::AccessCheck::findBestService(AccessRule &r, bool preferUp)
193{
62c7f90e
AR
194
195 const char *what = preferUp ? "up " : "";
26ac0430
AJ
196 debugs(93,7,HERE << "looking for the first matching " <<
197 what << "service in group " << r.groupId);
62c7f90e
AR
198
199 ServicePointer secondBest;
200
201 ServiceGroup *g = FindGroup(r.groupId);
202
203 if (!g) {
204 debugs(93,5,HERE << "lost " << r.groupId << " group in rule" << r.id);
205 return ServicePointer();
26ac0430 206 }
62c7f90e
AR
207
208 ServiceGroup::Loop loop(g->initialServices());
209 typedef ServiceGroup::iterator SGI;
210 for (SGI i = loop.begin; i != loop.end; ++i) {
211
212 ServicePointer service = FindService(*i);
213
214 if (!service)
215 continue;
216
217 if (method != service->cfg().method)
218 continue;
219
220 if (point != service->cfg().point)
221 continue;
222
223 // sending a message to a broken service is likely to cause errors
224 if (service->cfg().bypass && service->broken())
225 continue;
226
227 if (service->up()) {
228 // sending a message to a service that does not want it is useless
229 // note that we cannot check wantsUrl for service that is not "up"
230 // note that even essential services are skipped on unwanted URLs!
231 if (!service->wantsUrl(req->urlpath))
232 continue;
233 } else {
234 if (!secondBest)
235 secondBest = service;
236 if (preferUp) {
237 // the caller asked for an "up" service and we can bypass this one
238 if (service->cfg().bypass)
239 continue;
240 debugs(93,5,HERE << "cannot skip an essential down service");
241 what = "down-but-essential ";
242 }
243 }
244
245 debugs(93,5,HERE << "found first matching " <<
26ac0430
AJ
246 what << "service for " << r.groupId << " group in rule" << r.id <<
247 ": " << service->cfg().key);
62c7f90e
AR
248
249 return service;
250 }
251
252 if (secondBest != NULL) {
253 what = "down ";
254 debugs(93,5,HERE << "found first matching " <<
26ac0430
AJ
255 what << "service for " << r.groupId << " group in rule" << r.id <<
256 ": " << secondBest->cfg().key);
62c7f90e
AR
257 return secondBest;
258 }
259
26ac0430
AJ
260 debugs(93,5,HERE << "found no matching " <<
261 what << "services for " << r.groupId << " group in rule" << r.id);
62c7f90e
AR
262 return ServicePointer();
263}