]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/client_side.h
Source Format Enforcement (#532)
[thirdparty/squid.git] / src / client_side.h
index bce603db72bcb418e0d6ed942c159d5b8877de4e..cfa9ec08cffe013201d24837f329a68ef3143675 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2015 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 "SBuf.h"
+#include "log/forward.h"
+#include "proxyp/forward.h"
+#include "sbuf/SBuf.h"
 #include "servers/Server.h"
 #if USE_AUTH
 #include "auth/UserRequest.h"
 #endif
 #if USE_OPENSSL
+#include "security/Handshake.h"
 #include "ssl/support.h"
 #endif
+#if USE_DELAY_POOLS
+#include "MessageBucket.h"
+#endif
 
-class ConnStateData;
-class ClientHttpRequest;
-class clientStreamNode;
-namespace AnyP
-{
-class PortCfg;
-} // namespace Anyp
-
-/**
- * Badly named.
- * This is in fact the processing context for a single HTTP transaction.
- *
- * A context lifetime extends from directly after a request has been parsed
- * off the client connection buffer, until the last byte of both request
- * and reply payload (if any) have been written.
- *
- * (NOTE: it is not certain yet if an early reply to a POST/PUT is sent by
- * the server whether the context will remain in the pipeline until its
- * request payload has finished being read. It is supposed to, but may not)
- *
- * Contexts self-register with the Pipeline being managed by the Server
- * for the connection on which the request was received.
- *
- * When HTTP/1 pipeline is operating there may be multiple transactions using
- * the clientConnection. Only the back() context may read from the connection,
- * and only the front() context may write to it. A context which needs to read
- * or write to the connection but does not meet those criteria must be shifted
- * to the deferred state.
- *
- * When a context is completed the finished() method needs to be called which
- * will perform all cleanup and deregistration operations. If the reason for
- * finishing is an error, then notifyIoError() needs to be called prior to
- * the finished() method.
- * The caller should follow finished() with a call to ConnStateData::kick()
- * to resume processing of other transactions or I/O on the connection.
- *
- * Alternatively the initiateClose() method can be called to terminate the
- * whole client connection and all other pending contexts.
- *
- * The socket level management is done by a Server which owns us.
- * The scope of this objects control over a socket consists of the data
- * buffer received from the Server with an initially unknown length.
- * When that length is known it sets the end boundary of our access to the
- * buffer.
- *
- * The individual processing actions are done by other Jobs which we
- * kick off as needed.
- *
- * XXX: If an async call ends the ClientHttpRequest job, ClientSocketContext
- * (and ConnStateData) may not know about it, leading to segfaults and
- * assertions. This is difficult to fix
- * because ClientHttpRequest lacks a good way to communicate its ongoing
- * destruction back to the ClientSocketContext which pretends to "own" *http.
- */
-class ClientSocketContext : public RefCountable
-{
-    CBDATA_CLASS(ClientSocketContext);
-
-public:
-    typedef RefCount<ClientSocketContext> Pointer;
-    ClientSocketContext(const Comm::ConnectionPointer &aConn, ClientHttpRequest *aReq);
-    ~ClientSocketContext();
-    bool startOfOutput() const;
-    void writeComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag);
-
-    Comm::ConnectionPointer clientConnection; /// details about the client connection socket.
-    ClientHttpRequest *http;    /* we pretend to own that job */
-    HttpReply *reply;
-    char reqbuf[HTTP_REQBUF_SZ];
-
-    struct {
-
-        unsigned deferred:1; /* This is a pipelined request waiting for the current object to complete */
-
-        unsigned parsed_ok:1; /* Was this parsed correctly? */
-    } flags;
-    bool mayUseConnection() const {return mayUseConnection_;}
-
-    void mayUseConnection(bool aBool) {
-        mayUseConnection_ = aBool;
-        debugs(33,3, HERE << "This " << this << " marked " << aBool);
-    }
-
-    class DeferredParams
-    {
-
-    public:
-        clientStreamNode *node;
-        HttpReply *rep;
-        StoreIOBuffer queuedBuffer;
-    };
-
-    DeferredParams deferredparams;
-    int64_t writtenToSocket;
-    void pullData();
-    int64_t getNextRangeOffset() const;
-    bool canPackMoreRanges() const;
-    clientStream_status_t socketState();
-    void sendBody(HttpReply * rep, StoreIOBuffer bodyData);
-    void sendStartOfMessage(HttpReply * rep, StoreIOBuffer bodyData);
-    size_t lengthToSend(Range<int64_t> const &available);
-    void noteSentBodyBytes(size_t);
-    void buildRangeHeader(HttpReply * rep);
-    clientStreamNode * getTail() const;
-    clientStreamNode * getClientReplyContext() const;
-    ConnStateData *getConn() const;
-    void finished(); ///< cleanup when the transaction has finished. may destroy 'this'
-    void deferRecipientForLater(clientStreamNode * node, HttpReply * rep, StoreIOBuffer receivedData);
-    bool multipartRangeRequest() const;
-    void registerWithConn();
-    void noteIoError(const int xerrno); ///< update state to reflect I/O error
-
-    /// starts writing 1xx control message to the client
-    void writeControlMsg(HttpControlMsg &msg);
-
-protected:
-    static IOCB WroteControlMsg;
-    void wroteControlMsg(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, Comm::Flag errflag, int xerrno);
-
-private:
-    void prepareReply(HttpReply * rep);
-    void packChunk(const StoreIOBuffer &bodyData, MemBuf &mb);
-    void packRange(StoreIOBuffer const &, MemBuf * mb);
-    void doClose();
-    void initiateClose(const char *reason);
+#include <iosfwd>
 
-    AsyncCall::Pointer cbControlMsgSent; ///< notifies HttpControlMsg Source
+class ClientHttpRequest;
+class HttpHdrRangeSpec;
 
-    bool mayUseConnection_; /* This request may use the connection. Don't read anymore requests for now */
-    bool connRegistered_;
-};
+class MasterXaction;
+typedef RefCount<MasterXaction> MasterXactionPointer;
 
