include/mgba-util/socket.h (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#ifndef SOCKET_H
7#define SOCKET_H
8
9#include <mgba-util/common.h>
10
11CXX_GUARD_START
12
13#if defined(__cplusplus) && !defined(restrict)
14#define restrict __restrict__
15#endif
16
17#ifdef _WIN32
18#include <ws2tcpip.h>
19
20#define SOCKET_FAILED(s) ((s) == INVALID_SOCKET)
21typedef SOCKET Socket;
22#else
23#ifdef GEKKO
24#include <network.h>
25#else
26#include <arpa/inet.h>
27#include <netinet/in.h>
28#include <netinet/tcp.h>
29#include <sys/socket.h>
30#endif
31#include <errno.h>
32#include <fcntl.h>
33#include <sys/select.h>
34
35#ifndef GEKKO
36#define INVALID_SOCKET (-1)
37#endif
38#define SOCKET_FAILED(s) ((s) < 0)
39typedef int Socket;
40#endif
41
42enum IP {
43 IPV4,
44 IPV6
45};
46
47struct Address {
48 enum IP version;
49 union {
50 uint32_t ipv4;
51 uint8_t ipv6[16];
52 };
53};
54
55#ifdef _3DS
56#include <3ds.h>
57#include <malloc.h>
58
59#define SOCU_ALIGN 0x1000
60#define SOCU_BUFFERSIZE 0x100000
61
62extern u32* SOCUBuffer;
63#endif
64#ifdef __SWITCH__
65#include <switch.h>
66#endif
67
68static inline void SocketSubsystemInit() {
69#ifdef _WIN32
70 WSADATA data;
71 WSAStartup(MAKEWORD(2, 2), &data);
72#elif defined(_3DS)
73 if (!SOCUBuffer) {
74 SOCUBuffer = memalign(SOCU_ALIGN, SOCU_BUFFERSIZE);
75 socInit(SOCUBuffer, SOCU_BUFFERSIZE);
76 }
77#elif defined(__SWITCH__)
78 socketInitializeDefault();
79#elif defined(GEKKO)
80 net_init();
81#endif
82}
83
84static inline void SocketSubsystemDeinit() {
85#ifdef _WIN32
86 WSACleanup();
87#elif defined(_3DS)
88 socExit();
89 free(SOCUBuffer);
90 SOCUBuffer = NULL;
91#elif defined(__SWITCH__)
92 socketExit();
93#elif defined(GEKKO)
94 net_deinit();
95#endif
96}
97
98static inline int SocketError() {
99#ifdef _WIN32
100 return WSAGetLastError();
101#else
102 return errno;
103#endif
104}
105
106static inline bool SocketWouldBlock() {
107#ifdef _WIN32
108 return SocketError() == WSAEWOULDBLOCK;
109#else
110 return SocketError() == EWOULDBLOCK || SocketError() == EAGAIN;
111#endif
112}
113
114static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) {
115#ifdef _WIN32
116 return send(socket, (const char*) buffer, size, 0);
117#elif defined(GEKKO)
118 return net_write(socket, buffer, size);
119#else
120 return write(socket, buffer, size);
121#endif
122}
123
124static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
125#if defined(_WIN32) || defined(__SWITCH__)
126 return recv(socket, (char*) buffer, size, 0);
127#elif defined(GEKKO)
128 return net_read(socket, buffer, size);
129#else
130 return read(socket, buffer, size);
131#endif
132}
133
134static inline int SocketClose(Socket socket) {
135#ifdef _WIN32
136 return closesocket(socket) == 0;
137#elif defined(GEKKO)
138 return net_close(socket) >= 0;
139#else
140 return close(socket) >= 0;
141#endif
142}
143
144static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) {
145#ifdef GEKKO
146 Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
147#else
148 Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
149#endif
150 if (SOCKET_FAILED(sock)) {
151 return sock;
152 }
153
154 int err;
155 if (!bindAddress) {
156 struct sockaddr_in bindInfo;
157 memset(&bindInfo, 0, sizeof(bindInfo));
158 bindInfo.sin_family = AF_INET;
159 bindInfo.sin_port = htons(port);
160#ifndef _3DS
161 bindInfo.sin_addr.s_addr = INADDR_ANY;
162#else
163 bindInfo.sin_addr.s_addr = gethostid();
164#endif
165#ifdef GEKKO
166 err = net_bind(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
167#else
168 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
169#endif
170 } else if (bindAddress->version == IPV4) {
171 struct sockaddr_in bindInfo;
172 memset(&bindInfo, 0, sizeof(bindInfo));
173 bindInfo.sin_family = AF_INET;
174 bindInfo.sin_port = htons(port);
175 bindInfo.sin_addr.s_addr = htonl(bindAddress->ipv4);
176#ifdef GEKKO
177 err = net_bind(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
178#else
179 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
180#endif
181#if !defined(_3DS) && !defined(GEKKO)
182 } else {
183 struct sockaddr_in6 bindInfo;
184 memset(&bindInfo, 0, sizeof(bindInfo));
185 bindInfo.sin6_family = AF_INET6;
186 bindInfo.sin6_port = htons(port);
187 memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
188 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
189#endif
190 }
191 if (err) {
192 SocketClose(sock);
193 return INVALID_SOCKET;
194 }
195 return sock;
196}
197
198static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) {
199#ifdef GEKKO
200 Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
201#else
202 Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
203#endif
204 if (SOCKET_FAILED(sock)) {
205 return sock;
206 }
207
208 int err;
209 if (!destinationAddress) {
210 struct sockaddr_in bindInfo;
211 memset(&bindInfo, 0, sizeof(bindInfo));
212 bindInfo.sin_family = AF_INET;
213 bindInfo.sin_port = htons(port);
214#ifdef GEKKO
215 err = net_connect(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
216#else
217 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
218#endif
219 } else if (destinationAddress->version == IPV4) {
220 struct sockaddr_in bindInfo;
221 memset(&bindInfo, 0, sizeof(bindInfo));
222 bindInfo.sin_family = AF_INET;
223 bindInfo.sin_port = htons(port);
224 bindInfo.sin_addr.s_addr = htonl(destinationAddress->ipv4);
225#ifdef GEKKO
226 err = net_connect(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
227#else
228 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
229#endif
230#if !defined(_3DS) && !defined(GEKKO)
231 } else {
232 struct sockaddr_in6 bindInfo;
233 memset(&bindInfo, 0, sizeof(bindInfo));
234 bindInfo.sin6_family = AF_INET6;
235 bindInfo.sin6_port = htons(port);
236 memcpy(bindInfo.sin6_addr.s6_addr, destinationAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
237 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
238#endif
239 }
240
241 if (err) {
242 SocketClose(sock);
243 return INVALID_SOCKET;
244 }
245 return sock;
246}
247
248static inline Socket SocketListen(Socket socket, int queueLength) {
249#ifdef GEKKO
250 return net_listen(socket, queueLength);
251#else
252 return listen(socket, queueLength);
253#endif
254}
255
256static inline Socket SocketAccept(Socket socket, struct Address* address) {
257 if (!address) {
258#ifdef GEKKO
259 struct sockaddr_in addrInfo;
260 memset(&addrInfo, 0, sizeof(addrInfo));
261 socklen_t len = sizeof(addrInfo);
262 return net_accept(socket, (struct sockaddr*) &addrInfo, &len);
263#else
264 return accept(socket, 0, 0);
265#endif
266 }
267 if (address->version == IPV4) {
268 struct sockaddr_in addrInfo;
269 memset(&addrInfo, 0, sizeof(addrInfo));
270 addrInfo.sin_family = AF_INET;
271 addrInfo.sin_addr.s_addr = address->ipv4;
272 socklen_t len = sizeof(addrInfo);
273#ifdef GEKKO
274 return net_accept(socket, (struct sockaddr*) &addrInfo, &len);
275#else
276 return accept(socket, (struct sockaddr*) &addrInfo, &len);
277#endif
278#if !defined(_3DS) && !defined(GEKKO)
279 } else {
280 struct sockaddr_in6 addrInfo;
281 memset(&addrInfo, 0, sizeof(addrInfo));
282 addrInfo.sin6_family = AF_INET6;
283 memcpy(addrInfo.sin6_addr.s6_addr, address->ipv6, sizeof(addrInfo.sin6_addr.s6_addr));
284 socklen_t len = sizeof(addrInfo);
285 return accept(socket, (struct sockaddr*) &addrInfo, &len);
286#endif
287 }
288 return INVALID_SOCKET;
289}
290
291static inline int SocketSetBlocking(Socket socket, bool blocking) {
292#ifdef _WIN32
293 u_long unblocking = !blocking;
294 return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR;
295#else
296#ifdef GEKKO
297 int flags = net_fcntl(socket, F_GETFL, 0);
298#else
299 int flags = fcntl(socket, F_GETFL);
300#endif
301 if (flags == -1) {
302 return 0;
303 }
304 if (blocking) {
305 flags &= ~O_NONBLOCK;
306 } else {
307 flags |= O_NONBLOCK;
308 }
309#ifdef GEKKO
310 return net_fcntl(socket, F_SETFL, flags) >= 0;
311#else
312 return fcntl(socket, F_SETFL, flags) >= 0;
313#endif
314#endif
315}
316
317static inline int SocketSetTCPPush(Socket socket, int push) {
318#ifdef GEKKO
319 return net_setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
320#else
321 return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
322#endif
323}
324
325static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) {
326 fd_set rset;
327 fd_set wset;
328 fd_set eset;
329 FD_ZERO(&rset);
330 FD_ZERO(&wset);
331 FD_ZERO(&eset);
332 size_t i;
333 Socket maxFd = 0;
334 if (reads) {
335 for (i = 0; i < nSockets; ++i) {
336 if (SOCKET_FAILED(reads[i])) {
337 break;
338 }
339 if (reads[i] > maxFd) {
340 maxFd = reads[i];
341 }
342 FD_SET(reads[i], &rset);
343 reads[i] = INVALID_SOCKET;
344 }
345 }
346 if (writes) {
347 for (i = 0; i < nSockets; ++i) {
348 if (SOCKET_FAILED(writes[i])) {
349 break;
350 }
351 if (writes[i] > maxFd) {
352 maxFd = writes[i];
353 }
354 FD_SET(writes[i], &wset);
355 writes[i] = INVALID_SOCKET;
356 }
357 }
358 if (errors) {
359 for (i = 0; i < nSockets; ++i) {
360 if (SOCKET_FAILED(errors[i])) {
361 break;
362 }
363 if (errors[i] > maxFd) {
364 maxFd = errors[i];
365 }
366 FD_SET(errors[i], &eset);
367 errors[i] = INVALID_SOCKET;
368 }
369 }
370 ++maxFd;
371 struct timeval tv;
372 tv.tv_sec = timeoutMillis / 1000;
373 tv.tv_usec = (timeoutMillis % 1000) * 1000;
374#ifdef GEKKO
375 int result = net_select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
376#else
377 int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
378#endif
379 int r = 0;
380 int w = 0;
381 int e = 0;
382 Socket j;
383 for (j = 0; j < maxFd; ++j) {
384 if (reads && FD_ISSET(j, &rset)) {
385 reads[r] = j;
386 ++r;
387 }
388 if (writes && FD_ISSET(j, &wset)) {
389 writes[w] = j;
390 ++w;
391 }
392 if (errors && FD_ISSET(j, &eset)) {
393 errors[e] = j;
394 ++e;
395 }
396 }
397 return result;
398}
399
400CXX_GUARD_END
401
402#endif