sandboxedjs 0.1.53 → 0.1.55

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.
@@ -33,7 +33,20 @@ var Op = {
33
33
  getcwd: 1281,
34
34
  chdir: 1282,
35
35
  environ: 1283,
36
- getrandom: 1536
36
+ getrandom: 1536,
37
+ socket: 1792,
38
+ bind: 1793,
39
+ listen: 1794,
40
+ accept: 1795,
41
+ connect: 1796,
42
+ send: 1797,
43
+ recv: 1798,
44
+ shutdown: 1799,
45
+ getsockname: 1800,
46
+ getpeername: 1801,
47
+ setsockopt: 1802,
48
+ getsockopt: 1803,
49
+ socket_close: 1804
37
50
  };
38
51
  var Errno = {
39
52
  EPERM: 1,
@@ -70,6 +83,15 @@ var Errno = {
70
83
  ENOTEMPTY: 39,
71
84
  ELOOP: 40,
72
85
  ENODATA: 61,
86
+ ENOTSOCK: 88,
87
+ EAFNOSUPPORT: 97,
88
+ EADDRINUSE: 98,
89
+ EADDRNOTAVAIL: 99,
90
+ ENETUNREACH: 101,
91
+ ECONNRESET: 104,
92
+ EISCONN: 106,
93
+ ENOTCONN: 107,
94
+ ECONNREFUSED: 111,
73
95
  EPROTO: 71,
74
96
  EOVERFLOW: 75,
75
97
  ETIMEDOUT: 110,
@@ -1079,6 +1101,170 @@ var FileService = class {
1079
1101
  }
1080
1102
  };
1081
1103
 
1104
+ // src/net/virtual-socket.ts
1105
+ var VirtualTcpSocket = class _VirtualTcpSocket {
1106
+ constructor(network) {
1107
+ this.network = network;
1108
+ }
1109
+ network;
1110
+ listenerValue = null;
1111
+ connectionValue = null;
1112
+ boundPort = 0;
1113
+ boundAddress = "0.0.0.0";
1114
+ bind(address, port) {
1115
+ if (this.listenerValue || this.connectionValue || this.boundPort !== 0) {
1116
+ throw new PosixError(Errno.EINVAL, "socket is already bound");
1117
+ }
1118
+ this.boundAddress = address;
1119
+ this.boundPort = port;
1120
+ }
1121
+ listen(backlog) {
1122
+ if (this.listenerValue) return;
1123
+ if (this.boundPort === 0) {
1124
+ const port = this.findEphemeralPort();
1125
+ this.boundPort = port;
1126
+ }
1127
+ this.listenerValue = this.network.listen(this.boundPort, this.boundAddress, backlog);
1128
+ }
1129
+ connect(port, address = "127.0.0.1") {
1130
+ if (this.listenerValue || this.connectionValue) throw new PosixError(Errno.EISCONN);
1131
+ this.connectionValue = this.network.connect(port, this.boundPort, this.boundAddress);
1132
+ this.boundPort = this.connectionValue.local.port;
1133
+ this.boundAddress = this.connectionValue.local.address;
1134
+ }
1135
+ accept() {
1136
+ if (!this.listenerValue) throw new PosixError(Errno.EINVAL, "socket is not listening");
1137
+ const accepted = this.listenerValue.accept().connection;
1138
+ const socket = new _VirtualTcpSocket(this.network);
1139
+ socket.connectionValue = accepted;
1140
+ socket.boundPort = accepted.local.port;
1141
+ socket.boundAddress = accepted.local.address;
1142
+ return socket;
1143
+ }
1144
+ get listener() {
1145
+ return this.listenerValue;
1146
+ }
1147
+ get connection() {
1148
+ return this.connectionValue;
1149
+ }
1150
+ get local() {
1151
+ return { address: this.boundAddress, port: this.boundPort };
1152
+ }
1153
+ get remote() {
1154
+ return this.connectionValue?.remote ?? null;
1155
+ }
1156
+ get readable() {
1157
+ return this.listenerValue?.readable ?? this.connectionValue?.readable ?? false;
1158
+ }
1159
+ get writable() {
1160
+ return this.connectionValue?.writable ?? false;
1161
+ }
1162
+ waitForChange() {
1163
+ return (this.listenerValue ?? this.connectionValue)?.waitForChange() ?? Promise.resolve();
1164
+ }
1165
+ read(length) {
1166
+ if (!this.connectionValue) throw new PosixError(Errno.EINVAL);
1167
+ return this.connectionValue.read(length);
1168
+ }
1169
+ write(data) {
1170
+ if (!this.connectionValue) throw new PosixError(Errno.EINVAL);
1171
+ return this.connectionValue.write(data);
1172
+ }
1173
+ shutdown(read, write) {
1174
+ if (!this.connectionValue) throw new PosixError(Errno.ENOTCONN);
1175
+ this.connectionValue.shutdown(read, write);
1176
+ }
1177
+ close() {
1178
+ if (this.listenerValue) this.network.close(this.listenerValue);
1179
+ this.connectionValue?.close();
1180
+ this.listenerValue = null;
1181
+ this.connectionValue = null;
1182
+ }
1183
+ findEphemeralPort() {
1184
+ for (let port = 49152; port <= 65535; port += 1) if (!this.network.hasListener(port)) return port;
1185
+ throw new PosixError(Errno.EADDRINUSE, "no ephemeral ports available");
1186
+ }
1187
+ };
1188
+ var SocketDescription = class _SocketDescription {
1189
+ kind = "socket";
1190
+ flags;
1191
+ resource;
1192
+ closed = false;
1193
+ options = /* @__PURE__ */ new Map();
1194
+ constructor(resource, flags = 0) {
1195
+ this.resource = resource;
1196
+ this.flags = flags;
1197
+ }
1198
+ get listener() {
1199
+ return this.resource.listener;
1200
+ }
1201
+ get connection() {
1202
+ return this.resource.connection;
1203
+ }
1204
+ read(length) {
1205
+ return this.resource.read(length);
1206
+ }
1207
+ write(data) {
1208
+ return this.resource.write(data);
1209
+ }
1210
+ readable() {
1211
+ return this.resource.readable;
1212
+ }
1213
+ writable() {
1214
+ return this.resource.writable;
1215
+ }
1216
+ whenReady() {
1217
+ return this.resource.waitForChange();
1218
+ }
1219
+ stat() {
1220
+ return {
1221
+ ino: 0,
1222
+ mode: 49663,
1223
+ size: 0,
1224
+ uid: 0,
1225
+ gid: 0,
1226
+ nlink: 1,
1227
+ atimeMs: Date.now(),
1228
+ mtimeMs: Date.now(),
1229
+ ctimeMs: Date.now()
1230
+ };
1231
+ }
1232
+ close() {
1233
+ if (this.closed) return;
1234
+ this.closed = true;
1235
+ this.resource.close();
1236
+ }
1237
+ bind(address, port) {
1238
+ this.resource.bind(address, port);
1239
+ }
1240
+ listen(backlog) {
1241
+ this.resource.listen(backlog);
1242
+ }
1243
+ connect(address, port) {
1244
+ this.resource.connect(port, address);
1245
+ }
1246
+ accept() {
1247
+ return new _SocketDescription(this.resource.accept(), this.flags);
1248
+ }
1249
+ shutdown(read, write) {
1250
+ this.resource.shutdown(read, write);
1251
+ }
1252
+ setOption(level, option, value) {
1253
+ this.options.set(`${level}:${option}`, value.slice());
1254
+ }
1255
+ getOption(level, option) {
1256
+ return this.options.get(`${level}:${option}`)?.slice() ?? new Uint8Array(4);
1257
+ }
1258
+ localAddress() {
1259
+ return this.resource.local;
1260
+ }
1261
+ peerAddress() {
1262
+ const peer = this.resource.remote;
1263
+ if (!peer) throw new PosixError(Errno.ENOTCONN);
1264
+ return peer;
1265
+ }
1266
+ };
1267
+
1082
1268
  // src/runtime/python/syscall-server.ts
1083
1269
  var MAX_TRANSFER = 8 * 1024 * 1024;
1084
1270
  function createHostAbiServer(proc) {
@@ -1325,12 +1511,76 @@ function createHostAbiServer(proc) {
1325
1511
  crypto.getRandomValues(bytes);
1326
1512
  return { status: length, payload: bytes };
1327
1513
  }
1514
+ /* ----------------------------------------------------------- sockets */
1515
+ case Op.socket: {
1516
+ const family = r.u32();
1517
+ const type = r.u32();
1518
+ const protocol = r.i32();
1519
+ if (family !== 2 || (type & 15) !== 1 || protocol !== 0) throw new PosixError(Errno.EAFNOSUPPORT);
1520
+ if (!proc.sockets) throw new PosixError(Errno.ENOSYS);
1521
+ return { status: proc.table.add(new SocketDescription(new VirtualTcpSocket(proc.sockets))) };
1522
+ }
1523
+ case Op.bind: {
1524
+ const description = socketDescription(proc.table.get(r.i32()));
1525
+ description.bind(r.string(), r.u32());
1526
+ return { status: 0 };
1527
+ }
1528
+ case Op.listen: {
1529
+ const description = socketDescription(proc.table.get(r.i32()));
1530
+ description.listen(Math.max(1, Math.min(1024, r.u32())));
1531
+ return { status: 0 };
1532
+ }
1533
+ case Op.accept: {
1534
+ const description = socketDescription(proc.table.get(r.i32()));
1535
+ const accepted = await acceptBlocking(description);
1536
+ const fd = proc.table.add(accepted);
1537
+ const peer = accepted.peerAddress();
1538
+ return { status: fd, payload: new Writer().string(peer.address).u32(peer.port).finish() };
1539
+ }
1540
+ case Op.connect: {
1541
+ const description = socketDescription(proc.table.get(r.i32()));
1542
+ description.connect(r.string(), r.u32());
1543
+ return { status: 0 };
1544
+ }
1545
+ case Op.send: {
1546
+ const description = socketDescription(proc.table.get(r.i32()));
1547
+ return { status: await writeBlocking(description, r.bytes32()) };
1548
+ }
1549
+ case Op.recv: {
1550
+ const description = socketDescription(proc.table.get(r.i32()));
1551
+ const data = await readBlocking(description, clamp(r.u32()));
1552
+ return { status: data.length, payload: data };
1553
+ }
1554
+ case Op.shutdown: {
1555
+ const description = socketDescription(proc.table.get(r.i32()));
1556
+ const how = r.u32();
1557
+ if (how > 2) throw new PosixError(Errno.EINVAL);
1558
+ description.shutdown(how === 0 || how === 2, how === 1 || how === 2);
1559
+ return { status: 0 };
1560
+ }
1561
+ case Op.getsockname: {
1562
+ const address = socketDescription(proc.table.get(r.i32())).localAddress();
1563
+ return { status: 0, payload: new Writer().string(address.address).u32(address.port).finish() };
1564
+ }
1565
+ case Op.getpeername: {
1566
+ const address = socketDescription(proc.table.get(r.i32())).peerAddress();
1567
+ return { status: 0, payload: new Writer().string(address.address).u32(address.port).finish() };
1568
+ }
1569
+ case Op.setsockopt: {
1570
+ const description = socketDescription(proc.table.get(r.i32()));
1571
+ description.setOption(r.u32(), r.u32(), r.bytes32());
1572
+ return { status: 0 };
1573
+ }
1574
+ case Op.getsockopt: {
1575
+ const description = socketDescription(proc.table.get(r.i32()));
1576
+ return { status: 0, payload: new Writer().bytes32(description.getOption(r.u32(), r.u32())).finish() };
1577
+ }
1328
1578
  default:
1329
1579
  return { status: failure(Errno.ENOSYS) };
1330
1580
  }
1331
1581
  }
