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