aboutsummaryrefslogtreecommitdiffstats
path: root/src/platforms/windows/windowsSocket.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/platforms/windows/windowsSocket.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/platforms/windows/windowsSocket.cpp')
-rw-r--r--src/platforms/windows/windowsSocket.cpp240
1 files changed, 211 insertions, 29 deletions
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);
+ }
+}
+
//