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);
}
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
};
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
{
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;
}
/// 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
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
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
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
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_;