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#ifdef PSP2
68#include <psp2/net/net.h>
69#include <psp2/sysmodule.h>
70#endif
71
72static inline void SocketSubsystemInit() {
73#ifdef _WIN32
74 WSADATA data;
75 WSAStartup(MAKEWORD(2, 2), &data);
76#elif defined(_3DS)
77 if (!SOCUBuffer) {
78 SOCUBuffer = memalign(SOCU_ALIGN, SOCU_BUFFERSIZE);
79 socInit(SOCUBuffer, SOCU_BUFFERSIZE);
80 }
81#elif defined(__SWITCH__)
82 socketInitializeDefault();
83#elif defined(GEKKO)
84 net_init();
85#elif defined(PSP2)
86 static uint8_t netMem[1024*1024];
87 sceSysmoduleLoadModule(SCE_SYSMODULE_NET);
88 sceNetInit(&(SceNetInitParam) { netMem, sizeof(netMem) });
89#endif
90}
91
92static inline void SocketSubsystemDeinit() {
93#ifdef _WIN32
94 WSACleanup();
95#elif defined(_3DS)
96 socExit();
97 free(SOCUBuffer);
98 SOCUBuffer = NULL;
99#elif defined(__SWITCH__)
100 socketExit();
101#elif defined(GEKKO)
102 net_deinit();
103#elif defined(PSP2)
104 sceNetTerm();
105 sceSysmoduleUnloadModule(SCE_SYSMODULE_NET);
106#endif
107}
108
109static inline int SocketError() {
110#ifdef _WIN32
111 return WSAGetLastError();
112#else
113 return errno;
114#endif
115}
116
117static inline bool SocketWouldBlock() {
118#ifdef _WIN32
119 return SocketError() == WSAEWOULDBLOCK;
120#else
121 return SocketError() == EWOULDBLOCK || SocketError() == EAGAIN;
122#endif
123}
124
125static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) {
126#ifdef _WIN32
127 return send(socket, (const char*) buffer, size, 0);
128#elif defined(GEKKO)
129 return net_write(socket, buffer, size);
130#else
131 return write(socket, buffer, size);
132#endif
133}
134
135static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
136#if defined(_WIN32) || defined(__SWITCH__)
137 return recv(socket, (char*) buffer, size, 0);
138#elif defined(GEKKO)
139 return net_read(socket, buffer, size);
140#else
141 return read(socket, buffer, size);
142#endif
143}
144
145static inline int SocketClose(Socket socket) {
146#ifdef _WIN32
147 return closesocket(socket) == 0;
148#elif defined(GEKKO)
149 return net_close(socket) >= 0;
150#else
151 return close(socket) >= 0;
152#endif
153}
154
155static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) {
156#ifdef GEKKO
157 Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
158#else
159 Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
160#endif
161 if (SOCKET_FAILED(sock)) {
162 return sock;
163 }
164
165 int err;
166 if (!bindAddress) {
167 struct sockaddr_in bindInfo;
168 memset(&bindInfo, 0, sizeof(bindInfo));
169 bindInfo.sin_family = AF_INET;
170 bindInfo.sin_port = htons(port);
171#ifndef _3DS
172 bindInfo.sin_addr.s_addr = INADDR_ANY;
173#else
174 bindInfo.sin_addr.s_addr = gethostid();
175#endif
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 } else if (bindAddress->version == IPV4) {
182 struct sockaddr_in bindInfo;
183 memset(&bindInfo, 0, sizeof(bindInfo));
184 bindInfo.sin_family = AF_INET;
185 bindInfo.sin_port = htons(port);
186 bindInfo.sin_addr.s_addr = htonl(bindAddress->ipv4);
187#ifdef GEKKO
188 err = net_bind(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
189#else
190 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
191#endif
192#if !defined(_3DS) && !defined(GEKKO)
193 } else {
194 struct sockaddr_in6 bindInfo;
195 memset(&bindInfo, 0, sizeof(bindInfo));
196 bindInfo.sin6_family = AF_INET6;
197 bindInfo.sin6_port = htons(port);
198 memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
199 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
200#endif
201 }
202 if (err) {
203 SocketClose(sock);
204 return INVALID_SOCKET;
205 }
206 return sock;
207}
208
209static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) {
210#ifdef GEKKO
211 Socket sock = net_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
212#else
213 Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
214#endif
215 if (SOCKET_FAILED(sock)) {
216 return sock;
217 }
218
219 int err;
220 if (!destinationAddress) {
221 struct sockaddr_in bindInfo;
222 memset(&bindInfo, 0, sizeof(bindInfo));
223 bindInfo.sin_family = AF_INET;
224 bindInfo.sin_port = htons(port);
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 } else if (destinationAddress->version == IPV4) {
231 struct sockaddr_in bindInfo;
232 memset(&bindInfo, 0, sizeof(bindInfo));
233 bindInfo.sin_family = AF_INET;
234 bindInfo.sin_port = htons(port);
235 bindInfo.sin_addr.s_addr = htonl(destinationAddress->ipv4);
236#ifdef GEKKO
237 err = net_connect(sock, (struct sockaddr*) &bindInfo, sizeof(bindInfo));
238#else
239 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
240#endif
241#if !defined(_3DS) && !defined(GEKKO)
242 } else {
243 struct sockaddr_in6 bindInfo;
244 memset(&bindInfo, 0, sizeof(bindInfo));
245 bindInfo.sin6_family = AF_INET6;
246 bindInfo.sin6_port = htons(port);
247 memcpy(bindInfo.sin6_addr.s6_addr, destinationAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
248 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
249#endif
250 }
251
252 if (err) {
253 SocketClose(sock);
254 return INVALID_SOCKET;
255 }
256 return sock;
257}
258
259static inline Socket SocketListen(Socket socket, int queueLength) {
260#ifdef GEKKO
261 return net_listen(socket, queueLength);
262#else
263#ifdef PSP2
264 if (queueLength <= 0) {
265 queueLength = 1;
266 }
267#endif
268 return listen(socket, queueLength);
269#endif
270}
271
272static inline Socket SocketAccept(Socket socket, struct Address* address) {
273 if (!address) {
274#ifdef GEKKO
275 struct sockaddr_in addrInfo;
276 memset(&addrInfo, 0, sizeof(addrInfo));
277 socklen_t len = sizeof(addrInfo);
278 return net_accept(socket, (struct sockaddr*) &addrInfo, &len);
279#else
280 return accept(socket, 0, 0);
281#endif
282 }
283 if (address->version == IPV4) {
284 struct sockaddr_in addrInfo;
285 memset(&addrInfo, 0, sizeof(addrInfo));
286 addrInfo.sin_family = AF_INET;
287 addrInfo.sin_addr.s_addr = address->ipv4;
288 socklen_t len = sizeof(addrInfo);
289#ifdef GEKKO
290 return net_accept(socket, (struct sockaddr*) &addrInfo, &len);
291#else
292 return accept(socket, (struct sockaddr*) &addrInfo, &len);
293#endif
294#if !defined(_3DS) && !defined(GEKKO)
295 } else {
296 struct sockaddr_in6 addrInfo;
297 memset(&addrInfo, 0, sizeof(addrInfo));
298 addrInfo.sin6_family = AF_INET6;
299 memcpy(addrInfo.sin6_addr.s6_addr, address->ipv6, sizeof(addrInfo.sin6_addr.s6_addr));
300 socklen_t len = sizeof(addrInfo);
301 return accept(socket, (struct sockaddr*) &addrInfo, &len);
302#endif
303 }
304 return INVALID_SOCKET;
305}
306
307static inline int SocketSetBlocking(Socket socket, bool blocking) {
308#ifdef _WIN32
309 u_long unblocking = !blocking;
310 return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR;
311#else
312#ifdef GEKKO
313 int flags = net_fcntl(socket, F_GETFL, 0);
314#else
315 int flags = fcntl(socket, F_GETFL);
316#endif
317 if (flags == -1) {
318 return 0;
319 }
320 if (blocking) {
321 flags &= ~O_NONBLOCK;
322 } else {
323 flags |= O_NONBLOCK;
324 }
325#ifdef GEKKO
326 return net_fcntl(socket, F_SETFL, flags) >= 0;
327#else
328 return fcntl(socket, F_SETFL, flags) >= 0;
329#endif
330#endif
331}
332
333static inline int SocketSetTCPPush(Socket socket, int push) {
334#ifdef GEKKO
335 return net_setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
336#else
337 return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
338#endif
339}
340
341static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) {
342 fd_set rset;
343 fd_set wset;
344 fd_set eset;
345 FD_ZERO(&rset);
346 FD_ZERO(&wset);
347 FD_ZERO(&eset);
348 size_t i;
349 Socket maxFd = 0;
350 if (reads) {
351 for (i = 0; i < nSockets; ++i) {
352 if (SOCKET_FAILED(reads[i])) {
353 break;
354 }
355 if (reads[i] > maxFd) {
356 maxFd = reads[i];
357 }
358 FD_SET(reads[i], &rset);
359 reads[i] = INVALID_SOCKET;
360 }
361 }
362 if (writes) {
363 for (i = 0; i < nSockets; ++i) {
364 if (SOCKET_FAILED(writes[i])) {
365 break;
366 }
367 if (writes[i] > maxFd) {
368 maxFd = writes[i];
369 }
370 FD_SET(writes[i], &wset);
371 writes[i] = INVALID_SOCKET;
372 }
373 }
374 if (errors) {
375 for (i = 0; i < nSockets; ++i) {
376 if (SOCKET_FAILED(errors[i])) {
377 break;
378 }
379 if (errors[i] > maxFd) {
380 maxFd = errors[i];
381 }
382 FD_SET(errors[i], &eset);
383 errors[i] = INVALID_SOCKET;
384 }
385 }
386 ++maxFd;
387 struct timeval tv;
388 tv.tv_sec = timeoutMillis / 1000;
389 tv.tv_usec = (timeoutMillis % 1000) * 1000;
390#ifdef GEKKO
391 int result = net_select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
392#else
393 int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
394#endif
395 int r = 0;
396 int w = 0;
397 int e = 0;
398 Socket j;
399 for (j = 0; j < maxFd; ++j) {
400 if (reads && FD_ISSET(j, &rset)) {
401 reads[r] = j;
402 ++r;
403 }
404 if (writes && FD_ISSET(j, &wset)) {
405 writes[w] = j;
406 ++w;
407 }
408 if (errors && FD_ISSET(j, &eset)) {
409 errors[e] = j;
410 ++e;
411 }
412 }
413 return result;
414}
415
416CXX_GUARD_END
417
418#endif