@volter/browser-wali 0.1.166 → 0.1.167
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +8 -0
- package/README.md +9 -0
- package/dist/browser-wali-command.d.ts +7 -0
- package/dist/browser-wali-command.d.ts.map +1 -1
- package/dist/browser-wali-command.js +70 -18
- package/dist/browser-wali-command.js.map +1 -1
- package/dist/bulk-memory.d.ts +4 -2
- package/dist/bulk-memory.d.ts.map +1 -1
- package/dist/bulk-memory.js +32 -9
- package/dist/bulk-memory.js.map +1 -1
- package/dist/capabilities.d.ts +1 -1
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +5 -2
- package/dist/capabilities.js.map +1 -1
- package/dist/child-processes.d.ts +2 -0
- package/dist/child-processes.d.ts.map +1 -1
- package/dist/child-processes.js +9 -1
- package/dist/child-processes.js.map +1 -1
- package/dist/clocks.d.ts +3 -0
- package/dist/clocks.d.ts.map +1 -0
- package/dist/clocks.js +44 -0
- package/dist/clocks.js.map +1 -0
- package/dist/coherent-file-locks.d.ts.map +1 -1
- package/dist/coherent-file-locks.js +2 -1
- package/dist/coherent-file-locks.js.map +1 -1
- package/dist/credentials.d.ts +4 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/credentials.js +46 -0
- package/dist/credentials.js.map +1 -0
- package/dist/descriptors.d.ts +14 -0
- package/dist/descriptors.d.ts.map +1 -1
- package/dist/descriptors.js +45 -5
- package/dist/descriptors.js.map +1 -1
- package/dist/environment.d.ts +4 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/environment.js +23 -0
- package/dist/environment.js.map +1 -0
- package/dist/epoll-ready.d.ts +27 -0
- package/dist/epoll-ready.d.ts.map +1 -0
- package/dist/epoll-ready.js +88 -0
- package/dist/epoll-ready.js.map +1 -0
- package/dist/events.d.ts +1 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +55 -24
- package/dist/events.js.map +1 -1
- package/dist/filesystem-host-session.d.ts +65 -0
- package/dist/filesystem-host-session.d.ts.map +1 -0
- package/dist/filesystem-host-session.js +951 -0
- package/dist/filesystem-host-session.js.map +1 -0
- package/dist/filesystem-host.d.ts +13 -81
- package/dist/filesystem-host.d.ts.map +1 -1
- package/dist/filesystem-host.js +14 -882
- package/dist/filesystem-host.js.map +1 -1
- package/dist/filesystem-protocol.d.ts +3 -1
- package/dist/filesystem-protocol.d.ts.map +1 -1
- package/dist/filesystem-remote.d.ts +8 -3
- package/dist/filesystem-remote.d.ts.map +1 -1
- package/dist/filesystem-remote.js +42 -20
- package/dist/filesystem-remote.js.map +1 -1
- package/dist/filesystem-service-client.d.ts +4 -0
- package/dist/filesystem-service-client.d.ts.map +1 -0
- package/dist/filesystem-service-client.js +16 -0
- package/dist/filesystem-service-client.js.map +1 -0
- package/dist/filesystem.d.ts +25 -4
- package/dist/filesystem.d.ts.map +1 -1
- package/dist/filesystem.js +548 -169
- package/dist/filesystem.js.map +1 -1
- package/dist/fork-memory.d.ts +23 -0
- package/dist/fork-memory.d.ts.map +1 -0
- package/dist/fork-memory.js +93 -0
- package/dist/fork-memory.js.map +1 -0
- package/dist/fork-worker.js +32 -11
- package/dist/fork-worker.js.map +1 -1
- package/dist/jumps.d.ts +7 -0
- package/dist/jumps.d.ts.map +1 -1
- package/dist/jumps.js +19 -1
- package/dist/jumps.js.map +1 -1
- package/dist/kernel-filesystem-worker.d.ts +2 -0
- package/dist/kernel-filesystem-worker.d.ts.map +1 -0
- package/dist/kernel-filesystem-worker.js +28 -0
- package/dist/kernel-filesystem-worker.js.map +1 -0
- package/dist/kernel-filesystem.d.ts +12 -0
- package/dist/kernel-filesystem.d.ts.map +1 -0
- package/dist/kernel-filesystem.js +45 -0
- package/dist/kernel-filesystem.js.map +1 -0
- package/dist/kernel-network-worker.d.ts +2 -0
- package/dist/kernel-network-worker.d.ts.map +1 -0
- package/dist/kernel-network-worker.js +14 -0
- package/dist/kernel-network-worker.js.map +1 -0
- package/dist/kernel-network.d.ts +14 -0
- package/dist/kernel-network.d.ts.map +1 -0
- package/dist/kernel-network.js +50 -0
- package/dist/kernel-network.js.map +1 -0
- package/dist/kernel-process-client.d.ts +6 -1
- package/dist/kernel-process-client.d.ts.map +1 -1
- package/dist/kernel-process-client.js +10 -1
- package/dist/kernel-process-client.js.map +1 -1
- package/dist/module-layout.d.ts +8 -0
- package/dist/module-layout.d.ts.map +1 -1
- package/dist/module-layout.js +263 -2
- package/dist/module-layout.js.map +1 -1
- package/dist/network-host.d.ts +6 -0
- package/dist/network-host.d.ts.map +1 -0
- package/dist/network-host.js +1582 -0
- package/dist/network-host.js.map +1 -0
- package/dist/network-protocol.d.ts +9 -2
- package/dist/network-protocol.d.ts.map +1 -1
- package/dist/network-protocol.js +3 -0
- package/dist/network-protocol.js.map +1 -1
- package/dist/network-service-client.d.ts +4 -0
- package/dist/network-service-client.d.ts.map +1 -0
- package/dist/network-service-client.js +16 -0
- package/dist/network-service-client.js.map +1 -0
- package/dist/network-worker.js +5 -1150
- package/dist/network-worker.js.map +1 -1
- package/dist/network.d.ts +13 -1
- package/dist/network.d.ts.map +1 -1
- package/dist/network.js +175 -48
- package/dist/network.js.map +1 -1
- package/dist/paths.d.ts +11 -0
- package/dist/paths.d.ts.map +1 -0
- package/dist/paths.js +97 -0
- package/dist/paths.js.map +1 -0
- package/dist/process-bridge.d.ts +12 -4
- package/dist/process-bridge.d.ts.map +1 -1
- package/dist/process-bridge.js +53 -17
- package/dist/process-bridge.js.map +1 -1
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +8 -0
- package/dist/program.js.map +1 -1
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +150 -92
- package/dist/runtime.js.map +1 -1
- package/dist/shared-buffer-pool.d.ts +18 -5
- package/dist/shared-buffer-pool.d.ts.map +1 -1
- package/dist/shared-buffer-pool.js +86 -14
- package/dist/shared-buffer-pool.js.map +1 -1
- package/dist/shared-file-layer.d.ts +3 -1
- package/dist/shared-file-layer.d.ts.map +1 -1
- package/dist/shared-file-layer.js +150 -35
- package/dist/shared-file-layer.js.map +1 -1
- package/dist/shared-pipes.d.ts +3 -1
- package/dist/shared-pipes.d.ts.map +1 -1
- package/dist/shared-pipes.js +105 -33
- package/dist/shared-pipes.js.map +1 -1
- package/dist/signal-interruption.d.ts +9 -0
- package/dist/signal-interruption.d.ts.map +1 -0
- package/dist/signal-interruption.js +6 -0
- package/dist/signal-interruption.js.map +1 -0
- package/dist/signals.d.ts +17 -6
- package/dist/signals.d.ts.map +1 -1
- package/dist/signals.js +101 -22
- package/dist/signals.js.map +1 -1
- package/dist/snapshot-filesystem.d.ts +1 -0
- package/dist/snapshot-filesystem.d.ts.map +1 -1
- package/dist/snapshot-filesystem.js +3 -0
- package/dist/snapshot-filesystem.js.map +1 -1
- package/dist/socket-flags.d.ts +3 -0
- package/dist/socket-flags.d.ts.map +1 -0
- package/dist/socket-flags.js +9 -0
- package/dist/socket-flags.js.map +1 -0
- package/dist/special-files.d.ts +6 -0
- package/dist/special-files.d.ts.map +1 -0
- package/dist/special-files.js +56 -0
- package/dist/special-files.js.map +1 -0
- package/dist/stdin.d.ts.map +1 -1
- package/dist/stdin.js +13 -5
- package/dist/stdin.js.map +1 -1
- package/dist/symlink.d.ts +2 -1
- package/dist/symlink.d.ts.map +1 -1
- package/dist/symlink.js +25 -93
- package/dist/symlink.js.map +1 -1
- package/dist/system.d.ts +2 -4
- package/dist/system.d.ts.map +1 -1
- package/dist/system.js +46 -29
- package/dist/system.js.map +1 -1
- package/dist/thread-coordinator-worker.js +58 -24
- package/dist/thread-coordinator-worker.js.map +1 -1
- package/dist/thread-lifecycle.d.ts +9 -0
- package/dist/thread-lifecycle.d.ts.map +1 -0
- package/dist/thread-lifecycle.js +30 -0
- package/dist/thread-lifecycle.js.map +1 -0
- package/dist/thread-worker.js +125 -44
- package/dist/thread-worker.js.map +1 -1
- package/dist/threads.d.ts +6 -0
- package/dist/threads.d.ts.map +1 -1
- package/dist/threads.js +46 -6
- package/dist/threads.js.map +1 -1
- package/dist/tty-job-control.d.ts +13 -0
- package/dist/tty-job-control.d.ts.map +1 -0
- package/dist/tty-job-control.js +56 -0
- package/dist/tty-job-control.js.map +1 -0
- package/dist/tty-settings.d.ts +3 -0
- package/dist/tty-settings.d.ts.map +1 -0
- package/dist/tty-settings.js +25 -0
- package/dist/tty-settings.js.map +1 -0
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/wait-state.d.ts +8 -0
- package/dist/wait-state.d.ts.map +1 -1
- package/dist/wait-state.js +15 -0
- package/dist/wait-state.js.map +1 -1
- package/dist/worker-program-protocol.d.ts +22 -2
- package/dist/worker-program-protocol.d.ts.map +1 -1
- package/dist/worker-program-worker.js +31 -9
- package/dist/worker-program-worker.js.map +1 -1
- package/dist/worker-program.d.ts +12 -2
- package/dist/worker-program.d.ts.map +1 -1
- package/dist/worker-program.js +214 -36
- package/dist/worker-program.js.map +1 -1
- package/dist/write-vector.d.ts +3 -0
- package/dist/write-vector.d.ts.map +1 -0
- package/dist/write-vector.js +35 -0
- package/dist/write-vector.js.map +1 -0
- package/package.json +1 -1
package/dist/network-worker.js
CHANGED
|
@@ -1,1151 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { monotonicMilliseconds } from "./monotonic-clock.js";
|
|
7
|
-
import { inPrivateTreeWindow, PRIVATE_TREE_WINDOW_BASE, PRIVATE_TREE_WINDOW_SIZE } from "./private-tree.js";
|
|
8
|
-
const scope = globalThis;
|
|
9
|
-
const EPOLL_EVENT_BYTES = 16;
|
|
10
|
-
const EPOLL_DATA_OFFSET = 8;
|
|
11
|
-
const sockets = new Map();
|
|
12
|
-
const epolls = new Map();
|
|
13
|
-
const allowedHosts = new Set();
|
|
14
|
-
const hostByAddress = new Map();
|
|
15
|
-
const addressIdByHost = new Map();
|
|
16
|
-
let relayUrl = "/api/wali/tcp";
|
|
17
|
-
let maxConnections = 16;
|
|
18
|
-
let maxBytes = 64 * 1024 * 1024;
|
|
19
|
-
let networkBytes = 0;
|
|
20
|
-
const externalConnections = new Set();
|
|
21
|
-
let nextFd = NETWORK_FD_MIN;
|
|
22
|
-
/** The next descriptor of this worker's, never one of a private tree's window (ADR-0047). */
|
|
23
|
-
function takeFd() {
|
|
24
|
-
if (inPrivateTreeWindow(nextFd))
|
|
25
|
-
nextFd = PRIVATE_TREE_WINDOW_BASE + PRIVATE_TREE_WINDOW_SIZE;
|
|
26
|
-
return nextFd++;
|
|
27
|
-
}
|
|
28
|
-
const descriptorWaiters = new Set();
|
|
29
|
-
let signals;
|
|
30
|
-
let activity;
|
|
31
|
-
function notifyDescriptors() { for (const resolve of [...descriptorWaiters])
|
|
32
|
-
resolve(); }
|
|
33
|
-
const children = new WaliChildProcesses((minimum = NETWORK_FD_MIN) => {
|
|
34
|
-
nextFd = Math.max(nextFd, minimum);
|
|
35
|
-
return takeFd();
|
|
36
|
-
}, (pid, status, code) => { signals?.raise(17, pid, status, code); });
|
|
37
|
-
children.onChange(notifyDescriptors);
|
|
38
|
-
let sharedPipes;
|
|
39
|
-
let stdin;
|
|
40
|
-
let stdioAliasRegistry;
|
|
41
|
-
let loopbackPort;
|
|
42
|
-
let nextLoopbackRequestId = 1;
|
|
43
|
-
let nextOutboundConnectionId = -1;
|
|
44
|
-
const loopbackAcks = new Map();
|
|
45
|
-
const listenersByPort = new Map();
|
|
46
|
-
const loopbackConnections = new Map();
|
|
47
|
-
scope.onmessage = (event) => {
|
|
48
|
-
if (event.data.kind === "initialize") {
|
|
49
|
-
activity = event.data.activity ? new Int32Array(event.data.activity) : undefined;
|
|
50
|
-
allowedHosts.clear();
|
|
51
|
-
for (const host of event.data.allowedHosts)
|
|
52
|
-
allowedHosts.add(host);
|
|
53
|
-
relayUrl = event.data.relayUrl;
|
|
54
|
-
maxConnections = event.data.maxConnections;
|
|
55
|
-
maxBytes = event.data.maxBytes;
|
|
56
|
-
networkBytes = 0;
|
|
57
|
-
externalConnections.clear();
|
|
58
|
-
sharedPipes = event.data.sharedPipes;
|
|
59
|
-
stdin = event.data.stdin ? new SharedWaliStdinReader(event.data.stdin) : undefined;
|
|
60
|
-
stdioAliasRegistry = event.data.stdioAliasRegistry
|
|
61
|
-
? new Int32Array(event.data.stdioAliasRegistry)
|
|
62
|
-
: undefined;
|
|
63
|
-
if (event.data.signals) {
|
|
64
|
-
signals = new WaliSignals(event.data.signals);
|
|
65
|
-
const shared = signals.words;
|
|
66
|
-
const waitAsync = Atomics.waitAsync;
|
|
67
|
-
if (!waitAsync) {
|
|
68
|
-
scope.postMessage({ kind: "error", message: "WALI signal delivery requires Atomics.waitAsync." });
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
void (async () => {
|
|
72
|
-
for (;;) {
|
|
73
|
-
const generation = Atomics.load(shared, 1);
|
|
74
|
-
// wakes blocked polls and child waits, and so notifies descriptors
|
|
75
|
-
children.wake();
|
|
76
|
-
await waitAsync(shared, 1, generation).value;
|
|
77
|
-
}
|
|
78
|
-
})();
|
|
79
|
-
}
|
|
80
|
-
loopbackPort = event.data.loopbackPort;
|
|
81
|
-
if (loopbackPort) {
|
|
82
|
-
loopbackPort.onmessage = (message) => {
|
|
83
|
-
handleLoopbackMessage(message.data);
|
|
84
|
-
};
|
|
85
|
-
loopbackPort.start();
|
|
86
|
-
}
|
|
87
|
-
scope.postMessage({ kind: "ready" });
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
if (event.data.kind === "attach")
|
|
91
|
-
attach(event.data);
|
|
92
|
-
};
|
|
93
|
-
function attach(channel) {
|
|
94
|
-
const control = new Int32Array(channel.control);
|
|
95
|
-
const payload = new Uint8Array(channel.payload);
|
|
96
|
-
const waitState = channel.waitState && new Int32Array(channel.waitState);
|
|
97
|
-
channel.port.onmessage = (event) => {
|
|
98
|
-
void dispatch(control, payload, typeof event.data === "number" ? event.data : event.data.requestId, typeof event.data === "number" ? undefined : event.data.process, waitState);
|
|
99
|
-
};
|
|
100
|
-
channel.port.start();
|
|
101
|
-
}
|
|
102
|
-
async function dispatch(control, payload, requestId, process, waitState) {
|
|
103
|
-
if (Atomics.load(control, 0) !== requestId)
|
|
104
|
-
return;
|
|
105
|
-
// Reaping is irreversible. Claim a wait before it starts so the transport
|
|
106
|
-
// watchdog cannot retire it and lose the child's status or filesystem patch.
|
|
107
|
-
const claimed = [NetworkOperation.ChildWait, NetworkOperation.PollMany, NetworkOperation.EpollWait].includes(Atomics.load(control, 1));
|
|
108
|
-
if (claimed && Atomics.compareExchange(control, 0, requestId, -requestId) !== requestId)
|
|
109
|
-
return;
|
|
110
|
-
let result = -38;
|
|
111
|
-
let output = new Uint8Array();
|
|
112
|
-
let checkedActivity = activity ? Atomics.load(activity, 0) : 0;
|
|
113
|
-
const parked = (state) => {
|
|
114
|
-
if (!waitState)
|
|
115
|
-
return;
|
|
116
|
-
if (state === 0)
|
|
117
|
-
checkedActivity = activity ? Atomics.load(activity, 0) : 0;
|
|
118
|
-
Atomics.store(waitState, 1, checkedActivity);
|
|
119
|
-
Atomics.store(waitState, 0, state);
|
|
120
|
-
Atomics.notify(control, 0);
|
|
121
|
-
};
|
|
122
|
-
parked(0);
|
|
123
|
-
try {
|
|
124
|
-
const operation = Atomics.load(control, 1);
|
|
125
|
-
const inputLength = Atomics.load(control, 3);
|
|
126
|
-
const args = Array.from({ length: 10 }, (_, index) => Atomics.load(control, 5 + index));
|
|
127
|
-
const input = payload.slice(0, inputLength);
|
|
128
|
-
({ result, output } = await operate(operation, args, input, process, parked));
|
|
129
|
-
if ([NetworkOperation.EpollControl, NetworkOperation.Send, NetworkOperation.Receive,
|
|
130
|
-
NetworkOperation.Close, NetworkOperation.TimerFdSetTime].includes(operation))
|
|
131
|
-
notifyDescriptors();
|
|
132
|
-
}
|
|
133
|
-
catch {
|
|
134
|
-
result = -5;
|
|
135
|
-
}
|
|
136
|
-
parked(0);
|
|
137
|
-
if (output.byteLength > payload.byteLength) {
|
|
138
|
-
result = -90;
|
|
139
|
-
output = new Uint8Array();
|
|
140
|
-
}
|
|
141
|
-
// Claim this generation before touching the shared response buffers. A
|
|
142
|
-
// watchdog may have cancelled it and started a later request while operate()
|
|
143
|
-
// was pending; stale completions must never corrupt that request.
|
|
144
|
-
if (!claimed && Atomics.compareExchange(control, 0, requestId, -requestId) !== requestId)
|
|
145
|
-
return;
|
|
146
|
-
if (output.byteLength > 0)
|
|
147
|
-
payload.set(output);
|
|
148
|
-
Atomics.store(control, 2, result);
|
|
149
|
-
Atomics.store(control, 4, output.byteLength);
|
|
150
|
-
Atomics.store(control, 0, requestId + 1);
|
|
151
|
-
Atomics.notify(control, 0);
|
|
152
|
-
}
|
|
153
|
-
async function operate(operation, args, input, process, parked = () => { }) {
|
|
154
|
-
if (operation === NetworkOperation.ChildRegister)
|
|
155
|
-
return scalar(process ? children.register(args[0], process) : -22);
|
|
156
|
-
if (operation === NetworkOperation.ChildForget)
|
|
157
|
-
return scalar(children.forget(args[0]));
|
|
158
|
-
if (operation === NetworkOperation.ChildExists)
|
|
159
|
-
return scalar(children.has(args[0]) ? 1 : 0);
|
|
160
|
-
if (operation === NetworkOperation.ChildWait) {
|
|
161
|
-
const mask = signalMask(args[2] ?? 0, args[3] ?? 0);
|
|
162
|
-
return scalar(process ? await children.wait(args[0], args[1], process, () => !!signals && (signals.pending() & ~mask) !== 0n, value => parked(value ? 1 : 0)) : -22);
|
|
163
|
-
}
|
|
164
|
-
if (operation === NetworkOperation.PidFdOpen)
|
|
165
|
-
return scalar(children.open(args[0], args[1]));
|
|
166
|
-
if (operation === NetworkOperation.PollMany)
|
|
167
|
-
return pollMany(input, args[0], signalMask(args[1], args[2]), parked);
|
|
168
|
-
if (operation === NetworkOperation.Socket)
|
|
169
|
-
return scalar(createSocket(args[0], args[1], args[2]));
|
|
170
|
-
if (operation === NetworkOperation.EpollCreate)
|
|
171
|
-
return scalar(createEpoll());
|
|
172
|
-
if (operation === NetworkOperation.EventFd)
|
|
173
|
-
return scalar(createEventFd(args[0], args[1]));
|
|
174
|
-
if (operation === NetworkOperation.TimerFdCreate)
|
|
175
|
-
return scalar(createTimerFd(args[0], args[1]));
|
|
176
|
-
if (operation === NetworkOperation.SocketPair)
|
|
177
|
-
return createSocketPair(args[0], args[1], args[2]);
|
|
178
|
-
if (operation === NetworkOperation.EpollControl)
|
|
179
|
-
return scalar(epollControl(args[0], args[1], args[2], input));
|
|
180
|
-
if (operation === NetworkOperation.EpollWait)
|
|
181
|
-
return epollWait(args[0], args[1], args[2], signalMask(args[3], args[4]), parked);
|
|
182
|
-
const fd = args[0];
|
|
183
|
-
if (children.owns(fd)) {
|
|
184
|
-
if (operation === NetworkOperation.Close)
|
|
185
|
-
return scalar(children.close(fd));
|
|
186
|
-
if (operation === NetworkOperation.Poll)
|
|
187
|
-
return scalar(children.poll(fd, args[1]));
|
|
188
|
-
if (operation === NetworkOperation.Fcntl)
|
|
189
|
-
return scalar(children.fcntl(fd, args[1], args[2]));
|
|
190
|
-
if (operation === NetworkOperation.Duplicate)
|
|
191
|
-
return scalar(children.fcntl(fd, 0, 0));
|
|
192
|
-
return scalar(-22);
|
|
193
|
-
}
|
|
194
|
-
if (operation === NetworkOperation.Close && epolls.has(fd)) {
|
|
195
|
-
epolls.delete(fd);
|
|
196
|
-
return scalar(0);
|
|
197
|
-
}
|
|
198
|
-
if (operation === NetworkOperation.Fcntl && epolls.has(fd)) {
|
|
199
|
-
if (args[1] === 0 || args[1] === 1030)
|
|
200
|
-
return scalar(duplicateEpoll(fd, args[2]));
|
|
201
|
-
return scalar(args[1] === 3 || args[1] === 1 || args[1] === 2 || args[1] === 4 ? 0 : -22);
|
|
202
|
-
}
|
|
203
|
-
const socket = sockets.get(fd);
|
|
204
|
-
if (!socket)
|
|
205
|
-
return scalar(-9);
|
|
206
|
-
if (operation === NetworkOperation.Connect)
|
|
207
|
-
return scalar(await connectSocket(socket, input));
|
|
208
|
-
if (operation === NetworkOperation.Bind)
|
|
209
|
-
return scalar(bindSocket(socket, input));
|
|
210
|
-
if (operation === NetworkOperation.Listen)
|
|
211
|
-
return scalar(await listenSocket(socket, args[1]));
|
|
212
|
-
if (operation === NetworkOperation.Accept)
|
|
213
|
-
return acceptSocket(socket, args[1]);
|
|
214
|
-
if (operation === NetworkOperation.Send)
|
|
215
|
-
return scalar(sendSocket(socket, input));
|
|
216
|
-
if (operation === NetworkOperation.Receive) {
|
|
217
|
-
const response = receiveSocket(socket, Math.max(0, args[1]));
|
|
218
|
-
if (response.result > 0 && (pollSocket(socket, 1) & 1) === 0)
|
|
219
|
-
rearmEpollReadable(fd);
|
|
220
|
-
return response;
|
|
221
|
-
}
|
|
222
|
-
if (operation === NetworkOperation.Poll) {
|
|
223
|
-
const result = pollSocket(socket, args[1]);
|
|
224
|
-
return scalar(result);
|
|
225
|
-
}
|
|
226
|
-
if (operation === NetworkOperation.TimerFdSetTime)
|
|
227
|
-
return setTimerFd(socket, args[1], input);
|
|
228
|
-
if (operation === NetworkOperation.Fcntl)
|
|
229
|
-
return scalar(fcntlSocket(fd, socket, args[1], args[2]));
|
|
230
|
-
if (operation === NetworkOperation.SocketName)
|
|
231
|
-
return { result: 0, output: socketName(socket) };
|
|
232
|
-
if (operation === NetworkOperation.Duplicate)
|
|
233
|
-
return scalar(duplicateSocket(socket));
|
|
234
|
-
if (operation === NetworkOperation.Shutdown) {
|
|
235
|
-
closeSocket(socket);
|
|
236
|
-
return scalar(0);
|
|
237
|
-
}
|
|
238
|
-
if (operation === NetworkOperation.Close) {
|
|
239
|
-
sockets.delete(fd);
|
|
240
|
-
if (![...sockets.values()].includes(socket))
|
|
241
|
-
closeSocket(socket);
|
|
242
|
-
return scalar(0);
|
|
243
|
-
}
|
|
244
|
-
return scalar(-38);
|
|
245
|
-
}
|
|
246
|
-
function createEpoll() {
|
|
247
|
-
const fd = takeFd();
|
|
248
|
-
epolls.set(fd, new Map());
|
|
249
|
-
return fd;
|
|
250
|
-
}
|
|
251
|
-
function signalMask(low, high) { return BigInt(low >>> 0) | (BigInt(high >>> 0) << 32n); }
|
|
252
|
-
async function pollMany(input, timeout, mask = 0n, parked = () => { }) {
|
|
253
|
-
if (input.byteLength % 8 !== 0)
|
|
254
|
-
return scalar(-22);
|
|
255
|
-
const output = input.slice();
|
|
256
|
-
const view = new DataView(output.buffer);
|
|
257
|
-
const original = new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
258
|
-
const deadline = timeout < 0 ? Infinity : Date.now() + timeout;
|
|
259
|
-
for (;;) {
|
|
260
|
-
if (signals && (signals.pending() & ~mask) !== 0n)
|
|
261
|
-
return scalar(-4);
|
|
262
|
-
let count = 0;
|
|
263
|
-
let processOnly = true;
|
|
264
|
-
for (let offset = 0; offset < input.byteLength; offset += 8) {
|
|
265
|
-
const fd = view.getInt32(offset, true);
|
|
266
|
-
const events = view.getInt16(offset + 4, true);
|
|
267
|
-
const socket = sockets.get(fd);
|
|
268
|
-
const ready = fd < 0 ? 0 : children.poll(fd, events)
|
|
269
|
-
?? (sharedPipes ? pollWaliSharedPipeState(sharedPipes, fd, events) : undefined)
|
|
270
|
-
?? (isStdinDescriptor(fd) && stdin ? ((events & 1) && stdin.poll() ? 1 : 0) : undefined)
|
|
271
|
-
?? (socket ? pollSocket(socket, events) : undefined)
|
|
272
|
-
?? (fd >= NETWORK_FD_MIN && !inPrivateTreeWindow(fd) ? 0x20 : original.getInt16(offset + 6, true));
|
|
273
|
-
if (fd >= 0 && !children.owns(fd))
|
|
274
|
-
processOnly = false;
|
|
275
|
-
view.setInt16(offset + 6, ready, true);
|
|
276
|
-
if (ready !== 0)
|
|
277
|
-
count++;
|
|
278
|
-
}
|
|
279
|
-
if (count || timeout === 0 || Date.now() >= deadline)
|
|
280
|
-
return { result: count, output };
|
|
281
|
-
const fds = Array.from({ length: input.byteLength / 8 }, (_, index) => view.getInt32(index * 8, true));
|
|
282
|
-
parked(fds.some(outsideWake) ? 2 : 1);
|
|
283
|
-
try {
|
|
284
|
-
await waitForDescriptorChange(processOnly ? deadline - Date.now() : 2);
|
|
285
|
-
}
|
|
286
|
-
finally {
|
|
287
|
-
parked(0);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
function waitForDescriptorChange(delay) {
|
|
292
|
-
return new Promise((resolve) => {
|
|
293
|
-
const done = () => { if (timer !== undefined)
|
|
294
|
-
clearTimeout(timer); descriptorWaiters.delete(done); resolve(); };
|
|
295
|
-
descriptorWaiters.add(done);
|
|
296
|
-
const timer = Number.isFinite(delay) ? setTimeout(done, Math.max(0, delay)) : undefined;
|
|
297
|
-
});
|
|
298
|
-
}
|
|
299
|
-
function duplicateEpoll(fd, minimum) {
|
|
300
|
-
const registrations = epolls.get(fd);
|
|
301
|
-
if (!registrations)
|
|
302
|
-
return -9;
|
|
303
|
-
nextFd = Math.max(nextFd, minimum, NETWORK_FD_MIN);
|
|
304
|
-
const duplicate = takeFd();
|
|
305
|
-
epolls.set(duplicate, registrations);
|
|
306
|
-
return duplicate;
|
|
307
|
-
}
|
|
308
|
-
function createEventFd(initial, flags) {
|
|
309
|
-
if (initial < 0)
|
|
310
|
-
return -22;
|
|
311
|
-
const fd = takeFd();
|
|
312
|
-
sockets.set(fd, {
|
|
313
|
-
kind: "event",
|
|
314
|
-
counter: BigInt(initial >>> 0),
|
|
315
|
-
generation: 0,
|
|
316
|
-
semaphore: Boolean(flags & 1),
|
|
317
|
-
nonblocking: Boolean(flags & 0x800),
|
|
318
|
-
});
|
|
319
|
-
return fd;
|
|
320
|
-
}
|
|
321
|
-
function createTimerFd(clockId, flags) {
|
|
322
|
-
if (![0, 1, 7].includes(clockId) || (flags & ~0x8_0800) !== 0)
|
|
323
|
-
return -22;
|
|
324
|
-
const fd = takeFd();
|
|
325
|
-
sockets.set(fd, {
|
|
326
|
-
kind: "timer",
|
|
327
|
-
clockId,
|
|
328
|
-
intervalNanoseconds: 0n,
|
|
329
|
-
deadlineNanoseconds: undefined,
|
|
330
|
-
expirations: 0n,
|
|
331
|
-
nonblocking: Boolean(flags & 0x800),
|
|
332
|
-
});
|
|
333
|
-
return fd;
|
|
334
|
-
}
|
|
335
|
-
function setTimerFd(socket, flags, input) {
|
|
336
|
-
if (socket.kind !== "timer")
|
|
337
|
-
return scalar(-9);
|
|
338
|
-
if ((flags & ~1) !== 0 || input.byteLength !== 32)
|
|
339
|
-
return scalar(-22);
|
|
340
|
-
const view = new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
341
|
-
const interval = readTimespec(view, 0);
|
|
342
|
-
const value = readTimespec(view, 16);
|
|
343
|
-
if (interval === undefined || value === undefined)
|
|
344
|
-
return scalar(-22);
|
|
345
|
-
updateTimer(socket);
|
|
346
|
-
const output = new Uint8Array(32);
|
|
347
|
-
const outputView = new DataView(output.buffer);
|
|
348
|
-
writeTimespec(outputView, 0, socket.intervalNanoseconds);
|
|
349
|
-
writeTimespec(outputView, 16, timerRemaining(socket));
|
|
350
|
-
socket.intervalNanoseconds = interval;
|
|
351
|
-
socket.deadlineNanoseconds = value === 0n ? undefined
|
|
352
|
-
: (flags & 1) ? value : nowNanoseconds(socket.clockId) + value;
|
|
353
|
-
socket.expirations = 0n;
|
|
354
|
-
return { result: 0, output };
|
|
355
|
-
}
|
|
356
|
-
function createSocketPair(domain, type, protocol) {
|
|
357
|
-
if (domain !== 1)
|
|
358
|
-
return scalar(-97);
|
|
359
|
-
if ((type & 0xf) !== 1 || protocol !== 0)
|
|
360
|
-
return scalar(-93);
|
|
361
|
-
const left = { kind: "local", chunks: [], byteLength: 0, closed: false, peerClosed: false };
|
|
362
|
-
const right = { kind: "local", chunks: [], byteLength: 0, closed: false, peerClosed: false };
|
|
363
|
-
left.peer = right;
|
|
364
|
-
right.peer = left;
|
|
365
|
-
const leftFd = takeFd();
|
|
366
|
-
const rightFd = takeFd();
|
|
367
|
-
sockets.set(leftFd, left);
|
|
368
|
-
sockets.set(rightFd, right);
|
|
369
|
-
const output = new Uint8Array(8);
|
|
370
|
-
const view = new DataView(output.buffer);
|
|
371
|
-
view.setInt32(0, leftFd, true);
|
|
372
|
-
view.setInt32(4, rightFd, true);
|
|
373
|
-
return { result: 0, output };
|
|
374
|
-
}
|
|
375
|
-
function epollControl(epollFd, operation, fd, input) {
|
|
376
|
-
const sharedPipe = sharedPipes ? pollWaliSharedPipeState(sharedPipes, fd, 0) : undefined;
|
|
377
|
-
if (!sockets.has(fd) && !children.owns(fd) && sharedPipe === undefined && !(isStdinDescriptor(fd) && stdin))
|
|
378
|
-
return -9;
|
|
379
|
-
let registrations = epolls.get(epollFd);
|
|
380
|
-
if (!registrations) {
|
|
381
|
-
registrations = new Map();
|
|
382
|
-
epolls.set(epollFd, registrations);
|
|
383
|
-
}
|
|
384
|
-
if (operation === 2)
|
|
385
|
-
return registrations.delete(fd) ? 0 : -2;
|
|
386
|
-
if (operation !== 1 && operation !== 3 || input.byteLength < EPOLL_EVENT_BYTES)
|
|
387
|
-
return -22;
|
|
388
|
-
if (operation === 1 && registrations.has(fd))
|
|
389
|
-
return -17;
|
|
390
|
-
if (operation === 3 && !registrations.has(fd))
|
|
391
|
-
return -2;
|
|
392
|
-
const view = new DataView(input.buffer, input.byteOffset, input.byteLength);
|
|
393
|
-
const events = view.getUint32(0, true);
|
|
394
|
-
const socket = sockets.get(fd);
|
|
395
|
-
registrations.set(fd, {
|
|
396
|
-
events,
|
|
397
|
-
data: input.slice(EPOLL_DATA_OFFSET, EPOLL_EVENT_BYTES),
|
|
398
|
-
lastReady: 0,
|
|
399
|
-
lastPipeGeneration: sharedPipes ? waliSharedPipeGeneration(sharedPipes, fd) ?? 0 : 0,
|
|
400
|
-
lastStdinGeneration: isStdinDescriptor(fd) && stdin ? stdin.generation() : 0,
|
|
401
|
-
lastEventGeneration: socket?.kind === "event"
|
|
402
|
-
? socket.generation
|
|
403
|
-
: 0,
|
|
404
|
-
});
|
|
405
|
-
return 0;
|
|
406
|
-
}
|
|
407
|
-
async function epollWait(epollFd, maximum, timeout, mask = 0n, parked = () => { }) {
|
|
408
|
-
const registrations = epolls.get(epollFd);
|
|
409
|
-
if (!registrations)
|
|
410
|
-
return scalar(-9);
|
|
411
|
-
if (maximum <= 0)
|
|
412
|
-
return scalar(-22);
|
|
413
|
-
const startedAt = Date.now();
|
|
414
|
-
// This blocking RPC is claimed before dispatch, so an infinite guest wait
|
|
415
|
-
// needs no synthetic timeout to placate the request transport watchdog.
|
|
416
|
-
const deadline = timeout < 0 ? Infinity : startedAt + timeout;
|
|
417
|
-
do {
|
|
418
|
-
if (signals && (signals.pending() & ~mask) !== 0n)
|
|
419
|
-
return scalar(-4);
|
|
420
|
-
const events = [];
|
|
421
|
-
for (const [fd, registration] of registrations) {
|
|
422
|
-
const socket = sockets.get(fd);
|
|
423
|
-
const pollEvents = ((registration.events & 1) ? 1 : 0) | ((registration.events & 4) ? 4 : 0);
|
|
424
|
-
// Read the generation first so a write racing this poll is still seen
|
|
425
|
-
// on the next poll, even if readiness was already observed here.
|
|
426
|
-
const pipeGeneration = sharedPipes
|
|
427
|
-
? waliSharedPipeGeneration(sharedPipes, fd) ?? registration.lastPipeGeneration
|
|
428
|
-
: registration.lastPipeGeneration;
|
|
429
|
-
const externalReady = children.poll(fd, pollEvents)
|
|
430
|
-
?? (sharedPipes ? pollWaliSharedPipeState(sharedPipes, fd, pollEvents) : undefined);
|
|
431
|
-
const terminalReady = isStdinDescriptor(fd) && stdin
|
|
432
|
-
? ((pollEvents & 1) && stdin.poll() ? 1 : 0)
|
|
433
|
-
: undefined;
|
|
434
|
-
if (!socket && externalReady === undefined && terminalReady === undefined)
|
|
435
|
-
continue;
|
|
436
|
-
const currentReady = (terminalReady ?? externalReady ?? pollSocket(socket, pollEvents))
|
|
437
|
-
& (registration.events | 0x18);
|
|
438
|
-
const stdinGeneration = isStdinDescriptor(fd) && stdin
|
|
439
|
-
? stdin.generation()
|
|
440
|
-
: registration.lastStdinGeneration;
|
|
441
|
-
const edgeReady = (currentReady & ~registration.lastReady)
|
|
442
|
-
| (pipeGeneration !== registration.lastPipeGeneration ? currentReady : 0)
|
|
443
|
-
| (stdinGeneration !== registration.lastStdinGeneration ? currentReady : 0)
|
|
444
|
-
| (socket?.kind === "event" && socket.generation !== registration.lastEventGeneration
|
|
445
|
-
? currentReady
|
|
446
|
-
: 0);
|
|
447
|
-
// A browser terminal's shared byte queue behaves like a TTY, not a
|
|
448
|
-
// packet socket. Keep reporting readable until the consumer drains it;
|
|
449
|
-
// otherwise one-byte reads can strand the final key behind EPOLLET.
|
|
450
|
-
const ready = isStdinDescriptor(fd)
|
|
451
|
-
? currentReady
|
|
452
|
-
: (registration.events & 0x8000_0000) !== 0 ? edgeReady : currentReady;
|
|
453
|
-
registration.lastReady = currentReady;
|
|
454
|
-
registration.lastPipeGeneration = pipeGeneration;
|
|
455
|
-
registration.lastStdinGeneration = stdinGeneration;
|
|
456
|
-
registration.lastEventGeneration = socket?.kind === "event"
|
|
457
|
-
? socket.generation
|
|
458
|
-
: registration.lastEventGeneration;
|
|
459
|
-
if (ready !== 0)
|
|
460
|
-
events.push({ ready, data: registration.data });
|
|
461
|
-
if (events.length >= maximum)
|
|
462
|
-
break;
|
|
463
|
-
}
|
|
464
|
-
if (events.length > 0) {
|
|
465
|
-
const output = new Uint8Array(events.length * EPOLL_EVENT_BYTES);
|
|
466
|
-
const view = new DataView(output.buffer);
|
|
467
|
-
events.forEach((event, index) => {
|
|
468
|
-
const offset = index * EPOLL_EVENT_BYTES;
|
|
469
|
-
view.setUint32(offset, event.ready, true);
|
|
470
|
-
output.set(event.data, offset + EPOLL_DATA_OFFSET);
|
|
471
|
-
});
|
|
472
|
-
return { result: events.length, output };
|
|
473
|
-
}
|
|
474
|
-
if (timeout === 0 || Date.now() >= deadline)
|
|
475
|
-
return { result: 0, output: new Uint8Array() };
|
|
476
|
-
// Other descriptor sources retain the existing shared-pipe/socket scan.
|
|
477
|
-
let childrenOnly = true;
|
|
478
|
-
for (const fd of registrations.keys())
|
|
479
|
-
if (!children.owns(fd)) {
|
|
480
|
-
childrenOnly = false;
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
483
|
-
parked([...registrations.keys()].some(outsideWake) ? 2 : 1);
|
|
484
|
-
try {
|
|
485
|
-
await waitForDescriptorChange(childrenOnly ? deadline - Date.now() : 2);
|
|
486
|
-
}
|
|
487
|
-
finally {
|
|
488
|
-
parked(0);
|
|
489
|
-
}
|
|
490
|
-
} while (true);
|
|
491
|
-
}
|
|
492
|
-
function outsideWake(fd) {
|
|
493
|
-
const socket = sockets.get(fd);
|
|
494
|
-
if (socket?.kind === "tcp" || socket?.kind === "dns")
|
|
495
|
-
return true;
|
|
496
|
-
if (socket?.kind === "timer" && socket.deadlineNanoseconds !== undefined)
|
|
497
|
-
return true;
|
|
498
|
-
if (isStdinDescriptor(fd) && stdin)
|
|
499
|
-
return !stdin.closed;
|
|
500
|
-
return false;
|
|
501
|
-
}
|
|
502
|
-
function isStdinDescriptor(fd) {
|
|
503
|
-
if (fd === 0)
|
|
504
|
-
return true;
|
|
505
|
-
if (!stdioAliasRegistry || fd < 0 || fd >= stdioAliasRegistry.length)
|
|
506
|
-
return false;
|
|
507
|
-
return Atomics.load(stdioAliasRegistry, fd) === 0;
|
|
508
|
-
}
|
|
509
|
-
function scalar(result) {
|
|
510
|
-
return { result, output: new Uint8Array() };
|
|
511
|
-
}
|
|
512
|
-
function createSocket(domain, type, protocol) {
|
|
513
|
-
const baseType = type & 0xf;
|
|
514
|
-
let socket;
|
|
515
|
-
if ((domain === 2 || domain === 10) && baseType === 2 && (protocol === 0 || protocol === 17)) {
|
|
516
|
-
socket = { kind: "dns", domain, responses: [] };
|
|
517
|
-
}
|
|
518
|
-
else if ((domain === 2 || domain === 10) && baseType === 1 && (protocol === 0 || protocol === 6)) {
|
|
519
|
-
socket = {
|
|
520
|
-
kind: "tcp",
|
|
521
|
-
domain,
|
|
522
|
-
chunks: [],
|
|
523
|
-
byteLength: 0,
|
|
524
|
-
open: false,
|
|
525
|
-
closed: false,
|
|
526
|
-
failed: false,
|
|
527
|
-
nonblocking: Boolean(type & 0x800),
|
|
528
|
-
listening: false,
|
|
529
|
-
backlog: 0,
|
|
530
|
-
pending: [],
|
|
531
|
-
acceptWaiters: [],
|
|
532
|
-
peerClosed: false,
|
|
533
|
-
};
|
|
534
|
-
}
|
|
535
|
-
else {
|
|
536
|
-
return domain === 2 || domain === 10 ? -93 : -97;
|
|
537
|
-
}
|
|
538
|
-
const fd = takeFd();
|
|
539
|
-
sockets.set(fd, socket);
|
|
540
|
-
return fd;
|
|
541
|
-
}
|
|
542
|
-
async function connectSocket(socket, address) {
|
|
543
|
-
if (socket.kind === "dns")
|
|
544
|
-
return 0;
|
|
545
|
-
if (socket.kind === "event" || socket.kind === "timer")
|
|
546
|
-
return -88;
|
|
547
|
-
if (socket.kind === "local")
|
|
548
|
-
return -106;
|
|
549
|
-
const target = parseSocketAddress(address);
|
|
550
|
-
if (!target)
|
|
551
|
-
return -111;
|
|
552
|
-
if (isLoopbackConnectAddress(target.address)) {
|
|
553
|
-
if (!loopbackPort || socket.open)
|
|
554
|
-
return socket.open ? -106 : -111;
|
|
555
|
-
const connectionId = nextOutboundConnectionId--;
|
|
556
|
-
const result = await requestLoopbackConnect(target.port, connectionId);
|
|
557
|
-
if (result < 0)
|
|
558
|
-
return result;
|
|
559
|
-
socket.loopbackConnectionId = connectionId;
|
|
560
|
-
socket.open = true;
|
|
561
|
-
socket.closed = false;
|
|
562
|
-
socket.peerClosed = false;
|
|
563
|
-
socket.bound = { address: socket.domain === 10 ? "::1" : "127.0.0.1", port: 0 };
|
|
564
|
-
loopbackConnections.set(connectionId, socket);
|
|
565
|
-
return 0;
|
|
566
|
-
}
|
|
567
|
-
if (target.port !== 443)
|
|
568
|
-
return -111;
|
|
569
|
-
const hostname = hostByAddress.get(target.address);
|
|
570
|
-
if (!hostname || !allowedHosts.has(hostname))
|
|
571
|
-
return -13;
|
|
572
|
-
if (socket.open)
|
|
573
|
-
return -106;
|
|
574
|
-
if (externalConnections.size >= maxConnections)
|
|
575
|
-
return -24;
|
|
576
|
-
externalConnections.add(socket);
|
|
577
|
-
const url = new URL(relayUrl, scope.location.href);
|
|
578
|
-
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
579
|
-
url.search = "";
|
|
580
|
-
url.searchParams.append("host", hostname);
|
|
581
|
-
url.searchParams.append("port", "443");
|
|
582
|
-
return new Promise((resolve) => {
|
|
583
|
-
const websocket = new WebSocket(url);
|
|
584
|
-
socket.websocket = websocket;
|
|
585
|
-
websocket.binaryType = "arraybuffer";
|
|
586
|
-
let settled = false;
|
|
587
|
-
const settle = (value) => {
|
|
588
|
-
if (settled)
|
|
589
|
-
return;
|
|
590
|
-
settled = true;
|
|
591
|
-
clearTimeout(timeout);
|
|
592
|
-
if (value < 0)
|
|
593
|
-
externalConnections.delete(socket);
|
|
594
|
-
resolve(value);
|
|
595
|
-
};
|
|
596
|
-
const timeout = setTimeout(() => {
|
|
597
|
-
try {
|
|
598
|
-
websocket.close();
|
|
599
|
-
}
|
|
600
|
-
catch { }
|
|
601
|
-
settle(-110);
|
|
602
|
-
}, 15_000);
|
|
603
|
-
websocket.addEventListener("open", () => { socket.open = true; settle(0); });
|
|
604
|
-
websocket.addEventListener("message", (event) => {
|
|
605
|
-
if (!(event.data instanceof ArrayBuffer)) {
|
|
606
|
-
socket.failed = true;
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
const chunk = new Uint8Array(event.data);
|
|
610
|
-
if (!consumeNetworkBytes(chunk.byteLength)) {
|
|
611
|
-
socket.failed = true;
|
|
612
|
-
closeSocket(socket);
|
|
613
|
-
return;
|
|
614
|
-
}
|
|
615
|
-
socket.chunks.push(chunk);
|
|
616
|
-
socket.byteLength += chunk.byteLength;
|
|
617
|
-
});
|
|
618
|
-
websocket.addEventListener("close", () => { externalConnections.delete(socket); socket.closed = true; socket.open = false; settle(-111); });
|
|
619
|
-
websocket.addEventListener("error", () => { socket.failed = true; settle(-111); });
|
|
620
|
-
});
|
|
621
|
-
}
|
|
622
|
-
function bindSocket(socket, address) {
|
|
623
|
-
if (socket.kind === "dns") {
|
|
624
|
-
if (socket.bound)
|
|
625
|
-
return -22;
|
|
626
|
-
const target = parseSocketAddress(address);
|
|
627
|
-
if (!target || !isLoopbackBindAddress(target.address))
|
|
628
|
-
return target ? -99 : -22;
|
|
629
|
-
socket.bound = target;
|
|
630
|
-
return 0;
|
|
631
|
-
}
|
|
632
|
-
if (socket.kind !== "tcp")
|
|
633
|
-
return -88;
|
|
634
|
-
if (socket.open || socket.bound)
|
|
635
|
-
return -22;
|
|
636
|
-
const target = parseSocketAddress(address);
|
|
637
|
-
if (!target || !isLoopbackBindAddress(target.address))
|
|
638
|
-
return target ? -99 : -22;
|
|
639
|
-
const port = target.port === 0 ? allocateLoopbackPort() : target.port;
|
|
640
|
-
if (port <= 0 || listenersByPort.has(port))
|
|
641
|
-
return -98;
|
|
642
|
-
socket.bound = { address: target.address, port, ...(target.port === 0 ? { ephemeral: true } : {}) };
|
|
643
|
-
return 0;
|
|
644
|
-
}
|
|
645
|
-
async function listenSocket(socket, backlog) {
|
|
646
|
-
if (socket.kind !== "tcp")
|
|
647
|
-
return -95;
|
|
648
|
-
if (!socket.bound)
|
|
649
|
-
return -22;
|
|
650
|
-
if (socket.listening)
|
|
651
|
-
return 0;
|
|
652
|
-
if (!loopbackPort)
|
|
653
|
-
return -97;
|
|
654
|
-
const existing = listenersByPort.get(socket.bound.port);
|
|
655
|
-
if (existing && existing !== socket)
|
|
656
|
-
return -98;
|
|
657
|
-
let result = await requestLoopbackListen(socket.bound.port);
|
|
658
|
-
// This worker sees its own listeners, the project's port table every
|
|
659
|
-
// server's: a port it chose for port 0 that another server holds is
|
|
660
|
-
// passed over for the next free one, as the kernel would never have chosen it.
|
|
661
|
-
while (result === -98 && socket.bound.ephemeral) {
|
|
662
|
-
const next = allocateLoopbackPort(socket.bound.port + 1);
|
|
663
|
-
if (next <= 0)
|
|
664
|
-
break;
|
|
665
|
-
socket.bound = { ...socket.bound, port: next };
|
|
666
|
-
result = await requestLoopbackListen(next);
|
|
667
|
-
}
|
|
668
|
-
if (result < 0)
|
|
669
|
-
return result;
|
|
670
|
-
socket.listening = true;
|
|
671
|
-
socket.open = true;
|
|
672
|
-
socket.backlog = Math.max(1, Math.min(128, backlog));
|
|
673
|
-
listenersByPort.set(socket.bound.port, socket);
|
|
674
|
-
return 0;
|
|
675
|
-
}
|
|
676
|
-
async function acceptSocket(socket, flags) {
|
|
677
|
-
if (socket.kind !== "tcp" || !socket.listening)
|
|
678
|
-
return scalar(-22);
|
|
679
|
-
if ((flags & ~0x8_0800) !== 0)
|
|
680
|
-
return scalar(-22);
|
|
681
|
-
let accepted = socket.pending.shift();
|
|
682
|
-
if (!accepted && (socket.nonblocking || Boolean(flags & 0x800)))
|
|
683
|
-
return scalar(-11);
|
|
684
|
-
if (!accepted) {
|
|
685
|
-
accepted = await new Promise((resolve) => socket.acceptWaiters.push(resolve));
|
|
686
|
-
}
|
|
687
|
-
if (!accepted)
|
|
688
|
-
return scalar(-9);
|
|
689
|
-
accepted.nonblocking = accepted.nonblocking || Boolean(flags & 0x800);
|
|
690
|
-
const fd = takeFd();
|
|
691
|
-
sockets.set(fd, accepted);
|
|
692
|
-
return { result: fd, output: socketAddress(accepted.domain, "127.0.0.1", 0) };
|
|
693
|
-
}
|
|
694
|
-
function requestLoopbackListen(port) {
|
|
695
|
-
if (!loopbackPort)
|
|
696
|
-
return Promise.resolve(-97);
|
|
697
|
-
const requestId = nextLoopbackRequestId++;
|
|
698
|
-
return new Promise((resolve) => {
|
|
699
|
-
const timeout = setTimeout(() => {
|
|
700
|
-
if (!loopbackAcks.delete(requestId))
|
|
701
|
-
return;
|
|
702
|
-
resolve(-110);
|
|
703
|
-
}, 5_000);
|
|
704
|
-
loopbackAcks.set(requestId, (result) => {
|
|
705
|
-
clearTimeout(timeout);
|
|
706
|
-
resolve(result);
|
|
707
|
-
});
|
|
708
|
-
postLoopback({ type: "listen", requestId, port });
|
|
709
|
-
});
|
|
710
|
-
}
|
|
711
|
-
function requestLoopbackConnect(port, connectionId) {
|
|
712
|
-
if (!loopbackPort)
|
|
713
|
-
return Promise.resolve(-111);
|
|
714
|
-
const requestId = nextLoopbackRequestId++;
|
|
715
|
-
return new Promise((resolve) => {
|
|
716
|
-
const timeout = setTimeout(() => {
|
|
717
|
-
if (!loopbackAcks.delete(requestId))
|
|
718
|
-
return;
|
|
719
|
-
resolve(-110);
|
|
720
|
-
}, 5_000);
|
|
721
|
-
loopbackAcks.set(requestId, (result) => {
|
|
722
|
-
clearTimeout(timeout);
|
|
723
|
-
resolve(result);
|
|
724
|
-
});
|
|
725
|
-
postLoopback({ type: "connect", requestId, connectionId, port });
|
|
726
|
-
});
|
|
727
|
-
}
|
|
728
|
-
function handleLoopbackMessage(message) {
|
|
729
|
-
if (message.type === "ack") {
|
|
730
|
-
const resolve = loopbackAcks.get(message.requestId);
|
|
731
|
-
if (!resolve)
|
|
732
|
-
return;
|
|
733
|
-
loopbackAcks.delete(message.requestId);
|
|
734
|
-
resolve(message.result);
|
|
735
|
-
return;
|
|
736
|
-
}
|
|
737
|
-
if (message.type === "peer-close") {
|
|
738
|
-
const socket = loopbackConnections.get(message.connectionId);
|
|
739
|
-
if (socket)
|
|
740
|
-
socket.peerClosed = true;
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
if (message.type === "data") {
|
|
744
|
-
const socket = loopbackConnections.get(message.connectionId);
|
|
745
|
-
if (!socket || socket.closed)
|
|
746
|
-
return;
|
|
747
|
-
const bytes = message.bytes.slice();
|
|
748
|
-
socket.chunks.push(bytes);
|
|
749
|
-
socket.byteLength += bytes.byteLength;
|
|
750
|
-
return;
|
|
751
|
-
}
|
|
752
|
-
const listener = listenersByPort.get(message.port);
|
|
753
|
-
if (!listener || !listener.listening || listener.closed
|
|
754
|
-
|| listener.pending.length >= listener.backlog) {
|
|
755
|
-
postLoopback({ type: "closed", connectionId: message.connectionId });
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
const accepted = {
|
|
759
|
-
kind: "tcp",
|
|
760
|
-
domain: listener.domain,
|
|
761
|
-
chunks: [message.bytes.slice()],
|
|
762
|
-
byteLength: message.bytes.byteLength,
|
|
763
|
-
open: true,
|
|
764
|
-
closed: false,
|
|
765
|
-
failed: false,
|
|
766
|
-
nonblocking: false,
|
|
767
|
-
listening: false,
|
|
768
|
-
backlog: 0,
|
|
769
|
-
pending: [],
|
|
770
|
-
acceptWaiters: [],
|
|
771
|
-
loopbackConnectionId: message.connectionId,
|
|
772
|
-
peerClosed: false,
|
|
773
|
-
...(listener.bound ? { bound: listener.bound } : {}),
|
|
774
|
-
};
|
|
775
|
-
loopbackConnections.set(message.connectionId, accepted);
|
|
776
|
-
const waiter = listener.acceptWaiters.shift();
|
|
777
|
-
if (waiter)
|
|
778
|
-
waiter(accepted);
|
|
779
|
-
else
|
|
780
|
-
listener.pending.push(accepted);
|
|
781
|
-
}
|
|
782
|
-
function postLoopback(message) {
|
|
783
|
-
loopbackPort?.postMessage(message);
|
|
784
|
-
}
|
|
785
|
-
function isLoopbackBindAddress(address) {
|
|
786
|
-
return address === "0.0.0.0" || address === "127.0.0.1" || address === "::" || address === "::1";
|
|
787
|
-
}
|
|
788
|
-
function isLoopbackConnectAddress(address) {
|
|
789
|
-
return address === "127.0.0.1" || address === "::1";
|
|
790
|
-
}
|
|
791
|
-
function allocateLoopbackPort(from = 49_152) {
|
|
792
|
-
for (let port = from; port <= 65_535; port += 1) {
|
|
793
|
-
if (!listenersByPort.has(port))
|
|
794
|
-
return port;
|
|
795
|
-
}
|
|
796
|
-
return -1;
|
|
797
|
-
}
|
|
798
|
-
function sendSocket(socket, input) {
|
|
799
|
-
if (socket.kind === "event") {
|
|
800
|
-
if (input.byteLength < 8)
|
|
801
|
-
return -22;
|
|
802
|
-
const increment = new DataView(input.buffer, input.byteOffset, input.byteLength).getBigUint64(0, true);
|
|
803
|
-
if (increment === 0xffffffffffffffffn)
|
|
804
|
-
return -22;
|
|
805
|
-
socket.counter += increment;
|
|
806
|
-
socket.generation = socket.generation >= 0x7fff_ffff ? 1 : socket.generation + 1;
|
|
807
|
-
return 8;
|
|
808
|
-
}
|
|
809
|
-
if (socket.kind === "local") {
|
|
810
|
-
if (socket.closed)
|
|
811
|
-
return -9;
|
|
812
|
-
if (!socket.peer || socket.peer.closed)
|
|
813
|
-
return -32;
|
|
814
|
-
const copy = input.slice();
|
|
815
|
-
socket.peer.chunks.push(copy);
|
|
816
|
-
socket.peer.byteLength += copy.byteLength;
|
|
817
|
-
return copy.byteLength;
|
|
818
|
-
}
|
|
819
|
-
if (socket.kind === "timer")
|
|
820
|
-
return -22;
|
|
821
|
-
if (socket.kind === "dns") {
|
|
822
|
-
const response = dnsResponse(input);
|
|
823
|
-
if (!response)
|
|
824
|
-
return -13;
|
|
825
|
-
socket.responses.push(response);
|
|
826
|
-
return input.byteLength;
|
|
827
|
-
}
|
|
828
|
-
if (socket.loopbackConnectionId !== undefined) {
|
|
829
|
-
if (socket.closed || socket.peerClosed)
|
|
830
|
-
return -32;
|
|
831
|
-
postLoopback({ type: "outgoing", connectionId: socket.loopbackConnectionId, bytes: input.slice() });
|
|
832
|
-
return input.byteLength;
|
|
833
|
-
}
|
|
834
|
-
if (!socket.open || !socket.websocket)
|
|
835
|
-
return socket.closed ? -32 : -11;
|
|
836
|
-
if (!consumeNetworkBytes(input.byteLength)) {
|
|
837
|
-
closeSocket(socket);
|
|
838
|
-
return -122;
|
|
839
|
-
}
|
|
840
|
-
socket.websocket.send(input);
|
|
841
|
-
return input.byteLength;
|
|
842
|
-
}
|
|
843
|
-
function receiveSocket(socket, maximum) {
|
|
844
|
-
if (socket.kind === "event") {
|
|
845
|
-
if (maximum < 8)
|
|
846
|
-
return { result: -22, output: new Uint8Array() };
|
|
847
|
-
if (socket.counter === 0n)
|
|
848
|
-
return { result: socket.nonblocking ? -11 : -11, output: new Uint8Array() };
|
|
849
|
-
const value = socket.semaphore ? 1n : socket.counter;
|
|
850
|
-
socket.counter -= value;
|
|
851
|
-
const output = new Uint8Array(8);
|
|
852
|
-
new DataView(output.buffer).setBigUint64(0, value, true);
|
|
853
|
-
return { result: 8, output };
|
|
854
|
-
}
|
|
855
|
-
if (socket.kind === "local") {
|
|
856
|
-
if (socket.closed)
|
|
857
|
-
return { result: -9, output: new Uint8Array() };
|
|
858
|
-
if (socket.byteLength === 0)
|
|
859
|
-
return { result: socket.peerClosed ? 0 : -11, output: new Uint8Array() };
|
|
860
|
-
return drainChunks(socket, maximum);
|
|
861
|
-
}
|
|
862
|
-
if (socket.kind === "timer") {
|
|
863
|
-
if (maximum < 8)
|
|
864
|
-
return { result: -22, output: new Uint8Array() };
|
|
865
|
-
updateTimer(socket);
|
|
866
|
-
if (socket.expirations === 0n)
|
|
867
|
-
return { result: -11, output: new Uint8Array() };
|
|
868
|
-
const output = new Uint8Array(8);
|
|
869
|
-
new DataView(output.buffer).setBigUint64(0, socket.expirations, true);
|
|
870
|
-
socket.expirations = 0n;
|
|
871
|
-
return { result: 8, output };
|
|
872
|
-
}
|
|
873
|
-
if (maximum === 0)
|
|
874
|
-
return { result: 0, output: new Uint8Array() };
|
|
875
|
-
if (socket.kind === "dns") {
|
|
876
|
-
const response = socket.responses.shift();
|
|
877
|
-
if (!response)
|
|
878
|
-
return { result: -11, output: new Uint8Array() };
|
|
879
|
-
const output = response.subarray(0, maximum);
|
|
880
|
-
return { result: output.byteLength, output };
|
|
881
|
-
}
|
|
882
|
-
if (socket.byteLength === 0)
|
|
883
|
-
return {
|
|
884
|
-
result: socket.closed || socket.peerClosed ? 0 : socket.failed ? -5 : -11,
|
|
885
|
-
output: new Uint8Array(),
|
|
886
|
-
};
|
|
887
|
-
return drainChunks(socket, maximum);
|
|
888
|
-
}
|
|
889
|
-
function drainChunks(socket, maximum) {
|
|
890
|
-
const output = new Uint8Array(Math.min(maximum, socket.byteLength));
|
|
891
|
-
let offset = 0;
|
|
892
|
-
while (offset < output.byteLength) {
|
|
893
|
-
const chunk = socket.chunks[0];
|
|
894
|
-
const count = Math.min(chunk.byteLength, output.byteLength - offset);
|
|
895
|
-
output.set(chunk.subarray(0, count), offset);
|
|
896
|
-
offset += count;
|
|
897
|
-
socket.byteLength -= count;
|
|
898
|
-
if (count === chunk.byteLength)
|
|
899
|
-
socket.chunks.shift();
|
|
900
|
-
else
|
|
901
|
-
socket.chunks[0] = chunk.subarray(count);
|
|
902
|
-
}
|
|
903
|
-
return { result: output.byteLength, output };
|
|
904
|
-
}
|
|
905
|
-
function pollSocket(socket, events) {
|
|
906
|
-
if (socket.kind === "event")
|
|
907
|
-
return ((events & 1) && socket.counter > 0n ? 1 : 0) | ((events & 4) ? 4 : 0);
|
|
908
|
-
if (socket.kind === "timer") {
|
|
909
|
-
updateTimer(socket);
|
|
910
|
-
return (events & 1) && socket.expirations > 0n ? 1 : 0;
|
|
911
|
-
}
|
|
912
|
-
if (socket.kind === "local") {
|
|
913
|
-
return (socket.peerClosed ? 0x10 : 0)
|
|
914
|
-
| ((events & 1) && (socket.byteLength > 0 || socket.peerClosed) ? 1 : 0)
|
|
915
|
-
| ((events & 4) && !socket.peerClosed ? 4 : 0);
|
|
916
|
-
}
|
|
917
|
-
if (socket.kind === "dns")
|
|
918
|
-
return ((events & 1) && socket.responses.length ? 1 : 0) | ((events & 4) ? 4 : 0);
|
|
919
|
-
if (socket.listening)
|
|
920
|
-
return (events & 1) && socket.pending.length > 0 ? 1 : 0;
|
|
921
|
-
let ready = socket.closed || socket.peerClosed ? 0x10 : socket.failed ? 0x8 : 0;
|
|
922
|
-
if ((events & 1) && (socket.byteLength > 0 || socket.closed || socket.peerClosed))
|
|
923
|
-
ready |= 1;
|
|
924
|
-
if ((events & 4) && socket.open && !socket.peerClosed)
|
|
925
|
-
ready |= 4;
|
|
926
|
-
return ready;
|
|
927
|
-
}
|
|
928
|
-
function rearmEpollReadable(fd) {
|
|
929
|
-
const target = sockets.get(fd);
|
|
930
|
-
for (const registrations of epolls.values()) {
|
|
931
|
-
for (const [registeredFd, registration] of registrations) {
|
|
932
|
-
if (registeredFd === fd || (target && sockets.get(registeredFd) === target)) {
|
|
933
|
-
registration.lastReady &= ~1;
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
}
|
|
937
|
-
}
|
|
938
|
-
function fcntlSocket(_fd, socket, command, argument) {
|
|
939
|
-
if (command === 0 || command === 1030)
|
|
940
|
-
return duplicateSocket(socket, Math.max(argument, NETWORK_FD_MIN));
|
|
941
|
-
if (command === 1 || command === 2 || command === 4)
|
|
942
|
-
return 0;
|
|
943
|
-
if (command === 3)
|
|
944
|
-
return socket.kind === "timer" ? (socket.nonblocking ? 0x800 : 0) : 0x802;
|
|
945
|
-
return -22;
|
|
946
|
-
}
|
|
947
|
-
function duplicateSocket(socket, minimum = NETWORK_FD_MIN) {
|
|
948
|
-
nextFd = Math.max(nextFd, minimum);
|
|
949
|
-
const fd = takeFd();
|
|
950
|
-
sockets.set(fd, socket);
|
|
951
|
-
return fd;
|
|
952
|
-
}
|
|
953
|
-
function closeSocket(socket) {
|
|
954
|
-
if (socket.kind === "tcp") {
|
|
955
|
-
externalConnections.delete(socket);
|
|
956
|
-
if (socket.listening && socket.bound) {
|
|
957
|
-
listenersByPort.delete(socket.bound.port);
|
|
958
|
-
postLoopback({ type: "unlisten", port: socket.bound.port });
|
|
959
|
-
for (const resolve of socket.acceptWaiters.splice(0))
|
|
960
|
-
resolve(undefined);
|
|
961
|
-
for (const accepted of socket.pending.splice(0))
|
|
962
|
-
closeSocket(accepted);
|
|
963
|
-
}
|
|
964
|
-
if (socket.loopbackConnectionId !== undefined) {
|
|
965
|
-
loopbackConnections.delete(socket.loopbackConnectionId);
|
|
966
|
-
postLoopback({ type: "closed", connectionId: socket.loopbackConnectionId });
|
|
967
|
-
}
|
|
968
|
-
socket.closed = true;
|
|
969
|
-
socket.open = false;
|
|
970
|
-
try {
|
|
971
|
-
socket.websocket?.close();
|
|
972
|
-
}
|
|
973
|
-
catch { }
|
|
974
|
-
}
|
|
975
|
-
if (socket.kind === "local") {
|
|
976
|
-
socket.closed = true;
|
|
977
|
-
if (socket.peer)
|
|
978
|
-
socket.peer.peerClosed = true;
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
function consumeNetworkBytes(bytes) {
|
|
982
|
-
if (bytes < 0 || networkBytes > maxBytes - bytes)
|
|
983
|
-
return false;
|
|
984
|
-
networkBytes += bytes;
|
|
985
|
-
return true;
|
|
986
|
-
}
|
|
987
|
-
function socketName(socket) {
|
|
988
|
-
if (socket.kind === "event" || socket.kind === "timer")
|
|
989
|
-
return new Uint8Array();
|
|
990
|
-
if (socket.kind === "local")
|
|
991
|
-
return new Uint8Array();
|
|
992
|
-
if (socket.kind === "dns")
|
|
993
|
-
return socketAddress(socket.domain, socket.bound?.address ?? (socket.domain === 10 ? "::" : "0.0.0.0"), socket.bound?.port ?? 0);
|
|
994
|
-
return socketAddress(socket.domain, socket.bound?.address ?? (socket.domain === 10 ? "::" : "0.0.0.0"), socket.bound?.port ?? 0);
|
|
995
|
-
}
|
|
996
|
-
function socketAddress(domain, address, port) {
|
|
997
|
-
const output = new Uint8Array(domain === 10 ? 28 : 16);
|
|
998
|
-
const view = new DataView(output.buffer);
|
|
999
|
-
view.setUint16(0, domain, true);
|
|
1000
|
-
view.setUint16(2, port, false);
|
|
1001
|
-
if (domain === 2) {
|
|
1002
|
-
const parts = address === "0.0.0.0" ? [0, 0, 0, 0]
|
|
1003
|
-
: address === "127.0.0.1" ? [127, 0, 0, 1] : [127, 0, 0, 1];
|
|
1004
|
-
output.set(parts, 4);
|
|
1005
|
-
}
|
|
1006
|
-
else if (address === "::1") {
|
|
1007
|
-
output[23] = 1;
|
|
1008
|
-
}
|
|
1009
|
-
return output;
|
|
1010
|
-
}
|
|
1011
|
-
function updateTimer(timer) {
|
|
1012
|
-
if (timer.deadlineNanoseconds === undefined)
|
|
1013
|
-
return;
|
|
1014
|
-
const now = nowNanoseconds(timer.clockId);
|
|
1015
|
-
if (now < timer.deadlineNanoseconds)
|
|
1016
|
-
return;
|
|
1017
|
-
const elapsed = now - timer.deadlineNanoseconds;
|
|
1018
|
-
const count = timer.intervalNanoseconds === 0n ? 1n : 1n + elapsed / timer.intervalNanoseconds;
|
|
1019
|
-
const next = timer.expirations + count;
|
|
1020
|
-
timer.expirations = next > 0xffffffffffffffffn ? 0xffffffffffffffffn : next;
|
|
1021
|
-
timer.deadlineNanoseconds = timer.intervalNanoseconds === 0n
|
|
1022
|
-
? undefined : timer.deadlineNanoseconds + count * timer.intervalNanoseconds;
|
|
1023
|
-
}
|
|
1024
|
-
function timerRemaining(timer) {
|
|
1025
|
-
if (timer.deadlineNanoseconds === undefined)
|
|
1026
|
-
return 0n;
|
|
1027
|
-
const remaining = timer.deadlineNanoseconds - nowNanoseconds(timer.clockId);
|
|
1028
|
-
return remaining > 0n ? remaining : 0n;
|
|
1029
|
-
}
|
|
1030
|
-
function readTimespec(view, offset) {
|
|
1031
|
-
const seconds = view.getBigInt64(offset, true);
|
|
1032
|
-
const nanoseconds = view.getBigInt64(offset + 8, true);
|
|
1033
|
-
if (seconds < 0n || nanoseconds < 0n || nanoseconds >= 1000000000n)
|
|
1034
|
-
return;
|
|
1035
|
-
return seconds * 1000000000n + nanoseconds;
|
|
1036
|
-
}
|
|
1037
|
-
function writeTimespec(view, offset, nanoseconds) {
|
|
1038
|
-
view.setBigInt64(offset, nanoseconds / 1000000000n, true);
|
|
1039
|
-
view.setBigInt64(offset + 8, nanoseconds % 1000000000n, true);
|
|
1040
|
-
}
|
|
1041
|
-
function nowNanoseconds(clockId) {
|
|
1042
|
-
const milliseconds = clockId === 0 ? performance.timeOrigin + performance.now() : monotonicMilliseconds();
|
|
1043
|
-
return BigInt(Math.floor(milliseconds * 1_000_000));
|
|
1044
|
-
}
|
|
1045
|
-
function dnsResponse(query) {
|
|
1046
|
-
if (query.byteLength < 17)
|
|
1047
|
-
return;
|
|
1048
|
-
let offset = 12;
|
|
1049
|
-
const labels = [];
|
|
1050
|
-
while (offset < query.byteLength) {
|
|
1051
|
-
const length = query[offset++];
|
|
1052
|
-
if (length === 0)
|
|
1053
|
-
break;
|
|
1054
|
-
if (length > 63 || offset + length > query.byteLength)
|
|
1055
|
-
return;
|
|
1056
|
-
labels.push(new TextDecoder().decode(query.subarray(offset, offset + length)));
|
|
1057
|
-
offset += length;
|
|
1058
|
-
}
|
|
1059
|
-
if (offset + 4 > query.byteLength)
|
|
1060
|
-
return;
|
|
1061
|
-
const hostname = labels.join(".").toLowerCase();
|
|
1062
|
-
const qtype = (query[offset] << 8) | query[offset + 1];
|
|
1063
|
-
const questionEnd = offset + 4;
|
|
1064
|
-
const allowed = allowedHosts.has(hostname) && (qtype === 1 || qtype === 28);
|
|
1065
|
-
const answerLength = allowed ? (qtype === 1 ? 16 : 28) : 0;
|
|
1066
|
-
const response = new Uint8Array(questionEnd + answerLength);
|
|
1067
|
-
response.set(query.subarray(0, questionEnd));
|
|
1068
|
-
response[2] = 0x81;
|
|
1069
|
-
response[3] = allowed ? 0x80 : 0x83;
|
|
1070
|
-
response[6] = 0;
|
|
1071
|
-
response[7] = allowed ? 1 : 0;
|
|
1072
|
-
response.fill(0, 8, 12);
|
|
1073
|
-
if (!allowed)
|
|
1074
|
-
return response;
|
|
1075
|
-
const identifier = addressId(hostname);
|
|
1076
|
-
let answer = questionEnd;
|
|
1077
|
-
response[answer++] = 0xc0;
|
|
1078
|
-
response[answer++] = 0x0c;
|
|
1079
|
-
response[answer++] = 0;
|
|
1080
|
-
response[answer++] = qtype;
|
|
1081
|
-
response[answer++] = 0;
|
|
1082
|
-
response[answer++] = 1;
|
|
1083
|
-
response[answer++] = 0;
|
|
1084
|
-
response[answer++] = 0;
|
|
1085
|
-
response[answer++] = 0;
|
|
1086
|
-
response[answer++] = 60;
|
|
1087
|
-
response[answer++] = 0;
|
|
1088
|
-
response[answer++] = qtype === 1 ? 4 : 16;
|
|
1089
|
-
if (qtype === 1) {
|
|
1090
|
-
const address = `203.0.113.${identifier}`;
|
|
1091
|
-
hostByAddress.set(address, hostname);
|
|
1092
|
-
response.set([203, 0, 113, identifier], answer);
|
|
1093
|
-
}
|
|
1094
|
-
else {
|
|
1095
|
-
const address = `2001:db8::${identifier.toString(16)}`;
|
|
1096
|
-
hostByAddress.set(address, hostname);
|
|
1097
|
-
response.set([0x20, 0x01, 0x0d, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, identifier], answer);
|
|
1098
|
-
}
|
|
1099
|
-
return response;
|
|
1100
|
-
}
|
|
1101
|
-
function addressId(hostname) {
|
|
1102
|
-
const current = addressIdByHost.get(hostname);
|
|
1103
|
-
if (current)
|
|
1104
|
-
return current;
|
|
1105
|
-
const next = addressIdByHost.size + 1;
|
|
1106
|
-
if (next > 254)
|
|
1107
|
-
throw new Error("Too many WALI DNS targets.");
|
|
1108
|
-
addressIdByHost.set(hostname, next);
|
|
1109
|
-
return next;
|
|
1110
|
-
}
|
|
1111
|
-
function parseSocketAddress(value) {
|
|
1112
|
-
if (value.byteLength < 8)
|
|
1113
|
-
return;
|
|
1114
|
-
const view = new DataView(value.buffer, value.byteOffset, value.byteLength);
|
|
1115
|
-
const family = view.getUint16(0, true);
|
|
1116
|
-
const port = view.getUint16(2, false);
|
|
1117
|
-
if (family === 2 && value.byteLength >= 8) {
|
|
1118
|
-
return { address: [...value.subarray(4, 8)].join("."), port };
|
|
1119
|
-
}
|
|
1120
|
-
if (family === 10 && value.byteLength >= 24) {
|
|
1121
|
-
const groups = [];
|
|
1122
|
-
for (let offset = 8; offset < 24; offset += 2)
|
|
1123
|
-
groups.push(view.getUint16(offset, false).toString(16));
|
|
1124
|
-
return { address: compressIpv6(groups), port };
|
|
1125
|
-
}
|
|
1126
|
-
return;
|
|
1127
|
-
}
|
|
1128
|
-
function compressIpv6(groups) {
|
|
1129
|
-
let bestStart = -1;
|
|
1130
|
-
let bestLength = 0;
|
|
1131
|
-
for (let start = 0; start < groups.length;) {
|
|
1132
|
-
if (groups[start] !== "0") {
|
|
1133
|
-
start += 1;
|
|
1134
|
-
continue;
|
|
1135
|
-
}
|
|
1136
|
-
let end = start;
|
|
1137
|
-
while (groups[end] === "0")
|
|
1138
|
-
end += 1;
|
|
1139
|
-
if (end - start > bestLength) {
|
|
1140
|
-
bestStart = start;
|
|
1141
|
-
bestLength = end - start;
|
|
1142
|
-
}
|
|
1143
|
-
start = end;
|
|
1144
|
-
}
|
|
1145
|
-
if (bestLength < 2)
|
|
1146
|
-
return groups.join(":");
|
|
1147
|
-
const before = groups.slice(0, bestStart).join(":");
|
|
1148
|
-
const after = groups.slice(bestStart + bestLength).join(":");
|
|
1149
|
-
return `${before}::${after}`;
|
|
1150
|
-
}
|
|
1
|
+
import { createWaliNetworkSession } from "./network-host.js";
|
|
2
|
+
// Standalone clients retain their dedicated worker. Kernel clients use
|
|
3
|
+
// kernel-network-worker and attach one process session through a port.
|
|
4
|
+
const session = createWaliNetworkSession(message => postMessage(message));
|
|
5
|
+
self.onmessage = (event) => session.receive(event);
|
|
1151
6
|
//# sourceMappingURL=network-worker.js.map
|