1 | /* |
2 | * IXNetSystem.cpp |
3 | * Author: Korchynskyi Dmytro |
4 | * Copyright (c) 2019 Machine Zone. All rights reserved. |
5 | */ |
6 | |
7 | #include "IXNetSystem.h" |
8 | #include <cstdint> |
9 | #include <cstdio> |
10 | #ifdef _WIN32 |
11 | #ifndef EAFNOSUPPORT |
12 | #define EAFNOSUPPORT 102 |
13 | #endif |
14 | #ifndef ENOSPC |
15 | #define ENOSPC 28 |
16 | #endif |
17 | #include <vector> |
18 | #endif |
19 | |
20 | namespace ix |
21 | { |
22 | bool initNetSystem() |
23 | { |
24 | #ifdef _WIN32 |
25 | WORD wVersionRequested; |
26 | WSADATA wsaData; |
27 | int err; |
28 | |
29 | // Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h |
30 | wVersionRequested = MAKEWORD(2, 2); |
31 | err = WSAStartup(wVersionRequested, &wsaData); |
32 | |
33 | return err == 0; |
34 | #else |
35 | return true; |
36 | #endif |
37 | } |
38 | |
39 | bool uninitNetSystem() |
40 | { |
41 | #ifdef _WIN32 |
42 | int err = WSACleanup(); |
43 | return err == 0; |
44 | #else |
45 | return true; |
46 | #endif |
47 | } |
48 | |
49 | #ifdef _WIN32 |
50 | struct WSAEvent |
51 | { |
52 | public: |
53 | WSAEvent(struct pollfd* fd) |
54 | : _fd(fd) |
55 | { |
56 | _event = WSACreateEvent(); |
57 | } |
58 | |
59 | WSAEvent(WSAEvent&& source) noexcept |
60 | { |
61 | _event = source._event; |
62 | source._event = WSA_INVALID_EVENT; // invalidate the event in the source |
63 | _fd = source._fd; |
64 | } |
65 | |
66 | ~WSAEvent() |
67 | { |
68 | if (_event != WSA_INVALID_EVENT) |
69 | { |
70 | // We must deselect the networkevents from the socket event. Otherwise the |
71 | // socket will report states that aren't there. |
72 | if (_fd != nullptr && _fd->fd != -1) |
73 | WSAEventSelect(_fd->fd, _event, 0); |
74 | WSACloseEvent(_event); |
75 | } |
76 | } |
77 | |
78 | operator HANDLE() |
79 | { |
80 | return _event; |
81 | } |
82 | |
83 | operator struct pollfd*() |
84 | { |
85 | return _fd; |
86 | } |
87 | |
88 | private: |
89 | HANDLE _event; |
90 | struct pollfd* _fd; |
91 | }; |
92 | #endif |
93 | |
94 | // |
95 | // That function could 'return WSAPoll(pfd, nfds, timeout);' |
96 | // but WSAPoll is said to have weird behaviors on the internet |
97 | // (the curl folks have had problems with it). |
98 | // |
99 | // So we make it a select wrapper |
100 | // |
101 | // UPDATE: WSAPoll was fixed in Windows 10 Version 2004 |
102 | // |
103 | // The optional "event" is set to nullptr if it wasn't signaled. |
104 | int poll(struct pollfd* fds, nfds_t nfds, int timeout, void** event) |
105 | { |
106 | #ifdef _WIN32 |
107 | |
108 | if (event && *event) |
109 | { |
110 | HANDLE interruptEvent = reinterpret_cast<HANDLE>(*event); |
111 | *event = nullptr; // the event wasn't signaled yet |
112 | |
113 | if (nfds < 0 || nfds >= MAXIMUM_WAIT_OBJECTS - 1) |
114 | { |
115 | WSASetLastError(WSAEINVAL); |
116 | return SOCKET_ERROR; |
117 | } |
118 | |
119 | std::vector<WSAEvent> socketEvents; |
120 | std::vector<HANDLE> handles; |
121 | // put the interrupt event as first element, making it highest priority |
122 | handles.push_back(interruptEvent); |
123 | |
124 | // create the WSAEvents for the sockets |
125 | for (nfds_t i = 0; i < nfds; ++i) |
126 | { |
127 | struct pollfd* fd = &fds[i]; |
128 | fd->revents = 0; |
129 | if (fd->fd >= 0) |
130 | { |
131 | // create WSAEvent and add it to the vectors |
132 | socketEvents.push_back(std::move(WSAEvent(fd))); |
133 | HANDLE handle = socketEvents.back(); |
134 | if (handle == WSA_INVALID_EVENT) |
135 | { |
136 | WSASetLastError(WSAENOBUFS); |
137 | return SOCKET_ERROR; |
138 | } |
139 | handles.push_back(handle); |
140 | |
141 | // mapping |
142 | long networkEvents = 0; |
143 | if (fd->events & (POLLIN )) networkEvents |= FD_READ | FD_ACCEPT; |
144 | if (fd->events & (POLLOUT /*| POLLWRNORM | POLLWRBAND*/)) networkEvents |= FD_WRITE | FD_CONNECT; |
145 | //if (fd->events & (POLLPRI | POLLRDBAND )) networkEvents |= FD_OOB; |
146 | |
147 | if (WSAEventSelect(fd->fd, handle, networkEvents) != 0) |
148 | { |
149 | fd->revents = POLLNVAL; |
150 | socketEvents.pop_back(); |
151 | handles.pop_back(); |
152 | } |
153 | } |
154 | } |
155 | |
156 | DWORD n = WSAWaitForMultipleEvents(handles.size(), handles.data(), FALSE, timeout != -1 ? static_cast<DWORD>(timeout) : WSA_INFINITE, FALSE); |
157 | |
158 | if (n == WSA_WAIT_FAILED) return SOCKET_ERROR; |
159 | if (n == WSA_WAIT_TIMEOUT) return 0; |
160 | if (n == WSA_WAIT_EVENT_0) |
161 | { |
162 | // the interrupt event was signaled |
163 | *event = reinterpret_cast<void*>(interruptEvent); |
164 | return 1; |
165 | } |
166 | |
167 | int handleIndex = n - WSA_WAIT_EVENT_0; |
168 | int socketIndex = handleIndex - 1; |
169 | |
170 | WSANETWORKEVENTS netEvents; |
171 | int count = 0; |
172 | // WSAWaitForMultipleEvents returns the index of the first signaled event. And to emulate WSAPoll() |
173 | // all the signaled events must be processed. |
174 | while (socketIndex < socketEvents.size()) |
175 | { |
176 | struct pollfd* fd = socketEvents[socketIndex]; |
177 | |
178 | memset(&netEvents, 0, sizeof(netEvents)); |
179 | if (WSAEnumNetworkEvents(fd->fd, socketEvents[socketIndex], &netEvents) != 0) |
180 | { |
181 | fd->revents = POLLERR; |
182 | } |
183 | else if (netEvents.lNetworkEvents != 0) |
184 | { |
185 | // mapping |
186 | if (netEvents.lNetworkEvents & (FD_READ | FD_ACCEPT | FD_OOB)) fd->revents |= POLLIN; |
187 | if (netEvents.lNetworkEvents & (FD_WRITE | FD_CONNECT )) fd->revents |= POLLOUT; |
188 | |
189 | for (int i = 0; i < FD_MAX_EVENTS; ++i) |
190 | { |
191 | if (netEvents.iErrorCode[i] != 0) |
192 | { |
193 | fd->revents |= POLLERR; |
194 | break; |
195 | } |
196 | } |
197 | |
198 | if (fd->revents != 0) |
199 | { |
200 | // only signaled sockets count |
201 | count++; |
202 | } |
203 | } |
204 | socketIndex++; |
205 | } |
206 | |
207 | return count; |
208 | } |
209 | else |
210 | { |
211 | if (event && *event) *event = nullptr; |
212 | |
213 | socket_t maxfd = 0; |
214 | fd_set readfds, writefds, errorfds; |
215 | FD_ZERO(&readfds); |
216 | FD_ZERO(&writefds); |
217 | FD_ZERO(&errorfds); |
218 | |
219 | for (nfds_t i = 0; i < nfds; ++i) |
220 | { |
221 | struct pollfd* fd = &fds[i]; |
222 | |
223 | if (fd->fd > maxfd) |
224 | { |
225 | maxfd = fd->fd; |
226 | } |
227 | if ((fd->events & POLLIN)) |
228 | { |
229 | FD_SET(fd->fd, &readfds); |
230 | } |
231 | if ((fd->events & POLLOUT)) |
232 | { |
233 | FD_SET(fd->fd, &writefds); |
234 | } |
235 | if ((fd->events & POLLERR)) |
236 | { |
237 | FD_SET(fd->fd, &errorfds); |
238 | } |
239 | } |
240 | |
241 | struct timeval tv; |
242 | tv.tv_sec = timeout / 1000; |
243 | tv.tv_usec = (timeout % 1000) * 1000; |
244 | |
245 | int ret = select(maxfd + 1, &readfds, &writefds, &errorfds, timeout != -1 ? &tv : NULL); |
246 | |
247 | if (ret < 0) |
248 | { |
249 | return ret; |
250 | } |
251 | |
252 | for (nfds_t i = 0; i < nfds; ++i) |
253 | { |
254 | struct pollfd* fd = &fds[i]; |
255 | fd->revents = 0; |
256 | |
257 | if (FD_ISSET(fd->fd, &readfds)) |
258 | { |
259 | fd->revents |= POLLIN; |
260 | } |
261 | if (FD_ISSET(fd->fd, &writefds)) |
262 | { |
263 | fd->revents |= POLLOUT; |
264 | } |
265 | if (FD_ISSET(fd->fd, &errorfds)) |
266 | { |
267 | fd->revents |= POLLERR; |
268 | } |
269 | } |
270 | return ret; |
271 | } |
272 | #else |
273 | if (event && *event) *event = nullptr; |
274 | |
275 | // |
276 | // It was reported that on Android poll can fail and return -1 with |
277 | // errno == EINTR, which should be a temp error and should typically |
278 | // be handled by retrying in a loop. |
279 | // Maybe we need to put all syscall / C functions in |
280 | // a new IXSysCalls.cpp and wrap them all. |
281 | // |
282 | // The style from libuv is as such. |
283 | // |
284 | int ret = -1; |
285 | do |
286 | { |
287 | ret = ::poll(fds, nfds, timeout); |
288 | } while (ret == -1 && errno == EINTR); |
289 | |
290 | return ret; |
291 | #endif |
292 | } |
293 | |
294 | // |
295 | // mingw does not have inet_ntop, which were taken as is from the musl C library. |
296 | // |
297 | const char* inet_ntop(int af, const void* a0, char* s, socklen_t l) |
298 | { |
299 | #if defined(_WIN32) && defined(__GNUC__) |
300 | const unsigned char* a = (const unsigned char*) a0; |
301 | int i, j, max, best; |
302 | char buf[100]; |
303 | |
304 | switch (af) |
305 | { |
306 | case AF_INET: |
307 | if (snprintf(s, l, "%d.%d.%d.%d" , a[0], a[1], a[2], a[3]) < l) return s; |
308 | break; |
309 | case AF_INET6: |
310 | if (memcmp(a, "\0\0\0\0\0\0\0\0\0\0\377\377" , 12)) |
311 | snprintf(buf, |
312 | sizeof buf, |
313 | "%x:%x:%x:%x:%x:%x:%x:%x" , |
314 | 256 * a[0] + a[1], |
315 | 256 * a[2] + a[3], |
316 | 256 * a[4] + a[5], |
317 | 256 * a[6] + a[7], |
318 | 256 * a[8] + a[9], |
319 | 256 * a[10] + a[11], |
320 | 256 * a[12] + a[13], |
321 | 256 * a[14] + a[15]); |
322 | else |
323 | snprintf(buf, |
324 | sizeof buf, |
325 | "%x:%x:%x:%x:%x:%x:%d.%d.%d.%d" , |
326 | 256 * a[0] + a[1], |
327 | 256 * a[2] + a[3], |
328 | 256 * a[4] + a[5], |
329 | 256 * a[6] + a[7], |
330 | 256 * a[8] + a[9], |
331 | 256 * a[10] + a[11], |
332 | a[12], |
333 | a[13], |
334 | a[14], |
335 | a[15]); |
336 | /* Replace longest /(^0|:)[:0]{2,}/ with "::" */ |
337 | for (i = best = 0, max = 2; buf[i]; i++) |
338 | { |
339 | if (i && buf[i] != ':') continue; |
340 | j = strspn(buf + i, ":0" ); |
341 | if (j > max) best = i, max = j; |
342 | } |
343 | if (max > 3) |
344 | { |
345 | buf[best] = buf[best + 1] = ':'; |
346 | memmove(buf + best + 2, buf + best + max, i - best - max + 1); |
347 | } |
348 | if (strlen(buf) < l) |
349 | { |
350 | strcpy(s, buf); |
351 | return s; |
352 | } |
353 | break; |
354 | default: errno = EAFNOSUPPORT; return 0; |
355 | } |
356 | errno = ENOSPC; |
357 | return 0; |
358 | #else |
359 | return ::inet_ntop(af, a0, s, l); |
360 | #endif |
361 | } |
362 | |
363 | #if defined(_WIN32) && defined(__GNUC__) |
364 | static int hexval(unsigned c) |
365 | { |
366 | if (c - '0' < 10) return c - '0'; |
367 | c |= 32; |
368 | if (c - 'a' < 6) return c - 'a' + 10; |
369 | return -1; |
370 | } |
371 | #endif |
372 | |
373 | // |
374 | // mingw does not have inet_pton, which were taken as is from the musl C library. |
375 | // |
376 | int inet_pton(int af, const char* s, void* a0) |
377 | { |
378 | #if defined(_WIN32) && defined(__GNUC__) |
379 | uint16_t ip[8]; |
380 | unsigned char* a = (unsigned char*) a0; |
381 | int i, j, v, d, brk = -1, need_v4 = 0; |
382 | |
383 | if (af == AF_INET) |
384 | { |
385 | for (i = 0; i < 4; i++) |
386 | { |
387 | for (v = j = 0; j < 3 && isdigit(s[j]); j++) |
388 | v = 10 * v + s[j] - '0'; |
389 | if (j == 0 || (j > 1 && s[0] == '0') || v > 255) return 0; |
390 | a[i] = v; |
391 | if (s[j] == 0 && i == 3) return 1; |
392 | if (s[j] != '.') return 0; |
393 | s += j + 1; |
394 | } |
395 | return 0; |
396 | } |
397 | else if (af != AF_INET6) |
398 | { |
399 | errno = EAFNOSUPPORT; |
400 | return -1; |
401 | } |
402 | |
403 | if (*s == ':' && *++s != ':') return 0; |
404 | |
405 | for (i = 0;; i++) |
406 | { |
407 | if (s[0] == ':' && brk < 0) |
408 | { |
409 | brk = i; |
410 | ip[i & 7] = 0; |
411 | if (!*++s) break; |
412 | if (i == 7) return 0; |
413 | continue; |
414 | } |
415 | for (v = j = 0; j < 4 && (d = hexval(s[j])) >= 0; j++) |
416 | v = 16 * v + d; |
417 | if (j == 0) return 0; |
418 | ip[i & 7] = v; |
419 | if (!s[j] && (brk >= 0 || i == 7)) break; |
420 | if (i == 7) return 0; |
421 | if (s[j] != ':') |
422 | { |
423 | if (s[j] != '.' || (i < 6 && brk < 0)) return 0; |
424 | need_v4 = 1; |
425 | i++; |
426 | break; |
427 | } |
428 | s += j + 1; |
429 | } |
430 | if (brk >= 0) |
431 | { |
432 | memmove(ip + brk + 7 - i, ip + brk, 2 * (i + 1 - brk)); |
433 | for (j = 0; j < 7 - i; j++) |
434 | ip[brk + j] = 0; |
435 | } |
436 | for (j = 0; j < 8; j++) |
437 | { |
438 | *a++ = ip[j] >> 8; |
439 | *a++ = ip[j]; |
440 | } |
441 | if (need_v4 && inet_pton(AF_INET, (const char*) s, a - 4) <= 0) return 0; |
442 | return 1; |
443 | #else |
444 | return ::inet_pton(af, s, a0); |
445 | #endif |
446 | } |
447 | |
448 | // Convert network bytes to host bytes. Copied from the ASIO library |
449 | unsigned short network_to_host_short(unsigned short value) |
450 | { |
451 | #if defined(_WIN32) |
452 | unsigned char* value_p = reinterpret_cast<unsigned char*>(&value); |
453 | unsigned short result = (static_cast<unsigned short>(value_p[0]) << 8) |
454 | | static_cast<unsigned short>(value_p[1]); |
455 | return result; |
456 | #else // defined(_WIN32) |
457 | return ntohs(value); |
458 | #endif // defined(_WIN32) |
459 | } |
460 | |
461 | } // namespace ix |
462 | |