bump version numbers to 1.7.0 and updated changelog
[deb_libcec.git] / src / lib / platform / windows / 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 #include "../os.h"
35 #include "../util/timeutils.h"
36
37 #include <ws2spi.h>
38 #include <ws2ipdef.h>
39 #include <ws2tcpip.h>
40
41 #define SHUT_RDWR SD_BOTH
42
43 #ifndef ETIMEDOUT
44 #define ETIMEDOUT 138
45 #endif
46
47 namespace PLATFORM
48 {
49 #ifndef MSG_WAITALL
50 #define MSG_WAITALL 0x8
51 #endif
52
53 inline int GetSocketError(void)
54 {
55 int error = WSAGetLastError();
56 switch(error)
57 {
58 case WSAEINPROGRESS: return EINPROGRESS;
59 case WSAECONNRESET : return ECONNRESET;
60 case WSAETIMEDOUT : return ETIMEDOUT;
61 case WSAEWOULDBLOCK: return EAGAIN;
62 default : return error;
63 }
64 }
65
66 // Serial port
67 //@{
68 inline void SerialSocketClose(serial_socket_t socket)
69 {
70 if (socket != INVALID_HANDLE_VALUE)
71 CloseHandle(socket);
72 }
73
74 inline ssize_t SerialSocketWrite(serial_socket_t socket, int *iError, void* data, size_t len)
75 {
76 if (len != (DWORD)len)
77 {
78 *iError = EINVAL;
79 return -1;
80 }
81
82 DWORD iBytesWritten(0);
83 if (socket != INVALID_HANDLE_VALUE)
84 {
85 if (!WriteFile(socket, data, (DWORD)len, &iBytesWritten, NULL))
86 {
87 *iError = GetLastError();
88 return -1;
89 }
90 return (ssize_t)iBytesWritten;
91 }
92
93 return -1;
94 }
95
96 inline ssize_t SerialSocketRead(serial_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
97 {
98 if (len != (DWORD)len)
99 {
100 *iError = EINVAL;
101 return -1;
102 }
103
104 DWORD iBytesRead(0);
105 if (socket != INVALID_HANDLE_VALUE)
106 {
107 if(!ReadFile(socket, data, (DWORD)len, &iBytesRead, NULL) != 0)
108 {
109 *iError = GetLastError();
110 return -1;
111 }
112 return (ssize_t)iBytesRead;
113 }
114 return -1;
115 }
116 //@}
117
118 // TCP
119 //@{
120 inline void TcpSocketSetBlocking(tcp_socket_t socket, bool bSetTo)
121 {
122 u_long iSetTo = bSetTo ? 0 : 1;
123 ioctlsocket(socket, FIONBIO, &iSetTo);
124 }
125
126 inline void TcpSocketClose(tcp_socket_t socket)
127 {
128 closesocket(socket);
129 }
130
131 inline void TcpSocketShutdown(tcp_socket_t socket)
132 {
133 if (socket != INVALID_SOCKET &&
134 socket != SOCKET_ERROR)
135 shutdown(socket, SHUT_RDWR);
136 }
137
138 inline ssize_t TcpSocketWrite(tcp_socket_t socket, int *iError, void* data, size_t len)
139 {
140 if (socket == INVALID_SOCKET ||
141 socket == SOCKET_ERROR ||
142 len != (int)len)
143 {
144 *iError = EINVAL;
145 return -1;
146 }
147
148 ssize_t iReturn = send(socket, (char*)data, (int)len, 0);
149 if (iReturn < (ssize_t)len)
150 *iError = GetSocketError();
151 return iReturn;
152 }
153
154 inline ssize_t TcpSocketRead(tcp_socket_t socket, int *iError, void* data, size_t len, uint64_t iTimeoutMs /*= 0*/)
155 {
156 int64_t iNow(0), iTarget(0);
157 ssize_t iBytesRead(0);
158 *iError = 0;
159
160 if (socket == INVALID_SOCKET ||
161 socket == SOCKET_ERROR ||
162 len != (int)len)
163 {
164 *iError = EINVAL;
165 return -1;
166 }
167
168 if (iTimeoutMs > 0)
169 {
170 iNow = GetTimeMs();
171 iTarget = iNow + (int64_t) iTimeoutMs;
172 }
173
174 fd_set fd_read;
175 struct timeval tv;
176 while (iBytesRead >= 0 && iBytesRead < (ssize_t)len && (iTimeoutMs == 0 || iTarget > iNow))
177 {
178 if (iTimeoutMs > 0)
179 {
180 tv.tv_sec = (long)(iTimeoutMs / 1000);
181 tv.tv_usec = 1000 * (long)(iTimeoutMs % 1000);
182
183 FD_ZERO(&fd_read);
184 FD_SET(socket, &fd_read);
185
186 if (select((int)socket + 1, &fd_read, NULL, NULL, &tv) == 0)
187 {
188 *iError = ETIMEDOUT;
189 return ETIMEDOUT;
190 }
191 TcpSocketSetBlocking(socket, false);
192 }
193
194 ssize_t iReadResult = (iTimeoutMs > 0) ?
195 recv(socket, (char*)data + iBytesRead, (int)(len - iBytesRead), 0) :
196 recv(socket, (char*)data, (int)len, MSG_WAITALL);
197 *iError = GetSocketError();
198
199 if (iTimeoutMs > 0)
200 {
201 TcpSocketSetBlocking(socket, true);
202 iNow = GetTimeMs();
203 }
204
205 if (iReadResult < 0)
206 {
207 if (*iError == EAGAIN && iTimeoutMs > 0)
208 continue;
209 return -1;
210 }
211 else if (iReadResult == 0 || (iReadResult != (ssize_t)len && iTimeoutMs == 0))
212 {
213 *iError = ECONNRESET;
214 return -1;
215 }
216
217 iBytesRead += iReadResult;
218 }
219
220 if (iBytesRead < (ssize_t)len && *iError == 0)
221 *iError = ETIMEDOUT;
222
223 return iBytesRead;
224 }
225
226 inline bool TcpResolveAddress(const char *strHost, uint16_t iPort, int *iError, struct addrinfo **info)
227 {
228 struct addrinfo hints;
229 char service[33];
230 memset(&hints, 0, sizeof(hints));
231 hints.ai_family = AF_UNSPEC;
232 hints.ai_socktype = SOCK_STREAM;
233 hints.ai_protocol = IPPROTO_TCP;
234 sprintf(service, "%d", iPort);
235
236 *iError = getaddrinfo(strHost, service, &hints, info);
237 return !(*iError);
238 }
239
240 inline int TcpGetSocketError(tcp_socket_t socket)
241 {
242 int iReturn(0);
243 socklen_t optLen = sizeof(tcp_socket_t);
244 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)&iReturn, &optLen);
245 return iReturn;
246 }
247
248 inline bool TcpSetNoDelay(tcp_socket_t socket)
249 {
250 int iSetTo(1);
251 setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (const char*)&iSetTo, sizeof(iSetTo));
252 return true;
253 }
254
255 inline bool TcpConnectSocket(tcp_socket_t socket, struct addrinfo* addr, int *iError, uint64_t iTimeout = 0)
256 {
257 TcpSocketSetBlocking(socket, false);
258
259 *iError = 0;
260 int iConnectResult = connect(socket, addr->ai_addr, (int)addr->ai_addrlen);
261 if (iConnectResult == -1)
262 {
263 if (GetSocketError() == EINPROGRESS ||
264 GetSocketError() == EAGAIN)
265 {
266 fd_set fd_write, fd_except;
267 struct timeval tv;
268 tv.tv_sec = (long)(iTimeout / 1000);
269 tv.tv_usec = 1000 * (long)(iTimeout % 1000);
270
271 FD_ZERO(&fd_write);
272 FD_ZERO(&fd_except);
273 FD_SET(socket, &fd_write);
274 FD_SET(socket, &fd_except);
275
276 int iPollResult = select(sizeof(socket)*8, NULL, &fd_write, &fd_except, &tv);
277 if (iPollResult == 0)
278 *iError = ETIMEDOUT;
279 else if (iPollResult == -1)
280 *iError = GetSocketError();
281 else
282 {
283 socklen_t errlen = sizeof(int);
284 getsockopt(socket, SOL_SOCKET, SO_ERROR, (char *)iError, &errlen);
285 }
286 }
287 else
288 {
289 *iError = GetSocketError();
290 }
291 }
292
293 TcpSocketSetBlocking(socket, true);
294
295 return *iError == 0;
296 }
297 }