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 ++maxFd;
297 struct timeval tv;
298 tv.tv_sec = timeoutMillis / 1000;
299 tv.tv_usec = (timeoutMillis % 1000) * 1000;
300 int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
301 int r = 0;
302 int w = 0;
303 int e = 0;
304 Socket j;
305 for (j = 0; j < maxFd; ++j) {
306 if (reads && FD_ISSET(j, &rset)) {
307 reads[r] = j;
308 ++r;
309 }
310 if (writes && FD_ISSET(j, &wset)) {
311 writes[w] = j;
312 ++w;
313 }
314 if (errors && FD_ISSET(j, &eset)) {
315 errors[e] = j;
316 ++e;
317 }
318 }
319 return result;
320}
321
322CXX_GUARD_END
323
324#endif