all repos — mgba @ fa884d071ecaa3e05ff20b45a67bf9500dd3d6b6

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