/*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "http/forward.h"
#include "HttpControlMsg.h"
#include "ipc/FdNotes.h"
+#include "log/forward.h"
+#include "proxyp/forward.h"
#include "sbuf/SBuf.h"
#include "servers/Server.h"
#if USE_AUTH
#include "security/Handshake.h"
#include "ssl/support.h"
#endif
+#if USE_DELAY_POOLS
+#include "MessageBucket.h"
+#endif
+
+#include <iosfwd>
class ClientHttpRequest;
class HttpHdrRangeSpec;
+class MasterXaction;
+typedef RefCount<MasterXaction> MasterXactionPointer;
+
#if USE_OPENSSL
namespace Ssl
{
* managing, or for graceful half-close use the stopReceiving() or
* stopSending() methods.
*/
-class ConnStateData : public Server, public HttpControlMsgSink, public RegisteredRunner
+class ConnStateData : public Server, public HttpControlMsgSink, private IndependentRunner
{
public:
- explicit ConnStateData(const MasterXaction::Pointer &xact);
+ explicit ConnStateData(const MasterXactionPointer &xact);
virtual ~ConnStateData();
/* ::Server API */
/* HttpControlMsgSink API */
virtual void sendControlMsg(HttpControlMsg);
+ virtual void doneWithControlMsg();
/// Traffic parsing
bool clientParseRequests();
bool auth; /* pinned for www authentication */
bool reading; ///< we are monitoring for peer connection closure
bool zeroReply; ///< server closed w/o response (ERR_ZERO_SIZE_OBJECT)
+ bool peerAccessDenied; ///< cache_peer_access denied pinned connection reuse
CachePeer *peer; /* CachePeer the connection goes via */
AsyncCall::Pointer readHandler; ///< detects serverConnection closure
AsyncCall::Pointer closeHandler; /*The close handler for pinned server side connection*/
bool handleRequestBodyData();
- /// Forward future client requests using the given server connection.
- /// Optionally, monitor pinned server connection for remote-end closures.
- void pinConnection(const Comm::ConnectionPointer &pinServerConn, HttpRequest *request, CachePeer *peer, bool auth, bool monitor = true);
+ /// parameters for the async notePinnedConnectionBecameIdle() call
+ class PinnedIdleContext
+ {
+ public:
+ PinnedIdleContext(const Comm::ConnectionPointer &conn, const HttpRequest::Pointer &req): connection(conn), request(req) {}
+
+ Comm::ConnectionPointer connection; ///< to-server connection to be pinned
+ HttpRequest::Pointer request; ///< to-server request that initiated serverConnection
+ };
+
+ /// Called when a pinned connection becomes available for forwarding the next request.
+ void notePinnedConnectionBecameIdle(PinnedIdleContext pic);
+ /// Forward future client requests using the given to-server connection.
+ /// The connection is still being used by the current client request.
+ void pinBusyConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest::Pointer &request);
/// Undo pinConnection() and, optionally, close the pinned connection.
void unpinConnection(const bool andClose);
- /// Returns validated pinnned server connection (and stops its monitoring).
- Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *request, const CachePeer *aPeer);
- /**
- * Checks if there is pinning info if it is valid. It can close the server side connection
- * if pinned info is not valid.
- \param request if it is not NULL also checks if the pinning info refers to the request client side HttpRequest
- \param CachePeer if it is not NULL also check if the CachePeer is the pinning CachePeer
- \return The details of the server side connection (may be closed if failures were present).
- */
- const Comm::ConnectionPointer validatePinnedConnection(HttpRequest *request, const CachePeer *peer);
+
+ /// \returns validated pinned to-server connection, stopping its monitoring
+ /// \throws a newly allocated ErrorState if validation fails
+ static Comm::ConnectionPointer BorrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &);
/**
* returts the pinned CachePeer if exists, NULL otherwise
*/
/// The caller assumes responsibility for connection closure detection.
void stopPinnedConnectionMonitoring();
-#if USE_OPENSSL
/// the second part of old httpsAccept, waiting for future HttpsServer home
void postHttpsAccept();
+#if USE_OPENSSL
/// Initializes and starts a peek-and-splice negotiation with the SSL client
void startPeekAndSplice();
- /// Called when the initialization of peek-and-splice negotiation finidhed
- void startPeekAndSpliceDone();
+
/// Called when a peek-and-splice step finished. For example after
/// server SSL certificates received and fake server SSL certificates
/// generated
void doPeekAndSpliceStep();
/// called by FwdState when it is done bumping the server
- void httpsPeeked(Comm::ConnectionPointer serverConnection);
+ void httpsPeeked(PinnedIdleContext pic);
/// Splice a bumped client connection on peek-and-splice mode
- void splice();
-
- /// Check on_unsupported_protocol access list and splice if required
- /// \retval true on splice
- /// \retval false otherwise
- bool spliceOnError(const err_type err);
+ bool splice();
- /// Start to create dynamic Security::ContextPtr for host or uses static port SSL context.
+ /// Start to create dynamic Security::ContextPointer for host or uses static port SSL context.
void getSslContextStart();
- /**
- * Done create dynamic ssl certificate.
- *
- * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
- */
- void getSslContextDone(Security::ContextPtr sslContext, bool isNew = false);
+
+ /// finish configuring the newly created SSL context"
+ void getSslContextDone(Security::ContextPointer &);
+
/// Callback function. It is called when squid receive message from ssl_crtd.
static void sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply);
/// Proccess response from ssl_crtd.
void sslCrtdHandleReply(const Helper::Reply &reply);
- void switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode);
+ void switchToHttps(ClientHttpRequest *, Ssl::BumpMode bumpServerMode);
+ void parseTlsHandshake();
bool switchedToHttps() const { return switchedToHttps_; }
Ssl::ServerBump *serverBump() {return sslServerBump;}
inline void setServerBump(Ssl::ServerBump *srvBump) {
}
const SBuf &sslCommonName() const {return sslCommonName_;}
void resetSslCommonName(const char *name) {sslCommonName_ = name;}
+ const SBuf &tlsClientSni() const { return tlsClientSni_; }
/// Fill the certAdaptParams with the required data for certificate adaptation
/// and create the key for storing/retrieve the certificate to/from the cache
void buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties);
bool serveDelayedError(Http::Stream *);
Ssl::BumpMode sslBumpMode; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
+
+ /// Tls parser to use for client HELLO messages parsing on bumped
+ /// connections.
Security::HandshakeParser tlsParser;
#else
bool switchedToHttps() const { return false; }
#endif
-
- /* clt_conn_tag=tag annotation access */
- const SBuf &connectionTag() const { return connectionTag_; }
- void connectionTag(const char *aTag) { connectionTag_ = aTag; }
+ char *prepareTlsSwitchingURL(const Http1::RequestParserPointer &hp);
/// handle a control message received by context from a peer and call back
- virtual void writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
+ virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
/// ClientStream calls this to supply response header (once) and data
/// for the current Http::Stream.
/// generate a fake CONNECT request with the given payload
/// at the beginning of the client I/O buffer
- void fakeAConnectRequest(const char *reason, const SBuf &payload);
+ bool fakeAConnectRequest(const char *reason, const SBuf &payload);
+
+ /// generates and sends to tunnel.cc a fake request with a given payload
+ bool initiateTunneledRequest(HttpRequest::Pointer const &cause, Http::MethodType const method, const char *reason, const SBuf &payload);
+
+ /// whether we should start saving inBuf client bytes in anticipation of
+ /// tunneling them to the server later (on_unsupported_protocol)
+ bool shouldPreserveClientData() const;
- /// client data which may need to forward as-is to server after an
- /// on_unsupported_protocol tunnel decision.
+ // TODO: move to the protected section when removing clientTunnelOnError()
+ bool tunnelOnError(const HttpRequestMethod &, const err_type);
+
+ /// build a fake http request
+ ClientHttpRequest *buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload);
+
+ /// From-client handshake bytes (including bytes at the beginning of a
+ /// CONNECT tunnel) which we may need to forward as-is if their syntax does
+ /// not match the expected TLS or HTTP protocol (on_unsupported_protocol).
SBuf preservedClientData;
/* Registered Runner API */
virtual void startShutdown();
virtual void endingShutdown();
+ /// \returns existing non-empty connection annotations,
+ /// creates and returns empty annotations otherwise
+ NotePairs::Pointer notes();
+ bool hasNotes() const { return bool(theNotes) && !theNotes->empty(); }
+
+ const ProxyProtocol::HeaderPointer &proxyProtocolHeader() const { return proxyProtocolHeader_; }
+
protected:
void startDechunkingRequest();
void finishDechunkingRequest(bool withSuccess);
void abortChunkedRequestBody(const err_type error);
err_type handleChunkedRequestBody();
+ /// ConnStateData-specific part of BorrowPinnedConnection()
+ Comm::ConnectionPointer borrowPinnedConnection(HttpRequest *, const AccessLogEntryPointer &);
+
void startPinnedConnectionMonitoring();
void clientPinnedConnectionRead(const CommIoCbParams &io);
#if USE_OPENSSL
bool handleIdleClientPinnedTlsRead();
#endif
+ /// Parse an HTTP request
+ /// \note Sets result->flags.parsed_ok to 0 if failed to parse the request,
+ /// to 1 if the request was correctly parsed
+ /// \param[in] hp an Http1::RequestParser
+ /// \return NULL on incomplete requests,
+ /// a Http::Stream on success or failure.
+ /// TODO: Move to HttpServer. Warning: Move requires large code nonchanges!
+ Http::Stream *parseHttpRequest(const Http1::RequestParserPointer &);
+
/// parse input buffer prefix into a single transfer protocol request
/// return NULL to request more header bytes (after checking any limits)
/// use abortRequestParsing() to handle parsing errors w/o creating request
virtual Http::Stream *parseOneRequest() = 0;
/// start processing a freshly parsed request
- virtual void processParsedRequest(Http::Stream *) = 0;
+ virtual void processParsedRequest(Http::StreamPointer &) = 0;
/// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
virtual int pipelinePrefetchMax() const;
/// timeout to use when waiting for the next request
virtual time_t idleTimeout() const = 0;
+ /// Perform client data lookups that depend on client src-IP.
+ /// The PROXY protocol may require some data input first.
+ void whenClientIpKnown();
+
BodyPipe::Pointer bodyPipe; ///< set when we are reading request body
+ /// whether preservedClientData is valid and should be kept up to date
+ bool preservingClientData_;
+
private:
/* ::Server API */
virtual bool connFinishedWithConn(int size);
+ virtual void checkLogging();
void clientAfterReadingRequests();
bool concurrentRequestQueueFilled() const;
- void pinNewConnection(const Comm::ConnectionPointer &pinServer, HttpRequest *request, CachePeer *aPeer, bool auth);
+ void pinConnection(const Comm::ConnectionPointer &pinServerConn, const HttpRequest &request);
/* PROXY protocol functionality */
bool proxyProtocolValidateClient();
bool parseProxyProtocolHeader();
- bool parseProxy1p0();
- bool parseProxy2p0();
bool proxyProtocolError(const char *reason);
+#if USE_OPENSSL
+ /// \returns a pointer to the matching cached TLS context or nil
+ Security::ContextPointer getTlsContextFromCache(const SBuf &cacheKey, const Ssl::CertificateProperties &certProperties);
+
+ /// Attempts to add a given TLS context to the cache, replacing the old
+ /// same-key context, if any
+ void storeTlsContextToCache(const SBuf &cacheKey, Security::ContextPointer &ctx);
+#endif
+
/// whether PROXY protocol header is still expected
bool needProxyProtocolHeader_;
+ /// the parsed PROXY protocol header
+ ProxyProtocol::HeaderPointer proxyProtocolHeader_;
+
#if USE_AUTH
/// some user details that can be used to perform authentication on this connection
Auth::UserRequest::Pointer auth_;
#endif
- /// the parser state for current HTTP/1.x input buffer processing
- Http1::RequestParserPointer parser_;
-
#if USE_OPENSSL
bool switchedToHttps_;
- /// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
- String sslConnectHostOrIp; ///< The SSL server host name as passed in the CONNECT request
+ bool parsingTlsHandshake; ///< whether we are getting/parsing TLS Hello bytes
+ /// The number of parsed HTTP requests headers on a bumped client connection
+ uint64_t parsedBumpedRequestCount;
+
+ /// The TLS server host name appears in CONNECT request or the server ip address for the intercepted requests
+ SBuf tlsConnectHostOrIp; ///< The TLS server host name as passed in the CONNECT request
+ unsigned short tlsConnectPort; ///< The TLS server port number as passed in the CONNECT request
SBuf sslCommonName_; ///< CN name for SSL certificate generation
- String sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
+
+ /// TLS client delivered SNI value. Empty string if none has been received.
+ SBuf tlsClientSni_;
+ SBuf sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
/// HTTPS server cert. fetching state for bump-ssl-server-first
Ssl::ServerBump *sslServerBump;
Ssl::CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
- bool atTlsPeek;
#endif
/// the reason why we no longer write the response or nil
const char *stoppedSending_;
/// the reason why we no longer read the request or nil
const char *stoppedReceiving_;
-
- SBuf connectionTag_; ///< clt_conn_tag=Tag annotation for client connection
+ /// Connection annotations, clt_conn_tag and other tags are stored here.
+ /// If set, are propagated to the current and all future master transactions
+ /// on the connection.
+ NotePairs::Pointer theNotes;
};
-void setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl = false);
-
const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end = NULL);
int varyEvaluateMatch(StoreEntry * entry, HttpRequest * req);
void clientSetKeepaliveFlag(ClientHttpRequest *http);
/// append a "part" HTTP header (as in a multi-part/range reply) to the buffer
-void clientPackRangeHdr(const HttpReply *, const HttpHdrRangeSpec *, String boundary, MemBuf *);
+void clientPackRangeHdr(const HttpReplyPointer &, const HttpHdrRangeSpec *, String boundary, MemBuf *);
/// put terminating boundary for multiparts to the buffer
void clientPackTermBound(String boundary, MemBuf *);
CSCB clientSocketRecipient;
CSD clientSocketDetach;
-/* TODO: Move to HttpServer. Warning: Move requires large code nonchanges! */
-Http::Stream *parseHttpRequest(ConnStateData *, const Http1::RequestParserPointer &);
void clientProcessRequest(ConnStateData *, const Http1::RequestParserPointer &, Http::Stream *);
void clientPostHttpsAccept(ConnStateData *);
+std::ostream &operator <<(std::ostream &os, const ConnStateData::PinnedIdleContext &pic);
+
#endif /* SQUID_CLIENTSIDE_H */