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