-class ConnectionDetail;
 #if USE_OPENSSL
 namespace Ssl
 {
@@ -183,7 +64,7 @@ class ServerBump;
  * processed.
  *
  * Performs HTTP message processing to kick off the actual HTTP request
- * handling objects (ClientSocketContext, ClientHttpRequest, HttpRequest).
+ * handling objects (Http::Stream, ClientHttpRequest, HttpRequest).
  *
  * Performs SSL-Bump processing for switching between HTTP and HTTPS protocols.
  *
@@ -191,17 +72,22 @@ class ServerBump;
  * 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 */
     virtual void receivedFirstByte();
     virtual bool handleReadData();
     virtual void afterClientRead();
+    virtual void afterClientWrite(size_t);
+
+    /* HttpControlMsgSink API */
+    virtual void sendControlMsg(HttpControlMsg);
+    virtual void doneWithControlMsg();
 
     /// Traffic parsing
     bool clientParseRequests();
@@ -212,9 +98,6 @@ public:
 
     bool isOpen() const;
 
-    // HttpControlMsgSink API
-    virtual void sendControlMsg(HttpControlMsg msg);
-
     Http1::TeChunkedParser *bodyParser; ///< parses HTTP/1.1 chunked request body
 
     /** number of body bytes we need to comm_read for the "current" request
@@ -257,6 +140,7 @@ public:
         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*/
@@ -282,21 +166,27 @@ public:
 
     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
      */
@@ -326,43 +216,36 @@ public:
     /// 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();
+    bool splice();
 
-    /// Check on_unsupported_protocol access list and splice if required
-    /// \retval true on splice
-    /// \retval false otherwise
-    bool spliceOnError(const err_type err);
-
-    /// 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) {
@@ -373,6 +256,7 @@ public:
     }
     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);
@@ -380,23 +264,23 @@ public:
     /// Returns false if no [delayed] error should be written to the client.
     /// Otherwise, writes the error to the client and returns true. Also checks
     /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
-    bool serveDelayedError(ClientSocketContext *context);
+    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(ClientSocketContext *context, 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 ClientSocketContext.
+    /// for the current Http::Stream.
     virtual void handleReply(HttpReply *header, StoreIOBuffer receivedData) = 0;
 
     /// remove no longer needed leading bytes from the input buffer
@@ -405,26 +289,50 @@ public:
     /* TODO: Make the methods below (at least) non-public when possible. */
 
     /// stop parsing the request and create context for relaying error info
-    ClientSocketContext *abortRequestParsing(const char *const errUri);
+    Http::Stream *abortRequestParsing(const char *const errUri);
 
     /// 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;
+
+    // TODO: move to the protected section when removing clientTunnelOnError()
+    bool tunnelOnError(const HttpRequestMethod &, const err_type);
 
-    /// client data which may need to forward as-is to server after an
-    /// on_unsupported_protocol tunnel decision.
+    /// 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
@@ -434,13 +342,22 @@ protected:
     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 ClientSocketContext *parseOneRequest() = 0;
+    virtual Http::Stream *parseOneRequest() = 0;
 
     /// start processing a freshly parsed request
-    virtual void processParsedRequest(ClientSocketContext *context) = 0;
+    virtual void processParsedRequest(Http::StreamPointer &) = 0;
 
     /// returning N allows a pipeline of 1+N requests (see pipeline_prefetch)
     virtual int pipelinePrefetchMax() const;
@@ -448,41 +365,64 @@ protected:
     /// 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;
@@ -493,12 +433,12 @@ private:
     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);
@@ -513,6 +453,12 @@ void httpRequestFree(void *);
 /// decide whether to expect multiple requests on the corresponding connection
 void clientSetKeepaliveFlag(ClientHttpRequest *http);
 
+/// append a "part" HTTP header (as in a multi-part/range reply) to the buffer
+void clientPackRangeHdr(const HttpReplyPointer &, const HttpHdrRangeSpec *, String boundary, MemBuf *);
+
+/// put terminating boundary for multiparts to the buffer
+void clientPackTermBound(String boundary, MemBuf *);
+
 /* misplaced declaratrions of Stream callbacks provided/used by client side */
 SQUIDCEXTERN CSR clientGetMoreData;
 SQUIDCEXTERN CSS clientReplyStatus;
@@ -520,10 +466,10 @@ SQUIDCEXTERN CSD clientReplyDetach;
 CSCB clientSocketRecipient;
 CSD clientSocketDetach;
 
-/* TODO: Move to HttpServer. Warning: Move requires large code nonchanges! */
-ClientSocketContext *parseHttpRequest(ConnStateData *, const Http1::RequestParserPointer &);
-void clientProcessRequest(ConnStateData *, const Http1::RequestParserPointer &, ClientSocketContext *);
+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 */