1332
1582
  function implemented(capability) {
1333
- return ["files", "descriptors", "pipes", "readiness", "time", "identity", "entropy"].includes(capability);
1583
+ return ["files", "descriptors", "pipes", "readiness", "time", "identity", "entropy"].includes(capability) || capability === "sockets" && proc.sockets !== void 0;
1334
1584
  }
1335
1585
  function resolve2(path) {
1336
1586
  if (path.startsWith("/")) return proc.vfs.resolvePath(path);
@@ -1339,6 +1589,22 @@ function createHostAbiServer(proc) {
1339
1589
  function clamp(length) {
1340
1590
  return Math.min(length, MAX_TRANSFER);
1341
1591
  }
1592
+ function socketDescription(description) {
1593
+ if (!(description instanceof SocketDescription)) throw new PosixError(Errno.ENOTSOCK, "descriptor is not a socket");
1594
+ return description;
1595
+ }
1596
+ async function acceptBlocking(description) {
1597
+ for (; ; ) {
1598
+ try {
1599
+ return description.accept();
1600
+ } catch (error) {
1601
+ const posix = toPosixError(error);
1602
+ if (posix.errno !== Errno.EAGAIN || description.flags & O_NONBLOCK) throw posix;
1603
+ }
1604
+ await Promise.race([description.whenReady(), aborted()]);
1605
+ if (proc.signal.aborted) throw new PosixError(Errno.EINTR);
1606
+ }
1607
+ }
1342
1608
  async function readBlocking(description, length) {
1343
1609
  for (; ; ) {
1344
1610
  try {
@@ -1559,6 +1825,37 @@ var HostAbiClient = class {
1559
1825
  getrandom(length) {
1560
1826
  return this.call(Op.getrandom, new Writer().u32(length).finish()).payload;
1561
1827
  }
1828
+ socket(family, type, protocol = 0) {
1829
+ return this.call(Op.socket, new Writer().u32(family).u32(type).i32(protocol).finish()).status;
1830
+ }
1831
+ bind(fd, address, port) {
1832
+ this.call(Op.bind, new Writer().i32(fd).string(address).u32(port).finish());
1833
+ }
1834
+ listen(fd, backlog = 128) {
1835
+ this.call(Op.listen, new Writer().i32(fd).u32(backlog).finish());
1836
+ }
1837
+ accept(fd) {
1838
+ const result = this.call(Op.accept, new Writer().i32(fd).finish());
1839
+ const r = new Reader(result.payload);
1840
+ return { fd: result.status, address: r.string(), port: r.u32() };
1841
+ }
1842
+ connect(fd, address, port) {
1843
+ this.call(Op.connect, new Writer().i32(fd).string(address).u32(port).finish());
1844
+ }
1845
+ send(fd, data) {
1846
+ return this.call(Op.send, new Writer().i32(fd).bytes32(data).finish()).status;
1847
+ }
1848
+ recv(fd, length) {
1849
+ return this.call(Op.recv, new Writer().i32(fd).u32(length).finish()).payload;
1850
+ }
1851
+ shutdown(fd, how) {
1852
+ this.call(Op.shutdown, new Writer().i32(fd).u32(how).finish());
1853
+ }
1854
+ socketName(fd, peer = false) {
1855
+ const payload = this.call(peer ? Op.getpeername : Op.getsockname, new Writer().i32(fd).finish()).payload;
1856
+ const r = new Reader(payload);
1857
+ return { address: r.string(), port: r.u32() };
1858
+ }
1562
1859
  };
1563
1860
  function decodeStat(payload) {
1564
1861
  const r = new Reader(payload);