]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Refactored and probably sped up ServerBio reading.
authorAlex Rousskov <rousskov@measurement-factory.com>
Wed, 13 Apr 2016 05:40:24 +0000 (23:40 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Wed, 13 Apr 2016 05:40:24 +0000 (23:40 -0600)
I could not grok the logic of the ServerBio::read*() methods and
saw strange cache.log message sequences like this:

  | bio.cc(121) read: FD 13 read -1 <= 65535
  | bio.cc(126) read: error: 11 ignored: 1
  | bio.cc(139) readAndBuffer: read -1 bytes
  | bio.cc(266) read: Pass 5 bytes to openSSL as read
  | bio.cc(121) read: FD 13 read -1 <= 65535
  | bio.cc(126) read: error: 11 ignored: 1
  | bio.cc(139) readAndBuffer: read -1 bytes
  | bio.cc(266) read: Pass 4 bytes to openSSL as read

Initially, they looked like we were [incorrectly] subtracting -1 from
some buffer length (read -1, pass 5; read -1, pass 4). Now, I believe
they [just] indicated unnecessary network reads. The fixed sequence
looks similar to this (note no network reads):

  | bio.cc(289) giveBuffered: Pass 5 read bytes to openSSL
  | bio.cc(289) giveBuffered: Pass 4 read bytes to openSSL

The refactored ServerBio code starts in "parsing" state (SSL Hello
parsing is the primary ServerBio functionality). Only when that parsing
is over, ServerBio starts feeding OpenSSL with received bytes. This
internal logic allows us to hide parsing from callers and avoid the
confusing public holdRead API.

src/ssl/PeerConnector.cc
src/ssl/bio.cc
src/ssl/bio.h

index 73973794b44abfbacd55debe910255f88c16456e..44a0e292dfca7bef95284772de011f77652a01cf 100644 (file)
@@ -361,23 +361,6 @@ void
 Ssl::PeerConnector::noteWantRead()
 {
     const int fd = serverConnection()->fd;
-    Security::SessionPtr ssl = fd_table[fd].ssl.get();
-    BIO *b = SSL_get_rbio(ssl);
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
-    if (srvBio->holdRead()) {
-        if (srvBio->gotHello()) {
-            srvBio->holdRead(false);
-            // Schedule a negotiateSSl to allow openSSL parse received data
-            Ssl::PeerConnector::NegotiateSsl(fd, this);
-            return;
-        } else if (srvBio->gotHelloFailed()) {
-            srvBio->holdRead(false);
-            debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd);
-            // Schedule a negotiateSSl to allow openSSL parse received data
-            Ssl::PeerConnector::NegotiateSsl(fd, this);
-            return;
-        }
-    }
     setReadTimeout();
     Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0);
 }
index 506f0626415ab7847c38e84b8bad576de16e5085..bf85a16b64fd051dff3360a62308a03f42e586d2 100644 (file)
@@ -131,20 +131,6 @@ Ssl::Bio::read(char *buf, int size, BIO *table)
     return result;
 }
 
-int
-Ssl::Bio::readAndBuffer(BIO *table, const char *description)
-{
-    char buf[SQUID_TCP_SO_RCVBUF ];
-    const int bytes = Ssl::Bio::read(buf, sizeof(buf), table);
-    debugs(83, 5, "read " << bytes << " bytes"); // move to Ssl::Bio::read()
-
-    if (bytes > 0) {
-        rbuf.append(buf, bytes);
-        debugs(83, 5, "recorded " << bytes << " bytes of " << description);
-    }
-    return bytes;
-}
-
 /// Called whenever the SSL connection state changes, an alert appears, or an
 /// error occurs. See SSL_set_info_callback().
 void
@@ -226,50 +212,82 @@ Ssl::ServerBio::setClientFeatures(Security::TlsDetails::Pointer const &details,
 };
 
 int
