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