Chromium Code Reviews| Index: webrtc/base/openssladapter.cc |
| diff --git a/webrtc/base/openssladapter.cc b/webrtc/base/openssladapter.cc |
| index bc7b99b97a61449ab785eb24c8697e0dcfdf485b..fbab6c84efbd9c8936926a80574d2874274a1050 100644 |
| --- a/webrtc/base/openssladapter.cc |
| +++ b/webrtc/base/openssladapter.cc |
| @@ -154,6 +154,21 @@ static long socket_ctrl(BIO* b, int cmd, long num, void* ptr) { |
| } |
| } |
| +static void LogSslError() { |
| + // Walk down the error stack to find the SSL error. |
| + uint32_t error_code; |
| + const char* file; |
| + int line; |
| + do { |
| + error_code = ERR_get_error_line(&file, &line); |
| + if (ERR_GET_LIB(error_code) == ERR_LIB_SSL) { |
| + LOG(LS_ERROR) << "ERR_LIB_SSL: " << error_code << ", " << file << ":" |
| + << line; |
| + break; |
| + } |
| + } while (error_code != 0); |
| +} |
| + |
| ///////////////////////////////////////////////////////////////////////////// |
| // OpenSSLAdapter |
| ///////////////////////////////////////////////////////////////////////////// |
| @@ -334,6 +349,14 @@ OpenSSLAdapter::BeginSSL() { |
| SSL_set_app_data(ssl_, this); |
| SSL_set_bio(ssl_, bio, bio); |
| + // SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER allows different buffers to be passed |
| + // into SSL_write when a record could only be partially transmitted (and thus |
| + // requires another call to SSL_write to finish transmission). This allows us |
| + // to copy the data into our own buffer when this occurs, since the original |
| + // buffer can't safely be accessed after control exits Send. |
| + // TODO(deadbeef): Do we want SSL_MODE_ENABLE_PARTIAL_WRITE? It doesn't |
| + // appear Send handles partial writes properly, though maybe we never notice |
| + // since we never send more than 16KB at once.. |
| SSL_set_mode(ssl_, SSL_MODE_ENABLE_PARTIAL_WRITE | |
| SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); |
| @@ -426,6 +449,7 @@ OpenSSLAdapter::Cleanup() { |
| ssl_read_needs_write_ = false; |
| ssl_write_needs_read_ = false; |
| custom_verification_succeeded_ = false; |
| + pending_data_.Clear(); |
| if (ssl_) { |
| SSL_free(ssl_); |
| @@ -441,6 +465,46 @@ OpenSSLAdapter::Cleanup() { |
| Thread::Current()->Clear(this, MSG_TIMEOUT); |
| } |
| +int OpenSSLAdapter::DoSslWrite(const void* pv, size_t cb, int* error) { |
| + // If we have pending data (that was previously only partially written by |
| + // SSL_write), we shouldn't be attempting to write anything else. |
| + RTC_DCHECK(pending_data_.empty() || pv == pending_data_.data()); |
| + RTC_DCHECK(error != nullptr); |
| + |
| + ssl_write_needs_read_ = false; |
| + int ret = SSL_write(ssl_, pv, checked_cast<int>(cb)); |
| + *error = SSL_get_error(ssl_, ret); |
| + switch (*error) { |
| + case SSL_ERROR_NONE: |
| + // Success! |
| + return ret; |
| + case SSL_ERROR_WANT_READ: |
| + LOG(LS_INFO) << " -- error want read"; |
| + ssl_write_needs_read_ = true; |
| + SetError(EWOULDBLOCK); |
| + break; |
| + case SSL_ERROR_WANT_WRITE: |
| + LOG(LS_INFO) << " -- error want write"; |
| + SetError(EWOULDBLOCK); |
| + break; |
| + case SSL_ERROR_ZERO_RETURN: |
| + // LOG(LS_INFO) << " -- remote side closed"; |
| + SetError(EWOULDBLOCK); |
| + // do we need to signal closure? |
| + break; |
| + case SSL_ERROR_SSL: |
| + LogSslError(); |
| + Error("SSL_write", ret ? ret : -1, false); |
| + break; |
| + default: |
| + LOG(LS_WARNING) << "Unknown error from SSL_write: " << *error; |
| + Error("SSL_write", ret ? ret : -1, false); |
| + break; |
| + } |
| + |
| + return SOCKET_ERROR; |
| +} |
| + |
| // |
| // AsyncSocket Implementation |
| // |
| @@ -466,38 +530,52 @@ OpenSSLAdapter::Send(const void* pv, size_t cb) { |
| return SOCKET_ERROR; |
| } |
| + int ret; |
| + int error; |
| + |
| + if (!pending_data_.empty()) { |
| + ret = DoSslWrite(pending_data_.data(), pending_data_.size(), &error); |
| + if (ret != static_cast<int>(pending_data_.size())) { |
| + // We couldn't finish sending the pending data, so we definitely can't |
| + // send any more data. Return with an EWOULDBLOCK error. |
| + SetError(EWOULDBLOCK); |
| + return SOCKET_ERROR; |
| + } |
| + // We completed sending the data previously passed into SSL_write! Now |
| + // we're allowed to send more data. |
| + pending_data_.Clear(); |
| + } |
| + |
| // OpenSSL will return an error if we try to write zero bytes |
| if (cb == 0) |
| return 0; |
| - ssl_write_needs_read_ = false; |
| - |
| - int code = SSL_write(ssl_, pv, checked_cast<int>(cb)); |
| - switch (SSL_get_error(ssl_, code)) { |
| - case SSL_ERROR_NONE: |
| - //LOG(LS_INFO) << " -- success"; |
| - return code; |
| - case SSL_ERROR_WANT_READ: |
| - //LOG(LS_INFO) << " -- error want read"; |
| - ssl_write_needs_read_ = true; |
| - SetError(EWOULDBLOCK); |
| - break; |
| - case SSL_ERROR_WANT_WRITE: |
| - //LOG(LS_INFO) << " -- error want write"; |
| - SetError(EWOULDBLOCK); |
| - break; |
| - case SSL_ERROR_ZERO_RETURN: |
| - //LOG(LS_INFO) << " -- remote side closed"; |
| - SetError(EWOULDBLOCK); |
| - // do we need to signal closure? |
| - break; |
| - default: |
| - //LOG(LS_INFO) << " -- error " << code; |
| - Error("SSL_write", (code ? code : -1), false); |
| - break; |
| + ret = DoSslWrite(pv, cb, &error); |
| + |
| + // If SSL_write fails with SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE, this |
| + // means the underlying socket is blocked on reading or (more typically) |
| + // writing. When this happens, OpenSSL requires that the next call to |
| + // SSL_write uses the same arguments (though, with |
| + // SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER, the actual buffer pointer may be |
| + // different). |
| + // |
| + // However, after Send exits, we will have lost access to data the user of |
| + // this class is trying to send, and there's no guarantee that the user of |
| + // this class will call Send with the same arguements when it fails. So, we |
| + // buffer the data ourselves. When we know the underlying socket is writable |
| + // again from OnWriteEvent (or if Send is called again before that happens), |
| + // we'll retry sending this buffered data. |
| + if ((error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE) && |
| + pending_data_.empty()) { |
|
juberti1
2017/06/02 17:47:22
Is it possible for pending_data_ to *not* be empty
Taylor Brandstetter
2017/06/02 18:18:42
Oh, it isn't possible now that this code was shuff
|
| + LOG(LS_WARNING) |
| + << "SSL_write couldn't write to the underlying socket; buffering data."; |
| + pending_data_.SetData(static_cast<const uint8_t*>(pv), cb); |
| + // Since we're taking responsibility for sending this data, return its full |
| + // size. The user of this class can consider it sent. |
| + return cb; |
| } |
| - return SOCKET_ERROR; |
| + return ret; |
| } |
| int |
| @@ -539,28 +617,33 @@ int OpenSSLAdapter::Recv(void* pv, size_t cb, int64_t* timestamp) { |
| ssl_read_needs_write_ = false; |
| int code = SSL_read(ssl_, pv, checked_cast<int>(cb)); |
| - switch (SSL_get_error(ssl_, code)) { |
| - case SSL_ERROR_NONE: |
| - //LOG(LS_INFO) << " -- success"; |
| - return code; |
| - case SSL_ERROR_WANT_READ: |
| - //LOG(LS_INFO) << " -- error want read"; |
| - SetError(EWOULDBLOCK); |
| - break; |
| - case SSL_ERROR_WANT_WRITE: |
| - //LOG(LS_INFO) << " -- error want write"; |
| - ssl_read_needs_write_ = true; |
| - SetError(EWOULDBLOCK); |
| - break; |
| - case SSL_ERROR_ZERO_RETURN: |
| - //LOG(LS_INFO) << " -- remote side closed"; |
| - SetError(EWOULDBLOCK); |
| - // do we need to signal closure? |
| - break; |
| - default: |
| - //LOG(LS_INFO) << " -- error " << code; |
| - Error("SSL_read", (code ? code : -1), false); |
| - break; |
| + int error = SSL_get_error(ssl_, code); |
| + switch (error) { |
| + case SSL_ERROR_NONE: |
| + // LOG(LS_INFO) << " -- success"; |
| + return code; |
| + case SSL_ERROR_WANT_READ: |
| + // LOG(LS_INFO) << " -- error want read"; |
| + SetError(EWOULDBLOCK); |
| + break; |
| + case SSL_ERROR_WANT_WRITE: |
| + // LOG(LS_INFO) << " -- error want write"; |
| + ssl_read_needs_write_ = true; |
| + SetError(EWOULDBLOCK); |
| + break; |
| + case SSL_ERROR_ZERO_RETURN: |
| + // LOG(LS_INFO) << " -- remote side closed"; |
| + SetError(EWOULDBLOCK); |
| + // do we need to signal closure? |
| + break; |
| + case SSL_ERROR_SSL: |
| + LogSslError(); |
| + Error("SSL_read", (code ? code : -1), false); |
| + break; |
| + default: |
| + LOG(LS_WARNING) << "Unknown error from SSL_read: " << error; |
| + Error("SSL_read", (code ? code : -1), false); |
| + break; |
| } |
| return SOCKET_ERROR; |
| @@ -682,6 +765,16 @@ OpenSSLAdapter::OnWriteEvent(AsyncSocket* socket) { |
| AsyncSocketAdapter::OnReadEvent(socket); |
| } |
| + // If a previous SSL_write failed due to the underlying socket being blocked, |
| + // this will attempt finishing the write operation. |
| + if (!pending_data_.empty()) { |
| + int error; |
| + if (DoSslWrite(pending_data_.data(), pending_data_.size(), &error) == |
| + static_cast<int>(pending_data_.size())) { |
| + pending_data_.Clear(); |
| + } |
| + } |
| + |
| //LOG(LS_INFO) << " -- onStreamWriteable"; |
| AsyncSocketAdapter::OnWriteEvent(socket); |
| } |