-Ssl::ServerBio::readAndBufferServerHelloMsg(BIO *table, const char *description)
+Ssl::ServerBio::read(char *buf, int size, BIO *table)
 {
+    if (!parser_.parseDone && !parser_.parseError) // not done parsing yet
+        return readAndParse(buf, size, table);
+    else
+        return readAndGive(buf, size, table);
+}
 
-    int ret = readAndBuffer(table, description);
-    if (ret <= 0)
-        return ret;
+/// Read and give everything to OpenSSL.
+int
+Ssl::ServerBio::readAndGive(char *buf, const int size, BIO *table)
+{
+    // If we have unused buffered bytes, give those bytes to OpenSSL now,
+    // before reading more. TODO: Read if we have buffered less than size?
+    if (rbufConsumePos < rbuf.length())
+        return giveBuffered(buf, size);
+
+    if (record_) {
+        const int result = readAndBuffer(table);
+        if (result <= 0)
+            return result;
+        return giveBuffered(buf, size);
+    }
+    
+    return Ssl::Bio::read(buf, size, table);
+}
+
+/// Read and give everything to our parser.
+/// When/if parsing is done, start giving to OpenSSL.
+int
+Ssl::ServerBio::readAndParse(char *buf, const int size, BIO *table)
+{
+    const int result = readAndBuffer(table);
+    if (result <= 0)
+        return result;
 
     if (!parser_.parseServerHello(rbuf)) {
-        if (!parser_.parseError) 
+        if (!parser_.parseError) {
             BIO_set_retry_read(table);
-        return -1;
+            return -1;
+        }
+        debugs(83, DBG_IMPORTANT, "ERROR: Failed to parse SSL Server Hello Message"); // XXX: move to catch{}
     }
 
-    return 1;
+    Must(parser_.parseDone || parser_.parseError);
+    return giveBuffered(buf, size);
 }
 
+/// Reads more data into the read buffer. Returns either the number of bytes
+/// read or, on errors (including "try again" errors), a negative number.
 int
-Ssl::ServerBio::read(char *buf, int size, BIO *table)
+Ssl::ServerBio::readAndBuffer(BIO *table)
 {
-    if (!parser_.parseDone || record_) {
-        int ret = readAndBufferServerHelloMsg(table, "TLS server Hello");
-        if (!rbuf.length() && parser_.parseDone && ret <= 0)
-            return ret;
-    }
+    char *space = rbuf.rawSpace(SQUID_TCP_SO_RCVBUF);
+    const int result = Ssl::Bio::read(space, rbuf.spaceSize(), table);
+    if (result <= 0)
+        return result;
 
-    if (holdRead_) {
-        debugs(83, 7, "Hold flag is set on ServerBio, retry latter. (Hold " << size << "bytes)");
-        BIO_set_retry_read(table);
-        return -1;
-    }
-
-    if (parser_.parseDone && !parser_.parseError) {
-        int unsent = rbuf.length() - rbufConsumePos;
-        if (unsent > 0) {
-            int bytes = (size <= unsent ? size : unsent);
-            memcpy(buf, rbuf.rawContent() + rbufConsumePos, bytes);
-            rbufConsumePos += bytes;
-            debugs(83, 7, "Pass " << bytes << " bytes to openSSL as read");
-            return bytes;
-        } else
-            return Ssl::Bio::read(buf, size, table);
-    }
+    rbuf.forceSize(rbuf.length() + result);
+    return result;
+}
 
-    return -1;
+/// give previously buffered bytes to OpenSSL
+/// returns the number of bytes given
+int
+Ssl::ServerBio::giveBuffered(char *buf, const int size)
+{
+    if (rbuf.length() <= rbufConsumePos)
+        return -1; // buffered nothing yet
+
+    const int unsent = rbuf.length() - rbufConsumePos;
+    const int bytes = (size <= unsent ? size : unsent);
+    memcpy(buf, rbuf.rawContent() + rbufConsumePos, bytes);
+    rbufConsumePos += bytes;
+    debugs(83, 7, bytes << "<=" << size << " bytes to OpenSSL");
+    return bytes;
 }
 
 // This function makes the required checks to examine if the client hello
