]>
Commit | Line | Data |
---|---|---|
bbc27441 | 1 | /* |
1f7b830e | 2 | * Copyright (C) 1996-2025 The Squid Software Foundation and contributors |
bbc27441 AJ |
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 | ||
582c2af2 FC |
9 | #include "squid.h" |
10 | #include "acl/FilledChecklist.h" | |
351fe86d | 11 | #include "client_side.h" |
582c2af2 FC |
12 | #include "comm/Connection.h" |
13 | #include "comm/forward.h" | |
675b8408 | 14 | #include "debug/Messages.h" |
abdd93d0 | 15 | #include "ExternalACLEntry.h" |
d3dddfb5 | 16 | #include "http/Stream.h" |
582c2af2 FC |
17 | #include "HttpReply.h" |
18 | #include "HttpRequest.h" | |
4d5904f7 | 19 | #include "SquidConfig.h" |
2f1431ea | 20 | #if USE_AUTH |
351fe86d | 21 | #include "auth/AclProxyAuth.h" |
602d9612 | 22 | #include "auth/UserRequest.h" |
2f1431ea | 23 | #endif |
582c2af2 | 24 | |
351fe86d AR |
25 | CBDATA_CLASS_INIT(ACLFilledChecklist); |
26 | ||
351fe86d | 27 | ACLFilledChecklist::ACLFilledChecklist() : |
aee3523a | 28 | dst_rdns(nullptr), |
2f1431ea | 29 | #if USE_AUTH |
aee3523a | 30 | auth_user_request (nullptr), |
2f1431ea | 31 | #endif |
351fe86d | 32 | #if SQUID_SNMP |
aee3523a | 33 | snmp_community(nullptr), |
fa24d749 | 34 | #endif |
3248e962 | 35 | requestErrorType(ERR_MAX), |
aee3523a | 36 | conn_(nullptr), |
f53969cc SM |
37 | fd_(-1), |
38 | destinationDomainChecked_(false), | |
39 | sourceDomainChecked_(false) | |
351fe86d | 40 | { |
4dd643d5 AJ |
41 | my_addr.setEmpty(); |
42 | src_addr.setEmpty(); | |
43 | dst_addr.setEmpty(); | |
351fe86d AR |
44 | } |
45 | ||
351fe86d AR |
46 | ACLFilledChecklist::~ACLFilledChecklist() |
47 | { | |
48 | assert (!asyncInProgress()); | |
49 | ||
12ef783b AJ |
50 | safe_free(dst_rdns); // created by xstrdup(). |
51 | ||
351fe86d AR |
52 | cbdataReferenceDone(conn_); |
53 | ||
bf95c10a | 54 | debugs(28, 4, "ACLFilledChecklist destroyed " << this); |
351fe86d AR |
55 | } |
56 | ||
fbbea662 AJ |
57 | static void |
58 | showDebugWarning(const char *msg) | |
59 | { | |
60 | static uint16_t count = 0; | |
bdd92d83 | 61 | if (count > 10) |
fbbea662 AJ |
62 | return; |
63 | ||
64 | ++count; | |
d816f28d | 65 | debugs(28, Important(58), "ERROR: ALE missing " << msg); |
fbbea662 AJ |
66 | } |
67 | ||
68 | void | |
cb365059 | 69 | ACLFilledChecklist::verifyAle() const |
fbbea662 AJ |
70 | { |
71 | // make sure the ALE fields used by Format::assemble to | |
72 | // fill the old external_acl_type codes are set if any | |
73 | // data on them exists in the Checklist | |
74 | ||
75 | if (!al->cache.port && conn()) { | |
76 | showDebugWarning("listening port"); | |
77 | al->cache.port = conn()->port; | |
78 | } | |
79 | ||
80 | if (request) { | |
81 | if (!al->request) { | |
82 | showDebugWarning("HttpRequest object"); | |
cb365059 EB |
83 | // XXX: al->request should be original, |
84 | // but the request may be already adapted | |
f05e4f37 | 85 | al->request = request.getRaw(); |
fbbea662 AJ |
86 | HTTPMSGLOCK(al->request); |
87 | } | |
88 | ||
89 | if (!al->adapted_request) { | |
90 | showDebugWarning("adapted HttpRequest object"); | |
f05e4f37 | 91 | al->adapted_request = request.getRaw(); |
fbbea662 AJ |
92 | HTTPMSGLOCK(al->adapted_request); |
93 | } | |
94 | ||
f57ae909 | 95 | if (al->url.isEmpty()) { |
fbbea662 | 96 | showDebugWarning("URL"); |
cb365059 EB |
97 | // XXX: al->url should be the request URL from client, |
98 | // but request->url may be different (e.g.,redirected) | |
57a5679b | 99 | al->url = request->effectiveRequestUri(); |
fbbea662 AJ |
100 | } |
101 | } | |
102 | ||
b1c2ea7a | 103 | if (hasReply() && !al->reply) { |
fbbea662 | 104 | showDebugWarning("HttpReply object"); |
b1c2ea7a | 105 | al->reply = reply_; |
fbbea662 | 106 | } |
fbbea662 AJ |
107 | } |
108 | ||
cb365059 EB |
109 | void |
110 | ACLFilledChecklist::syncAle(HttpRequest *adaptedRequest, const char *logUri) const | |
111 | { | |
112 | if (!al) | |
113 | return; | |
7a023df9 | 114 | if (adaptedRequest && !al->adapted_request) { |
cb365059 EB |
115 | al->adapted_request = adaptedRequest; |
116 | HTTPMSGLOCK(al->adapted_request); | |
117 | } | |
7a023df9 | 118 | if (logUri && al->url.isEmpty()) |
cb365059 EB |
119 | al->url = logUri; |
120 | } | |
121 | ||
351fe86d AR |
122 | ConnStateData * |
123 | ACLFilledChecklist::conn() const | |
124 | { | |
6cf166fc | 125 | return cbdataReferenceValid(conn_) ? conn_ : nullptr; |
351fe86d AR |
126 | } |
127 | ||
128 | void | |
e227da8d | 129 | ACLFilledChecklist::setConn(ConnStateData *aConn) |
351fe86d | 130 | { |
e227da8d AR |
131 | if (conn_ == aConn) |
132 | return; // no new information | |
133 | ||
134 | // no conn_ replacement/removal to reduce inconsistent fill concerns | |
135 | assert(!conn_); | |
136 | assert(aConn); | |
137 | ||
138 | // To reduce inconsistent fill concerns, we should be the only ones calling | |
139 | // fillConnectionLevelDetails(). Set conn_ first so that the filling method | |
140 | // can detect (some) direct calls from others. | |
351fe86d | 141 | conn_ = cbdataReference(aConn); |
e227da8d | 142 | aConn->fillConnectionLevelDetails(*this); |
351fe86d AR |
143 | } |
144 | ||
145 | int | |
146 | ACLFilledChecklist::fd() const | |
147 | { | |
6cf166fc EB |
148 | const auto c = conn(); |
149 | return (c && c->clientConnection) ? c->clientConnection->fd : fd_; | |
351fe86d AR |
150 | } |
151 | ||
152 | void | |
153 | ACLFilledChecklist::fd(int aDescriptor) | |
154 | { | |
6cf166fc EB |
155 | const auto c = conn(); |
156 | assert(!c || !c->clientConnection || c->clientConnection->fd == aDescriptor); | |
351fe86d AR |
157 | fd_ = aDescriptor; |
158 | } | |
159 | ||
160 | bool | |
161 | ACLFilledChecklist::destinationDomainChecked() const | |
162 | { | |
163 | return destinationDomainChecked_; | |
164 | } | |
165 | ||
166 | void | |
167 | ACLFilledChecklist::markDestinationDomainChecked() | |
168 | { | |
169 | assert (!finished() && !destinationDomainChecked()); | |
170 | destinationDomainChecked_ = true; | |
171 | } | |
172 | ||
173 | bool | |
174 | ACLFilledChecklist::sourceDomainChecked() const | |
175 | { | |
176 | return sourceDomainChecked_; | |
177 | } | |
178 | ||
179 | void | |
180 | ACLFilledChecklist::markSourceDomainChecked() | |
181 | { | |
182 | assert (!finished() && !sourceDomainChecked()); | |
183 | sourceDomainChecked_ = true; | |
184 | } | |
185 | ||
186 | /* | |
187 | * There are two common ACLFilledChecklist lifecycles paths: | |
188 | * | |
c56edb4a EB |
189 | * "Fast" (always synchronous or "blocking"): The user constructs an |
190 | * ACLFilledChecklist object on stack, configures it as needed, and calls one | |
191 | * or both of its fastCheck() methods. | |
351fe86d | 192 | * |
c56edb4a EB |
193 | * "Slow" (usually asynchronous or "non-blocking"): The user allocates an |
194 | * ACLFilledChecklist object on heap (via Make()), configures it as needed, | |
195 | * and passes it to NonBlockingCheck() while specifying the callback function | |
196 | * to call with check results. NonBlockingCheck() calls the callback function | |
197 | * (if the corresponding cbdata is still valid), either immediately/directly | |
198 | * (XXX) or eventually/asynchronously. After this callback obligations are | |
199 | * fulfilled, checkCallback() deletes the checklist object (i.e. "this"). | |
351fe86d | 200 | */ |
e94ff527 | 201 | ACLFilledChecklist::ACLFilledChecklist(const acl_access *A, HttpRequest *http_request): |
aee3523a | 202 | dst_rdns(nullptr), |
cc8c4af2 | 203 | #if USE_AUTH |
aee3523a | 204 | auth_user_request(nullptr), |
2f1431ea | 205 | #endif |
351fe86d | 206 | #if SQUID_SNMP |
aee3523a | 207 | snmp_community(nullptr), |
fa24d749 | 208 | #endif |
cc8c4af2 | 209 | requestErrorType(ERR_MAX), |
aee3523a | 210 | conn_(nullptr), |
f53969cc SM |
211 | fd_(-1), |
212 | destinationDomainChecked_(false), | |
213 | sourceDomainChecked_(false) | |
351fe86d | 214 | { |
4dd643d5 AJ |
215 | my_addr.setEmpty(); |
216 | src_addr.setEmpty(); | |
217 | dst_addr.setEmpty(); | |
af6a12ee | 218 | |
3d29e126 | 219 | changeAcl(A); |
819be284 | 220 | setRequest(http_request); |
819be284 | 221 | } |
351fe86d | 222 | |
819be284 EB |
223 | void ACLFilledChecklist::setRequest(HttpRequest *httpRequest) |
224 | { | |
225 | assert(!request); | |
226 | if (httpRequest) { | |
227 | request = httpRequest; | |
351fe86d AR |
228 | #if FOLLOW_X_FORWARDED_FOR |
229 | if (Config.onoff.acl_uses_indirect_client) | |
230 | src_addr = request->indirect_client_addr; | |
231 | else | |
232 | #endif /* FOLLOW_X_FORWARDED_FOR */ | |
233 | src_addr = request->client_addr; | |
234 | my_addr = request->my_addr; | |
16a16ffe | 235 | |
e227da8d AR |
236 | if (const auto cmgr = request->clientConnectionManager.get()) |
237 | setConn(cmgr); | |
351fe86d | 238 | } |
819be284 | 239 | } |
351fe86d | 240 | |
b1c2ea7a AR |
241 | void |
242 | ACLFilledChecklist::updateAle(const AccessLogEntry::Pointer &a) | |
243 | { | |
244 | if (!a) | |
245 | return; | |
246 | ||
247 | al = a; // could have been set already (to a different value) | |
248 | if (!request) | |
249 | setRequest(a->request); | |
250 | updateReply(a->reply); | |
251 | } | |
252 | ||
253 | void | |
254 | ACLFilledChecklist::updateReply(const HttpReply::Pointer &r) | |
255 | { | |
256 | if (r) | |
257 | reply_ = r; // may already be set, including to r | |
258 | } | |
259 |