mgba/src/util/socket.h

252 lines
6.1 KiB
C
Raw Normal View History

/* Copyright (c) 2013-2014 Jeffrey Pfau
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
2014-02-04 07:01:26 +00:00
#ifndef SOCKET_H
#define SOCKET_H
2014-10-12 01:18:47 +00:00
#include "util/common.h"
2014-07-20 23:37:54 +00:00
#ifdef __cplusplus
#define restrict __restrict__
#endif
2014-02-04 07:01:26 +00:00
#ifdef _WIN32
#include <winsock2.h>
2014-04-24 04:42:08 +00:00
#include <ws2tcpip.h>
2014-02-04 07:01:26 +00:00
2015-01-16 08:11:50 +00:00
#define SOCKET_FAILED(s) ((s) == INVALID_SOCKET)
2014-02-04 07:01:26 +00:00
typedef SOCKET Socket;
#else
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
2014-02-04 07:01:26 +00:00
#include <sys/socket.h>
2014-04-24 04:42:08 +00:00
#define INVALID_SOCKET (-1)
2015-01-16 08:11:50 +00:00
#define SOCKET_FAILED(s) ((s) < 0)
2014-02-04 07:01:26 +00:00
typedef int Socket;
#endif
2015-01-09 10:03:42 +00:00
enum IP {
IPV4,
IPV6
};
struct Address {
enum IP version;
union {
uint32_t ipv4;
uint8_t ipv6[16];
};
};
2014-02-04 07:01:26 +00:00
2014-02-05 09:22:34 +00:00
static inline void SocketSubsystemInitialize() {
2014-02-04 07:01:26 +00:00
#ifdef _WIN32
WSAStartup(MAKEWORD(2, 2), 0);
#endif
}
2014-02-05 09:22:34 +00:00
static inline ssize_t SocketSend(Socket socket, const void* buffer, size_t size) {
2014-02-04 07:01:26 +00:00
return write(socket, buffer, size);
}
2014-02-05 09:22:34 +00:00
static inline ssize_t SocketRecv(Socket socket, void* buffer, size_t size) {
2014-02-04 07:01:26 +00:00
return read(socket, buffer, size);
}
2015-01-09 10:03:42 +00:00
static inline Socket SocketOpenTCP(int port, const struct Address* bindAddress) {
2014-02-04 07:01:26 +00:00
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
2014-04-24 04:42:08 +00:00
if (SOCKET_FAILED(sock)) {
2014-02-04 07:01:26 +00:00
return sock;
}
2015-01-09 10:03:42 +00:00
int err;
if (!bindAddress) {
struct sockaddr_in bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin_family = AF_INET;
bindInfo.sin_port = htons(port);
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
} else if (bindAddress->version == IPV4) {
struct sockaddr_in bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin_family = AF_INET;
bindInfo.sin_port = htons(port);
bindInfo.sin_addr.s_addr = bindAddress->ipv4;
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
} else {
struct sockaddr_in6 bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin6_family = AF_INET6;
bindInfo.sin6_port = htons(port);
memcpy(bindInfo.sin6_addr.s6_addr, bindAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
err = bind(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
}
2014-02-04 07:01:26 +00:00
if (err) {
close(sock);
return -1;
}
return sock;
}
2015-01-09 10:03:42 +00:00
static inline Socket SocketConnectTCP(int port, const struct Address* destinationAddress) {
Socket sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
2014-04-24 04:42:08 +00:00
if (SOCKET_FAILED(sock)) {
return sock;
}
2015-01-09 10:03:42 +00:00
int err;
if (!destinationAddress) {
struct sockaddr_in bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin_family = AF_INET;
bindInfo.sin_port = htons(port);
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
} else if (destinationAddress->version == IPV4) {
struct sockaddr_in bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin_family = AF_INET;
bindInfo.sin_port = htons(port);
bindInfo.sin_addr.s_addr = destinationAddress->ipv4;
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
} else {
struct sockaddr_in6 bindInfo;
memset(&bindInfo, 0, sizeof(bindInfo));
bindInfo.sin6_family = AF_INET6;
bindInfo.sin6_port = htons(port);
memcpy(bindInfo.sin6_addr.s6_addr, destinationAddress->ipv6, sizeof(bindInfo.sin6_addr.s6_addr));
err = connect(sock, (const struct sockaddr*) &bindInfo, sizeof(bindInfo));
}
if (err) {
close(sock);
return -1;
}
return sock;
}
2014-02-05 09:22:34 +00:00
static inline Socket SocketListen(Socket socket, int queueLength) {
2014-02-04 07:01:26 +00:00
return listen(socket, queueLength);
}
2015-01-09 10:03:42 +00:00
static inline Socket SocketAccept(Socket socket, struct Address* address) {
if (!address) {
return accept(socket, 0, 0);
}
if (address->version == IPV4) {
struct sockaddr_in addrInfo;
memset(&addrInfo, 0, sizeof(addrInfo));
addrInfo.sin_family = AF_INET;
addrInfo.sin_addr.s_addr = address->ipv4;
socklen_t len = sizeof(addrInfo);
return accept(socket, (struct sockaddr*) &addrInfo, &len);
} else {
struct sockaddr_in6 addrInfo;
memset(&addrInfo, 0, sizeof(addrInfo));
addrInfo.sin6_family = AF_INET6;
memcpy(addrInfo.sin6_addr.s6_addr, address->ipv6, sizeof(addrInfo.sin6_addr.s6_addr));
socklen_t len = sizeof(addrInfo);
return accept(socket, (struct sockaddr*) &addrInfo, &len);
}
2014-02-04 07:01:26 +00:00
}
2014-02-05 09:22:34 +00:00
static inline int SocketClose(Socket socket) {
2014-02-04 07:01:26 +00:00
return close(socket) >= 0;
}
static inline int SocketSetBlocking(Socket socket, bool blocking) {
2014-02-04 07:01:26 +00:00
#ifdef _WIN32
2014-04-24 04:42:08 +00:00
u_long unblocking = !blocking;
return ioctlsocket(socket, FIONBIO, &unblocking) == NO_ERROR;
2014-02-04 07:01:26 +00:00
#else
int flags = fcntl(socket, F_GETFL);
if (flags == -1) {
return 0;
}
if (blocking) {
flags &= ~O_NONBLOCK;
} else {
flags |= O_NONBLOCK;
}
return fcntl(socket, F_SETFL, flags) >= 0;
#endif
}
static inline int SocketSetTCPPush(Socket socket, int push) {
return setsockopt(socket, IPPROTO_TCP, TCP_NODELAY, (char*) &push, sizeof(int)) >= 0;
}
static inline int SocketPoll(int nSockets, Socket* reads, Socket* writes, Socket* errors, int64_t timeoutMillis) {
fd_set rset;
fd_set wset;
fd_set eset;
FD_ZERO(&rset);
FD_ZERO(&wset);
FD_ZERO(&eset);
int i;
int maxFd = 0;
if (reads) {
for (i = 0; i < nSockets; ++i) {
if (SOCKET_FAILED(reads[i])) {
break;
}
if (reads[i] > maxFd) {
maxFd = reads[i];
}
FD_SET(reads[i], &rset);
reads[i] = INVALID_SOCKET;
}
}
if (writes) {
for (i = 0; i < nSockets; ++i) {
if (SOCKET_FAILED(writes[i])) {
break;
}
if (writes[i] > maxFd) {
maxFd = writes[i];
}
FD_SET(writes[i], &wset);
writes[i] = INVALID_SOCKET;
}
}
if (errors) {
for (i = 0; i < nSockets; ++i) {
if (SOCKET_FAILED(errors[i])) {
break;
}
if (errors[i] > maxFd) {
maxFd = errors[i];
}
FD_SET(errors[i], &eset);
errors[i] = INVALID_SOCKET;
}
}
struct timeval tv;
tv.tv_sec = timeoutMillis / 1000;
tv.tv_usec = (timeoutMillis % 1000) * 1000;
int result = select(maxFd, &rset, &wset, &eset, timeoutMillis < 0 ? 0 : &tv);
int r = 0;
int w = 0;
int e = 0;
for (i = 0; i < maxFd; ++i) {
if (reads && FD_ISSET(i, &rset)) {
reads[r] = i;
++r;
}
if (writes && FD_ISSET(i, &wset)) {
writes[w] = i;
++w;
}
if (errors && FD_ISSET(i, &eset)) {
errors[e] = i;
++e;
}
}
return result;
}
2014-02-04 07:01:26 +00:00
#endif