Path: blob/main/core/kernel/src/wasm/posix/netdb.ts
1068 views
/*1Functions from netdb.23These are all very hard to implement with node, without just writing a node extension4module which is what I'll likely have to do.5*/67import { notImplemented } from "./util";8import type { Hostent } from "posix-node";9import constants from "./constants";1011export default function netdb({12memory,13posix,14callFunction,15recv,16send,17free,18}) {19const names = " getprotobyname getservbyname getservbyport getnameinfo";20const netdb: any = {};21for (const name of names.split(/\s+/)) {22netdb[name] = () => notImplemented(name);23}2425function recvHints(hintsPtr) {26const view = new DataView(memory.buffer);27const flags = view.getUint32(hintsPtr, true);28hintsPtr += 4;29let family = wasmToNativeFamily(posix, view.getUint32(hintsPtr, true));30hintsPtr += 4;31const socktype = wasmToNativeSocktype(32posix,33view.getUint32(hintsPtr, true)34);35hintsPtr += 4;36const protocol = view.getUint32(hintsPtr, true);37return {38flags,39family,40socktype,41protocol,42};43}4445// this is null terminated.46function sendArrayOfStrings(v: string[]): number {47const ptr = send.malloc(4 * (v.length + 1));48if (ptr == 0) {49throw Error("out of memory");50}51for (let i = 0; i < v.length; i++) {52const sPtr = send.string(v[i]);53send.pointer(ptr + 4 * i, sPtr);54}55send.pointer(ptr + 4 * v.length, 0);56return ptr;57}5859function sendHostent(hostent: Hostent): number {60// Convert from native posix constant to musl-wasm constant for address type.61const h_addrtype = nativeToWasmFamily(posix, hostent.h_addrtype);62return callFunction(63"sendHostent",64send.string(hostent.h_name),65sendArrayOfStrings(hostent.h_aliases),66h_addrtype,67hostent.h_length,68sendArrayOfStrings(hostent.h_addr_list),69hostent.h_addr_list.length70);71}7273// struct hostent *gethostbyname(const char *name);74// struct hostent75// {76// char *h_name; /* Official domain name of host */77// char **h_aliases; /* Null-terminated array of domain names */78// int h_addrtype; /* Host address type (AF_INET) */79// int h_length; /* Length of an address, in bytes */80// char **h_addr_list; /* Null-terminated array of in_addr structs */81// };82// struct in_addr83// {84// unsigned int s_addr; /* Network byte order (big-endian) */85// };86netdb.gethostbyname = (namePtr: number) => {87try {88if (posix.gethostbyname == null) {89notImplemented("gethostbyaddr", 0);90}91const name = recv.string(namePtr);92const hostent = posix.gethostbyname(name);93return sendHostent(hostent);94} catch (err) {95err.ret = 0;96throw err;97}98};99100// struct hostent *gethostbyaddr(const void *addr,101// socklen_t len, int type);102netdb.gethostbyaddr = (addrPtr: number, _len: number, type: number) => {103try {104if (posix.gethostbyaddr == null) {105notImplemented("gethostbyaddr", 0);106}107const addrStringPtr = callFunction("recvAddr", addrPtr, type);108if (addrStringPtr == 0) {109return 0;110}111const addrString = recv.string(addrStringPtr);112free(addrStringPtr);113const hostent = posix.gethostbyaddr(addrString);114return sendHostent(hostent);115} catch (err) {116err.ret = 0;117throw err;118}119};120121/* int getaddrinfo(const char *restrict node,122const char *restrict service,123const struct addrinfo *restrict hints,124struct addrinfo **restrict res);125126Since we are allocating this explicitly using malloc for the result,127it's critical to know precisely what this really is in 32-bit WebAssembly,128but nowhere else, which we due by grepping zig/lib/libc sources.129130struct addrinfo {131int ai_flags;132int ai_family;133int ai_socktype;134int ai_protocol;135socklen_t ai_addrlen;136struct sockaddr *ai_addr;137char *ai_canonname;138struct addrinfo *ai_next;139}140141typedef unsigned socklen_t;142143struct sockaddr {144_Alignas(max_align_t) sa_family_t sa_family;145char sa_data[0];146};147148typedef unsigned short sa_family_t; // unsigned short is 2 bytes149150That "char sa_data[0]" is scary but OK, since just a pointer; think of it as a char*.151152*/153netdb.getaddrinfo = (154nodePtr: number,155servicePtr: number,156hintsPtr: number,157resPtr: number158): number => {159if (posix.getaddrinfo == null) {160notImplemented("getaddrinfo");161return -1;162}163const node = recv.string(nodePtr);164const service = recv.string(servicePtr);165const hints = recvHints(hintsPtr);166let addrinfoArray;167try {168addrinfoArray = posix.getaddrinfo(node, service, hints);169} catch (err) {170if (err.code) {171// the exception has the error code, which should be returned.172return parseInt(err.code);173} else {174// just let it prop175throw err;176}177}178179let ai_next = 0;180let addrinfo = 0;181let n = addrinfoArray.length - 1;182while (n >= 0) {183const info = addrinfoArray[n];184info.ai_socktype = nativeToWasmSocktype(posix, info.ai_socktype);185info.ai_family = info.sa_family = nativeToWasmFamily(186posix,187info.ai_family188);189const ai_addr = sendSockaddr(190send,191memory,192null,193info.sa_family,194info.ai_addrlen,195info.sa_data196);197if (!ai_addr) {198throw Error("error creating sockaddr");199}200addrinfo = callFunction(201"sendAddrinfo",202info.ai_flags,203info.ai_family,204info.ai_socktype,205info.ai_protocol,206info.ai_addrlen,207ai_addr,208info.ai_canonname != null ? send.string(info.ai_canonname) : 0,209ai_next210);211if (!addrinfo) {212throw Error("error creating addrinfo structure");213}214ai_next = addrinfo;215n -= 1;216}217if (!addrinfo) {218throw Error("error creating addrinfo structure");219}220send.pointer(resPtr, addrinfo);221return 0;222};223224// use a cache to only leak memory once per error code.225const gai_strerror_cache: { [errcode: number]: number } = {};226netdb.gai_strerror = (errcode: number): number => {227if (gai_strerror_cache[errcode] != null) {228return gai_strerror_cache[errcode];229}230const strPtr = send.string(231posix.gai_strerror?.(errcode) ?? "Unknown error"232);233gai_strerror_cache[errcode] = strPtr;234return strPtr;235};236237const hstrerror_cache: { [errcode: number]: number } = {};238netdb.hstrerror = (errcode: number): number => {239if (hstrerror_cache[errcode] != null) {240return hstrerror_cache[errcode];241}242const strPtr = send.string(posix.hstrerror?.(errcode) ?? "Unknown error");243hstrerror_cache[errcode] = strPtr;244return strPtr;245};246247let h_errno_ptr: number | null = null;248netdb.__h_errno_location = (): number => {249/* After reading sources, this __h_errno_location returns the memory250location of an i32 where an error number is stored in memory. See:251int h_errno;252int *__h_errno_location(void) {253if (!__pthread_self()->stack) return &h_errno; }254Elsewhere, h_errno is #defined to calling the above and deref.255So for now we define such an i32 and set it to 0. This is a lot256better than returning 0 (the stub) and causing a segfault!257TODO: in the future, we could somehow mirror the error code from the258native side...?259*/260if (h_errno_ptr == null) {261h_errno_ptr = send.malloc(4); // an i32262send.i32(h_errno_ptr, 0); // set it to 0.263}264if (h_errno_ptr == null) throw Error("bug");265return h_errno_ptr;266};267268return netdb;269}270271export function wasmToNativeFamily(posix, family: number): number {272if (family == 0) return family; // default no flag given273// convert from musl-wasm AF_INET to native AF_INET274// (are totally different, and different for each native platform!).275if (family == constants.AF_INET) {276return posix.constants.AF_INET;277} else if (family == constants.AF_INET6) {278return posix.constants.AF_INET6;279} else {280throw Error(`unsupported WASM address family: ${family}`);281}282}283284export function nativeToWasmFamily(posix, family: number): number {285if (family == 0) return family; // default no flag given286if (family == posix.constants.AF_INET) {287return constants.AF_INET;288} else if (family == posix.constants.AF_INET6) {289return constants.AF_INET6;290} else {291throw Error(`unsupported native address family: ${family}`);292}293}294295// multiple socktypes can be |d together. E.g., Python does this in the socketmodule.c module296// where they do "type | SOCK_CLOEXEC".297export function wasmToNativeSocktype(posix, socktype: number): number {298if (!socktype) return socktype;299let nativeSocktype = 0;300for (const name in constants) {301if (name.startsWith("SOCK") && constants[name] & socktype) {302if (posix.constants[name] == null) {303const err = `We need the constant ${name} to be defined in the posix-node module.`;304console.warn(err);305throw Error(err);306}307nativeSocktype |= posix.constants[name];308socktype &= ~constants[name];309}310}311if (socktype != 0) {312const err = `Unable to convert remainging socktype ${socktype} to native. Make sure all SOCK* constants are defined.`;313console.warn(err);314throw Error(err);315}316return nativeSocktype;317}318319function nativeToWasmSocktype(posix, socktype: number): number {320if (!socktype) return socktype;321let wasmSocktype = 0;322for (const name in posix.constants) {323if (name.startsWith("SOCK") && posix.constants[name] & socktype) {324if (constants[name] == null) {325const err = `We need the constant ${name} to be defined in the posix-node module.`;326console.warn(err);327throw Error(err);328}329wasmSocktype |= constants[name];330socktype &= ~posix.constants[name];331}332}333if (socktype != 0) {334const err = `Unable to convert remainging socktype ${socktype} to native. Make sure all SOCK* posix.constants are defined.`;335console.warn(err);336throw Error(err);337}338return wasmSocktype;339}340341// This can't properly be done using zig, since struct sockaddr342// intensely abuses the C data types...343export function sendSockaddr(344send,345memory,346ptr,347sa_family,348ai_addrlen,349sa_data350): number {351if (ptr == null) {352ptr = send.malloc(2 + ai_addrlen);353}354const view = new DataView(memory.buffer);355view.setUint16(ptr, sa_family, true);356for (let i = 0; i < ai_addrlen; i++) {357view.setUint8(ptr + 2 + i, sa_data[i]);358}359return ptr;360}361362363