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