1 //
2 // VMime library (http://www.vmime.org)
3 // Copyright (C) 2002-2013 Vincent Richard <vincent@vmime.org>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 3 of
8 // the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // Linking this library statically or dynamically with other modules is making
20 // a combined work based on this library.  Thus, the terms and conditions of
21 // the GNU General Public License cover the whole combination.
22 //
23 
24 #include "vmime/config.hpp"
25 
26 
27 #if VMIME_PLATFORM_IS_POSIX && VMIME_HAVE_MESSAGING_FEATURES
28 
29 
30 #include "vmime/platforms/posix/posixSocket.hpp"
31 #include "vmime/platforms/posix/posixHandler.hpp"
32 
33 #ifndef _GNU_SOURCE
34 #define _GNU_SOURCE  // for getaddrinfo_a() in <netdb.h>
35 #endif
36 
37 #include <unistd.h>
38 #include <sys/socket.h>
39 #include <arpa/inet.h>
40 #include <sys/types.h>
41 #include <sys/time.h>
42 #include <netinet/in.h>
43 #include <netdb.h>
44 #include <fcntl.h>
45 #include <errno.h>
46 #include <string.h>
47 #include <stdio.h>
48 #include <poll.h>
49 
50 #include "vmime/utility/stringUtils.hpp"
51 
52 #include "vmime/exception.hpp"
53 
54 
55 #if defined(EWOULDBLOCK)
56 #   define IS_EAGAIN(x)  ((x) == EAGAIN || (x) == EWOULDBLOCK || (x) == EINTR || (x) == EINPROGRESS)
57 #else
58 #   define IS_EAGAIN(x)  ((x) == EAGAIN || (x) == EINTR || (x) == EINPROGRESS)
59 #endif
60 
61 
62 namespace vmime {
63 namespace platforms {
64 namespace posix {
65 
66 
67 //
68 // posixSocket
69 //
70 
posixSocket(shared_ptr<vmime::net::timeoutHandler> th)71 posixSocket::posixSocket(shared_ptr <vmime::net::timeoutHandler> th)
72 	: m_timeoutHandler(th), m_desc(-1), m_status(0)
73 {
74 }
75 
76 
~posixSocket()77 posixSocket::~posixSocket()
78 {
79 	if (m_desc != -1)
80 		::close(m_desc);
81 }
82 
83 
connect(const vmime::string & address,const vmime::port_t port)84 void posixSocket::connect(const vmime::string& address, const vmime::port_t port)
85 {
86 	// Close current connection, if any
87 	if (m_desc != -1)
88 	{
89 		::close(m_desc);
90 		m_desc = -1;
91 	}
92 
93 	if (m_tracer)
94 	{
95 		std::ostringstream trace;
96 		trace << "Connecting to " << address << ", port " << port;
97 
98 		m_tracer->traceSend(trace.str());
99 	}
100 
101 #if VMIME_HAVE_GETADDRINFO  // use thread-safe and IPv6-aware getaddrinfo() if available
102 
103 	// Resolve address, if needed
104 	m_serverAddress = address;
105 
106 	struct ::addrinfo* addrInfo = NULL;  // resolved addresses
107 	resolve(&addrInfo, address, port);
108 
109 	// Connect to host
110 	int sock = -1;
111 	int connectErrno = 0;
112 
113 	if (m_timeoutHandler != NULL)
114 		m_timeoutHandler->resetTimeOut();
115 
116 	for (struct ::addrinfo* curAddrInfo = addrInfo ;
117 	     sock == -1 && curAddrInfo != NULL ;
118 	     curAddrInfo = curAddrInfo->ai_next, connectErrno = ETIMEDOUT)
119 	{
120 		if (curAddrInfo->ai_family != AF_INET && curAddrInfo->ai_family != AF_INET6)
121 			continue;
122 
123 		sock = ::socket(curAddrInfo->ai_family, curAddrInfo->ai_socktype, curAddrInfo->ai_protocol);
124 
125 		if (sock < 0)
126 		{
127 			connectErrno = errno;
128 			continue;  // try next
129 		}
130 
131 #if VMIME_HAVE_SO_KEEPALIVE
132 
133 		// Enable TCP Keepalive
134 		int keepAlive_optval = 1;
135 		socklen_t keepAlive_optlen = sizeof(keepAlive_optval);
136 
137 		::setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive_optval, keepAlive_optlen);
138 
139 #endif // VMIME_HAVE_SO_KEEPALIVE
140 
141 #if VMIME_HAVE_SO_NOSIGPIPE
142 
143 		// Return EPIPE instead of generating SIGPIPE
144 		int nosigpipe_optval = 1;
145 		socklen_t nosigpipe_optlen = sizeof(nosigpipe_optval);
146 
147 		::setsockopt(sock, SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe_optval, nosigpipe_optlen);
148 
149 #endif // VMIME_HAVE_SO_NOSIGPIPE
150 
151 
152 		if (m_timeoutHandler != NULL)
153 		{
154 			::fcntl(sock, F_SETFL, ::fcntl(sock, F_GETFL) | O_NONBLOCK);
155 
156 			if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0)
157 			{
158 				switch (errno)
159 				{
160 				case 0:
161 				case EINPROGRESS:
162 				case EINTR:
163 #if defined(EAGAIN)
164 				case EAGAIN:
165 #endif // EAGAIN
166 #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
167 				case EWOULDBLOCK:
168 #endif // EWOULDBLOCK
169 
170 					// Connection in progress
171 					break;
172 
173 				default:
174 
175 					connectErrno = errno;
176 					::close(sock);
177 					sock = -1;
178 					continue;  // try next
179 				}
180 
181 				// Wait for socket to be connected.
182 				bool connected = false;
183 
184 				const int pollTimeout = 1000;   // poll() timeout (ms)
185 				const int tryNextTimeout = 5000;  // maximum time before trying next (ms)
186 
187 				timeval startTime = { 0, 0 };
188 				gettimeofday(&startTime, /* timezone */ NULL);
189 
190 				do
191 				{
192 					pollfd fds[1];
193 					fds[0].fd = sock;
194 					fds[0].events = POLLIN | POLLOUT;
195 
196 					const int ret = ::poll(fds, sizeof(fds) / sizeof(fds[0]), pollTimeout);
197 
198 					// Success
199 					if (ret > 0)
200 					{
201 						if (fds[0].revents & (POLLIN | POLLOUT))
202 						{
203 							int error = 0;
204 							socklen_t len = sizeof(error);
205 
206 							if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
207 							{
208 								connectErrno = errno;
209 							}
210 							else
211 							{
212 								if (error != 0)
213 									connectErrno = error;
214 								else
215 									connected = true;
216 							}
217 						}
218 
219 						break;
220 					}
221 					// Error
222 					else if (ret < -1)
223 					{
224 						if (errno != EAGAIN && errno != EINTR)
225 						{
226 							// Cancel connection
227 							connectErrno = errno;
228 							break;
229 						}
230 					}
231 
232 					// Check for timeout
233 					if (m_timeoutHandler->isTimeOut())
234 					{
235 						if (!m_timeoutHandler->handleTimeOut())
236 						{
237 							// Cancel connection
238 							connectErrno = ETIMEDOUT;
239 							break;
240 						}
241 						else
242 						{
243 							// Reset timeout and keep waiting for connection
244 							m_timeoutHandler->resetTimeOut();
245 						}
246 					}
247 					else
248 					{
249 						// Keep waiting for connection
250 					}
251 
252 					timeval curTime = { 0, 0 };
253 					gettimeofday(&curTime, /* timezone */ NULL);
254 
255 					if (curAddrInfo->ai_next != NULL &&
256 						curTime.tv_usec - startTime.tv_usec >= tryNextTimeout * 1000)
257 					{
258 						connectErrno = ETIMEDOUT;
259 						break;
260 					}
261 
262 				} while (true);
263 
264 				if (!connected)
265 				{
266 					::close(sock);
267 					sock = -1;
268 					continue;  // try next
269 				}
270 
271 				break;
272 			}
273 			else
274 			{
275 				// Connection successful
276 				break;
277 			}
278 		}
279 		else
280 		{
281 			if (::connect(sock, curAddrInfo->ai_addr, curAddrInfo->ai_addrlen) < 0)
282 			{
283 				connectErrno = errno;
284 				::close(sock);
285 				sock = -1;
286 				continue;  // try next
287 			}
288 		}
289 	}
290 
291 	::freeaddrinfo(addrInfo);
292 
293 	if (sock == -1)
294 	{
295 		try
296 		{
297 			throwSocketError(connectErrno);
298 		}
299 		catch (exceptions::socket_exception& e)
300 		{
301 			throw vmime::exceptions::connection_error
302 				("Error while connecting socket.", e);
303 		}
304 	}
305 
306 	m_desc = sock;
307 
308 #else // !VMIME_HAVE_GETADDRINFO
309 
310 	// Resolve address
311 	::sockaddr_in addr;
312 
313 	memset(&addr, 0, sizeof(addr));
314 
315 	addr.sin_family = AF_INET;
316 	addr.sin_port = htons(static_cast <unsigned short>(port));
317 	addr.sin_addr.s_addr = ::inet_addr(address.c_str());
318 
319 	if (addr.sin_addr.s_addr == static_cast <in_addr_t>(-1))
320 	{
321 		::hostent* hostInfo = ::gethostbyname(address.c_str());
322 
323 		if (hostInfo == NULL)
324 		{
325 			// Error: cannot resolve address
326 			throw vmime::exceptions::connection_error("Cannot resolve address.");
327 		}
328 
329 		::memcpy(reinterpret_cast <char*>(&addr.sin_addr), hostInfo->h_addr, hostInfo->h_length);
330 	}
331 
332 	m_serverAddress = address;
333 
334 	// Get a new socket
335 	m_desc = ::socket(AF_INET, SOCK_STREAM, 0);
336 
337 	if (m_desc == -1)
338 	{
339 		try
340 		{
341 			throwSocketError(errno);
342 		}
343 		catch (exceptions::socket_exception& e)
344 		{
345 			throw vmime::exceptions::connection_error
346 				("Error while creating socket.", e);
347 		}
348 	}
349 
350 	// Start connection
351 	if (::connect(m_desc, reinterpret_cast <sockaddr*>(&addr), sizeof(addr)) == -1)
352 	{
353 		try
354 		{
355 			throwSocketError(errno);
356 		}
357 		catch (exceptions::socket_exception& e)
358 		{
359 			::close(m_desc);
360 			m_desc = -1;
361 
362 			// Error
363 			throw vmime::exceptions::connection_error
364 				("Error while connecting socket.", e);
365 		}
366 	}
367 
368 #endif // VMIME_HAVE_GETADDRINFO
369 
370 	::fcntl(m_desc, F_SETFL, ::fcntl(m_desc, F_GETFL) | O_NONBLOCK);
371 }
372 
373 
resolve(struct::addrinfo ** addrInfo,const vmime::string & address,const vmime::port_t port)374 void posixSocket::resolve(struct ::addrinfo** addrInfo, const vmime::string& address, const vmime::port_t port)
375 {
376 	char portStr[16];
377 	snprintf(portStr, sizeof(portStr), "%u", static_cast <unsigned int>(port));
378 
379 
380 	struct ::addrinfo hints;
381 	memset(&hints, 0, sizeof(hints));
382 
383 	hints.ai_flags = AI_CANONNAME | AI_NUMERICSERV;
384 	hints.ai_family = PF_UNSPEC;
385 	hints.ai_socktype = SOCK_STREAM;
386 
387 #if VMIME_HAVE_GETADDRINFO_A
388 
389 	// If getaddrinfo_a() is available, use asynchronous resolving to allow
390 	// the timeout handler to cancel the operation
391 
392 	struct ::gaicb gaiRequest;
393 	memset(&gaiRequest, 0, sizeof(gaiRequest));
394 
395 	gaiRequest.ar_name = address.c_str();
396 	gaiRequest.ar_service = portStr;
397 	gaiRequest.ar_request = &hints;
398 
399 	struct ::gaicb* gaiRequests = &gaiRequest;
400 	int gaiError;
401 
402 	if ((gaiError = getaddrinfo_a(GAI_NOWAIT, &gaiRequests, 1, NULL)) != 0)
403 	{
404 		throw vmime::exceptions::connection_error
405 			("getaddrinfo_a() failed: " + std::string(gai_strerror(gaiError)));
406 	}
407 
408 	if (m_timeoutHandler != NULL)
409 		m_timeoutHandler->resetTimeOut();
410 
411 	while (true)
412 	{
413 		struct timespec gaiTimeout;
414 		gaiTimeout.tv_sec = 1;  // query timeout handler every second
415 		gaiTimeout.tv_nsec = 0;
416 
417 		gaiError = gai_suspend(&gaiRequests, 1, &gaiTimeout);
418 
419 		if (gaiError == 0 || gaiError == EAI_ALLDONE)
420 		{
421 			const int ret = gai_error(&gaiRequest);
422 
423 			if (ret != 0)
424 			{
425 				throw vmime::exceptions::connection_error
426 					("getaddrinfo_a() request failed: " + std::string(gai_strerror(ret)));
427 			}
428 			else
429 			{
430 				*addrInfo = gaiRequest.ar_result;
431 				break;
432 			}
433 		}
434 		else if (gaiError != EAI_AGAIN)
435 		{
436 			if (gaiError == EAI_SYSTEM)
437 			{
438 				const int ret = gai_error(&gaiRequest);
439 
440 				if (ret != EAI_INPROGRESS && errno != 0)
441 				{
442 					try
443 					{
444 						throwSocketError(errno);
445 					}
446 					catch (exceptions::socket_exception& e)
447 					{
448 						throw vmime::exceptions::connection_error
449 							("Error while connecting socket.", e);
450 					}
451 				}
452 			}
453 			else
454 			{
455 				throw vmime::exceptions::connection_error
456 					("gai_suspend() failed: " + std::string(gai_strerror(gaiError)));
457 			}
458 		}
459 
460 		// Check for timeout
461 		if (m_timeoutHandler && m_timeoutHandler->isTimeOut())
462 		{
463 			if (!m_timeoutHandler->handleTimeOut())
464 			{
465 				throw exceptions::operation_timed_out();
466 			}
467 			else
468 			{
469 				// Reset timeout and keep waiting for connection
470 				m_timeoutHandler->resetTimeOut();
471 			}
472 		}
473 	}
474 
475 #else  // !VMIME_HAVE_GETADDRINFO_A
476 
477 	if (::getaddrinfo(address.c_str(), portStr, &hints, addrInfo) != 0)
478 	{
479 		// Error: cannot resolve address
480 		throw vmime::exceptions::connection_error("Cannot resolve address.");
481 	}
482 
483 #endif  // VMIME_HAVE_GETADDRINFO_A
484 
485 }
486 
487 
isConnected() const488 bool posixSocket::isConnected() const
489 {
490 	if (m_desc == -1)
491 		return false;
492 
493 	char buff;
494 
495 	return ::recv(m_desc, &buff, 1, MSG_PEEK) != 0;
496 }
497 
498 
disconnect()499 void posixSocket::disconnect()
500 {
501 	if (m_desc != -1)
502 	{
503 		if (m_tracer)
504 			m_tracer->traceSend("Disconnecting");
505 
506 		::shutdown(m_desc, SHUT_RDWR);
507 		::close(m_desc);
508 
509 		m_desc = -1;
510 	}
511 }
512 
513 
isNumericAddress(const char * address)514 static bool isNumericAddress(const char* address)
515 {
516 
517 #if VMIME_HAVE_GETADDRINFO
518 
519 	struct addrinfo hint, *info = NULL;
520 	memset(&hint, 0, sizeof(hint));
521 
522 	hint.ai_family = AF_UNSPEC;
523 	hint.ai_flags = AI_NUMERICHOST;
524 
525 	if (getaddrinfo(address, 0, &hint, &info) == 0)
526 	{
527 		freeaddrinfo(info);
528 		return true;
529 	}
530 	else
531 	{
532 		return false;
533 	}
534 
535 #else
536 
537 	return inet_addr(address) != INADDR_NONE;
538 
539 #endif
540 
541 }
542 
543 
getPeerAddress() const544 const string posixSocket::getPeerAddress() const
545 {
546 	// Get address of connected peer
547 	sockaddr peer;
548 	socklen_t peerLen = sizeof(peer);
549 
550 	if (getpeername(m_desc, &peer, &peerLen) != 0)
551 	{
552 		throwSocketError(errno);
553 	}
554 
555 	// Convert to numerical presentation format
556 	char buf[INET6_ADDRSTRLEN];
557 
558 	if (!inet_ntop(peer.sa_family, &(reinterpret_cast <struct sockaddr_in *>(&peer))->sin_addr, buf, sizeof(buf)))
559 	{
560 		throwSocketError(errno);
561 	}
562 
563 	return string(buf);
564 }
565 
566 
getPeerName() const567 const string posixSocket::getPeerName() const
568 {
569 	// Get address of connected peer
570 	sockaddr peer;
571 	socklen_t peerLen = sizeof(peer);
572 
573 	if (getpeername(m_desc, &peer, &peerLen) != 0)
574 	{
575 		throwSocketError(errno);
576 	}
577 
578 	// If server address as specified when connecting is a numeric
579 	// address, try to get a host name for it
580 	if (isNumericAddress(m_serverAddress.c_str()))
581 	{
582 
583 #if VMIME_HAVE_GETNAMEINFO
584 
585 		char host[NI_MAXHOST + 1];
586 		char service[NI_MAXSERV + 1];
587 
588 		if (getnameinfo(reinterpret_cast <sockaddr *>(&peer), peerLen,
589 				host, sizeof(host), service, sizeof(service),
590 				/* flags */ NI_NAMEREQD) == 0)
591 		{
592 			return string(host);
593 		}
594 
595 #else
596 
597 		struct hostent *hp;
598 
599 		if ((hp = gethostbyaddr(reinterpret_cast <const void *>(&peer),
600 				sizeof(peer), peer.sa_family)) != NULL)
601 		{
602 			return string(hp->h_name);
603 		}
604 
605 #endif
606 
607 	}
608 
609 	return m_serverAddress;
610 }
611 
612 
getBlockSize() const613 size_t posixSocket::getBlockSize() const
614 {
615 	return 16384;  // 16 KB
616 }
617 
618 
waitForData(const bool read,const bool write,const int msecs)619 bool posixSocket::waitForData(const bool read, const bool write, const int msecs)
620 {
621 	for (int i = 0 ; i <= msecs / 10 ; ++i)
622 	{
623 		// Check whether data is available
624 		pollfd fds[1];
625 		fds[0].fd = m_desc;
626 		fds[0].events = 0;
627 
628 		if (read)
629 			fds[0].events |= POLLIN;
630 
631 		if (write)
632 			fds[0].events |= POLLOUT;
633 
634 		const int ret = ::poll(fds, sizeof(fds) / sizeof(fds[0]), 10 /* ms */);
635 
636 		if (ret < 0)
637 		{
638 			if (errno != EAGAIN && errno != EINTR)
639 				throwSocketError(errno);
640 		}
641 		else if (ret > 0)
642 		{
643 			if (fds[0].revents & (POLLIN | POLLOUT))
644 				return true;
645 		}
646 
647 		// No data available at this time
648 		// Check if we are timed out
649 		if (m_timeoutHandler &&
650 		    m_timeoutHandler->isTimeOut())
651 		{
652 			if (!m_timeoutHandler->handleTimeOut())
653 			{
654 				// Server did not react within timeout delay
655 				throw exceptions::operation_timed_out();
656 			}
657 			else
658 			{
659 				// Reset timeout
660 				m_timeoutHandler->resetTimeOut();
661 			}
662 		}
663 	}
664 
665 	return false;  // time out
666 }
667 
668 
waitForRead(const int msecs)669 bool posixSocket::waitForRead(const int msecs)
670 {
671 	return waitForData(/* read */ true, /* write */ false, msecs);
672 }
673 
674 
waitForWrite(const int msecs)675 bool posixSocket::waitForWrite(const int msecs)
676 {
677 	return waitForData(/* read */ false, /* write */ true, msecs);
678 }
679 
680 
receive(vmime::string & buffer)681 void posixSocket::receive(vmime::string& buffer)
682 {
683 	const size_t size = receiveRaw(m_buffer, sizeof(m_buffer));
684 	buffer = utility::stringUtils::makeStringFromBytes(m_buffer, size);
685 }
686 
687 
receiveRaw(byte_t * buffer,const size_t count)688 size_t posixSocket::receiveRaw(byte_t* buffer, const size_t count)
689 {
690 	m_status &= ~STATUS_WOULDBLOCK;
691 
692 	// Check whether data is available
693 	if (!waitForRead(50 /* msecs */))
694 	{
695 		m_status |= STATUS_WOULDBLOCK;
696 
697 		// Continue waiting for data
698 		return 0;
699 	}
700 
701 	// Read available data
702 	ssize_t ret = ::recv(m_desc, buffer, count, 0);
703 
704 	if (ret < 0)
705 	{
706 		if (!IS_EAGAIN(errno))
707 			throwSocketError(errno);
708 
709 		// Check if we are timed out
710 		if (m_timeoutHandler &&
711 		    m_timeoutHandler->isTimeOut())
712 		{
713 			if (!m_timeoutHandler->handleTimeOut())
714 			{
715 				// Server did not react within timeout delay
716 				throwSocketError(errno);
717 			}
718 			else
719 			{
720 				// Reset timeout
721 				m_timeoutHandler->resetTimeOut();
722 			}
723 		}
724 
725 		m_status |= STATUS_WOULDBLOCK;
726 
727 		// No data available at this time
728 		return 0;
729 	}
730 	else if (ret == 0)
731 	{
732 		// Host shutdown
733 		throwSocketError(ENOTCONN);
734 	}
735 	else
736 	{
737 		// Data received, reset timeout
738 		if (m_timeoutHandler)
739 			m_timeoutHandler->resetTimeOut();
740 	}
741 
742 	return ret;
743 }
744 
745 
send(const vmime::string & buffer)746 void posixSocket::send(const vmime::string& buffer)
747 {
748 	sendRaw(reinterpret_cast <const byte_t*>(buffer.data()), buffer.length());
749 }
750 
751 
send(const char * str)752 void posixSocket::send(const char* str)
753 {
754 	sendRaw(reinterpret_cast <const byte_t*>(str), ::strlen(str));
755 }
756 
757 
sendRaw(const byte_t * buffer,const size_t count)758 void posixSocket::sendRaw(const byte_t* buffer, const size_t count)
759 {
760 	m_status &= ~STATUS_WOULDBLOCK;
761 
762 	size_t size = count;
763 
764 	while (size > 0)
765 	{
766 
767 #if VMIME_HAVE_MSG_NOSIGNAL
768 		const ssize_t ret = ::send(m_desc, buffer, size, MSG_NOSIGNAL);
769 #else
770 		const ssize_t ret = ::send(m_desc, buffer, size, 0);
771 #endif
772 
773 		if (ret <= 0)
774 		{
775 			if (ret < 0 && !IS_EAGAIN(errno))
776 				throwSocketError(errno);
777 
778 			waitForWrite(50 /* msecs */);
779 		}
780 		else
781 		{
782 			buffer += ret;
783 			size -= ret;
784 		}
785 	}
786 
787 	// Reset timeout
788 	if (m_timeoutHandler)
789 		m_timeoutHandler->resetTimeOut();
790 }
791 
792 
sendRawNonBlocking(const byte_t * buffer,const size_t count)793 size_t posixSocket::sendRawNonBlocking(const byte_t* buffer, const size_t count)
794 {
795 	m_status &= ~STATUS_WOULDBLOCK;
796 
797 #if VMIME_HAVE_MSG_NOSIGNAL
798 	const ssize_t ret = ::send(m_desc, buffer, count, MSG_NOSIGNAL);
799 #else
800 	const ssize_t ret = ::send(m_desc, buffer, count, 0);
801 #endif
802 
803 	if (ret <= 0)
804 	{
805 		if (ret < 0 && !IS_EAGAIN(errno))
806 			throwSocketError(errno);
807 
808 		// Check if we are timed out
809 		if (m_timeoutHandler &&
810 		    m_timeoutHandler->isTimeOut())
811 		{
812 			if (!m_timeoutHandler->handleTimeOut())
813 			{
814 				// Could not send data within timeout delay
815 				throw exceptions::operation_timed_out();
816 			}
817 			else
818 			{
819 				// Reset timeout
820 				m_timeoutHandler->resetTimeOut();
821 			}
822 		}
823 
824 		m_status |= STATUS_WOULDBLOCK;
825 
826 		// No data can be written at this time
827 		return 0;
828 	}
829 
830 	// Reset timeout
831 	if (m_timeoutHandler)
832 		m_timeoutHandler->resetTimeOut();
833 
834 	return ret;
835 }
836 
837 
throwSocketError(const int err)838 void posixSocket::throwSocketError(const int err)
839 {
840 	const char* msg = NULL;
841 
842 	switch (err)
843 	{
844 	case EACCES:          msg = "EACCES: permission denied"; break;
845 	case EAFNOSUPPORT:    msg = "EAFNOSUPPORT: address family not supported"; break;
846 	case EMFILE:          msg = "EMFILE: process file table overflow"; break;
847 	case ENFILE:          msg = "ENFILE: system limit reached"; break;
848 	case EPROTONOSUPPORT: msg = "EPROTONOSUPPORT: protocol not supported"; break;
849 	case EAGAIN:          msg = "EGAIN: blocking operation"; break;
850 	case EBADF:           msg = "EBADF: invalid descriptor"; break;
851 	case ECONNRESET:      msg = "ECONNRESET: connection reset by peer"; break;
852 	case EFAULT:          msg = "EFAULT: bad user space address"; break;
853 	case EINTR:           msg = "EINTR: signal occured before transmission"; break;
854 	case EINVAL:          msg = "EINVAL: invalid argument"; break;
855 	case EMSGSIZE:        msg = "EMSGSIZE: message cannot be sent atomically"; break;
856 	case ENOBUFS:         msg = "ENOBUFS: output queue is full"; break;
857 	case ENOMEM:          msg = "ENOMEM: out of memory"; break;
858 	case EPIPE:           msg = "EPIPE: broken pipe"; break;
859 	case ENOTCONN:        msg = "ENOTCONN: not connected"; break;
860 	case ECONNREFUSED:    msg = "ECONNREFUSED: connection refused"; break;
861 	}
862 
863 	if (msg)
864 	{
865 		throw exceptions::socket_exception(msg);
866 	}
867 	else
868 	{
869 
870 		// Use strerror() to get string describing error number
871 
872 #if VMIME_HAVE_STRERROR_R
873 
874 		char errbuf[512];
875 
876 	#if 1
877 
878 		// XSI-compliant strerror_r()
879 		strerror_r(err, errbuf, sizeof(errbuf));
880 		throw exceptions::socket_exception(errbuf);
881 
882 	#else
883 
884 		// GNU-specific strerror_r()
885 		const std::string strmsg(strerror_r(err, errbuf, sizeof(errbuf)));
886 		throw exceptions::socket_exception(strmsg);
887 
888 	#endif
889 
890 #else  // !VMIME_HAVE_STRERROR_R
891 
892 		const std::string strmsg(strerror(err));
893 		throw exceptions::socket_exception(strmsg);
894 
895 #endif  // VMIME_HAVE_STRERROR_R
896 
897 	}
898 }
899 
900 
getStatus() const901 unsigned int posixSocket::getStatus() const
902 {
903 	return m_status;
904 }
905 
906 
getTimeoutHandler()907 shared_ptr <net::timeoutHandler> posixSocket::getTimeoutHandler()
908 {
909 	return m_timeoutHandler;
910 }
911 
912 
setTracer(shared_ptr<net::tracer> tracer)913 void posixSocket::setTracer(shared_ptr <net::tracer> tracer)
914 {
915 	m_tracer = tracer;
916 }
917 
918 
getTracer()919 shared_ptr <net::tracer> posixSocket::getTracer()
920 {
921 	return m_tracer;
922 }
923 
924 
925 
926 //
927 // posixSocketFactory
928 //
929 
create()930 shared_ptr <vmime::net::socket> posixSocketFactory::create()
931 {
932 	shared_ptr <vmime::net::timeoutHandler> th;
933 	return make_shared <posixSocket>(th);
934 }
935 
936 
create(shared_ptr<vmime::net::timeoutHandler> th)937 shared_ptr <vmime::net::socket> posixSocketFactory::create(shared_ptr <vmime::net::timeoutHandler> th)
938 {
939 	return make_shared <posixSocket>(th);
940 }
941 
942 
943 } // posix
944 } // platforms
945 } // vmime
946 
947 
948 #endif // VMIME_PLATFORM_IS_POSIX && VMIME_HAVE_MESSAGING_FEATURES
949