468cc3deb4dfa075a60bff9cd2ca899e8ac0d5d8
[deb_libcec.git] / src / lib / platform / posix / os-socket.h
1 #pragma once
2 /*
3 * This file is part of the libCEC(R) library.
4 *
5 * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved.
6 * libCEC(R) is an original work, containing original code.
7 *
8 * libCEC(R) is a trademark of Pulse-Eight Limited.
9 *
10 * This program is dual-licensed; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 *
24 *
25 * Alternatively, you can license this library under a commercial license,
26 * please contact Pulse-Eight Licensing for more information.
27 *
28 * For more information contact:
29 * Pulse-Eight Licensing <license@pulse-eight.com>
30 * http://www.pulse-eight.com/
31 * http://www.pulse-eight.net/
32 */
33
34
35 #include "../os.h"
36 #include "../util/timeutils.h"
37 #include <stdio.h>
38 #include <fcntl.h>
39 #include <sys/socket.h>
40 #include <netinet/in.h>
41 #include <netinet/tcp.h>
42 #include <arpa/inet.h>
43 #include <netdb.h>
44 #include <poll.h>
45
46 namespace PLATFORM
47 {
48 // Standard sockets
49 //@{
50 inline void SocketClose(socket_t socket)
51 {
52 if (socket != INVALID_SOCKET_VALUE)
53 close(socket);
54 }
55
56 inline void SocketSetBlocking(socket_t socket, bool bSetTo)
57 {
58 if (socket != INVALID_SOCKET_VALUE)
59 {
60 if (bSetTo)
61 fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) & ~O_NONBLOCK);
62 else
63 fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK);
64 }
65 }
66
67 inline ssize_t SocketWrite(socket_t socket, int *iError, void* data, size_t len)
68 {
69 fd_set port;
70
71 if (socket == INVALID_SOCKET_VALUE)
72 {
73 *iError = EINVAL;
74 return -1;
75 }
76
77 ssize_t iBytesWritten(0);
78 struct timeval *tv(NULL);
79
80 while (iBytesWritten < (ssize_t)len)
81 {
82 FD_ZERO(&port);
83 FD_SET(socket, &port);
84 int returnv = select(socket + 1, NULL, &port, NULL, tv);
85 if (returnv < 0)
86 {
87 *iError = errno;
88 return -1;
89 }
90 else if (returnv == 0)
91 {
92 *iError = ETIMEDOUT;
93 return -1;
94 }
95
96 returnv = write(socket, (char*)data + iBytesWritten, len - iBytesWritten);
97 if (returnv == -1)
98 {
99 *iError = errno;
100 return -1;
101 }
102 iBytesWritten += returnv;
103 }
104
105 return iBytesWritten;
106 }
107
108 inline ssize_t SocketRead(socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
109 {
110 fd_set port;
111 struct timeval timeout, *tv;
112 int64_t iNow(0), iTarget(0);
113 ssize_t iBytesRead(0);
114 *iError = 0;
115
116 if (socket == INVALID_SOCKET_VALUE)
117 {
118 *iError = EINVAL;
119 return -1;
120 }
121
122 if (iTimeoutMs > 0)
123 {
124 iNow = GetTimeMs();
125 iTarget = iNow + (int64_t) iTimeoutMs;
126 }
127
128 while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
129 {
130 if (iTimeoutMs == 0)
131 {
132 tv = NULL;
133 }
134 else
135 {
136 timeout.tv_sec = ((long int)iTarget - (long int)iNow) / (long int)1000.;
137 timeout.tv_usec = ((long int)iTarget - (long int)iNow) % (long int)1000.;
138 tv = &timeout;
139 }
140
141 FD_ZERO(&port);
142 FD_SET(socket, &port);
143 int32_t returnv = select(socket + 1, &port, NULL, NULL, tv);
144
145 if (returnv == -1)
146 {
147 *iError = errno;
148 return -1;
149 }
150 else if (returnv == 0)
151 {
152 break; //nothing to read
153 }
154
155 returnv = read(socket, (char*)data + iBytesRead, len - iBytesRead);
156 if (returnv == -1)
157 {
158 *iError = errno;
159 return -1;
160 }
161
162 iBytesRead += returnv;
163
164 if (iTimeoutMs > 0)
165 iNow = GetTimeMs();
166 }
167
168 return iBytesRead;
169 }
170 //@}
171
172 // TCP
173 //@{
174 inline void TcpSocketClose(tcp_socket_t socket)
175 {
176 SocketClose(socket);
177 }
178
179 inline void TcpSocketShutdown(tcp_socket_t socket)
180 {
181 if (socket != INVALID_SOCKET_VALUE)
182 shutdown(socket, SHUT_RDWR);
183 }
184
185 inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len)
186 {
187 if (socket == INVALID_SOCKET_VALUE)
188 {
189 *iError = EINVAL;
190 return -1;
191 }
192
193 ssize_t iReturn = send(socket, data, len, 0);
194 if (iReturn < (ssize_t)len)
195 *iError = errno;
196 return iReturn;
197 }
198
199 inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
200 {
201 int64_t iNow(0), iTarget(0);
202 ssize_t iBytesRead(0);
203 *iError = 0;
204
205 if (socket == INVALID_SOCKET_VALUE)
206 {
207 *iError = EINVAL;
208 return -1;
209 }
210
211 if (iTimeoutMs > 0)
212 {
213 iNow = GetTimeMs();
214 iTarget = iNow + (int64_t) iTimeoutMs;
215 }
216
217 struct pollfd fds;
218 fds.fd = socket;
219 fds.events = POLLIN;
220 fds.revents = 0;
221
222 while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
223 {
224 if (iTimeoutMs > 0)
225 {
226 int iPollResult = poll(&fds, 1, iTarget - iNow);
227 if (iPollResult == 0)
228 {
229 *iError = ETIMEDOUT;
230 return -ETIMEDOUT;
231 }
232 }
233
234 ssize_t iReadResult = (iTimeoutMs > 0) ?
235 recv(socket, (char*)data + iBytesRead, len - iBytesRead, MSG_DONTWAIT) :
236 recv(socket, data, len, MSG_WAITALL);
237 if (iReadResult < 0)
238 {
239 if (errno == EAGAIN && iTimeoutMs > 0)
240 continue;
241 *iError = errno;
242 return -errno;
243 }
244 else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
245 {
246 *iError = ECONNRESET;
247 return -ECONNRESET;
248 }
249
250 iBytesRead += iReadResult;
251
252 if (iTimeoutMs > 0)
253 iNow = GetTimeMs();
254 }
255
256 if (iBytesRead < (ssize_t)len)
257 *iError = ETIMEDOUT;
258 return iBytesRead;
259 }
260
261 inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info)
262 {
263 struct addrinfo hints;
264 char service[33];
265 memset(&hints, 0, sizeof(hints));
266 hints.ai_family = AF_UNSPEC;
267 hints.ai_socktype = SOCK_STREAM;
268 hints.ai_protocol = IPPROTO_TCP;
269 sprintf(service, "%d", iPort);
270
271 *iError = getaddrinfo(strHost, service, &hints, info);
272 return !(*iError);
273 }
274
275 inline int TcpGetSocketError(tcp_socket_t socket)
276 {
277 int iReturn(0);
278 socklen_t optLen = sizeof(socket_t);
279 getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&iReturn, &optLen);
280 return iReturn;
281 }
282
283 inline bool TcpSetNoDelay(tcp_socket_t socket)
284 {
285 int iSetTo(1);
286 setsockopt(socket, SOL_TCP, TCP_NODELAY, &iSetTo, sizeof(iSetTo));
287 return true;
288 }
289
290 inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0)
291 {
292 *iError = 0;
293 int iConnectResult = connect(socket, addr->ai_addr, addr->ai_addrlen);
294 if (iConnectResult == -1)
295 {
296 if (errno == EINPROGRESS)
297 {
298 struct pollfd pfd;
299 pfd.fd = socket;
300 pfd.events = POLLOUT;
301 pfd.revents = 0;
302
303 int iPollResult = poll(&pfd, 1, iTimeout);
304 if (iPollResult == 0)
305 *iError = ETIMEDOUT;
306 else if (iPollResult == -1)
307 *iError = errno;
308
309 socklen_t errlen = sizeof(int);
310 getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)iError, &errlen);
311 }
312 else
313 {
314 *iError = errno;
315 }
316 }
317
318 return *iError == 0;
319 }
320 //@}
321 }