aboutsummaryrefslogtreecommitdiffstats
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
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).
-rw-r--r--src/net/tls/gnutls/TLSSocket_GnuTLS.cpp56
-rwxr-xr-xsrc/net/tls/openssl/TLSSession_OpenSSL.cpp3
-rwxr-xr-xsrc/net/tls/openssl/TLSSocket_OpenSSL.cpp102
-rw-r--r--src/platforms/posix/posixSocket.cpp72
-rw-r--r--src/platforms/windows/windowsSocket.cpp240
-rw-r--r--src/security/sasl/SASLSocket.cpp34
-rw-r--r--vmime/net/socket.hpp25
-rw-r--r--vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp5
-rwxr-xr-xvmime/net/tls/openssl/TLSSocket_OpenSSL.hpp10
-rw-r--r--vmime/platforms/posix/posixSocket.hpp5
-rw-r--r--vmime/platforms/windows/windowsSocket.hpp19
-rw-r--r--vmime/security/sasl/SASLSocket.hpp3
12 files changed, 515 insertions, 59 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)));
}
diff --git a/src/platforms/posix/posixSocket.cpp b/src/platforms/posix/posixSocket.cpp
index 6fcbd118..b3e33f49 100644
--- a/src/platforms/posix/posixSocket.cpp
+++ b/src/platforms/posix/posixSocket.cpp
@@ -43,6 +43,13 @@
#include "vmime/exception.hpp"
+#if defined(EWOULDBLOCK)
+# define IS_EAGAIN(x) ((x) == EAGAIN || (x) == EWOULDBLOCK || (x) == EINTR)
+#else
+# define IS_EAGAIN(x) ((x) == EAGAIN || (x) == EINTR)
+#endif
+
+
namespace vmime {
namespace platforms {
namespace posix {
@@ -53,7 +60,7 @@ namespace posix {
//
posixSocket::posixSocket(ref <vmime::net::timeoutHandler> th)
- : m_timeoutHandler(th), m_desc(-1)
+ : m_timeoutHandler(th), m_desc(-1), m_status(0)
{
}
@@ -332,13 +339,15 @@ posixSocket::size_type posixSocket::getBlockSize() const
void posixSocket::receive(vmime::string& buffer)
{
- const int size = receiveRaw(m_buffer, sizeof(m_buffer));
+ const size_type size = receiveRaw(m_buffer, sizeof(m_buffer));
buffer = vmime::string(m_buffer, size);
}
posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type count)
{
+ m_status &= ~STATUS_WOULDBLOCK;
+
// Check whether data is available
fd_set fds;
FD_ZERO(&fds);
@@ -348,11 +357,11 @@ posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type cou
tv.tv_sec = 1;
tv.tv_usec = 0;
- int ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv);
+ ssize_t ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv);
if (ret < 0)
{
- if (errno != EAGAIN)
+ if (!IS_EAGAIN(errno))
throwSocketError(errno);
// No data available at this time
@@ -372,6 +381,8 @@ posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type cou
}
}
+ m_status |= STATUS_WOULDBLOCK;
+
// Continue waiting for data
return 0;
}
@@ -381,9 +392,27 @@ posixSocket::size_type posixSocket::receiveRaw(char* buffer, const size_type cou
if (ret < 0)
{
- if (errno != EAGAIN)
+ if (!IS_EAGAIN(errno))
throwSocketError(errno);
+ // Check if we are timed out
+ if (m_timeoutHandler &&
+ m_timeoutHandler->isTimeOut())
+ {
+ if (!m_timeoutHandler->handleTimeOut())
+ {
+ // Server did not react within timeout delay
+ throwSocketError(errno);
+ }
+ else
+ {
+ // Reset timeout
+ m_timeoutHandler->resetTimeOut();
+ }
+ }
+
+ m_status |= STATUS_WOULDBLOCK;
+
// No data available at this time
return 0;
}
@@ -411,15 +440,17 @@ void posixSocket::send(const vmime::string& buffer)
void posixSocket::sendRaw(const char* buffer, const size_type count)
{
+ m_status &= ~STATUS_WOULDBLOCK;
+
size_type size = count;
while (size > 0)
{
- const int ret = ::send(m_desc, buffer, size, 0);
+ const ssize_t ret = ::send(m_desc, buffer, size, 0);
if (ret < 0)
{
- if (errno != EAGAIN)
+ if (!IS_EAGAIN(errno))
throwSocketError(errno);
platform::getHandler()->wait();
@@ -437,6 +468,27 @@ void posixSocket::sendRaw(const char* buffer, const size_type count)
}
+posixSocket::size_type posixSocket::sendRawNonBlocking(const char* buffer, const size_type count)
+{
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ const ssize_t ret = ::send(m_desc, buffer, count, 0);
+
+ if (ret < 0)
+ {
+ if (!IS_EAGAIN(errno))
+ throwSocketError(errno);
+
+ m_status |= STATUS_WOULDBLOCK;
+
+ // No data can be written at this time
+ return 0;
+ }
+
+ return ret;
+}
+
+
void posixSocket::throwSocketError(const int err)
{
string msg;
@@ -473,6 +525,12 @@ void posixSocket::throwSocketError(const int err)
}
+unsigned int posixSocket::getStatus() const
+{
+ return m_status;
+}
+
+
//
// posixSocketFactory
diff --git a/src/platforms/windows/windowsSocket.cpp b/src/platforms/windows/windowsSocket.cpp
index 27cbcaa7..7f58e279 100644
--- a/src/platforms/windows/windowsSocket.cpp
+++ b/src/platforms/windows/windowsSocket.cpp
@@ -40,11 +40,11 @@ namespace windows {
//
-// posixSocket
+// windowsSocket
//
windowsSocket::windowsSocket(ref <vmime::net::timeoutHandler> th)
- : m_timeoutHandler(th), m_desc(-1)
+ : m_timeoutHandler(th), m_desc(INVALID_SOCKET), m_status(0)
{
WSAData wsaData;
WSAStartup(MAKEWORD(1, 1), &wsaData);
@@ -53,8 +53,9 @@ windowsSocket::windowsSocket(ref <vmime::net::timeoutHandler> th)
windowsSocket::~windowsSocket()
{
- if (m_desc != -1)
+ if (m_desc != INVALID_SOCKET)
::closesocket(m_desc);
+
WSACleanup();
}
@@ -62,10 +63,10 @@ windowsSocket::~windowsSocket()
void windowsSocket::connect(const vmime::string& address, const vmime::port_t port)
{
// Close current connection, if any
- if (m_desc != -1)
+ if (m_desc != INVALID_SOCKET)
{
::closesocket(m_desc);
- m_desc = -1;
+ m_desc = INVALID_SOCKET;
}
// Resolve address
@@ -93,35 +94,64 @@ void windowsSocket::connect(const vmime::string& address, const vmime::port_t po
// Get a new socket
m_desc = ::socket(AF_INET, SOCK_STREAM, 0);
- if (m_desc == -1)
- throw vmime::exceptions::connection_error("Error while creating socket.");
+ if (m_desc == INVALID_SOCKET)
+ {
+ try
+ {
+ int err = WSAGetLastError();
+ throwSocketError(err);
+ }
+ catch (exceptions::socket_exception& e)
+ {
+ throw vmime::exceptions::connection_error
+ ("Error while creating socket.", e);
+ }
+ }
// Start connection
if (::connect(m_desc, reinterpret_cast <sockaddr*>(&addr), sizeof(addr)) == -1)
{
- ::closesocket(m_desc);
- m_desc = -1;
+ try
+ {
+ int err = WSAGetLastError();
+ throwSocketError(err);
+ }
+ catch (exceptions::socket_exception& e)
+ {
+ ::closesocket(m_desc);
+ m_desc = INVALID_SOCKET;
- // Error
- throw vmime::exceptions::connection_error("Error while connecting socket.");
+ // Error
+ throw vmime::exceptions::connection_error
+ ("Error while connecting socket.", e);
+ }
}
+
+ // Set socket to non-blocking
+ unsigned long non_blocking = 1;
+ ::ioctlsocket(m_desc, FIONBIO, &non_blocking);
}
bool windowsSocket::isConnected() const
{
- return (m_desc != -1);
+ if (m_desc == INVALID_SOCKET)
+ return false;
+
+ char buff;
+
+ return ::recv(m_desc, &buff, 1, MSG_PEEK) != 0;
}
void windowsSocket::disconnect()
{
- if (m_desc != -1)
+ if (m_desc != INVALID_SOCKET)
{
::shutdown(m_desc, SD_BOTH);
::closesocket(m_desc);
- m_desc = -1;
+ m_desc = INVALID_SOCKET;
}
}
@@ -134,48 +164,200 @@ windowsSocket::size_type windowsSocket::getBlockSize() const
void windowsSocket::receive(vmime::string& buffer)
{
- int ret = ::recv(m_desc, m_buffer, sizeof(m_buffer), 0);
-
- if (ret == -1)
- {
- // Error or no data
- return;
- }
- else if (ret > 0)
- {
- buffer = vmime::string(m_buffer, ret);
- }
+ const size_type size = receiveRaw(m_buffer, sizeof(m_buffer));
+ buffer = vmime::string(m_buffer, size);
}
windowsSocket::size_type windowsSocket::receiveRaw(char* buffer, const size_type count)
{
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ // Check whether data is available
+ bool timedout;
+ waitForData(READ, timedout);
+
+ if (timedout)
+ {
+ // No data available at this time
+ // Check if we are timed out
+ if (m_timeoutHandler &&
+ m_timeoutHandler->isTimeOut())
+ {
+ if (!m_timeoutHandler->handleTimeOut())
+ {
+ // Server did not react within timeout delay
+ throwSocketError(WSAETIMEDOUT);
+ }
+ else
+ {
+ // Reset timeout
+ m_timeoutHandler->resetTimeOut();
+ }
+ }
+
+ // Continue waiting for data
+ return 0;
+ }
+
+ // Read available data
int ret = ::recv(m_desc, buffer, count, 0);
- if (ret == -1)
+ if (ret == SOCKET_ERROR)
{
+ int err = WSAGetLastError();
+
+ if (err != WSAEWOULDBLOCK)
+ throwSocketError(err);
+
+ m_status |= STATUS_WOULDBLOCK;
+
// Error or no data
return (0);
}
+ else if (ret == 0)
+ {
+ // Host shutdown
+ throwSocketError(WSAENOTCONN);
+ }
else
{
- return (ret);
+ // Data received, reset timeout
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+
+ return ret;
}
}
void windowsSocket::send(const vmime::string& buffer)
{
- ::send(m_desc, buffer.data(), buffer.length(), 0);
+ sendRaw(buffer.data(), buffer.length());
}
void windowsSocket::sendRaw(const char* buffer, const size_type count)
{
- ::send(m_desc, buffer, count, 0);
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ size_type size = count;
+
+ while (size > 0)
+ {
+ const int ret = ::send(m_desc, buffer, size, 0);
+
+ if (ret == SOCKET_ERROR)
+ {
+ int err = WSAGetLastError();
+
+ if (err != WSAEWOULDBLOCK)
+ throwSocketError(err);
+
+ bool timedout;
+ waitForData(WRITE, timedout);
+ }
+ else
+ {
+ buffer += ret;
+ size -= ret;
+ }
+ }
+
+ // Reset timeout
+ if (m_timeoutHandler)
+ m_timeoutHandler->resetTimeOut();
+}
+
+
+windowsSocket::size_type windowsSocket::sendRawNonBlocking(const char* buffer, const size_type count, const bool block)
+{
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ const int ret = ::send(m_desc, buffer, count, 0);
+
+ if (ret == SOCKET_ERROR)
+ {
+ int err = WSAGetLastError();
+
+ if (err == WSAEWOULDBLOCK)
+ {
+ m_status |= STATUS_WOULDBLOCK;
+
+ // No data can be written at this time
+ return 0;
+ }
+ else
+ {
+ throwSocketError(err);
+ }
+ }
+
+ return ret;
}
+unsigned int windowsSocket::getStatus() const
+{
+ return m_status;
+}
+
+
+void windowsSocket::throwSocketError(const int err)
+{
+ std::ostringstream oss;
+ string msg;
+
+ LPTSTR str;
+
+ if (::FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, err, 0, (LPTSTR) &str, 0, NULL) == 0)
+ {
+ // Failed getting message
+ oss << "Unknown socket error (code " << err << ")";
+ }
+ else
+ {
+ oss << str;
+ ::LocalFree(str);
+ }
+
+ msg = oss.str();
+
+ throw exceptions::socket_exception(msg);
+}
+
+
+void windowsSocket::waitForData(const WaitOpType t, bool& timedOut)
+{
+ // Check whether data is available
+ fd_set fds;
+ FD_ZERO(&fds);
+ FD_SET(m_desc, &fds);
+
+ struct timeval tv;
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ int ret;
+
+ if (t & READ)
+ ret = ::select(m_desc + 1, &fds, NULL, NULL, &tv);
+ else if (t & WRITE)
+ ret = ::select(m_desc + 1, NULL, &fds, NULL, &tv);
+ else
+ ret = ::select(m_desc + 1, &fds, &fds, NULL, &tv);
+
+ timedOut = (ret == 0);
+
+ if (ret == SOCKET_ERROR)
+ {
+ int err = WSAGetLastError();
+ throwSocketError(err);
+ }
+}
+
//
diff --git a/src/security/sasl/SASLSocket.cpp b/src/security/sasl/SASLSocket.cpp
index 28f5f6f6..531e4188 100644
--- a/src/security/sasl/SASLSocket.cpp
+++ b/src/security/sasl/SASLSocket.cpp
@@ -177,6 +177,40 @@ void SASLSocket::sendRaw(const char* buffer, const size_type count)
}
+SASLSocket::size_type SASLSocket::sendRawNonBlocking(const char* buffer, const size_type count)
+{
+ byte_t* output = 0;
+ int outputLen = 0;
+
+ m_session->getMechanism()->encode
+ (m_session, reinterpret_cast <const byte_t*>(buffer), count,
+ &output, &outputLen);
+
+ size_type bytesSent = 0;
+
+ try
+ {
+ bytesSent = m_wrapped->sendRawNonBlocking
+ (reinterpret_cast <const char*>(output), outputLen);
+ }
+ catch (...)
+ {
+ delete [] output;
+ throw;
+ }
+
+ delete [] output;
+
+ return bytesSent;
+}
+
+
+unsigned int SASLSocket::getStatus() const
+{
+ return m_wrapped->getStatus();
+}
+
+
} // sasl
} // security
} // vmime
diff --git a/vmime/net/socket.hpp b/vmime/net/socket.hpp
index 0fac6514..4551e3e2 100644
--- a/vmime/net/socket.hpp
+++ b/vmime/net/socket.hpp
@@ -47,11 +47,17 @@ class socket : public object
{
public:
+ enum Status
+ {
+ STATUS_WOULDBLOCK = 0x1 /**< The receive operation would block. */
+ };
+
+
virtual ~socket() { }
/** Type used for lengths in streams.
*/
- typedef int size_type;
+ typedef long size_type;
/** Connect to the specified address and port.
@@ -84,7 +90,7 @@ public:
* @param count maximum number of bytes to receive (size of buffer)
* @return number of bytes received/written into output buffer
*/
- virtual int receiveRaw(char* buffer, const size_type count) = 0;
+ virtual size_type receiveRaw(char* buffer, const size_type count) = 0;
/** Send (text) data to the socket.
*
@@ -99,6 +105,15 @@ public:
*/
virtual void sendRaw(const char* buffer, const size_type count) = 0;
+ /** Send (raw) data to the socket.
+ * Function may returns before all data is sent.
+ *
+ * @param buffer data to send
+ * @param count number of bytes to send (size of buffer)
+ * @return number of bytes sent
+ */
+ virtual size_type sendRawNonBlocking(const char* buffer, const size_type count) = 0;
+
/** Return the preferred maximum block size when reading
* from or writing to this stream.
*
@@ -106,6 +121,12 @@ public:
*/
virtual size_type getBlockSize() const = 0;
+ /** Return the current status of this socket.
+ *
+ * @return status flags for this socket
+ */
+ virtual unsigned int getStatus() const = 0;
+
protected:
socket() { }
diff --git a/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp b/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp
index 729923f3..ca113f17 100644
--- a/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp
+++ b/vmime/net/tls/gnutls/TLSSocket_GnuTLS.hpp
@@ -70,9 +70,12 @@ public:
void send(const string& buffer);
void sendRaw(const char* buffer, const size_type count);
+ size_type sendRawNonBlocking(const char* buffer, const size_type count);
size_type getBlockSize() const;
+ unsigned int getStatus() const;
+
private:
void internalThrow();
@@ -97,6 +100,8 @@ private:
ref <timeoutHandler> m_toHandler;
exception* m_ex;
+
+ unsigned int m_status;
};
diff --git a/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp b/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp
index 401fad12..ab4093f7 100755
--- a/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp
+++ b/vmime/net/tls/openssl/TLSSocket_OpenSSL.hpp
@@ -36,6 +36,8 @@
#include "vmime/net/tls/TLSSocket.hpp"
+#include <memory>
+
#include <openssl/ssl.h>
@@ -72,9 +74,12 @@ public:
void send(const string& buffer);
void sendRaw(const char* buffer, const size_type count);
+ size_type sendRawNonBlocking(const char* buffer, const size_type count);
size_type getBlockSize() const;
+ unsigned int getStatus() const;
+
private:
static int bio_write(BIO* bio, const char* buf, int len);
@@ -87,8 +92,10 @@ private:
void createSSLHandle();
+ void internalThrow();
void handleError(int rc);
+
ref <TLSSession_OpenSSL> m_session;
ref <socket> m_wrapped;
@@ -100,6 +107,9 @@ private:
ref <timeoutHandler> m_toHandler;
SSL* m_ssl;
+
+ // Last exception thrown from C BIO functions
+ std::auto_ptr <std::exception> m_ex;
};
diff --git a/vmime/platforms/posix/posixSocket.hpp b/vmime/platforms/posix/posixSocket.hpp
index 1332505b..78b1c0aa 100644
--- a/vmime/platforms/posix/posixSocket.hpp
+++ b/vmime/platforms/posix/posixSocket.hpp
@@ -55,9 +55,12 @@ public:
void send(const vmime::string& buffer);
void sendRaw(const char* buffer, const size_type count);
+ size_type sendRawNonBlocking(const char* buffer, const size_type count);
size_type getBlockSize() const;
+ unsigned int getStatus() const;
+
protected:
static void throwSocketError(const int err);
@@ -68,6 +71,8 @@ private:
char m_buffer[65536];
int m_desc;
+
+ unsigned int m_status;
};
diff --git a/vmime/platforms/windows/windowsSocket.hpp b/vmime/platforms/windows/windowsSocket.hpp
index c41d8bfe..ca007a06 100644
--- a/vmime/platforms/windows/windowsSocket.hpp
+++ b/vmime/platforms/windows/windowsSocket.hpp
@@ -43,6 +43,7 @@ namespace windows {
class windowsSocket : public vmime::net::socket
{
public:
+
windowsSocket();
windowsSocket(ref <vmime::net::timeoutHandler> th);
~windowsSocket();
@@ -58,15 +59,33 @@ public:
void send(const vmime::string& buffer);
void sendRaw(const char* buffer, const size_type count);
+ size_type sendRawNonBlocking(const char* buffer, const size_type count);
size_type getBlockSize() const;
+ unsigned int getStatus() const;
+
+protected:
+
+ void throwSocketError(const int err);
+
+ enum WaitOpType
+ {
+ READ = 1,
+ WRITE = 2,
+ BOTH = 4
+ };
+
+ void waitForData(const WaitOpType t, bool& timedOut);
+
private:
ref <vmime::net::timeoutHandler> m_timeoutHandler;
char m_buffer[65536];
SOCKET m_desc;
+
+ unsigned int m_status;
};
diff --git a/vmime/security/sasl/SASLSocket.hpp b/vmime/security/sasl/SASLSocket.hpp
index f05ed424..d6f4ecd9 100644
--- a/vmime/security/sasl/SASLSocket.hpp
+++ b/vmime/security/sasl/SASLSocket.hpp
@@ -63,9 +63,12 @@ public:
void send(const string& buffer);
void sendRaw(const char* buffer, const size_type count);
+ size_type sendRawNonBlocking(const char* buffer, const size_type count);
size_type getBlockSize() const;
+ unsigned int getStatus() const;
+
private:
ref <SASLSession> m_session;