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>
167 lines
6.8 KiB
C
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; }
|