all repos — mgba @ 5d1b05c9904bdc3dc285f0278b3f9cb928640ac8

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