aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/tls/openssl/TLSSocket_OpenSSL.cpp
diff options
context:
space:
mode:
authorVincent Richard <[email protected]>2012-11-29 21:08:25 +0000
committerVincent Richard <[email protected]>2012-11-29 21:33:31 +0000
commit3e9e8c9265f722d294c0060e1ccf29695fa5d2eb (patch)
tree73bb20a0855fbb3cf880f042804f3aa4583e9e8d /src/net/tls/openssl/TLSSocket_OpenSSL.cpp
parentMerge pull request #14 from mabrand/fix-wincrypt (diff)
downloadvmime-3e9e8c9265f722d294c0060e1ccf29695fa5d2eb.tar.gz
vmime-3e9e8c9265f722d294c0060e1ccf29695fa5d2eb.zip
Better handling of SSL_ERROR_WANT_READ/SSL_ERROR_WANT_WRITE. Sockets on Windows platform are now non-blocking (thanks to Mehmet Bozkurt).
Diffstat (limited to 'src/net/tls/openssl/TLSSocket_OpenSSL.cpp')
-rwxr-xr-xsrc/net/tls/openssl/TLSSocket_OpenSSL.cpp102
1 files changed, 86 insertions, 16 deletions
diff --git a/src/net/tls/openssl/TLSSocket_OpenSSL.cpp b/src/net/tls/openssl/TLSSocket_OpenSSL.cpp
index cbd1f9fd..25937e32 100755
--- a/src/net/tls/openssl/TLSSocket_OpenSSL.cpp
+++ b/src/net/tls/openssl/TLSSocket_OpenSSL.cpp
@@ -54,7 +54,7 @@ ref <TLSSocket> TLSSocket::wrap(ref <TLSSession> session, ref <socket> sok)
TLSSocket_OpenSSL::TLSSocket_OpenSSL(ref <TLSSession_OpenSSL> session, ref <socket> sok)
- : m_session(session), m_wrapped(sok), m_connected(false), m_ssl(0)
+ : m_session(session), m_wrapped(sok), m_connected(false), m_ssl(0), m_ex(NULL)
{
}
@@ -162,8 +162,10 @@ TLSSocket::size_type TLSSocket_OpenSSL::getBlockSize() const
void TLSSocket_OpenSSL::receive(string& buffer)
{
- const int size = receiveRaw(m_buffer, sizeof(m_buffer));
- buffer = vmime::string(m_buffer, size);
+ const size_type size = receiveRaw(m_buffer, sizeof(m_buffer));
+
+ if (size >= 0)
+ buffer = vmime::string(m_buffer, size);
}
@@ -175,19 +177,35 @@ void TLSSocket_OpenSSL::send(const string& buffer)
TLSSocket::size_type TLSSocket_OpenSSL::receiveRaw(char* buffer, const size_type count)
{
- int rc = SSL_read(m_ssl, buffer, count);
+ int rc = SSL_read(m_ssl, buffer, static_cast <int>(count));
handleError(rc);
+
+ if (rc < 0)
+ return 0;
+
return rc;
}
void TLSSocket_OpenSSL::sendRaw(const char* buffer, const size_type count)
{
- int rc = SSL_write(m_ssl, buffer, count);
+ int rc = SSL_write(m_ssl, buffer, static_cast <int>(count));
handleError(rc);
}
+TLSSocket_OpenSSL::size_type TLSSocket_OpenSSL::sendRawNonBlocking(const char* buffer, const size_type count)
+{
+ int rc = SSL_write(m_ssl, buffer, static_cast <int>(count));
+ handleError(rc);
+
+ if (rc < 0)
+ rc = 0;
+
+ return rc;
+}
+
+
void TLSSocket_OpenSSL::handshake(ref <timeoutHandler> toHandler)
{
if (toHandler)
@@ -260,6 +278,13 @@ ref <security::cert::certificateChain> TLSSocket_OpenSSL::getPeerCertificates()
}
+void TLSSocket_OpenSSL::internalThrow()
+{
+ if (m_ex.get())
+ throw *m_ex;
+}
+
+
void TLSSocket_OpenSSL::handleError(int rc)
{
if (rc > 0) return;
@@ -277,12 +302,14 @@ void TLSSocket_OpenSSL::handleError(int rc)
if (lastError == 0)
{
if (rc == 0)
+ {
throw exceptions::tls_exception("SSL connection unexpectedly closed");
+ }
else
{
vmime::string msg;
std::ostringstream oss(msg);
- oss << "The BIO reported an error: %d" << rc;
+ oss << "The BIO reported an error: " << rc;
oss.flush();
throw exceptions::tls_exception(oss.str());
}
@@ -290,10 +317,16 @@ void TLSSocket_OpenSSL::handleError(int rc)
break;
}
- //// Follwoing errors should not occur
- // With SSL_MODE_AUTO_RETRY these should not happen
case SSL_ERROR_WANT_READ:
+
+ BIO_set_retry_read(SSL_get_rbio(m_ssl));
+ break;
+
case SSL_ERROR_WANT_WRITE:
+
+ BIO_set_retry_write(SSL_get_wbio(m_ssl));
+ break;
+
// This happens only for BIOs of type BIO_s_connect() or BIO_s_accept()
case SSL_ERROR_WANT_CONNECT:
case SSL_ERROR_WANT_ACCEPT:
@@ -319,6 +352,12 @@ void TLSSocket_OpenSSL::handleError(int rc)
}
+unsigned int TLSSocket_OpenSSL::getStatus() const
+{
+ return m_wrapped->getStatus();
+}
+
+
// Implementation of custom BIO methods
@@ -330,9 +369,28 @@ int TLSSocket_OpenSSL::bio_write(BIO* bio, const char* buf, int len)
TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(bio->ptr);
- sok->m_wrapped->sendRaw(buf, len);
+ try
+ {
+ BIO_clear_retry_flags(bio);
+
+ const size_type n = sok->m_wrapped->sendRawNonBlocking(buf, len);
+
+ BIO_clear_retry_flags(bio);
+
+ if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK)
+ {
+ BIO_set_retry_write(bio);
+ return -1;
+ }
- return len;
+ return static_cast <int>(len);
+ }
+ catch (exception& e)
+ {
+ // Workaround for passing C++ exceptions from C BIO functions
+ sok->m_ex.reset(e.clone());
+ return -1;
+ }
}
@@ -344,21 +402,33 @@ int TLSSocket_OpenSSL::bio_read(BIO* bio, char* buf, int len)
TLSSocket_OpenSSL *sok = reinterpret_cast <TLSSocket_OpenSSL*>(bio->ptr);
- const int n = sok->m_wrapped->receiveRaw(buf, len);
+ try
+ {
+ const size_type n = sok->m_wrapped->receiveRaw(buf, len);
- if (n == 0)
- BIO_set_retry_read(bio); // This seems like a hack, really...
- else
BIO_clear_retry_flags(bio);
- return n;
+ if (sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK)
+ {
+ BIO_set_retry_read(bio);
+ return -1;
+ }
+
+ return static_cast <int>(n);
+ }
+ catch (exception& e)
+ {
+ // Workaround for passing C++ exceptions from C BIO functions
+ sok->m_ex.reset(e.clone());
+ return -1;
+ }
}
// static
int TLSSocket_OpenSSL::bio_puts(BIO* bio, const char* str)
{
- return bio_write(bio, str, strlen(str));
+ return bio_write(bio, str, static_cast <int>(strlen(str)));
}