diff options
| author | Vincent Richard <[email protected]> | 2016-03-02 19:33:55 +0000 |
|---|---|---|
| committer | Vincent Richard <[email protected]> | 2016-03-02 19:33:55 +0000 |
| commit | 194a797055b9c1624fe8d5fbce09f4d6822d1a90 (patch) | |
| tree | 67070c88a63eef5d61ed1da37f1422cb060c85cf /src | |
| parent | Added support for TCP Keepalive. (diff) | |
| download | vmime-194a797055b9c1624fe8d5fbce09f4d6822d1a90.tar.gz vmime-194a797055b9c1624fe8d5fbce09f4d6822d1a90.zip | |
Asynchronous resolving.
Diffstat (limited to 'src')
| -rw-r--r-- | src/vmime/platforms/posix/posixSocket.cpp | 139 | ||||
| -rw-r--r-- | src/vmime/platforms/posix/posixSocket.hpp | 5 |
2 files changed, 116 insertions, 28 deletions
diff --git a/src/vmime/platforms/posix/posixSocket.cpp b/src/vmime/platforms/posix/posixSocket.cpp index 6ebd2766..aa5ec58a 100644 --- a/src/vmime/platforms/posix/posixSocket.cpp +++ b/src/vmime/platforms/posix/posixSocket.cpp @@ -30,6 +30,10 @@ #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> @@ -95,42 +99,26 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port #if VMIME_HAVE_GETADDRINFO // use thread-safe and IPv6-aware getaddrinfo() if available // Resolve address, if needed - struct ::addrinfo hints; - memset(&hints, 0, sizeof(hints)); - - hints.ai_flags = AI_CANONNAME; - hints.ai_family = PF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - std::ostringstream portStr; - portStr.imbue(std::locale::classic()); - - portStr << port; - - struct ::addrinfo* res0; - - if (::getaddrinfo(address.c_str(), portStr.str().c_str(), &hints, &res0) != 0) - { - // Error: cannot resolve address - throw vmime::exceptions::connection_error("Cannot resolve address."); - } - m_serverAddress = address; + struct ::addrinfo* addrInfo = NULL; // resolved addresses + resolve(&addrInfo, address, port); + // Connect to host int sock = -1; - struct ::addrinfo* res = res0; int connectErrno = 0; if (m_timeoutHandler != NULL) m_timeoutHandler->resetTimeOut(); - for ( ; sock == -1 && res != NULL ; res = res->ai_next, connectErrno = ETIMEDOUT) + for (struct ::addrinfo* curAddrInfo = addrInfo ; + sock == -1 && curAddrInfo != NULL ; + curAddrInfo = curAddrInfo->ai_next, connectErrno = ETIMEDOUT) { - if (res->ai_family != AF_INET && res->ai_family != AF_INET6) + if (curAddrInfo->ai_family != AF_INET && curAddrInfo->ai_family != AF_INET6) continue; - sock = ::socket(res->ai_family, res->ai_socktype, res->ai_protocol); + sock = ::socket(curAddrInfo->ai_family, curAddrInfo->ai_socktype, curAddrInfo->ai_protocol); if (sock < 0) { @@ -152,7 +140,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port { ::fcntl(sock, F_SETFL, ::fcntl(sock, F_GETFL) | O_NONBLOCK); - if (::connect(sock, res->ai_addr, res->ai_addrlen) < 0) + if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0) { switch (errno) { @@ -254,7 +242,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port timeval curTime = { 0, 0 }; gettimeofday(&curTime, /* timezone */ NULL); - if (res->ai_next != NULL && + if (curAddrInfo->ai_next != NULL && curTime.tv_usec - startTime.tv_usec >= tryNextTimeout * 1000) { connectErrno = ETIMEDOUT; @@ -280,7 +268,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port } else { - if (::connect(sock, res->ai_addr, res->ai_addrlen) < 0) + if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0) { connectErrno = errno; ::close(sock); @@ -290,7 +278,7 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port } } - ::freeaddrinfo(res0); + ::freeaddrinfo(addrInfo); if (sock == -1) { @@ -373,6 +361,101 @@ void posixSocket::connect(const vmime::string& address, const vmime::port_t port } +void posixSocket::resolve(struct ::addrinfo** addrInfo, const vmime::string& address, const vmime::port_t port) +{ + std::ostringstream portStr; + portStr.imbue(std::locale::classic()); + portStr << 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.str().c_str(); + 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 != NULL) + 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) + { + 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.str().c_str(), &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) diff --git a/src/vmime/platforms/posix/posixSocket.hpp b/src/vmime/platforms/posix/posixSocket.hpp index 575a2a25..ebcb1a04 100644 --- a/src/vmime/platforms/posix/posixSocket.hpp +++ b/src/vmime/platforms/posix/posixSocket.hpp @@ -34,6 +34,9 @@ #include "vmime/net/socket.hpp" +struct addrinfo; + + namespace vmime { namespace platforms { namespace posix { @@ -75,6 +78,8 @@ public: protected: + void resolve(struct ::addrinfo** addrInfo, const vmime::string& address, const vmime::port_t port); + bool waitForData(const bool read, const bool write, const int msecs); static void throwSocketError(const int err); |
