diff options
Diffstat (limited to 'src/net/tls')
-rw-r--r-- | src/net/tls/gnutls/TLSSocket_GnuTLS.cpp | 56 | ||||
-rwxr-xr-x | src/net/tls/openssl/TLSSession_OpenSSL.cpp | 3 | ||||
-rwxr-xr-x | src/net/tls/openssl/TLSSocket_OpenSSL.cpp | 102 |
3 files changed, 140 insertions, 21 deletions
diff --git a/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp index 35e1d415..477f655b 100644 --- a/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp +++ b/src/net/tls/gnutls/TLSSocket_GnuTLS.cpp @@ -53,7 +53,7 @@ ref <TLSSocket> TLSSocket::wrap(ref <TLSSession> session, ref <socket> sok) TLSSocket_GnuTLS::TLSSocket_GnuTLS(ref <TLSSession_GnuTLS> session, ref <socket> sok) : m_session(session), m_wrapped(sok), m_connected(false), - m_handshaking(false), m_ex(NULL) + m_handshaking(false), m_ex(NULL), m_status(0) { gnutls_transport_set_ptr(*m_session->m_gnutlsSession, this); @@ -131,6 +131,8 @@ void TLSSocket_GnuTLS::send(const string& buffer) TLSSocket::size_type TLSSocket_GnuTLS::receiveRaw(char* buffer, const size_type count) { + m_status &= ~STATUS_WOULDBLOCK; + const ssize_t ret = gnutls_record_recv (*m_session->m_gnutlsSession, buffer, static_cast <size_t>(count)); @@ -141,23 +143,67 @@ TLSSocket::size_type TLSSocket_GnuTLS::receiveRaw(char* buffer, const size_type if (ret < 0) { if (ret == GNUTLS_E_AGAIN) + { + m_status |= STATUS_WOULDBLOCK; return 0; + } TLSSession_GnuTLS::throwTLSException("gnutls_record_recv", ret); } - return static_cast <int>(ret); + return static_cast <size_type>(ret); } void TLSSocket_GnuTLS::sendRaw(const char* buffer, const size_type count) { - gnutls_record_send + ssize_t ret = gnutls_record_send + (*m_session->m_gnutlsSession, + buffer, static_cast <size_t>(count)); + + if (m_ex) + internalThrow(); + + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN) + { + m_status |= STATUS_WOULDBLOCK; + return; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", ret); + } +} + + +TLSSocket::size_type TLSSocket_GnuTLS::sendRawNonBlocking(const char* buffer, const size_type count) +{ + ssize_t ret = gnutls_record_send (*m_session->m_gnutlsSession, buffer, static_cast <size_t>(count)); if (m_ex) internalThrow(); + + if (ret < 0) + { + if (ret == GNUTLS_E_AGAIN) + { + m_status |= STATUS_WOULDBLOCK; + return 0; + } + + TLSSession_GnuTLS::throwTLSException("gnutls_record_send", ret); + } + + return static_cast <size_type>(ret); +} + + +unsigned int TLSSocket_GnuTLS::getStatus() const +{ + return m_status | m_wrapped->getStatus(); } @@ -290,8 +336,8 @@ ssize_t TLSSocket_GnuTLS::gnutlsPullFunc (reinterpret_cast <char*>(data), static_cast <int>(len))); - if (n == 0) - return GNUTLS_E_AGAIN; // This seems like a hack, really... + if (n == 0 && sok->m_wrapped->getStatus() & socket::STATUS_WOULDBLOCK) + return GNUTLS_E_AGAIN; return n; } diff --git a/src/net/tls/openssl/TLSSession_OpenSSL.cpp b/src/net/tls/openssl/TLSSession_OpenSSL.cpp index 5d7e1052..248322d4 100755 --- a/src/net/tls/openssl/TLSSession_OpenSSL.cpp +++ b/src/net/tls/openssl/TLSSession_OpenSSL.cpp @@ -62,6 +62,9 @@ TLSSession_OpenSSL::TLSSession_OpenSSL(ref <vmime::security::cert::certificateVe m_sslctx = SSL_CTX_new(SSLv23_client_method()); SSL_CTX_set_options(m_sslctx, SSL_OP_ALL | SSL_OP_NO_SSLv2); + SSL_CTX_set_mode(m_sslctx, SSL_MODE_AUTO_RETRY); + SSL_CTX_set_cipher_list(m_sslctx, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + SSL_CTX_set_session_cache_mode(m_sslctx, SSL_SESS_CACHE_OFF); } 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))); } |