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#include <arpa/inet.h>
24#include <errno.h>
25#include <fcntl.h>
26#include <netinet/in.h>
27#include <netinet/tcp.h>
28#include <sys/select.h>
29#include <sys/socket.h>
30
31#define INVALID_SOCKET (-1)
32#define SOCKET_FAILED(s) ((s) < 0)
33typedef int Socket;
34#endif
35
36enum IP {
37 IPV4,
38 IPV6
39};
40
41struct Address {
42 enum IP version;
43 union {
44 uint32_t ipv4;
45 uint8_t ipv6[16];
46 };
47};
48
49#ifdef _3DS
50#include <3ds.h>
51#include <malloc.h>
52
53#define SOCU_ALIGN 0x1000
54#define SOCU_BUFFERSIZE 0x100000
55
56extern u32* SOCUBuffer;
57#endif
58#ifdef __SWITCH__
59#include <switch.h>
60#endif
61
62static inline void SocketSubsystemInit() {
63#ifdef _WIN32
64 WSADATA data;
65 WSAStartup(MAKEWORD(2, 2), &data);
66#elif defined(_3DS)
67 if (!SOCUBuffer) {
68 SOCUBuffer = memalign(SOCU_ALIGN, SOCU_BUFFERSIZE);
69 socInit(SOCUBuffer, SOCU_BUFFERSIZE);
70 }
71#elif defined(__SWITCH__)
72 socketInitializeDefault();
73#endif
74}
75
76static inline void SocketSubsystemDeinit() {
77#ifdef _WIN32
78 WSACleanup();
79#elif defined(_3DS)
80 socExit();
81 free(SOCUBuffer);
82 SOCUBuffer = NULL;
83#elif defined(__SWITCH__)
84 socketExit();
85#endif
86}
87
88static inline int SocketError() {
89#ifdef _WIN32
90 return WSAGetLastError();
91#else
92 return errno;
93#endif
94}
95
96static inline bool SocketWouldBlock() {
97#ifdef _WIN32
98 return SocketError() == WSAEWOULDBLOCK;
99#else
100 return SocketError() == EWOULDBLOCK || SocketError() == EAGAIN;
101#endif
102}
103
104static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) {
105#ifdef _WIN32
106 return send(socket, (const char*) buffer, size, 0);
107#else
108 return write(socket, buffer, size);
109#endif
110}
111
112static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
113#if defined(_WIN32) || defined(__SWITCH__)
114 return recv(socket, (char*) buffer, size, 0);
115#else
116 return read(socket, buffer, size);
117#endif
118}
119
120static inline int SocketClose(Socket socket) {
121#ifdef _WIN32
122 return closesocket(socket) == 0;
123#else
124 return close(socket) >= 0;
125#endif
126}
127
128static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) {
129 Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
130 if (SOCKET_FAILED(sock)) {
131 return sock;
132 }
133
134 int err;
135 if (!bindAddress) {
136 struct sockaddr_in bindInfo;
137 memset(&bindInfo, 0, sizeof(bindInfo));
138 bindInfo.sin_family = AF_INET;
139 bindInfo.sin_port = htons(port);
140#ifndef _3DS
141 bindInfo.sin_addr.s_addr = INADDR_ANY;
142#else
143 bindInfo.sin_addr.s_addr = gethostid();
144#endif
145 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
146 } else if (bindAddress->version == IPV4) {
147 struct sockaddr_in bindInfo;
148 memset(&bindInfo, 0, sizeof(bindInfo));
149 bindInfo.sin_family = AF_INET;
150 bindInfo.sin_port = htons(port);
151 bindInfo.sin_addr.s_addr = htonl(bindAddress->ipv4);
152 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
153#ifndef _3DS
154 } else {
155 struct sockaddr_in6 bindInfo;
156 memset(&bindInfo, 0, sizeof(bindInfo));
157 bindInfo.sin6_family = AF_INET6;
158 bindInfo.sin6_port = htons(port);
159 memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
160 err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
161#endif
162 }
163 if (err) {
164 SocketClose(sock);
165 return INVALID_SOCKET;
166 }
167 return sock;
168}
169
170static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) {
171 Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
172 if (SOCKET_FAILED(sock)) {
173 return sock;
174 }
175
176 int err;
177 if (!destinationAddress) {
178 struct sockaddr_in bindInfo;
179 memset(&bindInfo, 0, sizeof(bindInfo));
180 bindInfo.sin_family = AF_INET;
181 bindInfo.sin_port = htons(port);
182 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
183 } else if (destinationAddress->version == IPV4) {
184 struct sockaddr_in bindInfo;
185 memset(&bindInfo, 0, sizeof(bindInfo));
186 bindInfo.sin_family = AF_INET;
187 bindInfo.sin_port = htons(port);
188 bindInfo.sin_addr.s_addr = htonl(destinationAddress->ipv4);
189 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
190#ifndef _3DS
191 } else {
192 struct sockaddr_in6 bindInfo;
193 memset(&bindInfo, 0, sizeof(bindInfo));
194 bindInfo.sin6_family = AF_INET6;
195 bindInfo.sin6_port = htons(port);
196 memcpy(bindInfo.sin6_addr.s6_addr, destinationAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
197 err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
198#endif
199 }
200
201 if (err) {
202 SocketClose(sock);
203 return INVALID_SOCKET;
204 }
205 return sock;
206}
207
208static inline Socket SocketListen(Socket socket, int queueLength) {
209 return listen(socket, queueLength);
210}
211
212static inline Socket SocketAccept(Socket socket, struct Address* address) {
213 if (!address) {
214 return accept(socket, 0, 0);
215 }
216 if (address->version == IPV4) {
217 struct sockaddr_in addrInfo;
218 memset(&addrInfo, 0, sizeof(addrInfo));
219 addrInfo.sin_family = AF_INET;
220 addrInfo.sin_addr.s_addr = address->ipv4;
221 socklen_t len = sizeof(addrInfo);
222 return accept(socket, (struct sockaddr*) &addrInfo, &len);
223#ifndef _3DS
224 } else {
225 struct sockaddr_in6 addrInfo;
226 memset(&addrInfo, 0, sizeof(addrInfo));
227 addrInfo.sin6_family = AF_INET6;
228 memcpy(addrInfo.sin6_addr.s6_addr, address->ipv6, sizeof(addrInfo.sin6_addr.s6_addr));
229 socklen_t len = sizeof(addrInfo);
230 return accept(socket, (struct sockaddr*) &addrInfo, &len);
231#endif
232 }
233 return INVALID_SOCKET;
234}
235
236static inline int SocketSetBlocking(Socket socket, bool blocking) {
237#ifdef _WIN32
238 u_long unblocking = !blocking;
239 return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR;
240#else
241 int flags = fcntl(socket, F_GETFL);
242 if (flags == -1) {
243 return 0;
244 }
245 if (blocking) {
246 flags &= ~O_NONBLOCK;
247 } else {
248 flags |= O_NONBLOCK;
249 }
250 return fcntl(socket, F_SETFL, flags) >= 0;
251#endif
252}
253
254static inline int SocketSetTCPPush(Socket socket, int push) {
255 return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
256}
257
258static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) {
259 fd_set rset;
260 fd_set wset;
261 fd_set eset;
262 FD_ZERO(&rset);
263 FD_ZERO(&wset);
264 FD_ZERO(&eset);
265 size_t i;
266 Socket maxFd = 0;
267 if (reads) {
268 for (i = 0; i < nSockets; ++i) {
269 if (SOCKET_FAILED(reads[i])) {
270 break;
271 }
272 if (reads[i] > maxFd) {
273 maxFd = reads[i];
274 }
275 FD_SET(reads[i], &rset);
276 reads[i] = INVALID_SOCKET;
277 }
278 }
279 if (writes) {
280 for (i = 0; i < nSockets; ++i) {
281 if (SOCKET_FAILED(writes[i])) {
282 break;
283 }
284 if (writes[i] > maxFd) {
285 maxFd = writes[i];
286 }
287 FD_SET(writes[i], &wset);
288 writes[i] = INVALID_SOCKET;
289 }
290 }
291 if (errors) {
292 for (i = 0; i < nSockets; ++i) {
293 if (SOCKET_FAILED(errors[i])) {
294 break;
295 }
296 if (errors[i] > maxFd) {
297 maxFd = errors[i];
298 }
299 FD_SET(errors[i], &eset);
300 errors[i] = INVALID_SOCKET;
301 }
302 }
303 ++maxFd;
304 struct timeval tv;
305 tv.tv_sec = timeoutMillis / 1000;
306 tv.tv_usec = (timeoutMillis % 1000) * 1000;
307 int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
308 int r = 0;
309 int w = 0;
310 int e = 0;
311 Socket j;
312 for (j = 0; j < maxFd; ++j) {
313 if (reads && FD_ISSET(j, &rset)) {
314 reads[r] = j;
315 ++r;
316 }
317 if (writes && FD_ISSET(j, &wset)) {
318 writes[w] = j;
319 ++w;
320 }
321 if (errors && FD_ISSET(j, &eset)) {
322 errors[e] = j;
323 ++e;
324 }
325 }
326 return result;
327}
328
329CXX_GUARD_END
330
331#endif