#define _POSIX_C_SOURCE 200112L #include "dat_net.h" #include #include #ifdef _WIN32 #include #ifndef SIO_UDP_CONNRESET #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12) #endif static int datnet_winsock_ready; #else #include #include #include #include #include #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; }