Files
nt-lite/code/user/personalities/posix/posix_socket.c
T
maksw 73abd6f90d isolate POSIX builds from the host UCRT, wire lwext4 and ntfs-3g as ext4.sys and ntfs.sys, split the FAT driver into directory/file/async/driver/mount units, add posixsrv and dwm plus input/console/desktop syscall clients and a pile of new smoke targets, rename serial.sys to cereal.sys, name HDA/USB/lwIP/AHCI workers, harden ATA probing/reset/removal around actual hardware deadlines, absent-channel detection, diagnostics and explicit worker lifetime, redo ACPI S5 shutdown around GAS/MMIO and HW-reduced firmware, scan SSDTs for _S5 and deal with Intel SMI sleep trapping, make serial and SMP timeouts use real monotonic deadlines, add thread names and fix terminal thread teardown, scheduler and APC prepare/commit races, make advisory file locks queryable and preserve split ranges and add mutable file append state, add shared-memory pipes with kernel-assisted wait/wake, introduce capability-scoped input and display managers, split surface map rights into read/write, add explicit unmap and replace present with commit/generation/damage tracking while keeping physical presentation under the display manager, decouple native exception classes from Win32 exception numbers and translate them in kernel32, add opaque process startup data, bump the resource protocol to v3 with namespace directories, symlinks and enumeration and service control to v2 with generic READY handle exports, add the new POSIX errno/status surface and rebuild fds around posixsrv-owned shared open descriptions, descriptor-local flags, exact inheritance snapshots, dup/fcntl/record locks and shared-ring pipes, add waitpid, POSIX credentials/groups/sessions/passwd/group lookups and move sockets onto stable open descriptions, port the shells and apps to the new console/input services and surface commit API, teach init to load services.ini and start/supervise RSSRV, DWM and generic services under restricted capabilities while publishing their endpoints through RSSRV, split RSSRV into namespace, volume, display, session, input, console and resource components and make /SystemRoot a real symlink to the independently published system volume, shift the syscall table again, expand lock/resource/surface/POSIX/service/thread smoke coverage, make docs scream ASK QUESTIONS and DISCUSS at LLMs, replace MIT No Attribution with WTFPL+.
2026-09-16 10:33:20 +02:00

810 lines
26 KiB
C