@@ -404,7 +422,7 @@ Ssl::ServerBio::write(const char *buf, int size, BIO *table)
 {
 
     if (holdWrite_) {
-        debugs(83, 7,  "Hold write, for SSL connection on " << fd_ << "will not write bytes of size " << size);
+        debugs(83, 7, "postpone writing " << size << " bytes to SSL FD " << fd_);
         BIO_set_retry_write(table);
         return -1;
     }
index f29ac52b25956df0f5aa7d7090803ad797f5dafb..5e183d71729adb5f6dd346cf49e79e21dd271c27 100644 (file)
@@ -58,9 +58,6 @@ public:
     /// Tells ssl connection to use BIO and monitor state via stateChanged()
     static void Link(SSL *ssl, BIO *bio);
 
-    /// Reads data from socket and record them to a buffer
-    int readAndBuffer(BIO *table, const char *description);
-
     const SBuf &rBufData() {return rbuf;}
 protected:
     const int fd_; ///< the SSL socket we are reading and writing
@@ -114,7 +111,7 @@ private:
 class ServerBio: public Bio
 {
 public:
-    explicit ServerBio(const int anFd): Bio(anFd), helloMsgSize(0), helloBuild(false), allowSplice(false), allowBump(false), holdWrite_(false), holdRead_(true), record_(false), bumpMode_(bumpNone), rbufConsumePos(0) {}
+    explicit ServerBio(const int anFd): Bio(anFd), helloMsgSize(0), helloBuild(false), allowSplice(false), allowBump(false), holdWrite_(false), record_(false), bumpMode_(bumpNone), rbufConsumePos(0) {}
     /// The ServerBio version of the Ssl::Bio::stateChanged method
     virtual void stateChanged(const SSL *ssl, int where, int ret);
     /// The ServerBio version of the Ssl::Bio::write method
@@ -133,18 +130,10 @@ public:
 
     bool resumingSession();
 
-    /// Reads Server hello message+certificates+ServerHelloDone message sent
-    /// by server and buffer it to rbuf member
-    int readAndBufferServerHelloMsg(BIO *table, const char *description);
-
     /// The write hold state
     bool holdWrite() const {return holdWrite_;}
     /// Enables or disables the write hold state
     void holdWrite(bool h) {holdWrite_ = h;}
-    /// The read hold state
-    bool holdRead() const {return holdRead_;}
-    /// Enables or disables the read hold state
-    void holdRead(bool h) {holdRead_ = h;}
     /// Enables or disables the input data recording, for internal analysis.
     void recordInput(bool r) {record_ = r;}
     /// Whether we can splice or not the SSL stream
@@ -166,6 +155,11 @@ public:
     const Ssl::X509_STACK_Pointer &serverCertificatesIfAny() { return parser_.serverCertificates; } /* XXX: may be nil */
 
 private:
+    int readAndGive(char *buf, const int size, BIO *table);
+    int readAndParse(char *buf, const int size, BIO *table);
+    int readAndBuffer(BIO *table);
+    int giveBuffered(char *buf, const int size);
+
     /// SSL client features extracted from ClientHello message or SSL object
     Security::TlsDetails::Pointer clientTlsDetails;
     /// TLS client hello message, used to adapt our tls Hello message to the server
@@ -176,7 +170,6 @@ private:
     bool allowSplice; ///< True if the SSL stream can be spliced
     bool allowBump;  ///< True if the SSL stream can be bumped
     bool holdWrite_;  ///< The write hold state of the bio.
-    bool holdRead_;  ///< The read hold state of the bio.
     bool record_; ///< If true the input data recorded to rbuf for internal use
     Ssl::BumpMode bumpMode_;