1/*
2 * Copyright (c) 2019, Marcus Geelnard <m at bitsnbites dot eu>
3 *
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are met:
8 *
9 * * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * * Neither the name of Redis nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without
16 * specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#define REDIS_SOCKCOMPAT_IMPLEMENTATION
32#include "sockcompat.h"
33
34#ifdef _WIN32
35static int _wsaErrorToErrno(int err) {
36 switch (err) {
37 case WSAEWOULDBLOCK:
38 return EWOULDBLOCK;
39 case WSAEINPROGRESS:
40 return EINPROGRESS;
41 case WSAEALREADY:
42 return EALREADY;
43 case WSAENOTSOCK:
44 return ENOTSOCK;
45 case WSAEDESTADDRREQ:
46 return EDESTADDRREQ;
47 case WSAEMSGSIZE:
48 return EMSGSIZE;
49 case WSAEPROTOTYPE:
50 return EPROTOTYPE;
51 case WSAENOPROTOOPT:
52 return ENOPROTOOPT;
53 case WSAEPROTONOSUPPORT:
54 return EPROTONOSUPPORT;
55 case WSAEOPNOTSUPP:
56 return EOPNOTSUPP;
57 case WSAEAFNOSUPPORT:
58 return EAFNOSUPPORT;
59 case WSAEADDRINUSE:
60 return EADDRINUSE;
61 case WSAEADDRNOTAVAIL:
62 return EADDRNOTAVAIL;
63 case WSAENETDOWN:
64 return ENETDOWN;
65 case WSAENETUNREACH:
66 return ENETUNREACH;
67 case WSAENETRESET:
68 return ENETRESET;
69 case WSAECONNABORTED:
70 return ECONNABORTED;
71 case WSAECONNRESET:
72 return ECONNRESET;
73 case WSAENOBUFS:
74 return ENOBUFS;
75 case WSAEISCONN:
76 return EISCONN;
77 case WSAENOTCONN:
78 return ENOTCONN;
79 case WSAETIMEDOUT:
80 return ETIMEDOUT;
81 case WSAECONNREFUSED:
82 return ECONNREFUSED;
83 case WSAELOOP:
84 return ELOOP;
85 case WSAENAMETOOLONG:
86 return ENAMETOOLONG;
87 case WSAEHOSTUNREACH:
88 return EHOSTUNREACH;
89 case WSAENOTEMPTY:
90 return ENOTEMPTY;
91 default:
92 /* We just return a generic I/O error if we could not find a relevant error. */
93 return EIO;
94 }
95}
96
97static void _updateErrno(int success) {
98 errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError());
99}
100
101static int _initWinsock() {
102 static int s_initialized = 0;
103 if (!s_initialized) {
104 static WSADATA wsadata;
105 int err = WSAStartup(MAKEWORD(2,2), &wsadata);
106 if (err != 0) {
107 errno = _wsaErrorToErrno(err);
108 return 0;
109 }
110 s_initialized = 1;
111 }
112 return 1;
113}
114
115int win32_getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res) {
116 /* Note: This function is likely to be called before other functions, so run init here. */
117 if (!_initWinsock()) {
118 return EAI_FAIL;
119 }
120
121 switch (getaddrinfo(node, service, hints, res)) {
122 case 0: return 0;
123 case WSATRY_AGAIN: return EAI_AGAIN;
124 case WSAEINVAL: return EAI_BADFLAGS;
125 case WSAEAFNOSUPPORT: return EAI_FAMILY;
126 case WSA_NOT_ENOUGH_MEMORY: return EAI_MEMORY;
127 case WSAHOST_NOT_FOUND: return EAI_NONAME;
128 case WSATYPE_NOT_FOUND: return EAI_SERVICE;
129 case WSAESOCKTNOSUPPORT: return EAI_SOCKTYPE;
130 default: return EAI_FAIL; /* Including WSANO_RECOVERY */
131 }
132}
133
134const char *win32_gai_strerror(int errcode) {
135 switch (errcode) {
136 case 0: errcode = 0; break;
137 case EAI_AGAIN: errcode = WSATRY_AGAIN; break;
138 case EAI_BADFLAGS: errcode = WSAEINVAL; break;
139 case EAI_FAMILY: errcode = WSAEAFNOSUPPORT; break;
140 case EAI_MEMORY: errcode = WSA_NOT_ENOUGH_MEMORY; break;
141 case EAI_NONAME: errcode = WSAHOST_NOT_FOUND; break;
142 case EAI_SERVICE: errcode = WSATYPE_NOT_FOUND; break;
143 case EAI_SOCKTYPE: errcode = WSAESOCKTNOSUPPORT; break;
144 default: errcode = WSANO_RECOVERY; break; /* Including EAI_FAIL */
145 }
146 return gai_strerror(errcode);
147}
148
149void win32_freeaddrinfo(struct addrinfo *res) {
150 freeaddrinfo(res);
151}
152
153SOCKET win32_socket(int domain, int type, int protocol) {
154 SOCKET s;
155
156 /* Note: This function is likely to be called before other functions, so run init here. */
157 if (!_initWinsock()) {
158 return INVALID_SOCKET;
159 }
160
161 _updateErrno((s = socket(domain, type, protocol)) != INVALID_SOCKET);
162 return s;
163}
164
165int win32_ioctl(SOCKET fd, unsigned long request, unsigned long *argp) {
166 int ret = ioctlsocket(fd, (long)request, argp);
167 _updateErrno(ret != SOCKET_ERROR);
168 return ret != SOCKET_ERROR ? ret : -1;
169}
170
171int win32_bind(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
172 int ret = bind(sockfd, addr, addrlen);
173 _updateErrno(ret != SOCKET_ERROR);
174 return ret != SOCKET_ERROR ? ret : -1;
175}
176
177int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) {
178 int ret = connect(sockfd, addr, addrlen);
179 _updateErrno(ret != SOCKET_ERROR);
180
181 /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
182 * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
183 * logic consistent. */
184 if (errno == EWOULDBLOCK) {
185 errno = EINPROGRESS;
186 }
187
188 return ret != SOCKET_ERROR ? ret : -1;
189}
190
191int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, socklen_t *optlen) {
192 int ret = 0;
193 if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
194 if (*optlen >= sizeof (struct timeval)) {
195 struct timeval *tv = optval;
196 DWORD timeout = 0;
197 socklen_t dwlen = 0;
198 ret = getsockopt(sockfd, level, optname, (char *)&timeout, &dwlen);
199 tv->tv_sec = timeout / 1000;
200 tv->tv_usec = (timeout * 1000) % 1000000;
201 } else {
202 ret = WSAEFAULT;
203 }
204 *optlen = sizeof (struct timeval);
205 } else {
206 ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);
207 }
208 _updateErrno(ret != SOCKET_ERROR);
209 return ret != SOCKET_ERROR ? ret : -1;
210}
211
212int win32_setsockopt(SOCKET sockfd, int level, int optname, const void *optval, socklen_t optlen) {
213 int ret = 0;
214 if ((level == SOL_SOCKET) && ((optname == SO_RCVTIMEO) || (optname == SO_SNDTIMEO))) {
215 const struct timeval *tv = optval;
216 DWORD timeout = tv->tv_sec * 1000 + tv->tv_usec / 1000;
217 ret = setsockopt(sockfd, level, optname, (const char*)&timeout, sizeof(DWORD));
218 } else {
219 ret = setsockopt(sockfd, level, optname, (const char*)optval, optlen);
220 }
221 _updateErrno(ret != SOCKET_ERROR);
222 return ret != SOCKET_ERROR ? ret : -1;
223}
224
225int win32_close(SOCKET fd) {
226 int ret = closesocket(fd);
227 _updateErrno(ret != SOCKET_ERROR);
228 return ret != SOCKET_ERROR ? ret : -1;
229}
230
231ssize_t win32_recv(SOCKET sockfd, void *buf, size_t len, int flags) {
232 int ret = recv(sockfd, (char*)buf, (int)len, flags);
233 _updateErrno(ret != SOCKET_ERROR);
234 return ret != SOCKET_ERROR ? ret : -1;
235}
236
237ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags) {
238 int ret = send(sockfd, (const char*)buf, (int)len, flags);
239 _updateErrno(ret != SOCKET_ERROR);
240 return ret != SOCKET_ERROR ? ret : -1;
241}
242
243int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
244 int ret = WSAPoll(fds, nfds, timeout);
245 _updateErrno(ret != SOCKET_ERROR);
246 return ret != SOCKET_ERROR ? ret : -1;
247}
248#endif /* _WIN32 */
249