810 lines
26 KiB
C
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);
|
|
}
|