// POSIX compatibility support for posix socket. #include #include #include #include #include #include "posix.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); } } // Implements the socket Fd Get operation. static FD_ENTRY* SocketFdGet(int fd) { FD_ENTRY* e = FdGet(SysGetCurrentPid(), fd); if (!e) return (FD_ENTRY*)0; if (e->Type != FD_SOCKET) { errno = ENOTSOCK; return (FD_ENTRY*)0; } return e; } // Implements the socket operation. __declspec(dllexport) int socket(int domain, int type, int protocol) { uint32_t kh = 0; uint32_t s = SysCreateSocket((uint32_t)domain, (uint32_t)type, (uint32_t)protocol, &kh); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } int fd = FdAlloc(SysGetCurrentPid(), FD_SOCKET, kh); if (fd < 0) { SysCloseHandle(kh); return -1; } return fd; } // Implements the bind operation. __declspec(dllexport) int bind(int fd, const struct sockaddr* addr, socklen_t addrlen) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; SYS_SOCKET_ADDRESS a; if (SockAddrToSys(addr, addrlen, &a) != 0) return -1; uint32_t s = SysBindSocket(e->KHandle, &a, sizeof(a)); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return 0; } // Implements the listen operation. __declspec(dllexport) int listen(int fd, int backlog) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; uint32_t s = SysListenSocket(e->KHandle, backlog < 0 ? 0u : (uint32_t)backlog); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return 0; } // Implements the accept operation. __declspec(dllexport) int accept(int fd, struct sockaddr* addr, socklen_t* addrlen) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; SYS_SOCKET_ADDRESS remote; size_t remoteSize = sizeof(remote); uint32_t kh = 0; uint32_t s = SysAcceptSocket(e->KHandle, addr ? &remote : (SYS_SOCKET_ADDRESS*)0, addr ? &remoteSize : (size_t*)0, &kh); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } int outfd = FdAlloc(SysGetCurrentPid(), FD_SOCKET, kh); if (outfd < 0) { SysCloseHandle(kh); return -1; } if (addr) SysToSockAddr(&remote, addr, addrlen); return outfd; } // Implements the connect operation. __declspec(dllexport) int connect(int fd, const struct sockaddr* addr, socklen_t addrlen) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; SYS_SOCKET_ADDRESS a; if (SockAddrToSys(addr, addrlen, &a) != 0) return -1; uint32_t s = SysConnectSocket(e->KHandle, &a, sizeof(a)); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return 0; } // Implements the send operation. __declspec(dllexport) ssize_t send(int fd, const void* buf, size_t len, int flags) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; size_t sent = 0; uint32_t s = SysSendSocket(e->KHandle, buf, len, (uint32_t)flags, &sent); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return (ssize_t)sent; } // Implements the recv operation. __declspec(dllexport) ssize_t recv(int fd, void* buf, size_t len, int flags) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; size_t got = 0; uint32_t s = SysRecvSocket(e->KHandle, buf, len, (uint32_t)flags, &got); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return (ssize_t)got; } __declspec(dllexport) ssize_t sendto(int fd, const void* buf, size_t len, int flags, const struct sockaddr* dest, socklen_t destlen) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; SYS_SOCKET_ADDRESS remote; if (SockAddrToSys(dest, destlen, &remote) != 0) return -1; size_t sent = 0; SYS_SOCKET_SEND_TO_REQUEST req; memset(&req, 0, sizeof(req)); req.Buffer = buf; req.Length = len; req.Flags = (uint32_t)flags; req.RemoteAddress = &remote; req.RemoteAddressSize = sizeof(remote); req.SentOut = &sent; uint32_t s = SysSendToSocket(e->KHandle, &req); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return (ssize_t)sent; } __declspec(dllexport) ssize_t recvfrom(int fd, void* buf, size_t len, int flags, struct sockaddr* src, socklen_t* srclen) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; SYS_SOCKET_ADDRESS remote; size_t remoteSize = sizeof(remote); size_t got = 0; SYS_SOCKET_RECV_FROM_REQUEST req; memset(&req, 0, sizeof(req)); req.Buffer = buf; req.Length = len; req.Flags = (uint32_t)flags; req.RemoteAddressOut = src ? &remote : (SYS_SOCKET_ADDRESS*)0; req.RemoteAddressSizeInOut = src ? &remoteSize : (size_t*)0; req.ReceivedOut = &got; uint32_t s = SysRecvFromSocket(e->KHandle, &req); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } if (src) SysToSockAddr(&remote, src, srclen); return (ssize_t)got; } // Implements the shutdown operation. __declspec(dllexport) int shutdown(int fd, int how) { FD_ENTRY* e = SocketFdGet(fd); if (!e) return -1; uint32_t s = SysShutdownSocket(e->KHandle, (uint32_t)how); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return 0; } // Implements the timeval To Ms operation. 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; } // Implements the setsockopt operation. __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; } FD_ENTRY* e = SocketFdGet(fd); if (!e) 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 { errno = EINVAL; return -1; } } else if (optname == SO_NONBLOCK) { option = SYS_SOCKET_OPT_NONBLOCKING; if (optlen < sizeof(int)) { errno = EINVAL; return -1; } value = (*(const int*)optval) ? 1u : 0u; } else { errno = ENOSYS; return -1; } SYS_SOCKET_OPTION_REQUEST req; memset(&req, 0, sizeof(req)); req.Option = option; req.Value = &value; req.ValueSize = sizeof(value); uint32_t s = SysSetSocketOption(e->KHandle, &req); if (s != 0) { errno = PosixStatusToErrno(s); return -1; } return 0; } // Implements the getsockopt operation. __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; } FD_ENTRY* e = SocketFdGet(fd); if (!e) 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 { errno = ENOSYS; return -1; } uint32_t value = 0; size_t actual = sizeof(value); SYS_SOCKET_OPTION_REQUEST req; memset(&req, 0, sizeof(req)); req.Option = option; req.Value = &value; req.ValueSize = sizeof(value); req.ActualSizeOut = &actual; uint32_t s = SysGetSocketOption(e->KHandle, &req); if (s != 0) { errno = PosixStatusToErrno(s); 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 the requested operation. __declspec(dllexport) int poll(struct pollfd* fds, nfds_t nfds, int timeout) { if (!fds || nfds == 0 || nfds > 64) { errno = EINVAL; return -1; } SYS_POLLFD* kfds = (SYS_POLLFD*)malloc(sizeof(SYS_POLLFD) * nfds); if (!kfds) { errno = ENOMEM; return -1; } for (nfds_t i = 0; i < nfds; i++) { kfds[i].Handle = 0; kfds[i].Events = 0; kfds[i].ReturnedEvents = 0; fds[i].revents = 0; FD_ENTRY* e = FdGet(SysGetCurrentPid(), fds[i].fd); if (!e || e->Type != FD_SOCKET) { continue; } kfds[i].Handle = e->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 req; memset(&req, 0, sizeof(req)); req.Size = sizeof(req); req.TimeoutMs = timeoutMs; req.Fds = kfds; req.Count = nfds; req.ReadyOut = &ready; KSTATUS s = SysPoll(&req); if (s != 0) { free(kfds); errno = PosixStatusToErrno(s); return -1; } int out = 0; for (nfds_t i = 0; i < nfds; i++) { short re = 0; if (kfds[i].ReturnedEvents & SYS_POLL_READ) re |= POLLIN; if (kfds[i].ReturnedEvents & SYS_POLL_WRITE) re |= POLLOUT; if (kfds[i].ReturnedEvents & SYS_POLL_ERROR) re |= POLLERR; if (kfds[i].ReturnedEvents & SYS_POLL_HANGUP) re |= POLLHUP; if (kfds[i].ReturnedEvents & SYS_POLL_INVALID) re |= POLLNVAL; fds[i].revents = re; if (re) out++; } free(kfds); 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.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); }