all repos — mgba @ a0b94db9a78a6482579edd35e6eefeb4b15bb257

mGBA Game Boy Advance Emulator

src/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 "util/common.h"
 10
 11#ifdef __cplusplus
 12#define restrict __restrict__
 13#endif
 14
 15#ifdef _WIN32
 16#include <winsock2.h>
 17#include <ws2tcpip.h>
 18
 19#define SOCKET_FAILED(s) ((s) == INVALID_SOCKET)
 20typedef SOCKET Socket;
 21#else
 22#include <errno.h>
 23#include <fcntl.h>
 24#include <netinet/in.h>
 25#include <netinet/tcp.h>
 26#include <sys/socket.h>
 27
 28#define INVALID_SOCKET (-1)
 29#define SOCKET_FAILED(s) ((s) < 0)
 30typedef int Socket;
 31#endif
 32
 33enum IP {
 34	IPV4,
 35	IPV6
 36};
 37
 38struct Address {
 39	enum IP version;
 40	union {
 41		uint32_t ipv4;
 42		uint8_t ipv6[16];
 43	};
 44};
 45
 46static inline void SocketSubsystemInit() {
 47#ifdef _WIN32
 48	WSADATA data;
 49	WSAStartup(MAKEWORD(2, 2), &data);
 50#endif
 51}
 52
 53static inline int SocketError() {
 54#ifdef _WIN32
 55	return WSAGetLastError();
 56#else
 57	return errno;
 58#endif
 59}
 60
 61static inline bool SocketWouldBlock() {
 62#ifdef _WIN32
 63	return SocketError() == WSAEWOULDBLOCK;
 64#else
 65	return SocketError() == EWOULDBLOCK || SocketError() == EAGAIN;
 66#endif
 67}
 68
 69static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) {
 70	return send(socket, buffer, size, 0);
 71}
 72
 73static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
 74	return recv(socket, buffer, size, 0);
 75}
 76
 77static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) {
 78	Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
 79	if (SOCKET_FAILED(sock)) {
 80		return sock;
 81	}
 82
 83	int err;
 84	if (!bindAddress) {
 85		struct sockaddr_in bindInfo;
 86		memset(&bindInfo, 0, sizeof(bindInfo));
 87		bindInfo.sin_family = AF_INET;
 88		bindInfo.sin_port = htons(port);
 89		err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
 90	} else if (bindAddress->version == IPV4) {
 91		struct sockaddr_in bindInfo;
 92		memset(&bindInfo, 0, sizeof(bindInfo));
 93		bindInfo.sin_family = AF_INET;
 94		bindInfo.sin_port = htons(port);
 95		bindInfo.sin_addr.s_addr = bindAddress->ipv4;
 96		err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
 97	} else {
 98		struct sockaddr_in6 bindInfo;
 99		memset(&bindInfo, 0, sizeof(bindInfo));
100		bindInfo.sin6_family = AF_INET6;
101		bindInfo.sin6_port = htons(port);
102		memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
103		err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
104
105	}
106	if (err) {
107		close(sock);
108		return INVALID_SOCKET;
109	}
110	return sock;
111}
112
113static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) {
114	Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
115	if (SOCKET_FAILED(sock)) {
116		return sock;
117	}
118
119	int err;
120	if (!destinationAddress) {
121		struct sockaddr_in bindInfo;
122		memset(&bindInfo, 0, sizeof(bindInfo));
123		bindInfo.sin_family = AF_INET;
124		bindInfo.sin_port = htons(port);
125		err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
126	} else if (destinationAddress->version == IPV4) {
127		struct sockaddr_in bindInfo;
128		memset(&bindInfo, 0, sizeof(bindInfo));
129		bindInfo.sin_family = AF_INET;
130		bindInfo.sin_port = htons(port);
131		bindInfo.sin_addr.s_addr = destinationAddress->ipv4;
132		err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
133	} else {
134		struct sockaddr_in6 bindInfo;
135		memset(&bindInfo, 0, sizeof(bindInfo));
136		bindInfo.sin6_family = AF_INET6;
137		bindInfo.sin6_port = htons(port);
138		memcpy(bindInfo.sin6_addr.s6_addr, destinationAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
139		err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
140	}
141
142	if (err) {
143		close(sock);
144		return INVALID_SOCKET;
145	}
146	return sock;
147}
148
149static inline Socket SocketListen(Socket socket, int queueLength) {
150	return listen(socket, queueLength);
151}
152
153static inline Socket SocketAccept(Socket socket, struct Address* address) {
154	if (!address) {
155		return accept(socket, 0, 0);
156	}
157	if (address->version == IPV4) {
158		struct sockaddr_in addrInfo;
159		memset(&addrInfo, 0, sizeof(addrInfo));
160		addrInfo.sin_family = AF_INET;
161		addrInfo.sin_addr.s_addr = address->ipv4;
162		socklen_t len = sizeof(addrInfo);
163		return accept(socket, (struct sockaddr*) &addrInfo, &len);
164	} else {
165		struct sockaddr_in6 addrInfo;
166		memset(&addrInfo, 0, sizeof(addrInfo));
167		addrInfo.sin6_family = AF_INET6;
168		memcpy(addrInfo.sin6_addr.s6_addr, address->ipv6, sizeof(addrInfo.sin6_addr.s6_addr));
169		socklen_t len = sizeof(addrInfo);
170		return accept(socket, (struct sockaddr*) &addrInfo, &len);
171	}
172}
173
174static inline int SocketClose(Socket socket) {
175	return close(socket) >= 0;
176}
177
178static inline int SocketSetBlocking(Socket socket, bool blocking) {
179#ifdef _WIN32
180	u_long unblocking = !blocking;
181	return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR;
182#else
183	int flags = fcntl(socket, F_GETFL);
184	if (flags == -1) {
185		return 0;
186	}
187	if (blocking) {
188		flags &= ~O_NONBLOCK;
189	} else {
190		flags |= O_NONBLOCK;
191	}
192	return fcntl(socket, F_SETFL, flags) >= 0;
193#endif
194}
195
196static inline int SocketSetTCPPush(Socket socket, int push) {
197	return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
198}
199
200static inline int SocketPoll(size_t nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) {
201	fd_set rset;
202	fd_set wset;
203	fd_set eset;
204	FD_ZERO(&rset);
205	FD_ZERO(&wset);
206	FD_ZERO(&eset);
207	size_t i;
208	Socket maxFd = 0;
209	if (reads) {
210		for (i = 0; i < nSockets; ++i) {
211			if (SOCKET_FAILED(reads[i])) {
212				break;
213			}
214			if (reads[i] > maxFd) {
215				maxFd = reads[i];
216			}
217			FD_SET(reads[i], &rset);
218			reads[i] = INVALID_SOCKET;
219		}
220	}
221	if (writes) {
222		for (i = 0; i < nSockets; ++i) {
223			if (SOCKET_FAILED(writes[i])) {
224				break;
225			}
226			if (writes[i] > maxFd) {
227				maxFd = writes[i];
228			}
229			FD_SET(writes[i], &wset);
230			writes[i] = INVALID_SOCKET;
231		}
232	}
233	if (errors) {
234		for (i = 0; i < nSockets; ++i) {
235			if (SOCKET_FAILED(errors[i])) {
236				break;
237			}
238			if (errors[i] > maxFd) {
239				maxFd = errors[i];
240			}
241			FD_SET(errors[i], &eset);
242			errors[i] = INVALID_SOCKET;
243		}
244	}
245	struct timeval tv;
246	tv.tv_sec = timeoutMillis / 1000;
247	tv.tv_usec = (timeoutMillis % 1000) * 1000;
248	int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
249	int r = 0;
250	int w = 0;
251	int e = 0;
252	Socket j;
253	for (j = 0; j < maxFd; ++j) {
254		if (reads && FD_ISSET(j, &rset)) {
255			reads[r] = j;
256			++r;
257		}
258		if (writes && FD_ISSET(j, &wset)) {
259			writes[w] = j;
260			++w;
261		}
262		if (errors && FD_ISSET(j, &eset)) {
263			errors[e] = j;
264			++e;
265		}
266	}
267	return result;
268}
269
270#endif