diff options
author | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
---|---|---|
committer | Wojtek Kosior <wk@koszkonutek-tmp.pl.eu.org> | 2021-04-30 00:33:56 +0200 |
commit | aa4d426b4d3527d7e166df1a05058c9a4a0f6683 (patch) | |
tree | 4ff17ce8b89a2321b9d0ed4bcfc37c447bcb6820 /vmime-master/src/vmime/platforms/windows/windowsSocket.cpp | |
download | smtps-and-pop3s-console-program-master.tar.gz smtps-and-pop3s-console-program-master.zip |
Diffstat (limited to 'vmime-master/src/vmime/platforms/windows/windowsSocket.cpp')
-rw-r--r-- | vmime-master/src/vmime/platforms/windows/windowsSocket.cpp | 547 |
1 files changed, 547 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/platforms/windows/windowsSocket.cpp b/vmime-master/src/vmime/platforms/windows/windowsSocket.cpp new file mode 100644 index 0000000..3a93c53 --- /dev/null +++ b/vmime-master/src/vmime/platforms/windows/windowsSocket.cpp @@ -0,0 +1,547 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002 Vincent Richard <vincent@vmime.org> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/config.hpp" + + +#if VMIME_PLATFORM_IS_WINDOWS && VMIME_HAVE_MESSAGING_FEATURES + + +#pragma warning(disable: 4267) + +#include "vmime/platforms/windows/windowsSocket.hpp" + +#include "vmime/utility/stringUtils.hpp" +#include "vmime/exception.hpp" + +#include <ws2tcpip.h> + + +namespace vmime { +namespace platforms { +namespace windows { + + +// +// windowsSocket +// + +windowsSocket::windowsSocket(shared_ptr <vmime::net::timeoutHandler> th) + : m_timeoutHandler(th), + m_desc(INVALID_SOCKET), + m_status(0) { + + WSAData wsaData; + WSAStartup(MAKEWORD(1, 1), &wsaData); +} + + +windowsSocket::~windowsSocket() { + + if (m_desc != INVALID_SOCKET) { + ::closesocket(m_desc); + } + + WSACleanup(); +} + + +void windowsSocket::connect(const vmime::string& address, const vmime::port_t port) { + + // Close current connection, if any + if (m_desc != INVALID_SOCKET) { + ::closesocket(m_desc); + m_desc = INVALID_SOCKET; + } + + // Resolve address + ::sockaddr_in addr; + + memset(&addr, 0, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_port = htons(static_cast <unsigned short>(port)); + addr.sin_addr.s_addr = ::inet_addr(address.c_str()); + + if (m_tracer) { + + std::ostringstream trace; + trace << "Connecting to " << address << ", port " << port; + + m_tracer->traceSend(trace.str()); + } + + if (addr.sin_addr.s_addr == static_cast <int>(-1)) { + + ::hostent* hostInfo = ::gethostbyname(address.c_str()); + + if (!hostInfo) { + // Error: cannot resolve address + throw vmime::exceptions::connection_error("Cannot resolve address."); + } + + memcpy(reinterpret_cast <char*>(&addr.sin_addr), hostInfo->h_addr, hostInfo->h_length); + } + + m_serverAddress = address; + + // Get a new socket + m_desc = ::socket(AF_INET, SOCK_STREAM, 0); + + 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) { + + 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.", e); + } + } + + // Set socket to non-blocking + unsigned long non_blocking = 1; + ::ioctlsocket(m_desc, FIONBIO, &non_blocking); +} + + +bool windowsSocket::isConnected() const { + + if (m_desc == INVALID_SOCKET) { + return false; + } + + char buff; + + return ::recv(m_desc, &buff, 1, MSG_PEEK) != 0; +} + + +void windowsSocket::disconnect() { + + if (m_desc != INVALID_SOCKET) { + + if (m_tracer) { + m_tracer->traceSend("Disconnecting"); + } + + ::shutdown(m_desc, SD_BOTH); + ::closesocket(m_desc); + + m_desc = INVALID_SOCKET; + } +} + + +static bool isNumericAddress(const char* address) { + + struct addrinfo hint, *info = NULL; + memset(&hint, 0, sizeof(hint)); + + hint.ai_family = AF_UNSPEC; + hint.ai_flags = AI_NUMERICHOST; + + if (getaddrinfo(address, 0, &hint, &info) == 0) { + + freeaddrinfo(info); + return true; + + } else { + + return false; + } +} + + +const string windowsSocket::getPeerAddress() const { + + // Get address of connected peer + sockaddr peer; + socklen_t peerLen = sizeof(peer); + + getpeername(m_desc, reinterpret_cast <sockaddr*>(&peer), &peerLen); + + // Convert to numerical presentation format + char host[NI_MAXHOST + 1]; + char service[NI_MAXSERV + 1]; + + if (getnameinfo(reinterpret_cast <sockaddr *>(&peer), peerLen, + host, sizeof(host), service, sizeof(service), + /* flags */ NI_NUMERICHOST) == 0) { + + return string(host); + } + + return ""; // should not happen +} + + +const string windowsSocket::getPeerName() const { + + // Get address of connected peer + sockaddr peer; + socklen_t peerLen = sizeof(peer); + + getpeername(m_desc, reinterpret_cast <sockaddr*>(&peer), &peerLen); + + // If server address as specified when connecting is a numeric + // address, try to get a host name for it + if (isNumericAddress(m_serverAddress.c_str())) { + + char host[NI_MAXHOST + 1]; + char service[NI_MAXSERV + 1]; + + if (getnameinfo(reinterpret_cast <sockaddr *>(&peer), peerLen, + host, sizeof(host), service, sizeof(service), + /* flags */ NI_NAMEREQD) == 0) { + + return string(host); + } + } + + return m_serverAddress; +} + + +size_t windowsSocket::getBlockSize() const { + + return 16384; // 16 KB +} + + +void windowsSocket::receive(vmime::string& buffer) { + + const size_t size = receiveRaw(m_buffer, sizeof(m_buffer)); + buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size); +} + + +size_t windowsSocket::receiveRaw(byte_t* buffer, const size_t count) { + + m_status &= ~STATUS_WOULDBLOCK; + + // Check whether data is available + if (!waitForRead(50 /* msecs */)) { + + // 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, reinterpret_cast <char*>(buffer), count, 0); + + 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 { + + // Data received, reset timeout + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + return ret; + } +} + + +void windowsSocket::send(const vmime::string& buffer) { + + sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length()); +} + + +void windowsSocket::send(const char* str) { + + sendRaw(reinterpret_cast <const byte_t*>(str), strlen(str)); +} + + +void windowsSocket::sendRaw(const byte_t* buffer, const size_t count) { + + m_status &= ~STATUS_WOULDBLOCK; + + size_t size = count; + + while (size > 0) { + + const int ret = ::send(m_desc, reinterpret_cast <const char*>(buffer), size, 0); + + if (ret == SOCKET_ERROR) { + + int err = WSAGetLastError(); + + if (err != WSAEWOULDBLOCK) { + throwSocketError(err); + } + + waitForWrite(50 /* msecs */); + + } else { + + buffer += ret; + size -= ret; + } + } + + // Reset timeout + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } +} + + +size_t windowsSocket::sendRawNonBlocking(const byte_t* buffer, const size_t count) { + + m_status &= ~STATUS_WOULDBLOCK; + + const int ret = ::send(m_desc, reinterpret_cast <const char*>(buffer), count, 0); + + if (ret == SOCKET_ERROR) { + + int err = WSAGetLastError(); + + if (err == WSAEWOULDBLOCK) { + + // Check if we are timed out + if (m_timeoutHandler && + m_timeoutHandler->isTimeOut()) { + + if (!m_timeoutHandler->handleTimeOut()) { + + // Could not send data within timeout delay + throwSocketError(err); + + } else { + + // Reset timeout + m_timeoutHandler->resetTimeOut(); + } + } + + m_status |= STATUS_WOULDBLOCK; + + // No data can be written at this time + return 0; + + } else { + + throwSocketError(err); + } + } + + // Reset timeout + if (m_timeoutHandler) { + m_timeoutHandler->resetTimeOut(); + } + + 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); +} + + +bool windowsSocket::waitForData(const bool read, const bool write, const int msecs) { + + for (int i = 0 ; i <= msecs / 10 ; ++i) { + + // Check whether data is available + fd_set fds; + FD_ZERO(&fds); + FD_SET(m_desc, &fds); + + struct timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 10000; // 10 ms + + int ret = ::select(m_desc + 1, read ? &fds : NULL, write ? &fds : NULL, NULL, &tv); + + if (ret == SOCKET_ERROR) { + + int err = WSAGetLastError(); + throwSocketError(err); + + } else if (ret > 0) { + + return true; + } + + // 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 + throw exceptions::operation_timed_out(); + + } else { + + // Reset timeout + m_timeoutHandler->resetTimeOut(); + } + } + } + + return false; // time out +} + + +bool windowsSocket::waitForRead(const int msecs) { + + return waitForData(/* read */ true, /* write */ false, msecs); +} + + +bool windowsSocket::waitForWrite(const int msecs) { + + return waitForData(/* read */ false, /* write */ true, msecs); +} + + +shared_ptr <net::timeoutHandler> windowsSocket::getTimeoutHandler() { + + return m_timeoutHandler; +} + + +void windowsSocket::setTracer(const shared_ptr <net::tracer>& tracer) { + + m_tracer = tracer; +} + + +shared_ptr <net::tracer> windowsSocket::getTracer() { + + return m_tracer; +} + + + +// +// posixSocketFactory +// + +shared_ptr <vmime::net::socket> windowsSocketFactory::create() { + + shared_ptr <vmime::net::timeoutHandler> th; + return make_shared <windowsSocket>(th); +} + + +shared_ptr <vmime::net::socket> windowsSocketFactory::create(const shared_ptr <vmime::net::timeoutHandler>& th) { + + return make_shared <windowsSocket>(th); +} + + +} // posix +} // platforms +} // vmime + + +#endif // VMIME_PLATFORM_IS_WINDOWS && VMIME_HAVE_MESSAGING_FEATURES + |