@ai-ecoverse/slicc-kernel 1.95.3 → 1.97.0

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/dist/node.js CHANGED
@@ -1251,6 +1251,149 @@ function wasiErrno(code) {
1251
1251
  return WASI_ERRNO[code] ?? E.IO;
1252
1252
  }
1253
1253
 
1254
+ // src/kernel/net/loopback-names.ts
1255
+ var HOST_LOOPBACK = "host.slicc.internal";
1256
+ var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
1257
+ var PAGE_LOOPBACK = "kernel.localhost";
1258
+ var PAGE_LOOPBACK_URL = `http://{port}.${PAGE_LOOPBACK}`;
1259
+ function toHostLoopback(href) {
1260
+ const url = new URL(href);
1261
+ if (url.hostname.replace(/\.$/, "").toLowerCase() !== HOST_LOOPBACK) return href;
1262
+ url.hostname = "127.0.0.1";
1263
+ return url.href;
1264
+ }
1265
+ var LOOPBACK_V4 = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
1266
+ var LOOPBACK_V6 = /* @__PURE__ */ new Set(["::1", "::", "0:0:0:0:0:0:0:1"]);
1267
+ var MAPPED_V4 = /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/;
1268
+ var KERNEL_PORT = /^(\d{1,5})\.kernel\.localhost$/;
1269
+ var SCHEMES = {
1270
+ "http:": 80,
1271
+ "ws:": 80,
1272
+ "https:": 443,
1273
+ "wss:": 443
1274
+ };
1275
+ function isKernelLoopbackHost(hostname) {
1276
+ const host = hostname.replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "").toLowerCase();
1277
+ return host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0" || LOOPBACK_V4.test(host) || LOOPBACK_V6.has(host) || MAPPED_V4.test(host);
1278
+ }
1279
+ function kernelLoopbackPort(href) {
1280
+ let url;
1281
+ try {
1282
+ url = new URL(href);
1283
+ } catch {
1284
+ return void 0;
1285
+ }
1286
+ const fallback = SCHEMES[url.protocol];
1287
+ if (fallback === void 0 || !isKernelLoopbackHost(url.hostname)) return void 0;
1288
+ const named = KERNEL_PORT.exec(url.hostname.replace(/\.$/, "").toLowerCase())?.[1];
1289
+ const port = Number(named ?? (url.port || fallback));
1290
+ return port >= 1 && port <= 65535 ? port : void 0;
1291
+ }
1292
+ var DEFAULT_HOSTNAME = "slicc";
1293
+ var HOSTNAME_ADDRESS = "127.0.1.1";
1294
+ var LIBC_HOSTNAMES = ["emscripten", "wasmer.sh"];
1295
+ var LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
1296
+ function reserved(name2) {
1297
+ const lower = name2.toLowerCase();
1298
+ return lower === "localhost" || lower.endsWith(".localhost") || lower === HOST_LOOPBACK || lower === "wasmer.sh" || /^\d+(\.\d+){3}$/.test(lower);
1299
+ }
1300
+ function isHostname(name2) {
1301
+ if (typeof name2 !== "string" || name2.length > 253 || reserved(name2)) return false;
1302
+ return name2.split(".").every((l) => LABEL.test(l));
1303
+ }
1304
+ function ownNames(hostname) {
1305
+ return [.../* @__PURE__ */ new Set([hostname.toLowerCase(), ...LIBC_HOSTNAMES])];
1306
+ }
1307
+
1308
+ // src/kernel/net/routes.ts
1309
+ var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
1310
+ function ipv4Bytes(text) {
1311
+ const parts2 = text.split(".");
1312
+ if (parts2.length !== 4 || !parts2.every((p) => /^\d{1,3}$/.test(p))) return void 0;
1313
+ const bytes = parts2.map(Number);
1314
+ return bytes.every((b) => b <= 255) ? bytes : void 0;
1315
+ }
1316
+ function ipv6Groups(text) {
1317
+ if (!text.includes(":")) return void 0;
1318
+ let canonical;
1319
+ try {
1320
+ canonical = new URL(`http://[${text}]/`).hostname.slice(1, -1);
1321
+ } catch {
1322
+ return void 0;
1323
+ }
1324
+ const [head, tail] = canonical.split("::");
1325
+ const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
1326
+ const front = part(head);
1327
+ const back = part(tail);
1328
+ return tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
1329
+ }
1330
+ function toBits(values, width) {
1331
+ return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
1332
+ }
1333
+ function addressBits(host) {
1334
+ const v4 = ipv4Bytes(host);
1335
+ if (v4) return { v6: false, bits: toBits(v4, 8) };
1336
+ const v6 = ipv6Groups(host);
1337
+ return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
1338
+ }
1339
+ function parsePrefix(text) {
1340
+ const parts2 = text.trim().split("/");
1341
+ if (parts2.length > 2) return void 0;
1342
+ const [address = "", size] = parts2;
1343
+ const parsed2 = addressBits(address);
1344
+ if (!parsed2) return void 0;
1345
+ const max = parsed2.v6 ? 128 : 32;
1346
+ const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
1347
+ if (length2 < 0 || length2 > max) return void 0;
1348
+ return { ...parsed2, length: length2 };
1349
+ }
1350
+ function within(prefix, addr) {
1351
+ if (prefix.v6 !== addr.v6) return false;
1352
+ const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
1353
+ return addr.bits >> shift === prefix.bits >> shift;
1354
+ }
1355
+ var RESERVED = [
1356
+ "0.0.0.0/8",
1357
+ "127.0.0.0/8",
1358
+ "169.254.0.0/16",
1359
+ "224.0.0.0/4",
1360
+ "240.0.0.0/4",
1361
+ "::/96",
1362
+ "::ffff:0:0/96",
1363
+ "fe80::/10",
1364
+ "ff00::/8"
1365
+ ].map((p) => parsePrefix(p));
1366
+ var Routes = class {
1367
+ table = [];
1368
+ exit = false;
1369
+ ipv6;
1370
+ constructor(ipv62 = false) {
1371
+ this.ipv6 = ipv62;
1372
+ }
1373
+ set(table) {
1374
+ const parsed2 = (table.prefixes ?? []).map(parsePrefix);
1375
+ const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
1376
+ if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
1377
+ this.table = parsed2;
1378
+ this.exit = table.exit === true;
1379
+ }
1380
+ classify(host) {
1381
+ const name2 = host.toLowerCase().replace(/\.$/, "");
1382
+ if (name2 === "localhost" || name2.endsWith(".localhost") || name2 === "0.0.0.0") {
1383
+ return "loopback";
1384
+ }
1385
+ if (name2 === HOST_LOOPBACK || name2 === HOST_ADDRESS) return "host";
1386
+ const addr = addressBits(name2);
1387
+ if (!addr) return "unreachable";
1388
+ if (addr.v6 && addr.bits === 1n) return "loopback";
1389
+ if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
1390
+ if (RESERVED.some((p) => within(p, addr))) return "reserved";
1391
+ if (addr.v6 && !this.ipv6) return "unreachable";
1392
+ if (this.exit || this.table.some((p) => within(p, addr))) return "uplink";
1393
+ return "unreachable";
1394
+ }
1395
+ };
1396
+
1254
1397
  // src/kernel/net/uplink-socket.ts
