]> git.ipfire.org Git - thirdparty/squid.git/blob - src/acl/FilledChecklist.cc
59e338214bae4a9b73dc294a091205df4279669a
[thirdparty/squid.git] / src / acl / FilledChecklist.cc
1 /*
2 * Copyright (C) 1996-2025 The Squid Software Foundation and contributors
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
9 #include "squid.h"
10 #include "acl/FilledChecklist.h"
11 #include "client_side.h"
12 #include "comm/Connection.h"
13 #include "comm/forward.h"
14 #include "debug/Messages.h"
15 #include "ExternalACLEntry.h"
16 #include "http/Stream.h"
17 #include "HttpReply.h"
18 #include "HttpRequest.h"
19 #include "SquidConfig.h"
20 #if USE_AUTH
21 #include "auth/AclProxyAuth.h"
22 #include "auth/UserRequest.h"
23 #endif
24
25 CBDATA_CLASS_INIT(ACLFilledChecklist);
26
27 ACLFilledChecklist::ACLFilledChecklist() :
28 dst_rdns(nullptr),
29 #if USE_AUTH
30 auth_user_request (nullptr),
31 #endif
32 #if SQUID_SNMP
33 snmp_community(nullptr),
34 #endif
35 requestErrorType(ERR_MAX),
36 conn_(nullptr),
37 fd_(-1),
38 destinationDomainChecked_(false),
39 sourceDomainChecked_(false)
40 {
41 my_addr.setEmpty();
42 src_addr.setEmpty();
43 dst_addr.setEmpty();
44 }
45
46 ACLFilledChecklist::~ACLFilledChecklist()
47 {
48 assert (!asyncInProgress());
49
50 safe_free(dst_rdns); // created by xstrdup().
51
52 cbdataReferenceDone(conn_);
53
54 debugs(28, 4, "ACLFilledChecklist destroyed " << this);
55 }
56
57 static void
58 showDebugWarning(const char *msg)
59 {
60 static uint16_t count = 0;
61 if (count > 10)
62 return;
63
64 ++count;
65 debugs(28, Important(58), "ERROR: ALE missing " << msg);
66 }
67
68 void
69 ACLFilledChecklist::verifyAle() const
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");
83 // XXX: al->request should be original,
84 // but the request may be already adapted
85 al->request = request.getRaw();
86 HTTPMSGLOCK(al->request);
87 }
88
89 if (!al->adapted_request) {
90 showDebugWarning("adapted HttpRequest object");
91 al->adapted_request = request.getRaw();
92 HTTPMSGLOCK(al->adapted_request);
93 }
94
95 if (al->url.isEmpty()) {
96 showDebugWarning("URL");
97 // XXX: al->url should be the request URL from client,
98 // but request->url may be different (e.g.,redirected)
99 al->url = request->effectiveRequestUri();
100 }
101 }
102
103 if (hasReply() && !al->reply) {
104 showDebugWarning("HttpReply object");
105 al->reply = reply_;
106 }
107 }
108
109 void
110 ACLFilledChecklist::syncAle(HttpRequest *adaptedRequest, const char *logUri) const
111 {
112 if (!al)
113 return;
114 if (adaptedRequest && !al->adapted_request) {
115 al->adapted_request = adaptedRequest;
116 HTTPMSGLOCK(al->adapted_request);
117 }
118 if (logUri && al->url.isEmpty())
119 al->url = logUri;
120 }
121
122 ConnStateData *
123 ACLFilledChecklist::conn() const
124 {
125 return cbdataReferenceValid(conn_) ? conn_ : nullptr;
126 }
127
128 void
129 ACLFilledChecklist::setConn(ConnStateData *aConn)
130 {
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.
141 conn_ = cbdataReference(aConn);
142 aConn->fillConnectionLevelDetails(*this);
143 }
144
145 int
146 ACLFilledChecklist::fd() const
147 {
148 const auto c = conn();
149 return (c && c->clientConnection) ? c->clientConnection->fd : fd_;
150 }
151
152 void
153 ACLFilledChecklist::fd(int aDescriptor)
154 {
155 const auto c = conn();
156 assert(!c || !c->clientConnection || c->clientConnection->fd == aDescriptor);
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 *
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.
192 *
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").
200 */
201 ACLFilledChecklist::ACLFilledChecklist(const acl_access *A, HttpRequest *http_request):
202 dst_rdns(nullptr),
203 #if USE_AUTH
204 auth_user_request(nullptr),
205 #endif
206 #if SQUID_SNMP
207 snmp_community(nullptr),
208 #endif
209 requestErrorType(ERR_MAX),
210 conn_(nullptr),
211 fd_(-1),
212 destinationDomainChecked_(false),
213 sourceDomainChecked_(false)
214 {
215 my_addr.setEmpty();
216 src_addr.setEmpty();
217 dst_addr.setEmpty();
218
219 changeAcl(A);
220 setRequest(http_request);
221 }
222
223 void ACLFilledChecklist::setRequest(HttpRequest *httpRequest)
224 {
225 assert(!request);
226 if (httpRequest) {
227 request = httpRequest;
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;
235
236 if (const auto cmgr = request->clientConnectionManager.get())
237 setConn(cmgr);
238 }
239 }
240
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