aboutsummaryrefslogtreecommitdiff
path: root/vmime-master/src/vmime/platforms/posix/posixSocket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vmime-master/src/vmime/platforms/posix/posixSocket.cpp')
-rw-r--r--vmime-master/src/vmime/platforms/posix/posixSocket.cpp969
1 files changed, 969 insertions, 0 deletions
diff --git a/vmime-master/src/vmime/platforms/posix/posixSocket.cpp b/vmime-master/src/vmime/platforms/posix/posixSocket.cpp
new file mode 100644
index 0000000..aec6a83
--- /dev/null
+++ b/vmime-master/src/vmime/platforms/posix/posixSocket.cpp
@@ -0,0 +1,969 @@
+//
+// 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_POSIX && VMIME_HAVE_MESSAGING_FEATURES
+
+
+#include "vmime/platforms/posix/posixSocket.hpp"
+#include "vmime/platforms/posix/posixHandler.hpp"
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // for getaddrinfo_a() in <netdb.h>
+#endif
+
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <poll.h>
+
+#include "vmime/utility/stringUtils.hpp"
+
+#include "vmime/exception.hpp"
+
+
+#if defined(EWOULDBLOCK)
+# define IS_EAGAIN(x) ((x) == EAGAIN || (x) == EWOULDBLOCK || (x) == EINTR || (x) == EINPROGRESS)
+#else
+# define IS_EAGAIN(x) ((x) == EAGAIN || (x) == EINTR || (x) == EINPROGRESS)
+#endif
+
+
+// Workaround for detection of strerror_r variants
+#if VMIME_HAVE_STRERROR_R
+
+namespace {
+
+char* vmime_strerror_r_result(int /* res */, char* buf) {
+
+ // XSI-compliant prototype:
+ // int strerror_r(int errnum, char *buf, size_t buflen);
+ return buf;
+}
+
+char* vmime_strerror_r_result(char* res, char* /* buf */) {
+
+ // GNU-specific prototype:
+ // char *strerror_r(int errnum, char *buf, size_t buflen);
+ return res;
+}
+
+}
+
+#endif // VMIME_HAVE_STRERROR_R
+
+
+
+namespace vmime {
+namespace platforms {
+namespace posix {
+
+
+//
+// posixSocket
+//
+
+posixSocket::posixSocket(shared_ptr <vmime::net::timeoutHandler> th)
+ : m_timeoutHandler(th),
+ m_desc(-1),
+ m_status(0) {
+
+}
+
+
+posixSocket::~posixSocket() {
+
+ if (m_desc != -1) {
+ ::close(m_desc);
+ }
+}
+
+
+void posixSocket::connect(const vmime::string& address, const vmime::port_t port) {
+
+ // Close current connection, if any
+ if (m_desc != -1) {
+ ::close(m_desc);
+ m_desc = -1;
+ }
+
+ if (m_tracer) {
+
+ std::ostringstream trace;
+ trace << "Connecting to " << address << ", port " << port;
+
+ m_tracer->traceSend(trace.str());
+ }
+
+#if VMIME_HAVE_GETADDRINFO // use thread-safe and IPv6-aware getaddrinfo() if available
+
+ // Resolve address, if needed
+ m_serverAddress = address;
+
+ struct ::addrinfo* addrInfo = NULL; // resolved addresses
+ resolve(&addrInfo, address, port);
+
+ // Connect to host
+ int sock = -1;
+ int connectErrno = 0;
+
+ if (m_timeoutHandler != NULL) {
+ m_timeoutHandler->resetTimeOut();
+ }
+
+ for (struct ::addrinfo* curAddrInfo = addrInfo ;
+ sock == -1 && curAddrInfo != NULL ;
+ curAddrInfo = curAddrInfo->ai_next, connectErrno = ETIMEDOUT) {
+
+ if (curAddrInfo->ai_family != AF_INET && curAddrInfo->ai_family != AF_INET6) {
+ continue;
+ }
+
+ sock = ::socket(curAddrInfo->ai_family, curAddrInfo->ai_socktype, curAddrInfo->ai_protocol);
+
+ if (sock < 0) {
+ connectErrno = errno;
+ continue; // try next
+ }
+
+#if VMIME_HAVE_SO_KEEPALIVE
+
+ // Enable TCP Keepalive
+ int keepAlive_optval = 1;
+ socklen_t keepAlive_optlen = sizeof(keepAlive_optval);
+
+ ::setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive_optval, keepAlive_optlen);
+
+#endif // VMIME_HAVE_SO_KEEPALIVE
+
+#if VMIME_HAVE_SO_NOSIGPIPE
+
+ // Return EPIPE instead of generating SIGPIPE
+ int nosigpipe_optval = 1;
+ socklen_t nosigpipe_optlen = sizeof(nosigpipe_optval);
+
+ ::setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe_optval, nosigpipe_optlen);
+
+#endif // VMIME_HAVE_SO_NOSIGPIPE
+
+
+ if (m_timeoutHandler) {
+
+ ::fcntl(sock, F_SETFL, ::fcntl(sock, F_GETFL) | O_NONBLOCK);
+
+ if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0) {
+
+ switch (errno) {
+
+ case 0:
+ case EINPROGRESS:
+ case EINTR:
+#if defined(EAGAIN)
+ case EAGAIN:
+#endif // EAGAIN
+#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+ case EWOULDBLOCK:
+#endif // EWOULDBLOCK
+
+ // Connection in progress
+ break;
+
+ default:
+
+ connectErrno = errno;
+ ::close(sock);
+ sock = -1;
+ continue; // try next
+ }
+
+ // Wait for socket to be connected.
+ bool connected = false;
+
+ const int pollTimeout = 1000; // poll() timeout (ms)
+ const int tryNextTimeout = 5000; // maximum time before trying next (ms)
+
+ timeval startTime = { 0, 0 };
+ gettimeofday(&startTime, /* timezone */ NULL);
+
+ do {
+
+ pollfd fds[1];
+ fds[0].fd = sock;
+ fds[0].events = POLLIN | POLLOUT;
+
+ const int ret = ::poll(fds, sizeof(fds) / sizeof(fds[0]), pollTimeout);
+
+ // Success
+ if (ret > 0) {
+
+ if (fds[0].revents & (POLLIN | POLLOUT)) {
+
+ int error = 0;
+ socklen_t len = sizeof(error);
+
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+
+ connectErrno = errno;
+
+ } else {
+
+ if (error != 0) {
+ connectErrno = error;
+ } else {
+ connected = true;
+ }
+ }
+ }
+
+ break;
+
+ // Error
+ } else if (ret < -1) {
+
+ if (errno != EAGAIN && errno != EINTR) {
+
+ // Cancel connection
+ connectErrno = errno;
+ break;
+ }
+ }
+
+ // Check for timeout
+ if (m_timeoutHandler->isTimeOut()) {
+
+ if (!m_timeoutHandler->handleTimeOut()) {
+
+ // Cancel connection
+ connectErrno = ETIMEDOUT;
+ break;
+
+ } else {
+
+ // Reset timeout and keep waiting for connection
+ m_timeoutHandler->resetTimeOut();
+ }
+
+ } else {
+
+ // Keep waiting for connection
+ }
+
+ timeval curTime = { 0, 0 };
+ gettimeofday(&curTime, /* timezone */ NULL);
+
+ if (curAddrInfo->ai_next != NULL &&
+ curTime.tv_usec - startTime.tv_usec >= tryNextTimeout * 1000) {
+
+ connectErrno = ETIMEDOUT;
+ break;
+ }
+
+ } while (true);
+
+ if (!connected) {
+
+ ::close(sock);
+ sock = -1;
+ continue; // try next
+ }
+
+ break;
+
+ } else {
+
+ // Connection successful
+ break;
+ }
+
+ } else {
+
+ if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0) {
+
+ connectErrno = errno;
+ ::close(sock);
+ sock = -1;
+ continue; // try next
+ }
+ }
+ }
+
+ ::freeaddrinfo(addrInfo);
+
+ if (sock == -1) {
+
+ try {
+ throwSocketError(connectErrno);
+ } catch (exceptions::socket_exception& e) { // wrap
+ throw vmime::exceptions::connection_error("Error while connecting socket.", e);
+ }
+ }
+
+ m_desc = sock;
+
+#else // !VMIME_HAVE_GETADDRINFO
+
+ // 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 (addr.sin_addr.s_addr == static_cast <in_addr_t>(-1)) {
+
+ ::hostent* hostInfo = ::gethostbyname(address.c_str());
+
+ if (hostInfo == NULL) {
+ // 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 == -1) {
+
+ try {
+ throwSocketError(errno);
+ } catch (exceptions::socket_exception& e) { // wrap
+ throw vmime::exceptions::connection_error("Error while creating socket.", e);
+ }
+ }
+
+ // Start connection
+ if (::connect(m_desc, reinterpret_cast <sockaddr*>(&addr), sizeof(addr)) == -1) {
+
+ try {
+
+ throwSocketError(errno);
+
+ } catch (exceptions::socket_exception& e) { // wrap
+
+ ::close(m_desc);
+ m_desc = -1;
+
+ // Error
+ throw vmime::exceptions::connection_error("Error while connecting socket.", e);
+ }
+ }
+
+#endif // VMIME_HAVE_GETADDRINFO
+
+ ::fcntl(m_desc, F_SETFL, ::fcntl(m_desc, F_GETFL) | O_NONBLOCK);
+}
+
+
+void posixSocket::resolve(
+ struct ::addrinfo** addrInfo,
+ const vmime::string& address,
+ const vmime::port_t port
+) {
+
+ char portStr[16];
+ snprintf(portStr, sizeof(portStr), "%u", static_cast <unsigned int>(port));
+
+
+ struct ::addrinfo hints;
+ memset(&hints, 0, sizeof(hints));
+
+ hints.ai_flags = AI_CANONNAME | AI_NUMERICSERV;
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+
+#if VMIME_HAVE_GETADDRINFO_A
+
+ // If getaddrinfo_a() is available, use asynchronous resolving to allow
+ // the timeout handler to cancel the operation
+
+ struct ::gaicb gaiRequest;
+ memset(&gaiRequest, 0, sizeof(gaiRequest));
+
+ gaiRequest.ar_name = address.c_str();
+ gaiRequest.ar_service = portStr;
+ gaiRequest.ar_request = &hints;
+
+ struct ::gaicb* gaiRequests = &gaiRequest;
+ int gaiError;
+
+ if ((gaiError = getaddrinfo_a(GAI_NOWAIT, &gaiRequests, 1, NULL)) != 0) {
+
+ throw vmime::exceptions::connection_error(
+ "getaddrinfo_a() failed: " + std::string(gai_strerror(gaiError))
+ );
+ }
+
+ if (m_timeoutHandler) {
+ m_timeoutHandler->resetTimeOut();
+ }
+
+ while (true) {
+
+ struct timespec gaiTimeout;
+ gaiTimeout.tv_sec = 1; // query timeout handler every second
+ gaiTimeout.tv_nsec = 0;
+
+ gaiError = gai_suspend(&gaiRequests, 1, &gaiTimeout);
+
+ if (gaiError == 0 || gaiError == EAI_ALLDONE) {
+
+ const int ret = gai_error(&gaiRequest);
+
+ if (ret != 0) {
+
+ throw vmime::exceptions::connection_error(
+ "getaddrinfo_a() request failed: " + std::string(gai_strerror(ret))
+ );
+
+ } else {
+
+ *addrInfo = gaiRequest.ar_result;
+ break;
+ }
+
+ } else if (gaiError != EAI_AGAIN) {
+
+ if (gaiError == EAI_SYSTEM) {
+
+ const int ret = gai_error(&gaiRequest);
+
+ if (ret != EAI_INPROGRESS && errno != 0) {
+
+ try {
+ throwSocketError(errno);
+ } catch (exceptions::socket_exception& e) { // wrap
+ throw vmime::exceptions::connection_error("Error while connecting socket.", e);
+ }
+ }
+
+ } else {
+
+ throw vmime::exceptions::connection_error(
+ "gai_suspend() failed: " + std::string(gai_strerror(gaiError))
+ );
+ }
+ }
+
+ // Check for timeout
+ if (m_timeoutHandler && m_timeoutHandler->isTimeOut()) {
+
+ if (!m_timeoutHandler->handleTimeOut()) {
+
+ throw exceptions::operation_timed_out();
+
+ } else {
+
+ // Reset timeout and keep waiting for connection
+ m_timeoutHandler->resetTimeOut();
+ }
+ }
+ }
+
+#else // !VMIME_HAVE_GETADDRINFO_A
+
+ if (::getaddrinfo(address.c_str(), portStr, &hints, addrInfo) != 0) {
+
+ // Error: cannot resolve address
+ throw vmime::exceptions::connection_error("Cannot resolve address.");
+ }
+
+#endif // VMIME_HAVE_GETADDRINFO_A
+
+}
+
+
+bool posixSocket::isConnected() const {
+
+ if (m_desc == -1) {
+ return false;
+ }
+
+ char buff;
+
+ return ::recv(m_desc, &buff, 1, MSG_PEEK) != 0;
+}
+
+
+void posixSocket::disconnect() {
+
+ if (m_desc != -1) {
+
+ if (m_tracer) {
+ m_tracer->traceSend("Disconnecting");
+ }
+
+ ::shutdown(m_desc, SHUT_RDWR);
+ ::close(m_desc);
+
+ m_desc = -1;
+ }
+}
+
+
+static bool isNumericAddress(const char* address) {
+
+#if VMIME_HAVE_GETADDRINFO
+
+ 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;
+ }
+
+#else
+
+ return inet_addr(address) != INADDR_NONE;
+
+#endif
+
+}
+
+
+const string posixSocket::getPeerAddress() const {
+
+ // Get address of connected peer
+ sockaddr peer;
+ socklen_t peerLen = sizeof(peer);
+
+ if (getpeername(m_desc, &peer, &peerLen) != 0) {
+ throwSocketError(errno);
+ }
+
+ // Convert to numerical presentation format
+ char buf[INET6_ADDRSTRLEN];
+
+ if (!inet_ntop(peer.sa_family, &(reinterpret_cast <struct sockaddr_in *>(&peer))->sin_addr, buf, sizeof(buf))) {
+ throwSocketError(errno);
+ }
+
+ return string(buf);
+}
+
+
+const string posixSocket::getPeerName() const {
+
+ // Get address of connected peer
+ sockaddr peer;
+ socklen_t peerLen = sizeof(peer);
+
+ if (getpeername(m_desc, &peer, &peerLen) != 0) {
+ throwSocketError(errno);
+ }
+
+ // 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())) {
+
+#if VMIME_HAVE_GETNAMEINFO
+
+ 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);
+ }
+
+#else
+
+ struct hostent *hp;
+
+ if ((hp = gethostbyaddr(reinterpret_cast <const void *>(&peer),
+ sizeof(peer), peer.sa_family)) != NULL) {
+
+ return string(hp->h_name);
+ }
+
+#endif
+
+ }
+
+ return m_serverAddress;
+}
+
+
+size_t posixSocket::getBlockSize() const {
+
+ return 16384; // 16 KB
+}
+
+
+bool posixSocket::waitForData(const bool read, const bool write, const int msecs) {
+
+ for (int i = 0 ; i <= msecs / 10 ; ++i) {
+
+ // Check whether data is available
+ pollfd fds[1];
+ fds[0].fd = m_desc;
+ fds[0].events = 0;
+
+ if (read) {
+ fds[0].events |= POLLIN;
+ }
+
+ if (write) {
+ fds[0].events |= POLLOUT;
+ }
+
+ const int ret = ::poll(fds, sizeof(fds) / sizeof(fds[0]), 10 /* ms */);
+
+ if (ret < 0) {
+
+ if (errno != EAGAIN && errno != EINTR) {
+ throwSocketError(errno);
+ }
+
+ } else if (ret > 0) {
+
+ if (fds[0].revents & (POLLIN | POLLOUT)) {
+ 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 posixSocket::waitForRead(const int msecs) {
+
+ return waitForData(/* read */ true, /* write */ false, msecs);
+}
+
+
+bool posixSocket::waitForWrite(const int msecs) {
+
+ return waitForData(/* read */ false, /* write */ true, msecs);
+}
+
+
+void posixSocket::receive(vmime::string& buffer) {
+
+ const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
+ buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size);
+}
+
+
+size_t posixSocket::receiveRaw(byte_t* buffer, const size_t count) {
+
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ // Check whether data is available
+ if (!waitForRead(50 /* msecs */)) {
+
+ m_status |= STATUS_WOULDBLOCK;
+
+ // Continue waiting for data
+ return 0;
+ }
+
+ // Read available data
+ ssize_t ret = ::recv(m_desc, buffer, count, 0);
+
+ if (ret < 0) {
+
+ 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;
+
+ } else if (ret == 0) {
+
+ // Host shutdown
+ throwSocketError(ENOTCONN);
+
+ } else {
+
+ // Data received, reset timeout
+ if (m_timeoutHandler) {
+ m_timeoutHandler->resetTimeOut();
+ }
+ }
+
+ return ret;
+}
+
+
+void posixSocket::send(const vmime::string& buffer) {
+
+ sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length());
+}
+
+
+void posixSocket::send(const char* str) {
+
+ sendRaw(reinterpret_cast <const byte_t*>(str), ::strlen(str));
+}
+
+
+void posixSocket::sendRaw(const byte_t* buffer, const size_t count) {
+
+ m_status &= ~STATUS_WOULDBLOCK;
+
+ size_t size = count;
+
+ while (size > 0) {
+
+#if VMIME_HAVE_MSG_NOSIGNAL
+ const ssize_t ret = ::send(m_desc, buffer, size, MSG_NOSIGNAL);
+#else
+ const ssize_t ret = ::send(m_desc, buffer, size, 0);
+#endif
+
+ if (ret <= 0) {
+
+ if (ret < 0 && !IS_EAGAIN(errno)) {
+ throwSocketError(errno);
+ }
+
+ waitForWrite(50 /* msecs */);
+
+ } else {
+
+ buffer += ret;
+ size -= ret;
+ }
+ }
+
+ // Reset timeout
+ if (m_timeoutHandler) {
+ m_timeoutHandler->resetTimeOut();
+ }
+}
+
+
+size_t posixSocket::sendRawNonBlocking(const byte_t* buffer, const size_t count) {
+
+ m_status &= ~STATUS_WOULDBLOCK;
+
+#if VMIME_HAVE_MSG_NOSIGNAL
+ const ssize_t ret = ::send(m_desc, buffer, count, MSG_NOSIGNAL);
+#else
+ const ssize_t ret = ::send(m_desc, buffer, count, 0);
+#endif
+
+ if (ret <= 0) {
+
+ if (ret < 0 && !IS_EAGAIN(errno)) {
+ throwSocketError(errno);
+ }
+
+ // Check if we are timed out
+ if (m_timeoutHandler &&
+ m_timeoutHandler->isTimeOut()) {
+
+ if (!m_timeoutHandler->handleTimeOut()) {
+
+ // Could not send data within timeout delay
+ throw exceptions::operation_timed_out();
+
+ } else {
+
+ // Reset timeout
+ m_timeoutHandler->resetTimeOut();
+ }
+ }
+
+ m_status |= STATUS_WOULDBLOCK;
+
+ // No data can be written at this time
+ return 0;
+ }
+
+ // Reset timeout
+ if (m_timeoutHandler) {
+ m_timeoutHandler->resetTimeOut();
+ }
+
+ return ret;
+}
+
+
+void posixSocket::throwSocketError(const int err) {
+
+ const char* msg = NULL;
+
+ switch (err) {
+
+ case EACCES: msg = "EACCES: permission denied"; break;
+ case EAFNOSUPPORT: msg = "EAFNOSUPPORT: address family not supported"; break;
+ case EMFILE: msg = "EMFILE: process file table overflow"; break;
+ case ENFILE: msg = "ENFILE: system limit reached"; break;
+ case EPROTONOSUPPORT: msg = "EPROTONOSUPPORT: protocol not supported"; break;
+ case EAGAIN: msg = "EGAIN: blocking operation"; break;
+ case EBADF: msg = "EBADF: invalid descriptor"; break;
+ case ECONNRESET: msg = "ECONNRESET: connection reset by peer"; break;
+ case EFAULT: msg = "EFAULT: bad user space address"; break;
+ case EINTR: msg = "EINTR: signal occurred before transmission"; break;
+ case EINVAL: msg = "EINVAL: invalid argument"; break;
+ case EMSGSIZE: msg = "EMSGSIZE: message cannot be sent atomically"; break;
+ case ENOBUFS: msg = "ENOBUFS: output queue is full"; break;
+ case ENOMEM: msg = "ENOMEM: out of memory"; break;
+ case EPIPE: msg = "EPIPE: broken pipe"; break;
+ case ENOTCONN: msg = "ENOTCONN: not connected"; break;
+ case ECONNREFUSED: msg = "ECONNREFUSED: connection refused"; break;
+ }
+
+ if (msg) {
+
+ throw exceptions::socket_exception(msg);
+
+ } else {
+
+ // Use strerror() to get string describing error number
+
+#if VMIME_HAVE_STRERROR_R
+
+ char errbuf[512];
+
+ throw exceptions::socket_exception(
+ vmime_strerror_r_result(
+ strerror_r(err, errbuf, sizeof(errbuf)),
+ errbuf
+ )
+ );
+
+#else // !VMIME_HAVE_STRERROR_R
+
+ const std::string strmsg(strerror(err));
+ throw exceptions::socket_exception(strmsg);
+
+#endif // VMIME_HAVE_STRERROR_R
+
+ }
+}
+
+
+unsigned int posixSocket::getStatus() const {
+
+ return m_status;
+}
+
+
+shared_ptr <net::timeoutHandler> posixSocket::getTimeoutHandler() {
+
+ return m_timeoutHandler;
+}
+
+
+void posixSocket::setTracer(const shared_ptr <net::tracer>& tracer) {
+
+ m_tracer = tracer;
+}
+
+
+shared_ptr <net::tracer> posixSocket::getTracer() {
+
+ return m_tracer;
+}
+
+
+
+//
+// posixSocketFactory
+//
+
+shared_ptr <vmime::net::socket> posixSocketFactory::create() {
+
+ shared_ptr <vmime::net::timeoutHandler> th;
+ return make_shared <posixSocket>(th);
+}
+
+
+shared_ptr <vmime::net::socket> posixSocketFactory::create(const shared_ptr <vmime::net::timeoutHandler>& th) {
+
+ return make_shared <posixSocket>(th);
+}
+
+
+} // posix
+} // platforms
+} // vmime
+
+
+#endif // VMIME_PLATFORM_IS_POSIX && VMIME_HAVE_MESSAGING_FEATURES