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