]> git.ipfire.org Git - thirdparty/squid.git/blob - src/client_side.h
Maintenance: Removed most NULLs using modernize-use-nullptr (#1075)
[thirdparty/squid.git] / src / client_side.h
1 /*
2 * Copyright (C) 1996-2022 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 /* DEBUG: section 33 Client-side Routines */
10
11 #ifndef SQUID_CLIENTSIDE_H
12 #define SQUID_CLIENTSIDE_H
13
14 #include "acl/ChecklistFiller.h"
15 #include "base/RunnersRegistry.h"
16 #include "clientStreamForward.h"
17 #include "comm.h"
18 #include "error/Error.h"
19 #include "helper/forward.h"
20 #include "http/forward.h"
21 #include "HttpControlMsg.h"
22 #include "ipc/FdNotes.h"
23 #include "log/forward.h"
24 #include "proxyp/forward.h"
25 #include "sbuf/SBuf.h"
26 #include "servers/Server.h"
27 #if USE_AUTH
28 #include "auth/UserRequest.h"
29 #endif
30 #include "security/KeyLogger.h"
31 #if USE_OPENSSL
32 #include "security/forward.h"
33 #include "security/Handshake.h"
34 #include "ssl/support.h"
35 #endif
36 #if USE_DELAY_POOLS
37 #include "MessageBucket.h"
38 #endif
39
40 #include <iosfwd>
41
42 class ClientHttpRequest;
43 class HttpHdrRangeSpec;
44
45 class MasterXaction;
46 typedef RefCount<MasterXaction> MasterXactionPointer;
47
48 #if USE_OPENSSL
49 namespace Ssl
50 {
51 class ServerBump;
52 }
53 #endif
54
55 /**
56 * Legacy Server code managing a connection to a client.
57 *
58 * NP: presents AsyncJob API but does not operate autonomously as a Job.
59 * So Must() is not safe to use.
60 *
61 * Multiple requests (up to pipeline_prefetch) can be pipelined.
62 * This object is responsible for managing which one is currently being
63 * fulfilled and what happens to the queue if the current one causes the client
64 * connection to be closed early.
65 *
66 * Act as a manager for the client connection and passes data in buffer to a
67 * Parser relevant to the state (message headers vs body) that is being
68 * processed.
69 *
70 * Performs HTTP message processing to kick off the actual HTTP request
71 * handling objects (Http::Stream, ClientHttpRequest, HttpRequest).
72 *
73 * Performs SSL-Bump processing for switching between HTTP and HTTPS protocols.
74 *
75 * To terminate a ConnStateData close() the client Comm::Connection it is
76 * managing, or for graceful half-close use the stopReceiving() or
77 * stopSending() methods.
78 */
79 class ConnStateData:
80 public Server,
81 public HttpControlMsgSink,
82 public Acl::ChecklistFiller,
83 private IndependentRunner
84 {
85
86 public:
87 explicit ConnStateData(const MasterXactionPointer &xact);
88 virtual ~ConnStateData();
89
90 /* ::Server API */
91 virtual void receivedFirstByte();
92 virtual bool handleReadData();
93 virtual void afterClientRead();
94 virtual void afterClientWrite(size_t);
95
96 /* HttpControlMsgSink API */
97 virtual void sendControlMsg(HttpControlMsg);
98 virtual void doneWithControlMsg();
99
100 /// Traffic parsing
101 bool clientParseRequests();
102 void readNextRequest();
103
104 /// try to make progress on a transaction or read more I/O
105 void kick();
106
107 bool isOpen() const;
108
109 Http1::TeChunkedParser *bodyParser = nullptr; ///< parses HTTP/1.1 chunked request body
110
111 /** number of body bytes we need to comm_read for the "current" request
112 *
113 * \retval 0 We do not need to read any [more] body bytes
114 * \retval negative May need more but do not know how many; could be zero!
115 * \retval positive Need to read exactly that many more body bytes
116 */
117 int64_t mayNeedToReadMoreBody() const;
118
119 #if USE_AUTH
120 /**
121 * Fetch the user details for connection based authentication
122 * NOTE: this is ONLY connection based because NTLM and Negotiate is against HTTP spec.
123 */
124 const Auth::UserRequest::Pointer &getAuth() const { return auth_; }
125
126 /**
127 * Set the user details for connection-based authentication to use from now until connection closure.
128 *
129 * Any change to existing credentials shows that something invalid has happened. Such as:
130 * - NTLM/Negotiate auth was violated by the per-request headers missing a revalidation token
131 * - NTLM/Negotiate auth was violated by the per-request headers being for another user
132 * - SSL-Bump CONNECT tunnel with persistent credentials has ended
133 */
134 void setAuth(const Auth::UserRequest::Pointer &aur, const char *cause);
135 #endif
136
137 Ip::Address log_addr;
138
139 struct {
140 bool readMore = true; ///< needs comm_read (for this request or new requests)
141 bool swanSang = false; // XXX: temporary flag to check proper cleanup
142 } flags;
143 struct {
144 Comm::ConnectionPointer serverConnection; /* pinned server side connection */
145 char *host = nullptr; ///< host name of pinned connection
146 int port = -1; ///< port of pinned connection
147 bool pinned = false; ///< this connection was pinned
148 bool auth = false; ///< pinned for www authentication
149 bool reading = false; ///< we are monitoring for peer connection closure
150 bool zeroReply = false; ///< server closed w/o response (ERR_ZERO_SIZE_OBJECT)
151 bool peerAccessDenied = false; ///< cache_peer_access denied pinned connection reuse
152 CachePeer *peer = nullptr; ///< CachePeer the connection goes via
153 AsyncCall::Pointer readHandler; ///< detects serverConnection closure
154 AsyncCall::Pointer closeHandler; ///< The close handler for pinned server side connection
155 } pinning;
156
157 bool transparent() const;
158
159 /// true if we stopped receiving the request
160 const char *stoppedReceiving() const { return stoppedReceiving_; }
161 /// true if we stopped sending the response
162 const char *stoppedSending() const { return stoppedSending_; }
163 /// note request receiving error and close as soon as we write the response
164 void stopReceiving(const char *error);
165 /// note response sending error and close as soon as we read the request
166 void stopSending(const char *error);
167
168 /// (re)sets timeout for receiving more bytes from the client
169 void resetReadTimeout(time_t timeout);
170 /// (re)sets client_lifetime timeout
171 void extendLifetime();
172
173 void expectNoForwarding(); ///< cleans up virgin request [body] forwarding state
174
175 /* BodyPipe API */
176 BodyPipe::Pointer expectRequestBody(int64_t size);
177 virtual void noteMoreBodySpaceAvailable(BodyPipe::Pointer) = 0;
178 virtual void noteBodyConsumerAborted(BodyPipe::Pointer) = 0;
179
180 bool handleRequestBodyData();
181
182 /// parameters for the async notePinnedConnectionBecameIdle() call
183 class PinnedIdleContext
184 {
185 public:
186 PinnedIdleContext(const Comm::ConnectionPointer &conn, const HttpRequest::Pointer &req): connection(conn), request(req) {}
187
188 Comm::ConnectionPointer connection; ///< to-server connection to be pinned
189 HttpRequest::Pointer request; ///< to-server request that initiated serverConnection
190 };
191
192 /// Called when a pinned connection becomes available for forwarding the next request.
193 void notePinnedConnectionBecameIdle(PinnedIdleContext pic);
194 /// Forward future client requests using the given to-server connection.
195 /// The connection is still being used by the current client request.
196 void pinBusyConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest::Pointer &request);
197 /// Undo pinConnection() and, optionally, close the pinned connection.
198 void unpinConnection(const bool andClose);
199
200 /// \returns validated pinned to-server connection, stopping its monitoring
201 /// \throws a newly allocated ErrorState if validation fails
202 static Comm::ConnectionPointer BorrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &);
203 /// \returns the pinned CachePeer if one exists, nil otherwise
204 CachePeer *pinnedPeer() const {return pinning.peer;}
205 bool pinnedAuth() const {return pinning.auth;}
206
207 /// called just before a FwdState-dispatched job starts using connection
208 virtual void notePeerConnection(Comm::ConnectionPointer) {}
209
210 // pining related comm callbacks
211 virtual void clientPinnedConnectionClosed(const CommCloseCbParams &io);
212
213 /// noteTakeServerConnectionControl() callback parameter
214 class ServerConnectionContext {
215 public:
216 ServerConnectionContext(const Comm::ConnectionPointer &conn, const SBuf &post101Bytes) : preReadServerBytes(post101Bytes), conn_(conn) { conn_->enterOrphanage(); }
217
218 /// gives to-server connection to the new owner
219 Comm::ConnectionPointer connection() { conn_->leaveOrphanage(); return conn_; }
220
221 SBuf preReadServerBytes; ///< post-101 bytes received from the server
222
223 private:
224 friend std::ostream &operator <<(std::ostream &, const ServerConnectionContext &);
225 Comm::ConnectionPointer conn_; ///< to-server connection
226 };
227
228 /// Gives us the control of the Squid-to-server connection.
229 /// Used, for example, to initiate a TCP tunnel after protocol switching.
230 virtual void noteTakeServerConnectionControl(ServerConnectionContext) {}
231
232 // comm callbacks
233 void clientReadFtpData(const CommIoCbParams &io);
234 void connStateClosed(const CommCloseCbParams &io);
235 void requestTimeout(const CommTimeoutCbParams &params);
236 void lifetimeTimeout(const CommTimeoutCbParams &params);
237
238 // AsyncJob API
239 virtual void start();
240 virtual bool doneAll() const { return BodyProducer::doneAll() && false;}
241 virtual void swanSong();
242 virtual void callException(const std::exception &);
243
244 /// Changes state so that we close the connection and quit after serving
245 /// the client-side-detected error response instead of getting stuck.
246 void quitAfterError(HttpRequest *request); // meant to be private
247
248 /// The caller assumes responsibility for connection closure detection.
249 void stopPinnedConnectionMonitoring();
250
251 /// Starts or resumes accepting a TLS connection. TODO: Make this helper
252 /// method protected after converting clientNegotiateSSL() into a method.
253 Security::IoResult acceptTls();
254
255 /// the second part of old httpsAccept, waiting for future HttpsServer home
256 void postHttpsAccept();
257
258 #if USE_OPENSSL
259 /// Initializes and starts a peek-and-splice negotiation with the SSL client
260 void startPeekAndSplice();
261
262 /// Called when a peek-and-splice step finished. For example after
263 /// server SSL certificates received and fake server SSL certificates
264 /// generated
265 void doPeekAndSpliceStep();
266 /// called by FwdState when it is done bumping the server
267 void httpsPeeked(PinnedIdleContext pic);
268
269 /// Splice a bumped client connection on peek-and-splice mode
270 bool splice();
271
272 /// Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
273 void getSslContextStart();
274
275 /// finish configuring the newly created SSL context"
276 void getSslContextDone(Security::ContextPointer &);
277
278 /// Callback function. It is called when squid receive message from ssl_crtd.
279 static void sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply);
280 /// Process response from ssl_crtd.
281 void sslCrtdHandleReply(const Helper::Reply &reply);
282
283 void switchToHttps(ClientHttpRequest *, Ssl::BumpMode bumpServerMode);
284 void parseTlsHandshake();
285 bool switchedToHttps() const { return switchedToHttps_; }
286 Ssl::ServerBump *serverBump() {return sslServerBump;}
287 inline void setServerBump(Ssl::ServerBump *srvBump) {
288 if (!sslServerBump)
289 sslServerBump = srvBump;
290 else
291 assert(sslServerBump == srvBump);
292 }
293 const SBuf &sslCommonName() const {return sslCommonName_;}
294 void resetSslCommonName(const char *name) {sslCommonName_ = name;}
295 const SBuf &tlsClientSni() const { return tlsClientSni_; }
296 /// Fill the certAdaptParams with the required data for certificate adaptation
297 /// and create the key for storing/retrieve the certificate to/from the cache
298 void buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties);
299 /// Called when the client sends the first request on a bumped connection.
300 /// Returns false if no [delayed] error should be written to the client.
301 /// Otherwise, writes the error to the client and returns true. Also checks
302 /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
303 bool serveDelayedError(Http::Stream *);
304
305 Ssl::BumpMode sslBumpMode = Ssl::bumpEnd; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
306
307 /// Tls parser to use for client HELLO messages parsing on bumped
308 /// connections.
309 Security::HandshakeParser tlsParser;
310 #else
311 bool switchedToHttps() const { return false; }
312 #endif
313 char *prepareTlsSwitchingURL(const Http1::RequestParserPointer &hp);
314
315 /// registers a newly created stream
316 void add(const Http::StreamPointer &context);
317
318 /// handle a control message received by context from a peer and call back
319 virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
320
321 /// ClientStream calls this to supply response header (once) and data
322 /// for the current Http::Stream.
323 virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData) = 0;
324
325 /// remove no longer needed leading bytes from the input buffer
326 void consumeInput(const size_t byteCount);
327
328 /* TODO: Make the methods below (at least) non-public when possible. */
329
330 /// stop parsing the request and create context for relaying error info
331 Http::Stream *abortRequestParsing(const char *const errUri);
332
333 /// generate a fake CONNECT request with the given payload
334 /// at the beginning of the client I/O buffer
335 bool fakeAConnectRequest(const char *reason, const SBuf &payload);
336
337 /// generates and sends to tunnel.cc a fake request with a given payload
338 bool initiateTunneledRequest(HttpRequest::Pointer const &cause, const char *reason, const SBuf &payload);
339
340 /// whether we should start saving inBuf client bytes in anticipation of
341 /// tunneling them to the server later (on_unsupported_protocol)
342 bool shouldPreserveClientData() const;
343
344 /// build a fake http request
345 ClientHttpRequest *buildFakeRequest(SBuf &useHost, unsigned short usePort, const SBuf &payload);
346
347 /// From-client handshake bytes (including bytes at the beginning of a
348 /// CONNECT tunnel) which we may need to forward as-is if their syntax does
349 /// not match the expected TLS or HTTP protocol (on_unsupported_protocol).
350 SBuf preservedClientData;
351
352 /* Registered Runner API */
353 virtual void startShutdown();
354 virtual void endingShutdown();
355
356 /// \returns existing non-empty connection annotations,
357 /// creates and returns empty annotations otherwise
358 NotePairs::Pointer notes();
359 bool hasNotes() const { return bool(theNotes) && !theNotes->empty(); }
360
361 const ProxyProtocol::HeaderPointer &proxyProtocolHeader() const { return proxyProtocolHeader_; }
362
363 /// if necessary, stores new error information (if any)
364 void updateError(const Error &);
365
366 /// emplacement/convenience wrapper for updateError(const Error &)
367 void updateError(const err_type c, const ErrorDetailPointer &d) { updateError(Error(c, d)); }
368
369 /* Acl::ChecklistFiller API */
370 virtual void fillChecklist(ACLFilledChecklist &) const;
371
372 /// fillChecklist() obligations not fulfilled by the front request
373 /// TODO: This is a temporary ACLFilledChecklist::setConn() callback to
374 /// allow filling checklist using our non-public information sources. It
375 /// should be removed as unnecessary by making ACLs extract the information
376 /// they need from the ACLFilledChecklist::conn() without filling/copying.
377 void fillConnectionLevelDetails(ACLFilledChecklist &) const;
378
379 // Exposed to be accessible inside the ClientHttpRequest constructor.
380 // TODO: Remove. Make sure there is always a suitable ALE instead.
381 /// a problem that occurred without a request (e.g., while parsing headers)
382 Error bareError;
383
384 /// managers logging of the being-accepted TLS connection secrets
385 Security::KeyLogger keyLogger;
386
387 protected:
388 void startDechunkingRequest();
389 void finishDechunkingRequest(bool withSuccess);
390 void abortChunkedRequestBody(const err_type error);
391 err_type handleChunkedRequestBody();
392
393 /// ConnStateData-specific part of BorrowPinnedConnection()
394 Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &);
395
396 void startPinnedConnectionMonitoring();
397 void clientPinnedConnectionRead(const CommIoCbParams &io);
398 #if USE_OPENSSL
399 /// Handles a ready-for-reading TLS squid-to-server connection that
400 /// we thought was idle.
401 /// \return false if and only if the connection should be closed.
402 bool handleIdleClientPinnedTlsRead();
403 #endif
404
405 /// Parse an HTTP request
406 /// \note Sets result->flags.parsed_ok to 0 if failed to parse the request,
407 /// to 1 if the request was correctly parsed
408 /// \param[in] hp an Http1::RequestParser
409 /// \return NULL on incomplete requests,
410 /// a Http::Stream on success or failure.
411 /// TODO: Move to HttpServer. Warning: Move requires large code nonchanges!
412 Http::Stream *parseHttpRequest(const Http1::RequestParserPointer &);
413
414 /// parse input buffer prefix into a single transfer protocol request
415 /// return NULL to request more header bytes (after checking any limits)
416 /// use abortRequestParsing() to handle parsing errors w/o creating request
417 virtual Http::Stream *parseOneRequest() = 0;
418
419 /// start processing a freshly parsed request
420 virtual void processParsedRequest(Http::StreamPointer &) = 0;
421
422 /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
423 virtual int pipelinePrefetchMax() const;
424
425 /// timeout to use when waiting for the next request
426 virtual time_t idleTimeout() const = 0;
427
428 /// Perform client data lookups that depend on client src-IP.
429 /// The PROXY protocol may require some data input first.
430 void whenClientIpKnown();
431
432 BodyPipe::Pointer bodyPipe; ///< set when we are reading request body
433
434 /// whether preservedClientData is valid and should be kept up to date
435 bool preservingClientData_ = false;
436
437 bool tunnelOnError(const err_type);
438
439 private:
440 /* ::Server API */
441 virtual void terminateAll(const Error &, const LogTagsErrors &);
442 virtual bool shouldCloseOnEof() const;
443
444 void checkLogging();
445
446 void clientAfterReadingRequests();
447 bool concurrentRequestQueueFilled() const;
448
449 void pinConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest &request);
450
451 /* PROXY protocol functionality */
452 bool proxyProtocolValidateClient();
453 bool parseProxyProtocolHeader();
454 bool proxyProtocolError(const char *reason);
455
456 #if USE_OPENSSL
457 /// \returns a pointer to the matching cached TLS context or nil
458 Security::ContextPointer getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties);
459
460 /// Attempts to add a given TLS context to the cache, replacing the old
461 /// same-key context, if any
462 void storeTlsContextToCache(const SBuf &cacheKey, Security::ContextPointer &ctx);
463 void handleSslBumpHandshakeError(const Security::IoResult &);
464 #endif
465
466 /// whether PROXY protocol header is still expected
467 bool needProxyProtocolHeader_ = false;
468
469 /// the parsed PROXY protocol header
470 ProxyProtocol::HeaderPointer proxyProtocolHeader_;
471
472 #if USE_AUTH
473 /// some user details that can be used to perform authentication on this connection
474 Auth::UserRequest::Pointer auth_;
475 #endif
476
477 #if USE_OPENSSL
478 bool switchedToHttps_ = false;
479 bool parsingTlsHandshake = false; ///< whether we are getting/parsing TLS Hello bytes
480 /// The number of parsed HTTP requests headers on a bumped client connection
481 uint64_t parsedBumpedRequestCount = 0;
482
483 /// The TLS server host name appears in CONNECT request or the server ip address for the intercepted requests
484 SBuf tlsConnectHostOrIp; ///< The TLS server host name as passed in the CONNECT request
485 unsigned short tlsConnectPort = 0; ///< The TLS server port number as passed in the CONNECT request
486 SBuf sslCommonName_; ///< CN name for SSL certificate generation
487
488 /// TLS client delivered SNI value. Empty string if none has been received.
489 SBuf tlsClientSni_;
490 SBuf sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
491
492 /// HTTPS server cert. fetching state for bump-ssl-server-first
493 Ssl::ServerBump *sslServerBump = nullptr;
494 Ssl::CertSignAlgorithm signAlgorithm = Ssl::algSignTrusted; ///< The signing algorithm to use
495 #endif
496
497 /// the reason why we no longer write the response or nil
498 const char *stoppedSending_ = nullptr;
499 /// the reason why we no longer read the request or nil
500 const char *stoppedReceiving_ = nullptr;
501 /// Connection annotations, clt_conn_tag and other tags are stored here.
502 /// If set, are propagated to the current and all future master transactions
503 /// on the connection.
504 NotePairs::Pointer theNotes;
505 };
506
507 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end = nullptr);
508
509 int varyEvaluateMatch(StoreEntry * entry, HttpRequest * req);
510
511 /// accept requests to a given port and inform subCall about them
512 void clientStartListeningOn(AnyP::PortCfgPointer &port, const RefCount< CommCbFunPtrCallT<CommAcceptCbPtrFun> > &subCall, const Ipc::FdNoteId noteId);
513
514 void clientOpenListenSockets(void);
515 void clientConnectionsClose(void);
516 void httpRequestFree(void *);
517
518 /// decide whether to expect multiple requests on the corresponding connection
519 void clientSetKeepaliveFlag(ClientHttpRequest *http);
520
521 /// append a "part" HTTP header (as in a multi-part/range reply) to the buffer
522 void clientPackRangeHdr(const HttpReplyPointer &, const HttpHdrRangeSpec *, String boundary, MemBuf *);
523
524 /// put terminating boundary for multiparts to the buffer
525 void clientPackTermBound(String boundary, MemBuf *);
526
527 /* misplaced declaratrions of Stream callbacks provided/used by client side */
528 SQUIDCEXTERN CSR clientGetMoreData;
529 SQUIDCEXTERN CSS clientReplyStatus;
530 SQUIDCEXTERN CSD clientReplyDetach;
531 CSCB clientSocketRecipient;
532 CSD clientSocketDetach;
533
534 void clientProcessRequest(ConnStateData *, const Http1::RequestParserPointer &, Http::Stream *);
535 void clientProcessRequestFinished(ConnStateData *, const HttpRequest::Pointer &);
536 void clientPostHttpsAccept(ConnStateData *);
537
538 std::ostream &operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic);
539 std::ostream &operator <<(std::ostream &, const ConnStateData::ServerConnectionContext &);
540
541 #endif /* SQUID_CLIENTSIDE_H */
542