Files
LibretroAdmin 73aecc7546 network: DNS answers, the NAOMI link, sockets select() cannot take, ports bound again
A DNS answer was parsed by the offsets written in it: a remote host's
packet could have it read and write on the stack at an offset of the
sender's choosing. The parser keeps to the packet.

NAOMI link: a server that says "slot 0 of 3" crashed the client, and on
a 32-bit build a failed send was taken for a good one.

A socket whose descriptor is past what select() can hold is refused
when it is made: it was put in the set all the same.

Listening sockets are made so that their port can be bound again at once
after a restart (not on Windows, where that option means something
else). A retry in the path that patches a game's own address into a
packet tested a buffer that had not been filled.
2026-10-10 23:23:41 +02:00

172 lines
4.8 KiB
C

#ifndef _NET_PLATFORM_H
#define _NET_PLATFORM_H
#ifndef _WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef HAVE_LIBNX
#include <switch.h>
#define INADDR_NONE 0xffffffff
#define INET_ADDRSTRLEN sizeof(struct sockaddr_in)
#define SOL_TCP 6 // Shrug
#else
#include <netinet/ip.h>
#endif // HAVE_LIBNX
#include <netinet/tcp.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>
#else
#include <ws2tcpip.h>
#endif
/* libnx's <netinet/in.h> has no INADDR_LOOPBACK. 127.0.0.1, host order. */
#ifndef INADDR_LOOPBACK
#define INADDR_LOOPBACK 0x7f000001
#endif
#ifndef _WIN32
#define closesocket close
typedef int sock_t;
#define VALID(s) ((s) >= 0)
#define L_EWOULDBLOCK EWOULDBLOCK
#define L_EAGAIN EAGAIN
#define get_last_error() (errno)
#define INVALID_SOCKET (-1)
#define perror(s) do { INFO_LOG(MODEM, "%s: %s", (s) != NULL ? (s) : "", strerror(get_last_error())); } while (false)
#else
typedef SOCKET sock_t;
#define VALID(s) ((s) != INVALID_SOCKET)
#define L_EWOULDBLOCK WSAEWOULDBLOCK
#define L_EAGAIN WSAEWOULDBLOCK
#define get_last_error() (WSAGetLastError())
#define perror(s) do { INFO_LOG(MODEM, "%s: Winsock error: %d\n", (s) != NULL ? (s) : "", WSAGetLastError()); } while (false)
#define SHUT_WR SD_SEND
#define SHUT_RD SD_RECEIVE
#define SHUT_RDWR SD_BOTH
#endif
/* Writing to a TCP connection the other end has closed raises SIGPIPE, and
* a SIGPIPE nothing handles ends the whole process. The signal's handling
* belongs to the frontend, so it is never raised instead: by a flag on each
* send where the system has one, and by an option set once on the socket
* where it has that (Apple, the BSDs). Windows has no such signal. */
#if !defined(_WIN32) && defined(MSG_NOSIGNAL)
#define L_MSG_NOSIGNAL MSG_NOSIGNAL
#else
#define L_MSG_NOSIGNAL 0
#endif
static inline void set_no_sigpipe(sock_t fd)
{
#if !defined(_WIN32) && defined(SO_NOSIGPIPE)
int optval = 1;
setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (const void *)&optval, sizeof(optval));
#else
(void)fd;
#endif
}
/* A listening port can be bound again as soon as its socket is closed,
* though connections accepted on it are still winding down: without this
* the bind fails for a minute or more after a game is restarted. Not on
* Windows: a port can be bound again there without it, and with it any
* other program could bind the port while it is in use here. */
static inline void set_reuse_addr(sock_t fd)
{
#ifndef _WIN32
int optval = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *)&optval, sizeof(optval));
#else
(void)fd;
#endif
}
/* Whether a socket can be waited on with select(). A descriptor set has
* room for the numbers below FD_SETSIZE and FD_SET does not look: a higher
* one is written outside the set. Windows' sets hold the sockets themselves
* and FD_SET leaves out what does not fit. */
static inline bool fits_select(sock_t fd)
{
#ifndef _WIN32
return fd < FD_SETSIZE;
#else
(void)fd;
return true;
#endif
}
/* The socket if it is valid and select() can wait on it; otherwise it is
* closed and INVALID_SOCKET is returned. */
static inline sock_t selectable_socket(sock_t fd)
{
if (VALID(fd) && !fits_select(fd))
{
closesocket(fd);
return INVALID_SOCKET;
}
return fd;
}
static inline void set_non_blocking(sock_t fd)
{
#ifndef _WIN32
fcntl(fd, F_SETFL, O_NONBLOCK);
#else
u_long optl = 1;
ioctlsocket(fd, FIONBIO, &optl);
#endif
}
static inline void set_tcp_nodelay(sock_t fd)
{
int optval = 1;
socklen_t optlen = sizeof(optval);
#if defined(_WIN32)
struct protoent *tcp_proto = getprotobyname("TCP");
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, (const char *)&optval, optlen);
#elif !defined(__APPLE__) && !defined(__FreeBSD__) && !defined(__NetBSD__) && !defined(__HAIKU__)
setsockopt(fd, SOL_TCP, TCP_NODELAY, (const void *)&optval, optlen);
#else
struct protoent *tcp_proto = getprotobyname("TCP");
setsockopt(fd, tcp_proto->p_proto, TCP_NODELAY, &optval, optlen);
#endif
}
static inline bool set_recv_timeout(sock_t fd, int delayms)
{
#ifdef _WIN32
const DWORD dwDelay = delayms;
return setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char *)&dwDelay, sizeof(DWORD)) == 0;
#else
struct timeval tv;
tv.tv_sec = delayms / 1000;
tv.tv_usec = (delayms % 1000) * 1000;
return setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == 0;
#endif
}
#if defined(_WIN32) && _WIN32_WINNT < 0x0600
static inline const char *inet_ntop(int af, const void* src, char* dst, int cnt)
{
struct sockaddr_in srcaddr;
memset(&srcaddr, 0, sizeof(struct sockaddr_in));
memcpy(&srcaddr.sin_addr, src, sizeof(srcaddr.sin_addr));
srcaddr.sin_family = af;
if (WSAAddressToString((struct sockaddr *)&srcaddr, sizeof(struct sockaddr_in), 0, dst, (LPDWORD)&cnt) != 0)
return nullptr;
else
return dst;
}
#endif
#endif