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