]> git.ipfire.org Git - thirdparty/squid.git/blob - src/adaptation/AccessCheck.cc
Merged from trunk
[thirdparty/squid.git] / src / adaptation / AccessCheck.cc
1 #include "squid.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/Service.h"
9 #include "adaptation/ServiceGroups.h"
10 #include "adaptation/AccessRule.h"
11 #include "adaptation/Config.h"
12 #include "adaptation/AccessCheck.h"
13
14
15 /** \cond AUTODOCS-IGNORE */
16 cbdata_type Adaptation::AccessCheck::CBDATA_AccessCheck = CBDATA_UNKNOWN;
17 /** \endcond */
18
19 bool
20 Adaptation::AccessCheck::Start(Method method, VectPoint vp,
21 HttpRequest *req, HttpReply *rep, AccessCheckCallback *cb, void *cbdata)
22 {
23
24 if (Config::Enabled) {
25 // the new check will call the callback and delete self, eventually
26 return AsyncStart(new AccessCheck(
27 ServiceFilter(method, vp, req, rep), cb, cbdata));
28 }
29
30 debugs(83, 3, HERE << "adaptation off, skipping");
31 return false;
32 }
33
34 Adaptation::AccessCheck::AccessCheck(const ServiceFilter &aFilter,
35 AccessCheckCallback *aCallback,
36 void *aCallbackData):
37 AsyncJob("AccessCheck"), filter(aFilter),
38 callback(aCallback),
39 callback_data(cbdataReference(aCallbackData)),
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 if (callback_data)
60 cbdataReferenceDone(callback_data);
61 }
62
63 void
64 Adaptation::AccessCheck::start() {
65 AsyncJob::start();
66 check();
67 }
68
69 /// Walk the access rules list to find rules with applicable service groups
70 void
71 Adaptation::AccessCheck::check()
72 {
73 debugs(93, 4, HERE << "start checking");
74
75 typedef AccessRules::iterator ARI;
76 for (ARI i = AllRules().begin(); i != AllRules().end(); ++i) {
77 AccessRule *r = *i;
78 if (isCandidate(*r)) {
79 debugs(93, 5, HERE << "check: rule '" << r->id << "' is a candidate");
80 candidates += r->id;
81 }
82 }
83
84 checkCandidates();
85 }
86
87 // XXX: Here and everywhere we call FindRule(topCandidate()):
88 // Once we identified the candidate, we should not just ignore it
89 // if reconfigure changes rules. We should either lock the rule to
90 // prevent reconfigure from stealing it or restart the check with
91 // new rules. Throwing an exception may also be appropriate.
92 void
93 Adaptation::AccessCheck::checkCandidates()
94 {
95 debugs(93, 4, HERE << "has " << candidates.size() << " rules");
96
97 while (!candidates.empty()) {
98 if (AccessRule *r = FindRule(topCandidate())) {
99 /* BUG 2526: what to do when r->acl is empty?? */
100 // XXX: we do not have access to conn->rfc931 here.
101 acl_checklist = new ACLFilledChecklist(r->acl, filter.request, dash_str);
102 acl_checklist->reply = filter.reply ? HTTPMSGLOCK(filter.reply) : NULL;
103 acl_checklist->nonBlockingCheck(AccessCheckCallbackWrapper, this);
104 return;
105 }
106
107 candidates.shift(); // the rule apparently went away (reconfigure)
108 }
109
110 debugs(93, 4, HERE << "NO candidates left");
111 callBack(NULL);
112 Must(done());
113 }
114
115 void
116 Adaptation::AccessCheck::AccessCheckCallbackWrapper(int answer, void *data)
117 {
118 debugs(93, 8, HERE << "callback answer=" << answer);
119 AccessCheck *ac = (AccessCheck*)data;
120
121 /** \todo AYJ 2008-06-12: If answer == ACCESS_REQ_PROXY_AUTH
122 * we should be kicking off an authentication before continuing
123 * with this request. see bug 2400 for details.
124 */
125 ac->noteAnswer(answer==ACCESS_ALLOWED);
126 }
127
128 /// process the results of the ACL check
129 void
130 Adaptation::AccessCheck::noteAnswer(int answer)
131 {
132 Must(!candidates.empty()); // the candidate we were checking must be there
133 debugs(93,5, HERE << topCandidate() << " answer=" << answer);
134
135 if (answer) { // the rule matched
136 ServiceGroupPointer g = topGroup();
137 if (g != NULL) { // the corresponding group found
138 callBack(g);
139 Must(done());
140 return;
141 }
142 }
143
144 // no match or the group disappeared during reconfiguration
145 candidates.shift();
146 checkCandidates();
147 }
148
149 /// call back with a possibly nil group; the job ends here because all failures
150 /// at this point are fatal to the access check process
151 void
152 Adaptation::AccessCheck::callBack(const ServiceGroupPointer &g)
153 {
154 debugs(93,3, HERE << g);
155
156 void *validated_cbdata;
157 if (cbdataReferenceValidDone(callback_data, &validated_cbdata)) {
158 callback(g, validated_cbdata);
159 }
160 mustStop("done"); // called back or will never be able to call back
161 }
162
163 Adaptation::ServiceGroupPointer
164 Adaptation::AccessCheck::topGroup() const
165 {
166 ServiceGroupPointer g;
167 if (candidates.size()) {
168 if (AccessRule *r = FindRule(topCandidate())) {
169 g = FindGroup(r->groupId);
170 debugs(93,5, HERE << "top group for " << r->id << " is " << g);
171 } else {
172 debugs(93,5, HERE << "no rule for " << topCandidate());
173 }
174 } else {
175 debugs(93,5, HERE << "no candidates"); // should not happen
176 }
177
178 return g;
179 }
180
181 /** Returns true iff the rule's service group will be used after ACL matches.
182 Used to detect rules worth ACl-checking. */
183 bool
184 Adaptation::AccessCheck::isCandidate(AccessRule &r)
185 {
186 debugs(93,7,HERE << "checking candidacy of " << r.id << ", group " <<
187 r.groupId);
188
189 ServiceGroupPointer g = FindGroup(r.groupId);
190
191 if (!g) {
192 debugs(93,7,HERE << "lost " << r.groupId << " group in rule" << r.id);
193 return false;
194 }
195
196 const bool wants = g->wants(filter);
197 debugs(93,7,HERE << r.groupId << (wants ? " wants" : " ignores"));
198 return wants;
199 }