// POSIX compatibility support for posix socket.
#include <stddef.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ntlite/syscall.h>
#include "posix_internal.h"
#include "posix_fd.h"
// Implements the bswap16 operation.
static uint16_t bswap16(uint16_t x) {
return (uint16_t)((x << 8) | (x >> 8));
}
// Implements the bswap32 operation.
static uint32_t bswap32(uint32_t x) {
return ((x & 0x000000FFu) << 24) |
((x & 0x0000FF00u) << 8) |
((x & 0x00FF0000u) >> 8) |
((x & 0xFF000000u) >> 24);
}
// Implements the htons operation.
__declspec(dllexport) uint16_t htons(uint16_t x) { return bswap16(x); }
// Implements the ntohs operation.
__declspec(dllexport) uint16_t ntohs(uint16_t x) { return bswap16(x); }
// Implements the htonl operation.
__declspec(dllexport) uint32_t htonl(uint32_t x) { return bswap32(x); }
// Implements the ntohl operation.
__declspec(dllexport) uint32_t ntohl(uint32_t x) { return bswap32(x); }
// Parses byte.
static int ParseByte(const char** p, uint8_t* out) {
const char* s = *p;
if (*s < '0' || *s > '9') return 0;
unsigned v = 0;
while (*s >= '0' && *s <= '9') {
v = v * 10 + (unsigned)(*s - '0');
if (v > 255) return 0;
s++;
}
*out = (uint8_t)v;
*p = s;
return 1;
}
// Parses ipv4 Bytes.
static int ParseIpv4Bytes(const char* text, uint8_t out[4]) {
const char* p = text;
for (int i = 0; i < 4; i++) {
if (!ParseByte(&p, &out[i])) return 0;
if (i != 3) {
if (*p != '.') return 0;
p++;
}
}
return *p == '\0';
}
static int ParseHex(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
return -1;
}
static int ParseIpv6Bytes(const char* text, uint8_t out[16]) {
uint16_t words[8];
int count = 0;
int compress = -1;
const char* p = text;
if (!text || !*text || !out)
return 0;
memset(words, 0, sizeof(words));
if (p[0] == ':' && p[1] == ':') {
compress = 0;
p += 2;
if (*p == '\0')
count = 0;
}
while (*p) {
if (count >= 8)
return 0;
unsigned value = 0;
int digits = 0;
while (digits < 4) {
int h = ParseHex(*p);
if (h < 0)
break;
value = (value << 4) | (unsigned)h;
p++;
digits++;
}
if (digits == 0)
return 0;
words[count++] = (uint16_t)value;
if (*p == '\0')
break;
if (*p != ':')
return 0;
p++;
if (*p == ':') {
if (compress >= 0)
return 0;
compress = count;
p++;
if (*p == '\0')
break;
}
}
if (compress >= 0) {
int zeros = 8 - count;
if (zeros <= 0)
return 0;
for (int i = count - 1; i >= compress; --i)
words[i + zeros] = words[i];
for (int i = 0; i < zeros; ++i)
words[compress + i] = 0;
} else if (count != 8) {
return 0;
}
for (int i = 0; i < 8; ++i) {
out[i * 2 + 0] = (uint8_t)(words[i] >> 8);
out[i * 2 + 1] = (uint8_t)words[i];
}
return 1;
}
static char* AppendHex16(char* out, uint16_t value) {
static const char hex[] = "0123456789abcdef";
int started = 0;
for (int shift = 12; shift >= 0; shift -= 4) {
unsigned nibble = (value >> shift) & 0xfu;
if (nibble || started || shift == 0) {
*out++ = hex[nibble];
started = 1;
}
}
return out;
}
// Implements the inet addr operation.
__declspec(dllexport) in_addr_t inet_addr(const char* text) {
uint8_t b[4];
if (!text || !ParseIpv4Bytes(text, b)) return INADDR_NONE;
in_addr_t out = 0;
uint8_t* p = (uint8_t*)&out;
p[0] = b[0]; p[1] = b[1]; p[2] = b[2]; p[3] = b[3];
return out;
}
// Implements the inet ntoa operation.
__declspec(dllexport) char* inet_ntoa(struct in_addr in) {
static char buf[16];
const uint8_t* p = (const uint8_t*)&in.s_addr;
// Tiny decimal formatter, avoiding stdio dependency.
char* w = buf;
for (int i = 0; i < 4; i++) {
unsigned v = p[i];
if (v >= 100) { *w++ = (char)('0' + v / 100); v %= 100; *w++ = (char)('0' + v / 10); *w++ = (char)('0' + v % 10); }
else if (v >= 10) { *w++ = (char)('0' + v / 10); *w++ = (char)('0' + v % 10); }
else { *w++ = (char)('0' + v); }
if (i != 3) *w++ = '.';
}
*w = '\0';
return buf;
}
// Implements the inet pton operation.
__declspec(dllexport) int inet_pton(int af, const char* src, void* dst) {
if (!src || !dst) { errno = EINVAL; return -1; }
if (af == AF_INET) {
uint8_t b[4];
if (!ParseIpv4Bytes(src, b))
return 0;
memcpy(dst, b, 4);
return 1;
}
if (af == AF_INET6) {
uint8_t b[16];
if (!ParseIpv6Bytes(src, b))
return 0;
memcpy(dst, b, 16);
return 1;
}
errno = EAFNOSUPPORT;
return -1;
}
// Implements the inet ntop operation.
__declspec(dllexport) const char* inet_ntop(int af, const void* src, char* dst, socklen_t size) {
if (!src || !dst) { errno = EINVAL; return (const char*)0; }
if (af == AF_INET) {
if (size < 16) { errno = ERANGE; return (const char*)0; }
struct in_addr in;
memcpy(&in, src, sizeof(in));
strcpy(dst, inet_ntoa(in));
return dst;
}
if (af == AF_INET6) {
if (size < 40) { errno = ERANGE; return (const char*)0; }
const uint8_t* b = (const uint8_t*)src;
char* w = dst;
for (int i = 0; i < 8; ++i) {
uint16_t value = ((uint16_t)b[i * 2] << 8) | b[i * 2 + 1];
w = AppendHex16(w, value);
if (i != 7)
*w++ = ':';
}
*w = '\0';
return dst;
}
errno = EAFNOSUPPORT;
return (const char*)0;
}
// Implements the sock Addr To Sys operation.
static int SockAddrToSys(const struct sockaddr* addr, socklen_t addrlen, SYS_SOCKET_ADDRESS* out) {
if (!addr || !out) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
if (addr->sa_family == AF_INET) {
if (addrlen < sizeof(struct sockaddr_in)) { errno = EINVAL; return -1; }
const struct sockaddr_in* in = (const struct sockaddr_in*)addr;
out->Family = SYS_AF_INET;
out->Port = ntohs(in->sin_port);
memcpy(out->Address, &in->sin_addr.s_addr, 4);
return 0;
}
if (addr->sa_family == AF_INET6) {
if (addrlen < sizeof(struct sockaddr_in6)) { errno = EINVAL; return -1; }
const struct sockaddr_in6* in6 = (const struct sockaddr_in6*)addr;
out->Family = SYS_AF_INET6;
out->Port = ntohs(in6->sin6_port);
memcpy(out->Address, in6->sin6_addr.s6_addr, 16);
out->ScopeId = in6->sin6_scope_id;
return 0;
}
errno = EAFNOSUPPORT;
return -1;
}
// Implements the to Sock Addr operation.
static void SysToSockAddr(const SYS_SOCKET_ADDRESS* in, struct sockaddr* addr, socklen_t* addrlen) {
if (!in || !addr || !addrlen) return;
if (in->Family == SYS_AF_INET) {
if (*addrlen < sizeof(struct sockaddr_in)) {
*addrlen = sizeof(struct sockaddr_in);
return;
}
struct sockaddr_in* out = (struct sockaddr_in*)addr;
memset(out, 0, sizeof(*out));
out->sin_family = AF_INET;
out->sin_port = htons(in->Port);
memcpy(&out->sin_addr.s_addr, in->Address, 4);
*addrlen = sizeof(struct sockaddr_in);
return;
}
if (in->Family == SYS_AF_INET6) {
if (*addrlen < sizeof(struct sockaddr_in6)) {
*addrlen = sizeof(struct sockaddr_in6);
return;
}
struct sockaddr_in6* out = (struct sockaddr_in6*)addr;
memset(out, 0, sizeof(*out));
out->sin6_family = AF_INET6;
out->sin6_port = htons(in->Port);
memcpy(out->sin6_addr.s6_addr, in->Address, 16);
out->sin6_scope_id = in->ScopeId;
*addrlen = sizeof(struct sockaddr_in6);
}
}
// Acquires one socket open-description reference.
static POSIX_OPEN_DESCRIPTION* SocketFdAcquire(int fd) {
POSIX_OPEN_DESCRIPTION* description = PosixFdAcquire(fd);
if (!description)
return (POSIX_OPEN_DESCRIPTION*)0;
if (description->Type != PosixFdSocket) {
PosixFdRelease(description);
errno = ENOTSOCK;
return (POSIX_OPEN_DESCRIPTION*)0;
}
return description;
}
// Creates a socket and installs it as a process-local descriptor.
__declspec(dllexport) int socket(int domain, int type, int protocol) {
uint32_t kh = SYS_HANDLE_INVALID;
uint32_t status = SysCreateSocket((uint32_t)domain, (uint32_t)type, (uint32_t)protocol, &kh);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
int fd = PosixFdCreate(PosixFdSocket, kh, O_RDWR);
if (fd < 0)
SysCloseHandle(kh);
return fd;
}
// Binds one socket descriptor to a local address.
__declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t addrlen) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
SYS_SOCKET_ADDRESS address;
if (SockAddrToSys(addr, addrlen, &address) != 0) {
PosixFdRelease(description);
return -1;
}
uint32_t status = SysBindSocket(description->KHandle, &address, sizeof(address));
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Places one socket into listening state.
__declspec(dllexport) int listen(int fd, int backlog) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
uint32_t status = SysListenSocket(description->KHandle, backlog < 0 ? 0u : (uint32_t)backlog);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Accepts one socket and installs the accepted endpoint as a new descriptor.
__declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrlen) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
uint32_t kh = SYS_HANDLE_INVALID;
uint32_t status = SysAcceptSocket(description->KHandle,
addr ? &remote : (SYS_SOCKET_ADDRESS*)0,
addr ? &remoteSize : (size_t*)0,
&kh);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
int outfd = PosixFdCreate(PosixFdSocket, kh, O_RDWR);
if (outfd < 0) {
SysCloseHandle(kh);
return -1;
}
if (addr) SysToSockAddr(&remote, addr, addrlen);
return outfd;
}
// Connects one socket descriptor to a remote address.
__declspec(dllexport) int connect(int fd, const struct sockaddr* addr, socklen_t addrlen) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
SYS_SOCKET_ADDRESS address;
if (SockAddrToSys(addr, addrlen, &address) != 0) {
PosixFdRelease(description);
return -1;
}
uint32_t status = SysConnectSocket(description->KHandle, &address, sizeof(address));
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Sends bytes through one socket descriptor.
__declspec(dllexport) ssize_t send(int fd, const void* buf, size_t len, int flags) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
size_t sent = 0;
SYS_SOCKET_SEND_REQUEST request = {
sizeof(SYS_SOCKET_SEND_REQUEST), description->KHandle, (uint32_t)flags,
buf, len, &sent
};
uint32_t status = SysSendSocket(&request);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return (ssize_t)sent;
}
// Receives bytes through one socket descriptor.
__declspec(dllexport) ssize_t recv(int fd, void* buf, size_t len, int flags) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
size_t got = 0;
SYS_SOCKET_RECV_REQUEST request = {
sizeof(SYS_SOCKET_RECV_REQUEST), description->KHandle, (uint32_t)flags,
buf, len, &got
};
uint32_t status = SysRecvSocket(&request);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return (ssize_t)got;
}
// Sends one datagram to a remote address.
__declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int flags,
const struct sockaddr* dest, socklen_t destlen) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
SYS_SOCKET_ADDRESS remote;
if (SockAddrToSys(dest, destlen, &remote) != 0) {
PosixFdRelease(description);
return -1;
}
size_t sent = 0;
SYS_SOCKET_SEND_TO_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Buffer = buf;
request.Length = len;
request.Flags = (uint32_t)flags;
request.RemoteAddress = &remote;
request.RemoteAddressSize = sizeof(remote);
request.SentOut = &sent;
uint32_t status = SysSendToSocket(description->KHandle, &request);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return (ssize_t)sent;
}
// Receives one datagram and optionally reports its source address.
__declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags,
struct sockaddr* src, socklen_t* srclen) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
SYS_SOCKET_ADDRESS remote;
size_t remoteSize = sizeof(remote);
size_t got = 0;
SYS_SOCKET_RECV_FROM_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Buffer = buf;
request.Length = len;
request.Flags = (uint32_t)flags;
request.RemoteAddressOut = src ? &remote : (SYS_SOCKET_ADDRESS*)0;
request.RemoteAddressSizeInOut = src ? &remoteSize : (size_t*)0;
request.ReceivedOut = &got;
uint32_t status = SysRecvFromSocket(description->KHandle, &request);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
if (src) SysToSockAddr(&remote, src, srclen);
return (ssize_t)got;
}
// Shuts down one or both directions of a socket.
__declspec(dllexport) int shutdown(int fd, int how) {
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
uint32_t status = SysShutdownSocket(description->KHandle, (uint32_t)how);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Converts one timeval into a bounded millisecond count.
static uint32_t TimevalToMs(const struct timeval* tv) {
if (!tv) return 0;
uint64_t ms = (uint64_t)tv->tv_sec * 1000u + (uint64_t)(tv->tv_usec / 1000);
return ms > 0xFFFFFFFFu ? 0xFFFFFFFFu : (uint32_t)ms;
}
// Sets one supported socket option.
__declspec(dllexport) int setsockopt(int fd, int level, int optname, const void* optval, socklen_t optlen) {
if (level != SOL_SOCKET || !optval) { errno = EINVAL; return -1; }
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
uint32_t option = 0;
uint32_t value = 0;
if (optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) {
option = (optname == SO_RCVTIMEO) ? SYS_SOCKET_OPT_RECV_TIMEOUT_MS : SYS_SOCKET_OPT_SEND_TIMEOUT_MS;
if (optlen == sizeof(struct timeval)) value = TimevalToMs((const struct timeval*)optval);
else if (optlen == sizeof(uint32_t)) memcpy(&value, optval, sizeof(value));
else {
PosixFdRelease(description);
errno = EINVAL;
return -1;
}
} else if (optname == SO_NONBLOCK) {
option = SYS_SOCKET_OPT_NONBLOCKING;
if (optlen < sizeof(int)) {
PosixFdRelease(description);
errno = EINVAL;
return -1;
}
value = (*(const int*)optval) ? 1u : 0u;
} else {
PosixFdRelease(description);
errno = ENOSYS;
return -1;
}
if (optname == SO_NONBLOCK) {
int flags = PosixFdGetStatusFlags(description);
if (value) flags |= O_NONBLOCK;
else flags &= ~O_NONBLOCK;
int result = PosixFdSetStatusFlags(description, flags);
PosixFdRelease(description);
return result;
}
SYS_SOCKET_OPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Option = option;
request.Value = &value;
request.ValueSize = sizeof(value);
uint32_t status = SysSetSocketOption(description->KHandle, &request);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return 0;
}
// Queries one supported socket option.
__declspec(dllexport) int getsockopt(int fd, int level, int optname, void* optval, socklen_t* optlen) {
if (level != SOL_SOCKET || !optval || !optlen) { errno = EINVAL; return -1; }
POSIX_OPEN_DESCRIPTION* description = SocketFdAcquire(fd);
if (!description) return -1;
uint32_t option = 0;
if (optname == SO_RCVTIMEO) option = SYS_SOCKET_OPT_RECV_TIMEOUT_MS;
else if (optname == SO_SNDTIMEO) option = SYS_SOCKET_OPT_SEND_TIMEOUT_MS;
else if (optname == SO_NONBLOCK) option = SYS_SOCKET_OPT_NONBLOCKING;
else {
PosixFdRelease(description);
errno = ENOSYS;
return -1;
}
uint32_t value = 0;
size_t actual = sizeof(value);
SYS_SOCKET_OPTION_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.Option = option;
request.Value = &value;
request.ValueSize = sizeof(value);
request.ActualSizeOut = &actual;
uint32_t status = SysGetSocketOption(description->KHandle, &request);
PosixFdRelease(description);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
if ((optname == SO_RCVTIMEO || optname == SO_SNDTIMEO) && *optlen >= sizeof(struct timeval)) {
struct timeval* tv = (struct timeval*)optval;
tv->tv_sec = (long)(value / 1000u);
tv->tv_usec = (long)((value % 1000u) * 1000u);
*optlen = sizeof(struct timeval);
} else if (*optlen >= sizeof(uint32_t)) {
memcpy(optval, &value, sizeof(value));
*optlen = sizeof(value);
} else {
errno = ERANGE;
return -1;
}
return 0;
}
// Polls socket and pipe descriptors while holding stable open-description references.
__declspec(dllexport) int poll(struct pollfd* fds, nfds_t nfds, int timeout) {
if (!fds || nfds == 0 || nfds > POSIX_FD_MAX) { errno = EINVAL; return -1; }
SYS_POLLFD* kfds = (SYS_POLLFD*)calloc(nfds, sizeof(SYS_POLLFD));
POSIX_OPEN_DESCRIPTION** descriptions =
(POSIX_OPEN_DESCRIPTION**)calloc(nfds, sizeof(POSIX_OPEN_DESCRIPTION*));
if (!kfds || !descriptions) {
free(kfds);
free(descriptions);
errno = ENOMEM;
return -1;
}
for (nfds_t i = 0; i < nfds; i++) {
fds[i].revents = 0;
descriptions[i] = PosixFdAcquire(fds[i].fd);
if (!descriptions[i] ||
(descriptions[i]->Type != PosixFdSocket &&
descriptions[i]->Type != PosixFdPipeRead &&
descriptions[i]->Type != PosixFdPipeWrite)) {
if (descriptions[i]) {
PosixFdRelease(descriptions[i]);
descriptions[i] = (POSIX_OPEN_DESCRIPTION*)0;
}
fds[i].revents = POLLNVAL;
continue;
}
kfds[i].Handle = descriptions[i]->KHandle;
if (fds[i].events & POLLIN) kfds[i].Events |= SYS_POLL_READ;
if (fds[i].events & POLLOUT) kfds[i].Events |= SYS_POLL_WRITE;
if (fds[i].events & POLLERR) kfds[i].Events |= SYS_POLL_ERROR;
if (fds[i].events & POLLHUP) kfds[i].Events |= SYS_POLL_HANGUP;
}
size_t ready = 0;
uint32_t timeoutMs = timeout < 0 ? SYS_POLL_TIMEOUT_INFINITE : (uint32_t)timeout;
SYS_POLL_REQUEST request;
memset(&request, 0, sizeof(request));
request.Size = sizeof(request);
request.TimeoutMs = timeoutMs;
request.Fds = kfds;
request.Count = nfds;
request.ReadyOut = &ready;
KSTATUS status = SysPoll(&request);
int out = 0;
if (status == NTLSTATUS_SUCCESS) {
for (nfds_t i = 0; i < nfds; i++) {
short revents = fds[i].revents;
if (kfds[i].ReturnedEvents & SYS_POLL_READ) revents |= POLLIN;
if (kfds[i].ReturnedEvents & SYS_POLL_WRITE) revents |= POLLOUT;
if (kfds[i].ReturnedEvents & SYS_POLL_ERROR) revents |= POLLERR;
if (kfds[i].ReturnedEvents & SYS_POLL_HANGUP) revents |= POLLHUP;
if (kfds[i].ReturnedEvents & SYS_POLL_INVALID) revents |= POLLNVAL;
fds[i].revents = revents;
if (revents) out++;
}
}
for (nfds_t i = 0; i < nfds; i++)
PosixFdRelease(descriptions[i]);
free(descriptions);
free(kfds);
if (status != NTLSTATUS_SUCCESS) {
errno = PosixStatusToErrno(status);
return -1;
}
return out;
}
// Parses port.
static unsigned ParsePort(const char* s) {
unsigned v = 0;
if (!s) return 0;
while (*s >= '0' && *s <= '9') {
v = v * 10 + (unsigned)(*s - '0');
if (v > 65535u) return 0;
s++;
}
return *s == '\0' ? v : 0;
}
// Implements the resolve Name operation.
static int ResolveNameFamily(const char* name, int family, SYS_SOCKET_ADDRESS* out) {
if (!name || !out) { errno = EINVAL; return -1; }
memset(out, 0, sizeof(*out));
out->Size = sizeof(*out);
if (family == AF_INET) {
uint8_t b[4];
if (ParseIpv4Bytes(name, b)) {
out->Family = SYS_AF_INET;
memcpy(out->Address, b, 4);
return 0;
}
} else if (family == AF_INET6) {
uint8_t b[16];
if (ParseIpv6Bytes(name, b)) {
out->Family = SYS_AF_INET6;
memcpy(out->Address, b, 16);
return 0;
}
} else {
errno = EAFNOSUPPORT;
return -1;
}
size_t size = sizeof(*out);
SYS_RESOLVE_HOST_REQUEST req;
memset(&req, 0, sizeof(req));
req.Size = sizeof(req);
req.Name = SysSvFromCstr(name);
req.Family = family == AF_INET6 ? SYS_AF_INET6 : SYS_AF_INET;
req.AddressOut = out;
req.AddressSizeInOut = &size;
uint32_t s = SysResolveHost(&req);
if (s != 0) { errno = PosixStatusToErrno(s); return -1; }
return 0;
}
static int ResolveNameAny(const char* name, int family, SYS_SOCKET_ADDRESS* out) {
if (family == AF_INET || family == AF_INET6)
return ResolveNameFamily(name, family, out);
if (family != AF_UNSPEC) { errno = EAFNOSUPPORT; return -1; }
if (ResolveNameFamily(name, AF_INET6, out) == 0)
return 0;
return ResolveNameFamily(name, AF_INET, out);
}
// Implements the gethostbyname operation.
__declspec(dllexport) struct hostent* gethostbyname(const char* name) {
static struct hostent h;
static char hname[256];
static char* aliases[1];
static char* addr_list[2];
static uint8_t addr[4];
if (!name || strlen(name) >= sizeof(hname)) { errno = EINVAL; return (struct hostent*)0; }
SYS_SOCKET_ADDRESS a;
if (ResolveNameFamily(name, AF_INET, &a) != 0) return (struct hostent*)0;
strcpy(hname, name);
memcpy(addr, a.Address, 4);
aliases[0] = (char*)0;
addr_list[0] = (char*)addr;
addr_list[1] = (char*)0;
h.h_name = hname;
h.h_aliases = aliases;
h.h_addrtype = AF_INET;
h.h_length = 4;
h.h_addr_list = addr_list;
return &h;
}
__declspec(dllexport) int getaddrinfo(const char* node, const char* service,
const struct addrinfo* hints, struct addrinfo** res) {
if (!res) return EINVAL;
*res = (struct addrinfo*)0;
int family = hints ? hints->ai_family : AF_UNSPEC;
if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) return EAFNOSUPPORT;
SYS_SOCKET_ADDRESS a;
memset(&a, 0, sizeof(a));
a.Size = sizeof(a);
if (!node) {
a.Family = family == AF_INET6 ? SYS_AF_INET6 : SYS_AF_INET;
} else if (ResolveNameAny(node, family, &a) != 0) {
return errno;
}
a.Port = service ? ParsePort(service) : 0;
socklen_t addrLen = a.Family == SYS_AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in);
size_t total = sizeof(struct addrinfo) + addrLen;
struct addrinfo* ai = (struct addrinfo*)malloc(total);
if (!ai) return ENOMEM;
memset(ai, 0, total);
struct sockaddr* sa = (struct sockaddr*)((uint8_t*)ai + sizeof(struct addrinfo));
socklen_t cap = addrLen;
SysToSockAddr(&a, sa, &cap);
ai->ai_family = a.Family == SYS_AF_INET6 ? AF_INET6 : AF_INET;
ai->ai_socktype = hints ? hints->ai_socktype : 0;
ai->ai_protocol = hints ? hints->ai_protocol : 0;
ai->ai_addrlen = cap;
ai->ai_addr = sa;
ai->ai_next = (struct addrinfo*)0;
*res = ai;
return 0;
}
// Implements the freeaddrinfo operation.
__declspec(dllexport) void freeaddrinfo(struct addrinfo* res) {
if (res) free(res);
}