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