all repos — mgba @ 9de8f084ba55460b02d300c1dd8b8e6c56f691d5

mGBA Game Boy Advance Emulator

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