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 |
35 | static 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 | |
97 | static void _updateErrno(int success) { |
98 | errno = success ? 0 : _wsaErrorToErrno(WSAGetLastError()); |
99 | } |
100 | |
101 | static 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 | |
115 | int 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 | |
134 | const 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 | |
149 | void win32_freeaddrinfo(struct addrinfo *res) { |
150 | freeaddrinfo(res); |
151 | } |
152 | |
153 | SOCKET 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 | |
165 | int 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 | |
171 | int 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 | |
177 | int 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 | |
191 | int 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 | |
212 | int 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 | |
225 | int win32_close(SOCKET fd) { |
226 | int ret = closesocket(fd); |
227 | _updateErrno(ret != SOCKET_ERROR); |
228 | return ret != SOCKET_ERROR ? ret : -1; |
229 | } |
230 | |
231 | ssize_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 | |
237 | ssize_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 | |
243 | int 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 | |