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
20namespace 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