Commit | Line | Data |
---|---|---|
ba65909d LOK |
1 | #pragma once |
2 | /* | |
3 | * This file is part of the libCEC(R) library. | |
4 | * | |
9b53a148 | 5 | * libCEC(R) is Copyright (C) 2011-2012 Pulse-Eight Limited. All rights reserved. |
ba65909d LOK |
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 | ||
2b44051c LOK |
35 | #include "lib/platform/os.h" |
36 | #include "lib/platform/util/timeutils.h" | |
ba65909d LOK |
37 | #include <stdio.h> |
38 | #include <fcntl.h> | |
99666519 LOK |
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> | |
ba65909d | 45 | |
1a47476a LOK |
46 | /* Needed on Mac OS/X */ |
47 | #ifndef SOL_TCP | |
48 | #define SOL_TCP IPPROTO_TCP | |
49 | #endif | |
50 | ||
ba65909d LOK |
51 | namespace PLATFORM |
52 | { | |
99666519 LOK |
53 | // Standard sockets |
54 | //@{ | |
ba65909d LOK |
55 | inline void SocketClose(socket_t socket) |
56 | { | |
99666519 | 57 | if (socket != INVALID_SOCKET_VALUE) |
ba65909d LOK |
58 | close(socket); |
59 | } | |
60 | ||
61 | inline void SocketSetBlocking(socket_t socket, bool bSetTo) | |
62 | { | |
99666519 LOK |
63 | if (socket != INVALID_SOCKET_VALUE) |
64 | { | |
65 | if (bSetTo) | |
66 | fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) & ~O_NONBLOCK); | |
67 | else | |
68 | fcntl(socket, F_SETFL, fcntl(socket, F_GETFL) | O_NONBLOCK); | |
69 | } | |
ba65909d LOK |
70 | } |
71 | ||
99666519 | 72 | inline ssize_t SocketWrite(socket_t socket, int *iError, void* data, size_t len) |
ba65909d LOK |
73 | { |
74 | fd_set port; | |
75 | ||
99666519 | 76 | if (socket == INVALID_SOCKET_VALUE) |
ba65909d LOK |
77 | { |
78 | *iError = EINVAL; | |
15d01ae0 | 79 | return -EINVAL; |
ba65909d LOK |
80 | } |
81 | ||
99666519 | 82 | ssize_t iBytesWritten(0); |
cb2397e9 | 83 | struct timeval *tv(NULL); |
ba65909d | 84 | |
99666519 | 85 | while (iBytesWritten < (ssize_t)len) |
ba65909d LOK |
86 | { |
87 | FD_ZERO(&port); | |
88 | FD_SET(socket, &port); | |
dbe84b6e | 89 | ssize_t returnv = (ssize_t)select(socket + 1, NULL, &port, NULL, tv); |
ba65909d LOK |
90 | if (returnv < 0) |
91 | { | |
92 | *iError = errno; | |
15d01ae0 | 93 | return -errno; |
ba65909d LOK |
94 | } |
95 | else if (returnv == 0) | |
96 | { | |
97 | *iError = ETIMEDOUT; | |
15d01ae0 | 98 | return -ETIMEDOUT; |
ba65909d LOK |
99 | } |
100 | ||
99666519 | 101 | returnv = write(socket, (char*)data + iBytesWritten, len - iBytesWritten); |
ba65909d LOK |
102 | if (returnv == -1) |
103 | { | |
104 | *iError = errno; | |
15d01ae0 | 105 | return -errno; |
ba65909d LOK |
106 | } |
107 | iBytesWritten += returnv; | |
108 | } | |
109 | ||
110 | return iBytesWritten; | |
111 | } | |
112 | ||
99666519 | 113 | inline ssize_t SocketRead(socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/) |
ba65909d LOK |
114 | { |
115 | fd_set port; | |
116 | struct timeval timeout, *tv; | |
117 | int64_t iNow(0), iTarget(0); | |
99666519 | 118 | ssize_t iBytesRead(0); |
ba65909d LOK |
119 | *iError = 0; |
120 | ||
99666519 | 121 | if (socket == INVALID_SOCKET_VALUE) |
ba65909d LOK |
122 | { |
123 | *iError = EINVAL; | |
15d01ae0 | 124 | return -EINVAL; |
ba65909d LOK |
125 | } |
126 | ||
127 | if (iTimeoutMs > 0) | |
128 | { | |
129 | iNow = GetTimeMs(); | |
130 | iTarget = iNow + (int64_t) iTimeoutMs; | |
131 | } | |
132 | ||
99666519 | 133 | while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow)) |
ba65909d LOK |
134 | { |
135 | if (iTimeoutMs == 0) | |
136 | { | |
137 | tv = NULL; | |
138 | } | |
139 | else | |
140 | { | |
141 | timeout.tv_sec = ((long int)iTarget - (long int)iNow) / (long int)1000.; | |
142 | timeout.tv_usec = ((long int)iTarget - (long int)iNow) % (long int)1000.; | |
143 | tv = &timeout; | |
144 | } | |
145 | ||
146 | FD_ZERO(&port); | |
147 | FD_SET(socket, &port); | |
dbe84b6e | 148 | ssize_t returnv = (ssize_t)select(socket + 1, &port, NULL, NULL, tv); |
ba65909d LOK |
149 | |
150 | if (returnv == -1) | |
151 | { | |
152 | *iError = errno; | |
15d01ae0 | 153 | return -errno; |
ba65909d LOK |
154 | } |
155 | else if (returnv == 0) | |
156 | { | |
157 | break; //nothing to read | |
158 | } | |
159 | ||
99666519 | 160 | returnv = read(socket, (char*)data + iBytesRead, len - iBytesRead); |
ba65909d LOK |
161 | if (returnv == -1) |
162 | { | |
163 | *iError = errno; | |
15d01ae0 | 164 | return -errno; |
ba65909d LOK |
165 | } |
166 | ||
167 | iBytesRead += returnv; | |
168 | ||
169 | if (iTimeoutMs > 0) | |
170 | iNow = GetTimeMs(); | |
171 | } | |
172 | ||
173 | return iBytesRead; | |
174 | } | |
99666519 LOK |
175 | //@} |
176 | ||
177 | // TCP | |
178 | //@{ | |
179 | inline void TcpSocketClose(tcp_socket_t socket) | |
180 | { | |
181 | SocketClose(socket); | |
182 | } | |
183 | ||
184 | inline void TcpSocketShutdown(tcp_socket_t socket) | |
185 | { | |
186 | if (socket != INVALID_SOCKET_VALUE) | |
187 | shutdown(socket, SHUT_RDWR); | |
188 | } | |
189 | ||
190 | inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len) | |
191 | { | |
192 | if (socket == INVALID_SOCKET_VALUE) | |
193 | { | |
194 | *iError = EINVAL; | |
195 | return -1; | |
196 | } | |
197 | ||
198 | ssize_t iReturn = send(socket, data, len, 0); | |
199 | if (iReturn < (ssize_t)len) | |
200 | *iError = errno; | |
201 | return iReturn; | |
202 | } | |
203 | ||
204 | inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/) | |
205 | { | |
206 | int64_t iNow(0), iTarget(0); | |
207 | ssize_t iBytesRead(0); | |
208 | *iError = 0; | |
209 | ||
210 | if (socket == INVALID_SOCKET_VALUE) | |
211 | { | |
212 | *iError = EINVAL; | |
15d01ae0 | 213 | return -EINVAL; |
99666519 LOK |
214 | } |
215 | ||
216 | if (iTimeoutMs > 0) | |
217 | { | |
218 | iNow = GetTimeMs(); | |
219 | iTarget = iNow + (int64_t) iTimeoutMs; | |
220 | } | |
221 | ||
222 | struct pollfd fds; | |
223 | fds.fd = socket; | |
224 | fds.events = POLLIN; | |
225 | fds.revents = 0; | |
226 | ||
227 | while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow)) | |
228 | { | |
229 | if (iTimeoutMs > 0) | |
230 | { | |
dbe84b6e | 231 | int iPollResult = poll(&fds, 1, (int)(iTarget - iNow)); |
99666519 LOK |
232 | if (iPollResult == 0) |
233 | { | |
234 | *iError = ETIMEDOUT; | |
235 | return -ETIMEDOUT; | |
236 | } | |
237 | } | |
238 | ||
239 | ssize_t iReadResult = (iTimeoutMs > 0) ? | |
240 | recv(socket, (char*)data + iBytesRead, len - iBytesRead, MSG_DONTWAIT) : | |
241 | recv(socket, data, len, MSG_WAITALL); | |
242 | if (iReadResult < 0) | |
243 | { | |
244 | if (errno == EAGAIN && iTimeoutMs > 0) | |
245 | continue; | |
246 | *iError = errno; | |
247 | return -errno; | |
248 | } | |
249 | else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0)) | |
250 | { | |
251 | *iError = ECONNRESET; | |
252 | return -ECONNRESET; | |
253 | } | |
254 | ||
255 | iBytesRead += iReadResult; | |
256 | ||
257 | if (iTimeoutMs > 0) | |
258 | iNow = GetTimeMs(); | |
259 | } | |
260 | ||
261 | if (iBytesRead < (ssize_t)len) | |
262 | *iError = ETIMEDOUT; | |
263 | return iBytesRead; | |
264 | } | |
265 | ||
266 | inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info) | |
267 | { | |
268 | struct addrinfo hints; | |
269 | char service[33]; | |
270 | memset(&hints, 0, sizeof(hints)); | |
271 | hints.ai_family = AF_UNSPEC; | |
272 | hints.ai_socktype = SOCK_STREAM; | |
273 | hints.ai_protocol = IPPROTO_TCP; | |
274 | sprintf(service, "%d", iPort); | |
275 | ||
276 | *iError = getaddrinfo(strHost, service, &hints, info); | |
277 | return !(*iError); | |
278 | } | |
279 | ||
280 | inline int TcpGetSocketError(tcp_socket_t socket) | |
281 | { | |
282 | int iReturn(0); | |
283 | socklen_t optLen = sizeof(socket_t); | |
284 | getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)&iReturn, &optLen); | |
285 | return iReturn; | |
286 | } | |
287 | ||
288 | inline bool TcpSetNoDelay(tcp_socket_t socket) | |
289 | { | |
290 | int iSetTo(1); | |
291 | setsockopt(socket, SOL_TCP, TCP_NODELAY, &iSetTo, sizeof(iSetTo)); | |
292 | return true; | |
293 | } | |
294 | ||
295 | inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0) | |
296 | { | |
297 | *iError = 0; | |
298 | int iConnectResult = connect(socket, addr->ai_addr, addr->ai_addrlen); | |
299 | if (iConnectResult == -1) | |
300 | { | |
301 | if (errno == EINPROGRESS) | |
302 | { | |
303 | struct pollfd pfd; | |
304 | pfd.fd = socket; | |
305 | pfd.events = POLLOUT; | |
306 | pfd.revents = 0; | |
307 | ||
dbe84b6e | 308 | int iPollResult = poll(&pfd, 1, (int)iTimeout); |
99666519 LOK |
309 | if (iPollResult == 0) |
310 | *iError = ETIMEDOUT; | |
311 | else if (iPollResult == -1) | |
312 | *iError = errno; | |
313 | ||
314 | socklen_t errlen = sizeof(int); | |
315 | getsockopt(socket, SOL_SOCKET, SO_ERROR, (void *)iError, &errlen); | |
316 | } | |
317 | else | |
318 | { | |
319 | *iError = errno; | |
320 | } | |
321 | } | |
322 | ||
323 | return *iError == 0; | |
324 | } | |
325 | //@} | |
ba65909d | 326 | } |