Files
dat-thang/extra/dat-dpi/src/dat_net.c
T
divorce b735900f2e 0.0.002
ddat interop with dat js
cable interop with cabal js
ddat's frontend = dat-thang
cable fe = cable-thang
dillo support based on cel's dillo-dat
bootstrap node
xplatform support

Signed-off-by: divo <418@cryspace.space>
2026-10-19 00:17:02 -04:00

167 lines
6.8 KiB
C

#define _POSIX_C_SOURCE 200112L
#include "dat_net.h"
#include <errno.h>
#include <string.h>
#ifdef _WIN32
#include <windows.h>
#ifndef SIO_UDP_CONNRESET
#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
#endif
static int datnet_winsock_ready;
#else
#include <netdb.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#endif
#ifdef _WIN32
static int datnet_winsock_errno(int error)
{
switch (error) {
case WSAEINTR: return EINTR;
case WSAEACCES: return EACCES;
case WSAEADDRINUSE: return EADDRINUSE;
case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL;
case WSAENETDOWN: return ENETDOWN;
case WSAENETUNREACH: return ENETUNREACH;
case WSAEHOSTUNREACH: return EHOSTUNREACH;
case WSAECONNRESET: return ECONNRESET;
case WSAECONNREFUSED: return ECONNREFUSED;
case WSAETIMEDOUT: return ETIMEDOUT;
case WSAEMSGSIZE: return EMSGSIZE;
case WSAENOBUFS: return ENOBUFS;
case WSAEWOULDBLOCK: return EAGAIN;
default: return EIO;
}
}
#endif
int datnet_init(datnet_t *net, const uint8_t topic[32], const uint8_t peerid[32]) {
if (!net || !topic || !peerid) { errno = EINVAL; return -1; }
#ifdef _WIN32
if (!datnet_winsock_ready) { WSADATA data; if (WSAStartup(MAKEWORD(2,2), &data)) { errno = ENETDOWN; return -1; } datnet_winsock_ready = 1; }
#endif
memset(net, 0, sizeof(*net)); net->sock = DAT_INVALID_SOCKET; memcpy(net->topic, topic, 32); memcpy(net->peerid, peerid, 32); return 0;
}
int datnet_bind(datnet_t *net, const char *host, uint16_t port) {
struct sockaddr_in address; int broadcast = 1;
if (!net) { errno = EINVAL; return -1; }
memset(&address, 0, sizeof(address)); address.sin_family = AF_INET; address.sin_port = htons(port ? port : DATNET_PORT);
if (!host || !*host || !strcmp(host, "*")) address.sin_addr.s_addr = htonl(INADDR_ANY);
else {
struct addrinfo hints, *resolved = NULL;
memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_DGRAM;
if (getaddrinfo(host, NULL, &hints, &resolved) != 0 || !resolved) {
errno = EADDRNOTAVAIL; return -1;
}
address.sin_addr = ((struct sockaddr_in *)resolved->ai_addr)->sin_addr;
freeaddrinfo(resolved);
}
net->sock = socket(AF_INET, SOCK_DGRAM, 0);
if (net->sock == DAT_INVALID_SOCKET) {
#ifdef _WIN32
errno = ENETDOWN;
#endif
return -1;
}
#ifdef _WIN32
if (setsockopt(net->sock, SOL_SOCKET, SO_BROADCAST, (const char *)&broadcast, sizeof(broadcast))) {
int error = WSAGetLastError();
closesocket(net->sock); net->sock = DAT_INVALID_SOCKET;
errno = datnet_winsock_errno(error);
return -1;
}
/* A peer that is not listening can send an ICMP port-unreachable response.
* Winsock reports that asynchronously as WSAECONNRESET on a UDP socket;
* that is a peer/network condition, not a dead local socket. */
{
BOOL disable_udp_connreset = FALSE;
DWORD returned = 0;
(void)WSAIoctl(net->sock, SIO_UDP_CONNRESET, &disable_udp_connreset,
sizeof(disable_udp_connreset), NULL, 0, &returned, NULL, NULL);
}
if (bind(net->sock, (struct sockaddr *)&address, sizeof(address))) {
int error = WSAGetLastError();
closesocket(net->sock); net->sock = DAT_INVALID_SOCKET;
errno = error == WSAEADDRINUSE ? EADDRINUSE :
error == WSAEACCES ? EACCES : EIO;
return -1;
}
#else
if (setsockopt(net->sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast))) {
int saved = errno;
close(net->sock); net->sock = DAT_INVALID_SOCKET; errno = saved;
return -1;
}
if (bind(net->sock, (struct sockaddr *)&address, sizeof(address))) { close(net->sock); net->sock = DAT_INVALID_SOCKET; return -1; }
#endif
return 0;
}
int datnet_poll(datnet_t *net, int timeout_ms) {
uint8_t packet[DATNET_MAX_PACKET], peer_id[DATNET_PEERID_BYTES]; struct sockaddr_storage source; socklen_t source_len = sizeof(source); ssize_t n; dat_peer_t peer;
if (!net || net->sock == DAT_INVALID_SOCKET) { errno = EINVAL; return -1; }
memcpy(peer_id, net->peerid, sizeof(peer_id));
#ifdef _WIN32
{ WSAPOLLFD pfd; int ready; pfd.fd = net->sock; pfd.events = POLLRDNORM; pfd.revents = 0;
ready = WSAPoll(&pfd, 1, timeout_ms);
if (ready == SOCKET_ERROR) { errno = datnet_winsock_errno(WSAGetLastError()); return -1; }
if (ready == 0) return 0;
if (pfd.revents & (POLLHUP | POLLNVAL)) { errno = EIO; return -1; }
if (pfd.revents & POLLERR) {
int socket_error = 0, error_length = sizeof(socket_error);
if (getsockopt(net->sock, SOL_SOCKET, SO_ERROR, (char *)&socket_error,
&error_length) == SOCKET_ERROR) {
errno = datnet_winsock_errno(WSAGetLastError());
return -1;
}
if (socket_error == WSAECONNRESET || socket_error == WSAECONNREFUSED ||
socket_error == WSAEHOSTUNREACH || socket_error == WSAENETUNREACH)
return 0;
if (socket_error) { errno = datnet_winsock_errno(socket_error); return -1; }
}
if (!(pfd.revents & POLLRDNORM)) return 0; }
#else
{ struct pollfd pfd; int ready; pfd.fd = net->sock; pfd.events = POLLIN; pfd.revents = 0;
ready = poll(&pfd, 1, timeout_ms);
if (ready < 0) return -1;
if (ready == 0) return 0;
if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { errno = EIO; return -1; }
if (!(pfd.revents & POLLIN)) return 0; }
#endif
#ifdef _WIN32
{ int source_length = (int)source_len; n = recvfrom(net->sock, (char *)packet, sizeof(packet), 0, (struct sockaddr *)&source, &source_length); source_len = (socklen_t)source_length;
if (n < 0) { errno = datnet_winsock_errno(WSAGetLastError()); return -1; } }
#else
n = recvfrom(net->sock, packet, sizeof(packet), 0, (struct sockaddr *)&source, &source_len); if (n < 0) return -1;
#endif
if (n < 0) return -1;
memset(&peer, 0, sizeof(peer)); peer.fd = net->sock; peer.addr = source; peer.addrlen = source_len;
/* Cesspool decodes discovery records here; other datagrams are passed to
* its authenticated peer-transport hook. */
if (net->on_message) net->on_message(net, &peer, packet, (size_t)n);
/* The identity bound to this UDP socket is immutable. Keep it intact even
* if a protocol callback uses an aliased view while handling the packet. */
memcpy(net->peerid, peer_id, sizeof(peer_id));
return 1;
}
int datnet_send(datnet_t *net, const dat_peer_t *peer, const uint8_t *message, size_t length) {
ssize_t n; if (!net || !peer || net->sock == DAT_INVALID_SOCKET || (!message && length) || length > DATNET_MAX_PACKET) { errno = EINVAL; return -1; }
#ifdef _WIN32
n = sendto(net->sock, (const char *)message, (int)length, 0, (const struct sockaddr *)&peer->addr, (int)peer->addrlen);
if (n == SOCKET_ERROR) { errno = datnet_winsock_errno(WSAGetLastError()); return -1; }
#else
n = sendto(net->sock, message, length, 0, (const struct sockaddr *)&peer->addr, peer->addrlen);
if (n < 0) return -1;
#endif
return n == (ssize_t)length ? 0 : -1;
}
int datnet_close(datnet_t *net) { int rc = 0; if (!net) { errno = EINVAL; return -1; } if (net->sock != DAT_INVALID_SOCKET) {
#ifdef _WIN32
rc = closesocket(net->sock);
#else
rc = close(net->sock);
#endif
} net->sock = DAT_INVALID_SOCKET; return rc; }