1255
1398
  var CHUNK = 64 * 1024;
1256
1399
  function hostPort(text) {
@@ -1655,8 +1798,10 @@ function canonicalHost(host) {
1655
1798
  function keyOf(addr) {
1656
1799
  return addr.family === "inet" ? `inet:${addr.port}` : `unix:${addr.path}`;
1657
1800
  }
1801
+ var LOCAL_ONLY = new Routes();
1658
1802
  var LoopbackNet = class {
1659
1803
  bound = /* @__PURE__ */ new Map();
1804
+ dgrams = /* @__PURE__ */ new Map();
1660
1805
  activators = /* @__PURE__ */ new Map();
1661
1806
  internal = /* @__PURE__ */ new Set();
1662
1807
  reserved = /* @__PURE__ */ new Set();
@@ -1669,7 +1814,31 @@ var LoopbackNet = class {
1669
1814
  uplinkFor(host) {
1670
1815
  const route = this.route;
1671
1816
  if (!route) return void 0;
1672
- return route.routes.classify(host) === "uplink" ? route : void 0;
1817
+ return this.destination(host) === "uplink" ? route : void 0;
1818
+ }
1819
+ destination(host) {
1820
+ return (this.route?.routes ?? LOCAL_ONLY).classify(host);
1821
+ }
1822
+ bindDgram(socket, addr, kernel = false) {
1823
+ if (addr.family !== "inet") throw new KernelError("EAFNOSUPPORT");
1824
+ this.bindable(addr, kernel);
1825
+ const port = addr.port === 0 ? this.ephemeral() : addr.port;
1826
+ if (this.dgrams.has(port)) throw new KernelError("EADDRINUSE");
1827
+ this.dgrams.set(port, socket);
1828
+ return { ...addr, port };
1829
+ }
1830
+ releaseDgram(addr, socket) {
1831
+ if (this.dgrams.get(addr.port) === socket) this.dgrams.delete(addr.port);
1832
+ }
1833
+ deliverDgram(port, datagram2) {
1834
+ const target = this.dgrams.get(port);
1835
+ return target ? target.deliver(datagram2) : false;
1836
+ }
1837
+ bindable(addr, kernel) {
1838
+ if (addr.host !== "0.0.0.0" && !isLoopback(addr.host)) {
1839
+ throw new KernelError("EADDRNOTAVAIL");
1840
+ }
1841
+ if (!kernel && this.reserved.has(addr.port)) throw new KernelError("EADDRINUSE");
1673
1842
  }
1674
1843
  kernelOnly(port, reserve = false) {
1675
1844
  if (reserve) this.internal.add(port);
@@ -1707,11 +1876,7 @@ var LoopbackNet = class {
1707
1876
  bind(socket, addr, kernel = false) {
1708
1877
  if (addr.family === "unix") {
1709
1878
  if (addr.path === "") throw new KernelError("EINVAL");
1710
- } else if (addr.host !== "0.0.0.0" && !isLoopback(addr.host)) {
1711
- throw new KernelError("EADDRNOTAVAIL");
1712
- } else if (!kernel && this.reserved.has(addr.port)) {
1713
- throw new KernelError("EADDRINUSE");
1714
- }
1879
+ } else this.bindable(addr, kernel);
1715
1880
  const named = addr.family === "inet" && addr.port === 0 ? { ...addr, port: this.ephemeral() } : addr;
1716
1881
  const key = keyOf(named);
1717
1882
  if (this.bound.has(key)) throw new KernelError("EADDRINUSE");
@@ -1737,7 +1902,8 @@ var LoopbackNet = class {
1737
1902
  for (let i = 0; i < span; i++) {
1738
1903
  const port = this.nextEphemeral;
1739
1904
  this.nextEphemeral = port === EPHEMERAL_LAST ? EPHEMERAL_FIRST : port + 1;
1740
- if (!this.bound.has(`inet:${port}`) && !this.reserved.has(port)) return port;
1905
+ const taken = this.bound.has(`inet:${port}`) || this.dgrams.has(port);
1906
+ if (!taken && !this.reserved.has(port)) return port;
1741
1907
  }
1742
1908
  throw new KernelError("EADDRINUSE");
1743
1909
  }
@@ -3485,14 +3651,14 @@ function protectedPrefixes(paths) {
3485
3651
  }
3486
3652
  return [...new Set(paths.map((p) => normalizePath(p)))];
3487
3653
  }
3488
- function within(path, prefix) {
3654
+ function within2(path, prefix) {
3489
3655
  return prefix === "/" || path === prefix || path.startsWith(`${prefix}/`);
3490
3656
  }
3491
3657
  function isProtected(prefixes2, path) {
3492
- return prefixes2.some((prefix) => within(path, prefix));
3658
+ return prefixes2.some((prefix) => within2(path, prefix));
3493
3659
  }
3494
3660
  function holdsProtected(prefixes2, path) {
3495
- return prefixes2.some((prefix) => within(prefix, path));
3661
+ return prefixes2.some((prefix) => within2(prefix, path));
3496
3662
  }
3497
3663
  var MAX_LINKS4 = 40;
3498
3664
  function partsOf(path) {
@@ -3868,7 +4034,7 @@ function prefixes(path) {
3868
4034
  const parts2 = split(path);
3869
4035
  return parts2.map((_, i) => `/${parts2.slice(0, i + 1).join("/")}`);
3870
4036
  }
3871
- function within2(path, root) {
4037
+ function within3(path, root) {
3872
4038
  return path === root || path.startsWith(`${root}/`);
3873
4039
  }
3874
4040
  var LOCKED = /* @__PURE__ */ new Set(["NotReadableError", "NoModificationAllowedError", "InvalidStateError"]);
@@ -4308,11 +4474,11 @@ var OpfsFs = class {
4308
4474
  movePending(from, to) {
4309
4475
  if (to !== void 0) {
4310
4476
  for (const path of [...this.fileTimes.keys()]) {
4311
- if (within2(path, to)) this.fileTimes.delete(path);
4477
+ if (within3(path, to)) this.fileTimes.delete(path);
4312
4478
  }
4313
4479
  }
4314
4480
  for (const [path, ms] of [...this.fileTimes]) {
4315
- if (!within2(path, from)) continue;
4481
+ if (!within3(path, from)) continue;
4316
4482
  this.fileTimes.delete(path);
4317
4483
  if (to !== void 0) this.fileTimes.set(to + path.slice(from.length), ms);
4318
4484
  }
@@ -4736,7 +4902,7 @@ var OpfsFs = class {
4736
4902
  }
4737
4903
  async moveTo(source, parent, destination, to) {
4738
4904
  const sourceStat = await this.describe(source);
4739
- if (sourceStat.isDirectory && within2(destination, source.path)) throw fsError("EINVAL", to);
4905
+ if (sourceStat.isDirectory && within3(destination, source.path)) throw fsError("EINVAL", to);
4740
4906
  const target = await this.locate(destination, false).catch((err) => {
4741
4907
  if (missing2(err)) return null;
4742
4908
  throw err;
@@ -4985,7 +5151,7 @@ function pidOwner(proc) {
4985
5151
  function webLocks() {
4986
5152
  return globalThis.navigator?.locks;
4987
5153
  }
4988
- var within3 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
5154
+ var within4 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
4989
5155
  var parentOf5 = (path) => path.slice(0, path.lastIndexOf("/")) || "/";
4990
5156
  var lockName = (instance) => `slicc-unlinked:${instance}`;
4991
5157
  var dirOf = (root) => root === "/" ? `/${UNLINKED_DIR}` : `${root}/${UNLINKED_DIR}`;
@@ -5085,7 +5251,7 @@ var UnlinkedKeeper = class {
5085
5251
  for (const holder of this.holders) {
5086
5252
  if (holder.owns(target)) continue;
5087
5253
  for (const path of holder.held()) {
5088
- if (!within3(path, target) || holder.isKept(path)) continue;
5254
+ if (!within4(path, target) || holder.isKept(path)) continue;
5089
5255
  held.set(path, [...held.get(path) ?? [], holder]);
5090
5256
  }
5091
5257
  }
@@ -6316,60 +6482,6 @@ function binfmtOf(binfmts, path) {
6316
6482
  return best === void 0 ? void 0 : binfmts.get(best);
6317
6483
  }
6318
6484
 
6319
- // src/kernel/net/loopback-names.ts
6320
- var HOST_LOOPBACK = "host.slicc.internal";
6321
- var HOST_LOOPBACK_ADDRESS = [10, 0, 2, 2];
6322
- var PAGE_LOOPBACK = "kernel.localhost";
6323
- var PAGE_LOOPBACK_URL = `http://{port}.${PAGE_LOOPBACK}`;
6324
- function toHostLoopback(href) {
6325
- const url = new URL(href);
6326
- if (url.hostname.replace(/\.$/, "").toLowerCase() !== HOST_LOOPBACK) return href;
6327
- url.hostname = "127.0.0.1";
6328
- return url.href;
6329
- }
6330
- var LOOPBACK_V4 = /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;
6331
- var LOOPBACK_V6 = /* @__PURE__ */ new Set(["::1", "::", "0:0:0:0:0:0:0:1"]);
6332
- var MAPPED_V4 = /^::ffff:7f[0-9a-f]{2}:[0-9a-f]{1,4}$/;
6333
- var KERNEL_PORT = /^(\d{1,5})\.kernel\.localhost$/;
6334
- var SCHEMES = {
6335
- "http:": 80,
6336
- "ws:": 80,
6337
- "https:": 443,
6338
- "wss:": 443
6339
- };
6340
- function isKernelLoopbackHost(hostname) {
6341
- const host = hostname.replace(/^\[(.*)\]$/, "$1").replace(/\.$/, "").toLowerCase();
6342
- return host === "localhost" || host.endsWith(".localhost") || host === "0.0.0.0" || LOOPBACK_V4.test(host) || LOOPBACK_V6.has(host) || MAPPED_V4.test(host);
6343
- }
6344
- function kernelLoopbackPort(href) {
6345
- let url;
6346
- try {
6347
- url = new URL(href);
6348
- } catch {
6349
- return void 0;
6350
- }
6351
- const fallback = SCHEMES[url.protocol];
6352
- if (fallback === void 0 || !isKernelLoopbackHost(url.hostname)) return void 0;
6353
- const named = KERNEL_PORT.exec(url.hostname.replace(/\.$/, "").toLowerCase())?.[1];
6354
- const port = Number(named ?? (url.port || fallback));
6355
- return port >= 1 && port <= 65535 ? port : void 0;
6356
- }
6357
- var DEFAULT_HOSTNAME = "slicc";
6358
- var HOSTNAME_ADDRESS = "127.0.1.1";
6359
- var LIBC_HOSTNAMES = ["emscripten", "wasmer.sh"];
6360
- var LABEL = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
6361
- function reserved(name2) {
6362
- const lower = name2.toLowerCase();
6363
- return lower === "localhost" || lower.endsWith(".localhost") || lower === HOST_LOOPBACK || lower === "wasmer.sh" || /^\d+(\.\d+){3}$/.test(lower);
6364
- }
6365
- function isHostname(name2) {
6366
- if (typeof name2 !== "string" || name2.length > 253 || reserved(name2)) return false;
6367
- return name2.split(".").every((l) => LABEL.test(l));
6368
- }
6369
- function ownNames(hostname) {
6370
- return [.../* @__PURE__ */ new Set([hostname.toLowerCase(), ...LIBC_HOSTNAMES])];
6371
- }
6372
-
6373
6485
  // src/kernel/net/proxy-service.ts
6374
6486
  var SEND_BUFFER2 = 1024 * 1024;
6375
6487
  var TLS_HANDSHAKE = 22;
@@ -7371,7 +7483,7 @@ var DEFAULT_UMASK = 18;
7371
7483
  function isModule(bytes) {
7372
7484
  return MAGIC.every((b, i) => bytes[i] === b);
7373
7485
  }
7374
- function within4(path, dir) {
7486
+ function within5(path, dir) {
7375
7487
  return path.startsWith(dir === "/" ? "/" : `${dir}/`);
7376
7488
  }
7377
7489
  var FreshModules = class {
@@ -7395,10 +7507,10 @@ var FreshModules = class {
7395
7507
  const target = normalizePath(to);
7396
7508
  if (source === target) return;
7397
7509
  for (const key of [...this.fresh.keys()]) {
7398
- if (key === target || within4(key, target)) this.fresh.delete(key);
7510
+ if (key === target || within5(key, target)) this.fresh.delete(key);
7399
7511
  }
7400
7512
  for (const [key, fresh2] of [...this.fresh]) {
7401
- if (key !== source && !within4(key, source)) continue;
7513
+ if (key !== source && !within5(key, source)) continue;
7402
7514
  this.fresh.delete(key);
7403
7515
  this.fresh.set(target + key.slice(source.length), fresh2);
7404
7516
  }
@@ -7440,7 +7552,7 @@ var FreshModules = class {
7440
7552
  await fs.rm(path, options);
7441
7553
  const at = normalizePath(path);
7442
7554
  for (const key of [...this.fresh.keys()]) {
7443
- if (key === at || within4(key, at)) this.fresh.delete(key);
7555
+ if (key === at || within5(key, at)) this.fresh.delete(key);
7444
7556
  }
7445
7557
  },
7446
7558
  rename: async (from, to) => {
@@ -8221,7 +8333,7 @@ function linkedTo(mount, path) {
8221
8333
  return own?.kind !== "directory" && (attr2.nlink ?? 1) > 1;
8222
8334
  };
8223
8335
  }
8224
- var within5 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
8336
+ var within6 = (path, target) => path === target || target === "/" || path.startsWith(`${target}/`);
8225
8337
  var HeldSet = class extends Set {
8226
8338
  idle;
8227
8339
  constructor(idle) {
@@ -8240,7 +8352,7 @@ var HeldSet = class extends Set {
8240
8352
  }
8241
8353
  };
8242
8354
  function heldUnder(held, target) {
8243
- for (const paths of held) for (const path of paths.keys()) if (within5(path, target)) return true;
8355
+ for (const paths of held) for (const path of paths.keys()) if (within6(path, target)) return true;
8244
8356
  return false;
8245
8357
  }
8246
8358
  var HeldPaths = class extends Map {
@@ -8356,24 +8468,24 @@ var HeldPaths = class extends Map {
8356
8468
  return this.owning === path;
8357
8469
  }
8358
8470
  revoke(prefix) {
8359
- for (const path of this.keys()) if (within5(path, prefix)) this.revoked.add(path);
8471
+ for (const path of this.keys()) if (within6(path, prefix)) this.revoked.add(path);
8360
8472
  }
8361
8473
  renamed(from, to) {
8362
8474
  for (const [path, count2] of [...this]) {
8363
- if (!within5(path, from)) continue;
8475
+ if (!within6(path, from)) continue;
8364
8476
  const moved2 = to + path.slice(from.length);
8365
8477
  this.delete(path);
8366
8478
  this.set(moved2, (this.get(moved2) ?? 0) + count2);
8367
8479
  if (this.revoked.delete(path)) this.revoked.add(moved2);
8368
8480
  }
8369
8481
  for (const [path, count2] of [...this.opens]) {
8370
- if (!within5(path, from)) continue;
8482
+ if (!within6(path, from)) continue;
8371
8483
  const moved2 = to + path.slice(from.length);
8372
8484
  this.opens.delete(path);
8373
8485
  this.opens.set(moved2, (this.opens.get(moved2) ?? 0) + count2);
8374
8486
  }
8375
8487
  for (const [id2, key] of this.handles) {
8376
- if (within5(key, from)) this.handles.set(id2, to + key.slice(from.length));
8488
+ if (within6(key, from)) this.handles.set(id2, to + key.slice(from.length));
8377
8489
  }
8378
8490
  }
8379
8491
  isRevoked(path) {
@@ -8478,13 +8590,13 @@ var MountTable = class {
8478
8590
  });
8479
8591
  }
8480
8592
  under(path) {
8481
- for (const mount of this.mounts.values()) if (within5(mount.target, path)) return true;
8593
+ for (const mount of this.mounts.values()) if (within6(mount.target, path)) return true;
8482
8594
  return false;
8483
8595
  }
8484
8596
  locate(path) {
8485
8597
  let best;
8486
8598
  for (const mount of this.mounts.values()) {
8487
- if (within5(path, mount.target) && (!best || mount.target.length > best.target.length))
8599
+ if (within6(path, mount.target) && (!best || mount.target.length > best.target.length))
8488
8600
  best = mount;
8489
8601
  }
8490
8602
  if (!best) return void 0;
@@ -8627,7 +8739,7 @@ var MountTable = class {
8627
8739
  async commitUnder(prefix) {
8628
8740
  const at = normalizePath(prefix);
8629
8741
  await Promise.all(
8630
- [...this.sessions.keys()].filter((p) => within5(p, at)).map((p) => this.commit(p))
8742
+ [...this.sessions.keys()].filter((p) => within6(p, at)).map((p) => this.commit(p))
8631
8743
  );
8632
8744
  }
8633
8745
  async commitOverlapping(real, offset, length2) {
@@ -8664,11 +8776,11 @@ var MountTable = class {
8664
8776
  mount.epoch++;
8665
8777
  const linked2 = mount.caps.links ? linkedTo(mount, path) : () => false;
8666
8778
  for (const [key, cached] of [...mount.attrs])
8667
- if (within5(key, path) || linked2(cached.value)) mount.attrs.delete(key);
8779
+ if (within6(key, path) || linked2(cached.value)) mount.attrs.delete(key);
8668
8780
  mount.lists.delete(path);
8669
8781
  mount.lists.delete(parentOf7(path));
8670
8782
  for (const [key, cached] of [...mount.lists])
8671
- if (within5(key, path) || cached.value.some((e) => linked2(e.attr))) mount.lists.delete(key);
8783
+ if (within6(key, path) || cached.value.some((e) => linked2(e.attr))) mount.lists.delete(key);
8672
8784
  }
8673
8785
  async getattr(mount, path, rel2, settled = false) {
8674
8786
  for (let round = 1; ; round++) {
@@ -8808,7 +8920,7 @@ var MountFs = class _MountFs {
8808
8920
  if (redirects >= MAX_LINKS7) throw errnoError("ELOOP", paths[0]);
8809
8921
  const at = paths.map((path) => {
8810
8922
  const real = normalizePath(path);
8811
- return within5(real, moved2.of) ? normalizePath(moved2.to + real.slice(moved2.of.length)) : real;
8923
+ return within6(real, moved2.of) ? normalizePath(moved2.to + real.slice(moved2.of.length)) : real;
8812
8924
  });
8813
8925
  return again(at, redirects + 1);
8814
8926
  }
@@ -10401,6 +10513,282 @@ var ProcessClocks = class {
10401
10513
  }
10402
10514
  };
10403
10515
 
10516
+ // src/kernel/net/uplink.ts
10517
+ function carriesUdp(uplink) {
10518
+ return uplink.traits.udp === true && typeof uplink.udp === "function";
10519
+ }
10520
+ function answerOf(result) {
10521
+ const answer = Array.isArray(result) ? { addresses: result } : result;
10522
+ const addresses = Array.isArray(answer?.addresses) ? answer.addresses.filter((a) => typeof a === "string") : [];
10523
+ return {
10524
+ addresses,
10525
+ ...typeof answer?.ttl === "number" && answer.ttl >= 0 ? { ttl: answer.ttl } : {}
10526
+ };
10527
+ }
10528
+ var NEVER = new Routes(true);
10529
+
10530
+ // src/kernel/dgram.ts
10531
+ var DGRAM_QUEUE_BYTES = 256 * 1024;
10532
+ var DGRAM_QUEUE_COUNT = 512;
10533
+ var DGRAM_SEND_ROOM = 256 * 1024;
10534
+ var MAX_DATAGRAM_V4 = 65507;
10535
+ var MAX_DATAGRAM_V6 = 65527;
10536
+ var SOL_SOCKET2 = 1;
10537
+ var IPPROTO_IP = 0;
10538
+ var SO_TYPE2 = 3;
10539
+ var SO_ERROR2 = 4;
10540
+ var SO_BROADCAST = 6;
10541
+ var SO_SNDBUF2 = 7;
10542
+ var SO_RCVBUF2 = 8;
10543
+ var SO_DOMAIN2 = 39;
10544
+ var SOCK_DGRAM = 2;
10545
+ var AF_INET2 = 2;
10546
+ var IP_MULTICAST_FIRST = 32;
10547
+ var IP_MULTICAST_LAST = 36;
10548
+ var SEND_ERRORS = ["ENETUNREACH", "EHOSTUNREACH", "EACCES", "EMSGSIZE"];
10549
+ var UNREACHABLE = ["EACCES", "ENETUNREACH", "EHOSTUNREACH"];
10550
+ function sendError(e) {
10551
+ if (e instanceof KernelError) return e;
10552
+ const code = e?.code;
10553
+ return new KernelError(SEND_ERRORS.find((errno2) => errno2 === code) ?? "EHOSTUNREACH");
10554
+ }
10555
+ function sameAddr(a, b) {
10556
+ return a.port === b.port && canonicalHost(a.host) === canonicalHost(b.host);
10557
+ }
10558
+ function familyOf(host) {
10559
+ return host.includes(":") ? 6 : 4;
10560
+ }
10561
+ var KernelDgram = class {
10562
+ local;
10563
+ peer;
10564
+ net;
10565
+ queue = [];
10566
+ queued = 0;
10567
+ pending;
10568
+ closed = false;
10569
+ sending = 0;
10570
+ changes = new Changes();
10571
+ options = /* @__PURE__ */ new Map();
10572
+ uplinks = /* @__PURE__ */ new Map();
10573
+ stop = new AbortController();
10574
+ constructor(net) {
10575
+ this.net = net;
10576
+ }
10577
+ bind(addr) {
10578
+ if (this.local) throw new KernelError("EINVAL");
10579
+ this.local = this.net.bindDgram(this, addr);
10580
+ }
10581
+ bound() {
10582
+ this.local ??= this.net.bindDgram(this, { family: "inet", host: "0.0.0.0", port: 0 });
10583
+ return this.local;
10584
+ }
10585
+ connectTo(addr) {
10586
+ if (addr.family !== "inet") throw new KernelError("EAFNOSUPPORT");
10587
+ this.route(addr.host);
10588
+ this.bound();
10589
+ this.peer = { family: "inet", host: canonicalHost(addr.host), port: addr.port };
10590
+ this.pending = void 0;
10591
+ this.drain((d) => sameAddr(d.from, addr));
10592
+ }
10593
+ drain(keep2) {
10594
+ const kept = this.queue.filter(keep2);
10595
+ this.queue.splice(0, this.queue.length, ...kept);
10596
+ this.queued = kept.reduce((sum, d) => sum + d.bytes.length, 0);
10597
+ }
10598
+ route(host) {
10599
+ const where = this.net.destination(host);
10600
+ if (where === "loopback" || where === "uplink") return where;
10601
+ throw new KernelError("ENETUNREACH");
10602
+ }
10603
+ takeError() {
10604
+ const pending = this.pending;
10605
+ this.pending = void 0;
10606
+ return pending;
10607
+ }
10608
+ async send(bytes, to) {
10609
+ if (this.closed) throw new KernelError("EBADF");
10610
+ const pending = this.takeError();
10611
+ if (pending) throw new KernelError(pending);
10612
+ const target = to ?? this.peer;
10613
+ if (!target) throw new KernelError("EDESTADDRREQ");
10614
+ if (target.family !== "inet") throw new KernelError("EAFNOSUPPORT");
10615
+ const max = familyOf(target.host) === 6 ? MAX_DATAGRAM_V6 : MAX_DATAGRAM_V4;
10616
+ if (bytes.length > max) throw new KernelError("EMSGSIZE");
10617
+ const where = this.route(target.host);
10618
+ const local = this.bound();
10619
+ if (where === "uplink") return this.sendUp(bytes, target);
10620
+ const from = {
10621
+ family: "inet",
10622
+ host: local.host === "0.0.0.0" ? "127.0.0.1" : local.host,
10623
+ port: local.port
10624
+ };
10625
+ const delivered = this.net.deliverDgram(target.port, { bytes: bytes.slice(), from });
10626
+ if (!delivered && this.peer && sameAddr(this.peer, target)) {
10627
+ this.pending = "ECONNREFUSED";
10628
+ this.changes.wake();
10629
+ }
10630
+ return bytes.length;
10631
+ }
10632
+ async sendUp(bytes, to) {
10633
+ const uplink = this.net.uplinkFor(to.host)?.uplink;
10634
+ if (!uplink || !carriesUdp(uplink)) throw new KernelError("ENETUNREACH");
10635
+ this.sending += bytes.length;
10636
+ try {
10637
+ const socket = await this.uplinkSocket(uplink, familyOf(to.host));
10638
+ await socket.send(bytes.slice(), to.host, to.port);
10639
+ } catch (e) {
10640
+ throw sendError(e);
10641
+ } finally {
10642
+ this.sending -= bytes.length;
10643
+ this.changes.wake();
10644
+ }
10645
+ return bytes.length;
10646
+ }
10647
+ uplinkSocket(uplink, family) {
10648
+ const known = this.uplinks.get(family);
10649
+ if (known !== void 0) return known;
10650
+ const opening2 = Promise.resolve().then(() => {
10651
+ if (!uplink.udp) throw new KernelError("ENETUNREACH");
10652
+ return uplink.udp({ family, signal: this.stop.signal });
10653
+ });
10654
+ this.uplinks.set(family, opening2);
10655
+ opening2.then(
10656
+ (socket) => {
10657
+ if (this.closed) {
10658
+ socket.close();
10659
+ return;
10660
+ }
10661
+ void this.pump(socket, family);
10662
+ },
10663
+ () => this.uplinks.delete(family)
10664
+ );
10665
+ return opening2;
10666
+ }
10667
+ async pump(socket, family) {
10668
+ const iterate = async () => {
10669
+ for await (const message3 of socket.messages) {
10670
+ if (this.closed) return;
10671
+ const from = { family: "inet", host: message3.host, port: message3.port };
10672
+ if ("bytes" in message3) this.deliver({ bytes: message3.bytes, from });
10673
+ else this.unreachable(message3.error, from);
10674
+ }
10675
+ };
10676
+ await iterate().catch(() => void 0);
10677
+ if (!this.closed) this.uplinks.delete(family);
10678
+ socket.close();
10679
+ }
10680
+ unreachable(error, from) {
10681
+ if (!this.peer || !sameAddr(this.peer, from)) return;
10682
+ this.pending = UNREACHABLE.find((errno2) => errno2 === error) ?? "EHOSTUNREACH";
10683
+ this.changes.wake();
10684
+ }
10685
+ deliver(datagram2) {
10686
+ if (this.closed) return false;
10687
+ if (this.peer && !sameAddr(this.peer, datagram2.from)) return true;
10688
+ const full = this.queue.length >= DGRAM_QUEUE_COUNT || this.queued + datagram2.bytes.length > DGRAM_QUEUE_BYTES;
10689
+ if (full) return true;
10690
+ this.queue.push(datagram2);
10691
+ this.queued += datagram2.bytes.length;
10692
+ this.changes.wake();
10693
+ return true;
10694
+ }
10695
+ async receive(max, peek, signal) {
10696
+ for (; ; ) {
10697
+ const pending = this.takeError();
10698
+ if (pending) throw new KernelError(pending);
10699
+ if (this.closed) throw new KernelError("EBADF");
10700
+ const next2 = this.queue[0];
10701
+ if (next2) {
10702
+ if (!peek) {
10703
+ this.queue.shift();
10704
+ this.queued -= next2.bytes.length;
10705
+ this.changes.wake();
10706
+ }
10707
+ const size = Math.max(0, max);
10708
+ return {
10709
+ bytes: next2.bytes.slice(0, size),
10710
+ from: next2.from,
10711
+ truncated: next2.bytes.length > size
10712
+ };
10713
+ }
10714
+ await this.changes.wait(signal);
10715
+ }
10716
+ }
10717
+ async read(max, signal) {
10718
+ return (await this.receive(max, false, signal)).bytes;
10719
+ }
10720
+ async peek(max, signal) {
10721
+ return (await this.receive(max, true, signal)).bytes;
10722
+ }
10723
+ write(bytes) {
10724
+ return this.send(bytes);
10725
+ }
10726
+ poll() {
10727
+ return {
10728
+ readable: this.queue.length > 0 || this.pending !== void 0 || this.closed,
10729
+ writable: this.sending < DGRAM_SEND_ROOM,
10730
+ hangup: this.closed,
10731
+ ...this.pending !== void 0 ? { error: true } : {}
10732
+ };
10733
+ }
10734
+ changed(signal) {
10735
+ return this.changes.wait(signal);
10736
+ }
10737
+ shutdown(how) {
10738
+ if (!this.peer) throw new KernelError("ENOTCONN");
10739
+ if (how < 0 || how > 2) throw new KernelError("EINVAL");
10740
+ }
10741
+ getOption(level, name2) {
10742
+ if (level === SOL_SOCKET2) {
10743
+ switch (name2) {
10744
+ case SO_TYPE2:
10745
+ return SOCK_DGRAM;
10746
+ case SO_ERROR2: {
10747
+ const pending = this.takeError();
10748
+ return pending ? wasiErrno(pending) : 0;
10749
+ }
10750
+ case SO_DOMAIN2:
10751
+ return AF_INET2;
10752
+ case SO_BROADCAST:
10753
+ return 0;
10754
+ case SO_SNDBUF2:
10755
+ return this.options.get(`${level}:${name2}`) ?? DGRAM_SEND_ROOM;
10756
+ case SO_RCVBUF2:
10757
+ return this.options.get(`${level}:${name2}`) ?? DGRAM_QUEUE_BYTES;
10758
+ }
10759
+ }
10760
+ if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
10761
+ throw new KernelError("ENOPROTOOPT");
10762
+ }
10763
+ return this.options.get(`${level}:${name2}`) ?? 0;
10764
+ }
10765
+ setOption(level, name2, value) {
10766
+ if (level === SOL_SOCKET2 && name2 === SO_BROADCAST && value !== 0) {
10767
+ throw new KernelError("EOPNOTSUPP");
10768
+ }
10769
+ if (level === IPPROTO_IP && name2 >= IP_MULTICAST_FIRST && name2 <= IP_MULTICAST_LAST) {
10770
+ throw new KernelError("ENOPROTOOPT");
10771
+ }
10772
+ this.options.set(`${level}:${name2}`, value);
10773
+ }
10774
+ close() {
10775
+ if (this.closed) return;
10776
+ this.closed = true;
10777
+ this.stop.abort();
10778
+ for (const opening2 of this.uplinks.values()) {
10779
+ opening2.then(
10780
+ (socket) => socket.close(),
10781
+ () => void 0
10782
+ );
10783
+ }
10784
+ this.uplinks.clear();
10785
+ if (this.local) this.net.releaseDgram(this.local, this);
10786
+ this.queue.length = 0;
10787
+ this.queued = 0;
10788
+ this.changes.wake();
10789
+ }
10790
+ };
10791
+
10404
10792
  // src/fs/pages.ts
10405
10793
  var PAGE = 64 * 1024;
10406
10794
  var READAHEAD = 1024 * 1024;
@@ -10659,7 +11047,7 @@ function mountTable(mounts = []) {
10659
11047
  }
10660
11048
 
10661
11049
  // src/version.ts
10662
- var KERNEL_VERSION = true ? "1.95.3" : "0.0.0-dev";
11050
+ var KERNEL_VERSION = true ? "1.97.0" : "0.0.0-dev";
10663
11051
  var OS_RELEASE = "6.0.0-slicc";
10664
11052
  function procVersion() {
10665
11053
  return `Linux version ${OS_RELEASE} (slicc-kernel ${KERNEL_VERSION}) #1 SMP PREEMPT_DYNAMIC
@@ -11146,6 +11534,7 @@ function levelOf(file) {
11146
11534
  if (state.readable) bits2 |= EPOLL.IN;
11147
11535
  if (state.writable) bits2 |= EPOLL.OUT;
11148
11536
  if (state.rdhup) bits2 |= EPOLL.RDHUP;
11537
+ if (state.error) bits2 |= EPOLL.ERR;
11149
11538
  if (state.hangup) bits2 |= file.read ? EPOLL.HUP : EPOLL.ERR;
11150
11539
  return bits2;
11151
11540
  }
@@ -12839,106 +13228,6 @@ var HttpHandles = class {
12839
13228
  }
12840
13229
  };
12841
13230
 
12842
- // src/kernel/net/routes.ts
12843
- var HOST_ADDRESS = HOST_LOOPBACK_ADDRESS.join(".");
12844
- function ipv4Bytes(text) {
12845
- const parts2 = text.split(".");
12846
- if (parts2.length !== 4 || !parts2.every((p) => /^\d{1,3}$/.test(p))) return void 0;
12847
- const bytes = parts2.map(Number);
12848
- return bytes.every((b) => b <= 255) ? bytes : void 0;
12849
- }
12850
- function ipv6Groups(text) {
12851
- if (!text.includes(":")) return void 0;
12852
- let canonical;
12853
- try {
12854
- canonical = new URL(`http://[${text}]/`).hostname.slice(1, -1);
12855
- } catch {
12856
- return void 0;
12857
- }
12858
- const [head, tail] = canonical.split("::");
12859
- const part = (s) => s ? s.split(":").map((h) => Number.parseInt(h, 16)) : [];
12860
- const front = part(head);
12861
- const back = part(tail);
12862
- return tail === void 0 ? front : [...front, ...new Array(8 - front.length - back.length).fill(0), ...back];
12863
- }
12864
- function toBits(values, width) {
12865
- return values.reduce((acc, v) => acc << BigInt(width) | BigInt(v), 0n);
12866
- }
12867
- function addressBits(host) {
12868
- const v4 = ipv4Bytes(host);
12869
- if (v4) return { v6: false, bits: toBits(v4, 8) };
12870
- const v6 = ipv6Groups(host);
12871
- return v6 ? { v6: true, bits: toBits(v6, 16) } : void 0;
12872
- }
12873
- function parsePrefix(text) {
12874
- const parts2 = text.trim().split("/");
12875
- if (parts2.length > 2) return void 0;
12876
- const [address = "", size] = parts2;
12877
- const parsed2 = addressBits(address);
12878
- if (!parsed2) return void 0;
12879
- const max = parsed2.v6 ? 128 : 32;
12880
- const length2 = size === void 0 ? max : /^\d{1,3}$/.test(size) ? Number(size) : -1;
12881
- if (length2 < 0 || length2 > max) return void 0;
12882
- return { ...parsed2, length: length2 };
12883
- }
12884
- function within6(prefix, addr) {
12885
- if (prefix.v6 !== addr.v6) return false;
12886
- const shift = BigInt((prefix.v6 ? 128 : 32) - prefix.length);
12887
- return addr.bits >> shift === prefix.bits >> shift;
12888
- }
12889
- var RESERVED = [
12890
- "0.0.0.0/8",
12891
- "127.0.0.0/8",
12892
- "169.254.0.0/16",
12893
- "224.0.0.0/4",
12894
- "240.0.0.0/4",
12895
- "::/96",
12896
- "::ffff:0:0/96",
12897
- "fe80::/10",
12898
- "ff00::/8"
12899
- ].map((p) => parsePrefix(p));
12900
- var Routes = class {
12901
- table = [];
12902
- exit = false;
12903
- ipv6;
12904
- constructor(ipv62 = false) {
12905
- this.ipv6 = ipv62;
12906
- }
12907
- set(table) {
12908
- const parsed2 = (table.prefixes ?? []).map(parsePrefix);
12909
- const bad = (table.prefixes ?? []).find((_, i) => !parsed2[i]);
12910
- if (bad !== void 0) throw new Error(`not an address prefix: ${bad}`);
12911
- this.table = parsed2;
12912
- this.exit = table.exit === true;
12913
- }
12914
- classify(host) {
12915
- const name2 = host.toLowerCase().replace(/\.$/, "");
12916
- if (name2 === "localhost" || name2.endsWith(".localhost") || name2 === "0.0.0.0") {
12917
- return "loopback";
12918
- }
12919
- if (name2 === HOST_LOOPBACK || name2 === HOST_ADDRESS) return "host";
12920
- const addr = addressBits(name2);
12921
- if (!addr) return "unreachable";
12922
- if (addr.v6 && addr.bits === 1n) return "loopback";
12923
- if (!addr.v6 && addr.bits >> 24n === 127n) return "loopback";
12924
- if (RESERVED.some((p) => within6(p, addr))) return "reserved";
12925
- if (addr.v6 && !this.ipv6) return "unreachable";
12926
- if (this.exit || this.table.some((p) => within6(p, addr))) return "uplink";
12927
- return "unreachable";
12928
- }
12929
- };
12930
-
12931
- // src/kernel/net/uplink.ts
12932
- function answerOf(result) {
12933
- const answer = Array.isArray(result) ? { addresses: result } : result;
12934
- const addresses = Array.isArray(answer?.addresses) ? answer.addresses.filter((a) => typeof a === "string") : [];
12935
- return {
12936
- addresses,
12937
- ...typeof answer?.ttl === "number" && answer.ttl >= 0 ? { ttl: answer.ttl } : {}
12938
- };
12939
- }
12940
- var NEVER = new Routes(true);
12941
-
12942
13231
  // src/kernel/net/resolver.ts
12943
13232
  var RESOLVE_TTL_MAX = 60;
12944
13233
  var RESOLVE_TIMEOUT_MS = 1e4;
@@ -13675,6 +13964,19 @@ async function waitReady(fds, read, write, deadline, interrupted3) {
13675
13964
  }
13676
13965
  }
13677
13966
 
13967
+ // src/kernel/net/datagram-wire.ts
13968
+ function encodeReceived(got) {
13969
+ const host = new TextEncoder().encode(got.host);
13970
+ const out = new Uint8Array(4 + host.length + got.bytes.length);
13971
+ const view2 = new DataView(out.buffer);
13972
+ view2.setUint8(0, got.truncated ? 1 : 0);
13973
+ view2.setUint16(1, got.port);
13974
+ view2.setUint8(3, host.length);
13975
+ out.set(host, 4);
13976
+ out.set(got.bytes, 4 + host.length);
13977
+ return out;
13978
+ }
13979
+
13678
13980
  // src/kernel/socket-syscalls.ts
13679
13981
  var SOCKET_OPS = [
13680
13982
  "sock-open",
@@ -13687,19 +13989,36 @@ var SOCKET_OPS = [
13687
13989
  "sock-name",
13688
13990
  "sock-getopt",
13689
13991
  "sock-setopt",
13690
- "sock-resolve"
13992
+ "sock-resolve",
13993
+ "sock-sendto",
13994
+ "sock-recvfrom"
13691
13995
  ];
13692
13996
  function socketAt(fds, fd) {
13997
+ const file = anySocketAt(fds, fd);
13998
+ if (file instanceof KernelDgram) throw new KernelError("EOPNOTSUPP");
13999
+ return file;
14000
+ }
14001
+ function anySocketAt(fds, fd) {
13693
14002
  const file = fds.get(fd).file;
13694
- if (!(file instanceof KernelSocket)) throw new KernelError("ENOTSOCK");
14003
+ if (!(file instanceof KernelSocket) && !(file instanceof KernelDgram)) {
14004
+ throw new KernelError("ENOTSOCK");
14005
+ }
14006
+ return file;
14007
+ }
14008
+ function dgramAt(fds, fd) {
14009
+ const file = anySocketAt(fds, fd);
14010
+ if (!(file instanceof KernelDgram)) throw new KernelError("EOPNOTSUPP");
13695
14011
  return file;
13696
14012
  }
13697
14013
  var ok2 = (json5) => json5 === void 0 ? { ok: true, kind: "void" } : { ok: true, kind: "json", json: json5 };
13698
14014
  async function socketSyscall(req, proc) {
13699
14015
  const { fds, net } = proc;
13700
14016
  switch (req.op) {
13701
- case "sock-open":
13702
- return ok2(fds.install(new OpenFile(net.socket(req.domain)), 3));
14017
+ case "sock-open": {
14018
+ if (req.type === "dgram" && req.domain !== "inet") throw new KernelError("EPROTONOSUPPORT");
14019
+ const file = req.type === "dgram" ? new KernelDgram(net) : net.socket(req.domain);
14020
+ return ok2(fds.install(new OpenFile(file), 3));
14021
+ }
13703
14022
  case "sock-pair": {
13704
14023
  const [a, b] = KernelSocket.pair(net, req.domain, proc.cred?.());
13705
14024
  const first = fds.install(new OpenFile(a), 3);
@@ -13711,44 +14030,64 @@ async function socketSyscall(req, proc) {
13711
14030
  }
13712
14031
  }
13713
14032
  case "sock-bind":
13714
- socketAt(fds, req.fd).bind(req.addr);
14033
+ anySocketAt(fds, req.fd).bind(req.addr);
13715
14034
  return ok2();
13716
14035
  case "sock-listen":
13717
14036
  socketAt(fds, req.fd).listen(req.backlog, proc.cred?.());
13718
14037
  return ok2();
13719
14038
  case "sock-accept":
13720
14039
  return ok2(await accept(proc, req.fd, req.nonblock));
13721
- case "sock-connect": {
13722
- const socket = socketAt(fds, req.fd);
13723
- const stamp = net.facadeAt(req.addr) && await proc.stamp?.();
13724
- const dialing = socket.connect(req.addr, true, proc.cred?.(), stamp || void 0);
13725
- if (req.nonblock) {
13726
- dialing?.catch(() => void 0);
13727
- throw new KernelError("EINPROGRESS");
13728
- }
13729
- if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
13730
- return ok2();
13731
- }
14040
+ case "sock-connect":
14041
+ return connect(req, proc);
13732
14042
  case "sock-shutdown":
13733
- socketAt(fds, req.fd).shutdown(req.how);
14043
+ anySocketAt(fds, req.fd).shutdown(req.how);
13734
14044
  return ok2();
13735
14045
  case "sock-name": {
13736
- const socket = socketAt(fds, req.fd);
14046
+ const socket = anySocketAt(fds, req.fd);
13737
14047
  const addr = req.peer ? socket.peer : socket.local;
13738
14048
  if (req.peer && !addr) throw new KernelError("ENOTCONN");
13739
- return ok2(addr ?? unnamed(socket.domain));
14049
+ return ok2(addr ?? unnamed(socket instanceof KernelDgram ? "inet" : socket.domain));
13740
14050
  }
13741
14051
  case "sock-getopt":
13742
- return ok2(socketAt(fds, req.fd).getOption(req.level, req.name));
14052
+ return ok2(anySocketAt(fds, req.fd).getOption(req.level, req.name));
13743
14053
  case "sock-setopt":
13744
- socketAt(fds, req.fd).setOption(req.level, req.name, req.value);
14054
+ anySocketAt(fds, req.fd).setOption(req.level, req.name, req.value);
13745
14055
  return ok2();
13746
14056
  case "sock-resolve": {
13747
14057
  const addresses = await proc.resolve(req.name, req.family, () => proc.blocking());
13748
14058
  if (addresses.length === 0) throw new KernelError("ENOENT");
13749
14059
  return ok2(addresses);
13750
14060
  }
14061
+ default:
14062
+ return datagram(req, proc);
14063
+ }
14064
+ }
14065
+ async function connect(req, proc) {
14066
+ const target = anySocketAt(proc.fds, req.fd);
14067
+ if (target instanceof KernelDgram) {
14068
+ target.connectTo(req.addr);
14069
+ return ok2();
14070
+ }
14071
+ const stamp = proc.net.facadeAt(req.addr) && await proc.stamp?.();
14072
+ const dialing = target.connect(req.addr, true, proc.cred?.(), stamp || void 0);
14073
+ if (req.nonblock) {
14074
+ dialing?.catch(() => void 0);
14075
+ throw new KernelError("EINPROGRESS");
14076
+ }
14077
+ if (dialing !== void 0) await interruptible2(dialing, proc.blocking());
14078
+ return ok2();
14079
+ }
14080
+ async function datagram(req, proc) {
14081
+ const dgram = dgramAt(proc.fds, req.fd);
14082
+ if (req.op === "sock-sendto") {
14083
+ if (req.nonblock && !pollFile(dgram).writable) throw new KernelError("EAGAIN");
14084
+ return ok2(await dgram.send(req.body, req.addr));
13751
14085
  }
14086
+ const ready2 = pollFile(dgram).readable;
14087
+ if (req.nonblock && !ready2) throw new KernelError("EAGAIN");
14088
+ const got = await dgram.receive(req.max, req.peek, ready2 ? void 0 : proc.blocking());
14089
+ const { bytes, from, truncated } = got;
14090
+ return { ok: true, kind: "bytes", bytes: encodeReceived({ bytes, truncated, ...from }) };
13752
14091
  }
13753
14092
  function interruptible2(dialing, signal) {
13754
14093
  return new Promise((resolve2, reject) => {
@@ -15352,7 +15691,8 @@ var WasmProcess = class {
15352
15691
  const terminal = (file.tty ?? file.pty?.slave)?.name;
15353
15692
  return {
15354
15693
  tty: file.tty !== void 0,
15355
- kind: file instanceof KernelSocket ? "socket" : kernelFdKind(file),
15694
+ kind: file instanceof KernelSocket || file instanceof KernelDgram ? "socket" : kernelFdKind(file),
15695
+ ...file instanceof KernelDgram ? { dgram: true } : {},
15356
15696
  ...file.heldMeta ? { meta: file.heldMeta } : {},
15357
15697
  ...flags2 !== void 0 ? { flags: flags2 } : {},
15358
15698
  ...this.fds.closesOnExec(fd) ? { cloexec: true } : {},
@@ -15741,7 +16081,7 @@ function inheritedFds(fds) {
15741
16081
  return fds.numbers().filter((fd) => fd > 2 && !fds.get(fd).file.held).map((fd) => {
15742
16082
  const open = fds.get(fd);
15743
16083
  const flags2 = fds.statusFlags(fd);
15744
- const kind = open.file instanceof KernelSocket ? "socket" : kernelFdKind(open.file);
16084
+ const kind = open.file instanceof KernelSocket || open.file instanceof KernelDgram ? "socket" : kernelFdKind(open.file);
15745
16085
  const meta = open.file.heldMeta;
15746
16086
  return {
15747
16087
  fd,
@@ -18480,7 +18820,7 @@ function grantConnect(hook, fetch2, liveness = {}) {
18480
18820
  return new HostfsClient(first, grant, fetch2);
18481
18821
  };
18482
18822
  }
18483
- async function openHostfs(spec, connect, timing = {}) {
18823
+ async function openHostfs(spec, connect2, timing = {}) {
18484
18824
  const readonly2 = spec.options?.ro !== void 0;
18485
18825
  const { port1, port2 } = new MessageChannel();
18486
18826
  const slot2 = mediumSlot(
@@ -18507,7 +18847,7 @@ async function openHostfs(spec, connect, timing = {}) {
18507
18847
  );
18508
18848
  const watching = keepWatching(
18509
18849
  {
18510
- connect: () => connect(spec.source, { readonly: readonly2 }),
18850
+ connect: () => connect2(spec.source, { readonly: readonly2 }),
18511
18851
  up(client) {
18512
18852
  active = timed(client, timing.callMs ?? HOSTFS_CALL_MS);
18513
18853
  slot2.insert(client, handlers);