1 /*
2  * Copyright (c) 2011-2014, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 #include "Socket.h"
31 #include <sys/types.h>
32 #include <sys/socket.h>
33 #include <unistd.h>
34 #include <assert.h>
35 #include <netdb.h>
36 #include <strings.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <netinet/in.h>
40 #include <netinet/tcp.h>
41 #include <sys/time.h>
42 #include <signal.h>
43 
CSocket()44 CSocket::CSocket() : _iSockFd(socket(AF_INET, SOCK_STREAM, 0)), mSendFlag(0)
45 {
46     assert(_iSockFd != -1);
47 
48     int iNoDelay = 1;
49     // (see man 7 tcp)
50     // Setting TCP_NODELAY allows us sending commands and responses as soon as
51     // they are ready to be sent, instead of waiting for more data on the
52     // socket.
53     setsockopt(_iSockFd, IPPROTO_TCP, TCP_NODELAY, (char *)&iNoDelay, sizeof(iNoDelay));
54 
55     // Disable sigpipe reception on send
56 #   if not defined(SIGPIPE)
57         // Pipe signal does not exist, there no sigpipe to ignore on send
58 #   elif defined(SO_NOSIGPIPE)
59         const int set = 1;
60         setsockopt(_iSockFd, SOL_SOCKET, SO_NOSIGPIPE, &set, sizeof(set));
61 #   elif defined(MSG_NOSIGNAL)
62         // Use flag NOSIGNAL on send call
63         mSendFlag = MSG_NOSIGNAL;
64 #   else
65 #       error Can not disable SIGPIPE
66 #   endif
67 }
68 
CSocket(int iSockId)69 CSocket::CSocket(int iSockId) : _iSockFd(iSockId)
70 {
71     assert(_iSockFd != -1);
72 }
73 
~CSocket()74 CSocket::~CSocket()
75 {
76     // fd might be invalide if send had an error.
77     // valgrind displays a warning if closing an invalid fd.
78     if (_iSockFd != -1) {
79         close(_iSockFd);
80     }
81 }
82 
83 // Socket address init
initSockAddrIn(struct sockaddr_in * pSockAddrIn,uint32_t uiInAddr,uint16_t uiPort) const84 void CSocket::initSockAddrIn(struct sockaddr_in* pSockAddrIn, uint32_t uiInAddr, uint16_t uiPort) const
85 {
86     // Fill server address
87     pSockAddrIn->sin_family = AF_INET;
88     pSockAddrIn->sin_port = htons(uiPort);
89     pSockAddrIn->sin_addr.s_addr = uiInAddr;
90     bzero(&pSockAddrIn->sin_zero, sizeof(pSockAddrIn->sin_zero));
91 }
92 
93 // Non blocking state
setNonBlocking(bool bNonBlocking)94 void CSocket::setNonBlocking(bool bNonBlocking)
95 {
96     int iFlags = fcntl(_iSockFd, F_GETFL, 0);
97 
98     assert(iFlags != -1);
99 
100     if (bNonBlocking) {
101 
102         iFlags |= O_NONBLOCK;
103     } else {
104 
105         iFlags &= ~O_NONBLOCK;
106     }
107     fcntl(_iSockFd, F_SETFL, iFlags);
108 }
109 
110 // Communication timeout
setTimeout(uint32_t uiMilliseconds)111 void CSocket::setTimeout(uint32_t uiMilliseconds)
112 {
113     struct timeval tv;
114     tv.tv_sec = uiMilliseconds / 1000;
115     tv.tv_usec = (uiMilliseconds % 1000) * 1000;
116 
117     setsockopt(_iSockFd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
118     setsockopt(_iSockFd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
119 }
120 
121 // Read
read(void * pvData,uint32_t uiSize)122 bool CSocket::read(void* pvData, uint32_t uiSize)
123 {
124     uint32_t uiOffset = 0;
125     uint8_t* pucData = (uint8_t*)pvData;
126 
127     while (uiSize) {
128 
129         int32_t iAccessedSize = ::recv(_iSockFd, &pucData[uiOffset], uiSize, 0);
130 
131         switch (iAccessedSize) {
132         case 0:
133             // recv return value is 0 when the peer has performed an orderly shutdown.
134             _disconnected = true;
135             errno = ECONNRESET; // Warn the client that the client disconnected.
136             return false;
137 
138         case -1:
139             // errno == EINTR => The recv system call was interrupted, try again
140             if (errno != EINTR) {
141                 return false;
142             }
143             break;
144 
145         default:
146             uiSize -= iAccessedSize;
147             uiOffset += iAccessedSize;
148         }
149     }
150     return true;
151 }
152 
153 // Write
write(const void * pvData,uint32_t uiSize)154 bool CSocket::write(const void* pvData, uint32_t uiSize)
155 {
156     uint32_t uiOffset = 0;
157     const uint8_t* pucData = (const uint8_t*)pvData;
158 
159     while (uiSize) {
160 
161         int32_t iAccessedSize = ::send(_iSockFd, &pucData[uiOffset], uiSize, mSendFlag);
162 
163         if (iAccessedSize == -1) {
164             if (errno == EINTR) {
165                 // The send system call was interrupted, try again
166                 continue;
167             }
168 
169             // An error occured, forget this socket
170             _disconnected = true;
171             close(_iSockFd);
172             _iSockFd = -1; // Avoid writing again on the same socket
173             return false;
174         } else {
175             uiSize -= iAccessedSize;
176             uiOffset += iAccessedSize;
177         }
178     }
179     return true;
180 }
181 
182 // Fd
getFd() const183 int CSocket::getFd() const
184 {
185     return _iSockFd;
186 }
187 
hasPeerDisconnected()188 bool CSocket::hasPeerDisconnected() {
189     return _disconnected;
190 }
191