@aptre/v86 0.5.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.
Files changed (111) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.MIT +22 -0
  3. package/Readme.md +237 -0
  4. package/dist/v86.browser.js +26666 -0
  5. package/dist/v86.browser.js.map +7 -0
  6. package/dist/v86.js +26632 -0
  7. package/dist/v86.js.map +7 -0
  8. package/gen/generate_analyzer.ts +512 -0
  9. package/gen/generate_interpreter.ts +522 -0
  10. package/gen/generate_jit.ts +624 -0
  11. package/gen/rust_ast.ts +107 -0
  12. package/gen/util.ts +35 -0
  13. package/gen/x86_table.ts +1836 -0
  14. package/lib/9p.ts +1547 -0
  15. package/lib/filesystem.ts +1879 -0
  16. package/lib/marshall.ts +168 -0
  17. package/lib/softfloat/softfloat.c +32501 -0
  18. package/lib/zstd/zstddeclib.c +13520 -0
  19. package/package.json +75 -0
  20. package/src/acpi.ts +267 -0
  21. package/src/browser/dummy_screen.ts +106 -0
  22. package/src/browser/fake_network.ts +1771 -0
  23. package/src/browser/fetch_network.ts +361 -0
  24. package/src/browser/filestorage.ts +124 -0
  25. package/src/browser/inbrowser_network.ts +57 -0
  26. package/src/browser/keyboard.ts +564 -0
  27. package/src/browser/main.ts +3415 -0
  28. package/src/browser/mouse.ts +255 -0
  29. package/src/browser/network.ts +142 -0
  30. package/src/browser/print_stats.ts +336 -0
  31. package/src/browser/screen.ts +978 -0
  32. package/src/browser/serial.ts +316 -0
  33. package/src/browser/speaker.ts +1223 -0
  34. package/src/browser/starter.ts +1688 -0
  35. package/src/browser/wisp_network.ts +332 -0
  36. package/src/browser/worker_bus.ts +64 -0
  37. package/src/buffer.ts +652 -0
  38. package/src/bus.ts +78 -0
  39. package/src/const.ts +128 -0
  40. package/src/cpu.ts +2891 -0
  41. package/src/dma.ts +474 -0
  42. package/src/elf.ts +251 -0
  43. package/src/floppy.ts +1778 -0
  44. package/src/ide.ts +3455 -0
  45. package/src/io.ts +504 -0
  46. package/src/iso9660.ts +317 -0
  47. package/src/kernel.ts +250 -0
  48. package/src/lib.ts +645 -0
  49. package/src/log.ts +149 -0
  50. package/src/main.ts +199 -0
  51. package/src/ne2k.ts +1589 -0
  52. package/src/pci.ts +815 -0
  53. package/src/pit.ts +406 -0
  54. package/src/ps2.ts +820 -0
  55. package/src/rtc.ts +537 -0
  56. package/src/rust/analysis.rs +101 -0
  57. package/src/rust/codegen.rs +2660 -0
  58. package/src/rust/config.rs +3 -0
  59. package/src/rust/control_flow.rs +425 -0
  60. package/src/rust/cpu/apic.rs +658 -0
  61. package/src/rust/cpu/arith.rs +1207 -0
  62. package/src/rust/cpu/call_indirect.rs +2 -0
  63. package/src/rust/cpu/cpu.rs +4501 -0
  64. package/src/rust/cpu/fpu.rs +923 -0
  65. package/src/rust/cpu/global_pointers.rs +112 -0
  66. package/src/rust/cpu/instructions.rs +2486 -0
  67. package/src/rust/cpu/instructions_0f.rs +5261 -0
  68. package/src/rust/cpu/ioapic.rs +316 -0
  69. package/src/rust/cpu/memory.rs +351 -0
  70. package/src/rust/cpu/misc_instr.rs +613 -0
  71. package/src/rust/cpu/mod.rs +16 -0
  72. package/src/rust/cpu/modrm.rs +133 -0
  73. package/src/rust/cpu/pic.rs +402 -0
  74. package/src/rust/cpu/sse_instr.rs +361 -0
  75. package/src/rust/cpu/string.rs +701 -0
  76. package/src/rust/cpu/vga.rs +175 -0
  77. package/src/rust/cpu_context.rs +69 -0
  78. package/src/rust/dbg.rs +98 -0
  79. package/src/rust/gen/analyzer.rs +3807 -0
  80. package/src/rust/gen/analyzer0f.rs +3992 -0
  81. package/src/rust/gen/interpreter.rs +4447 -0
  82. package/src/rust/gen/interpreter0f.rs +5404 -0
  83. package/src/rust/gen/jit.rs +5080 -0
  84. package/src/rust/gen/jit0f.rs +5547 -0
  85. package/src/rust/gen/mod.rs +14 -0
  86. package/src/rust/jit.rs +2443 -0
  87. package/src/rust/jit_instructions.rs +7881 -0
  88. package/src/rust/js_api.rs +6 -0
  89. package/src/rust/leb.rs +46 -0
  90. package/src/rust/lib.rs +29 -0
  91. package/src/rust/modrm.rs +330 -0
  92. package/src/rust/opstats.rs +249 -0
  93. package/src/rust/page.rs +15 -0
  94. package/src/rust/paging.rs +25 -0
  95. package/src/rust/prefix.rs +15 -0
  96. package/src/rust/profiler.rs +155 -0
  97. package/src/rust/regs.rs +38 -0
  98. package/src/rust/softfloat.rs +286 -0
  99. package/src/rust/state_flags.rs +27 -0
  100. package/src/rust/wasmgen/mod.rs +2 -0
  101. package/src/rust/wasmgen/wasm_builder.rs +1047 -0
  102. package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
  103. package/src/rust/zstd.rs +105 -0
  104. package/src/sb16.ts +1928 -0
  105. package/src/state.ts +359 -0
  106. package/src/uart.ts +472 -0
  107. package/src/vga.ts +2791 -0
  108. package/src/virtio.ts +1756 -0
  109. package/src/virtio_balloon.ts +273 -0
  110. package/src/virtio_console.ts +372 -0
  111. package/src/virtio_net.ts +326 -0
@@ -0,0 +1,1771 @@
1
+ import { LOG_FETCH } from '../const.js'
2
+ import { h } from '../lib.js'
3
+ import { dbg_assert, dbg_log } from '../log.js'
4
+
5
+ // https://www.iana.org/assignments/ieee-802-numbers/ieee-802-numbers.xhtml
6
+ const ETHERTYPE_IPV4 = 0x0800
7
+ const ETHERTYPE_ARP = 0x0806
8
+ const ETHERTYPE_IPV6 = 0x86dd
9
+
10
+ const IPV4_PROTO_ICMP = 1
11
+ const IPV4_PROTO_TCP = 6
12
+ const IPV4_PROTO_UDP = 17
13
+
14
+ const UNIX_EPOCH = new Date('1970-01-01T00:00:00Z').getTime()
15
+ const NTP_EPOCH = new Date('1900-01-01T00:00:00Z').getTime()
16
+ const NTP_EPOC_DIFF = UNIX_EPOCH - NTP_EPOCH
17
+ const TWO_TO_32 = Math.pow(2, 32)
18
+
19
+ const DHCP_MAGIC_COOKIE = 0x63825363
20
+ const V86_ASCII = [118, 56, 54]
21
+
22
+ /* For the complete TCP state diagram see:
23
+ *
24
+ * https://en.wikipedia.org/wiki/File:Tcp_state_diagram_fixed_new.svg
25
+ *
26
+ * State TIME_WAIT is not needed, we can skip it and transition directly to CLOSED instead.
27
+ */
28
+ export const TCP_STATE_CLOSED = 'closed'
29
+ export const TCP_STATE_SYN_RECEIVED = 'syn-received'
30
+ export const TCP_STATE_SYN_SENT = 'syn-sent'
31
+ export const TCP_STATE_SYN_PROBE = 'syn-probe'
32
+ //const TCP_STATE_LISTEN = "listen";
33
+ export const TCP_STATE_ESTABLISHED = 'established'
34
+ export const TCP_STATE_FIN_WAIT_1 = 'fin-wait-1'
35
+ export const TCP_STATE_CLOSE_WAIT = 'close-wait'
36
+ export const TCP_STATE_FIN_WAIT_2 = 'fin-wait-2'
37
+ export const TCP_STATE_LAST_ACK = 'last-ack'
38
+ export const TCP_STATE_CLOSING = 'closing'
39
+ //const TCP_STATE_TIME_WAIT = "time-wait";
40
+
41
+ // source: RFC6335, 6. Port Number Ranges
42
+ const TCP_DYNAMIC_PORT_START = 49152
43
+ const TCP_DYNAMIC_PORT_END = 65535
44
+ const TCP_DYNAMIC_PORT_RANGE = TCP_DYNAMIC_PORT_END - TCP_DYNAMIC_PORT_START
45
+
46
+ const ETH_HEADER_SIZE = 14
47
+ const ETH_PAYLOAD_OFFSET = ETH_HEADER_SIZE
48
+ const MTU_DEFAULT = 1500
49
+ const ETH_TRAILER_SIZE = 4
50
+ const IPV4_HEADER_SIZE = 20
51
+ const IPV4_PAYLOAD_OFFSET = ETH_PAYLOAD_OFFSET + IPV4_HEADER_SIZE
52
+ const UDP_HEADER_SIZE = 8
53
+ const UDP_PAYLOAD_OFFSET = IPV4_PAYLOAD_OFFSET + UDP_HEADER_SIZE
54
+ const TCP_HEADER_SIZE = 20
55
+ const TCP_PAYLOAD_OFFSET = IPV4_PAYLOAD_OFFSET + TCP_HEADER_SIZE
56
+ const ICMP_HEADER_SIZE = 4
57
+
58
+ const DEFAULT_DOH_SERVER = 'cloudflare-dns.com'
59
+
60
+ // Suppress unused variable warnings for protocol offset constants
61
+ void TCP_PAYLOAD_OFFSET
62
+
63
+ export interface EthEncoderBuf {
64
+ eth_frame: Uint8Array
65
+ eth_frame_view: DataView
66
+ eth_payload_view: DataView
67
+ ipv4_payload_view: DataView
68
+ udp_payload_view: DataView
69
+ text_encoder: TextEncoder
70
+ }
71
+
72
+ interface EthHeader {
73
+ ethertype: number
74
+ src: Uint8Array
75
+ dest: Uint8Array
76
+ dest_s?: string
77
+ src_s?: string
78
+ }
79
+
80
+ interface ArpHeader {
81
+ htype: number
82
+ ptype: number
83
+ oper: number
84
+ sha: Uint8Array
85
+ spa: Uint8Array
86
+ tha: Uint8Array
87
+ tpa: Uint8Array
88
+ }
89
+
90
+ interface Ipv4Header {
91
+ version?: number
92
+ ihl?: number
93
+ tos?: number
94
+ len?: number
95
+ id?: number
96
+ ttl?: number
97
+ proto: number
98
+ ip_checksum?: number
99
+ src: Uint8Array
100
+ dest: Uint8Array
101
+ }
102
+
103
+ interface IcmpHeader {
104
+ type: number
105
+ code: number
106
+ checksum?: number
107
+ data: Uint8Array
108
+ }
109
+
110
+ interface TcpHeader {
111
+ sport: number
112
+ dport: number
113
+ seq: number
114
+ ackn: number
115
+ doff?: number
116
+ winsize: number
117
+ checksum?: number
118
+ urgent?: number
119
+ fin?: boolean
120
+ syn?: boolean
121
+ rst?: boolean
122
+ psh?: boolean
123
+ ack?: boolean
124
+ urg?: boolean
125
+ ece?: boolean
126
+ cwr?: boolean
127
+ options?: {
128
+ mss?: number
129
+ }
130
+ }
131
+
132
+ interface UdpHeader {
133
+ sport: number
134
+ dport: number
135
+ len?: number
136
+ checksum?: number
137
+ data?: Uint8Array
138
+ data_s?: string
139
+ }
140
+
141
+ interface DnsQuestion {
142
+ name: string[]
143
+ type: number
144
+ class: number
145
+ }
146
+
147
+ interface DnsAnswer {
148
+ name: string[]
149
+ type: number
150
+ class: number
151
+ ttl: number
152
+ data: Uint8Array | number[]
153
+ }
154
+
155
+ interface DnsHeader {
156
+ id: number
157
+ flags: number
158
+ questions: DnsQuestion[]
159
+ answers: DnsAnswer[]
160
+ }
161
+
162
+ interface DhcpHeader {
163
+ op: number
164
+ htype: number
165
+ hlen: number
166
+ hops: number
167
+ xid: number
168
+ secs: number
169
+ flags: number
170
+ ciaddr: number
171
+ yiaddr: number
172
+ siaddr: number
173
+ giaddr: number
174
+ chaddr: Uint8Array
175
+ magic?: number
176
+ options: Uint8Array[]
177
+ }
178
+
179
+ interface NtpHeader {
180
+ flags: number
181
+ stratum: number
182
+ poll: number
183
+ precision: number
184
+ root_delay: number
185
+ root_disp: number
186
+ ref_id: number
187
+ ref_ts_i: number
188
+ ref_ts_f: number
189
+ ori_ts_i: number
190
+ ori_ts_f: number
191
+ rec_ts_i: number
192
+ rec_ts_f: number
193
+ trans_ts_i: number
194
+ trans_ts_f: number
195
+ }
196
+
197
+ export interface PacketSpec {
198
+ eth: EthHeader
199
+ arp?: ArpHeader
200
+ ipv4?: Ipv4Header
201
+ icmp?: IcmpHeader
202
+ tcp?: TcpHeader
203
+ tcp_data?: Uint8Array
204
+ udp?: UdpHeader
205
+ dns?: DnsHeader
206
+ dhcp?: DhcpHeader
207
+ ntp?: NtpHeader
208
+ dhcp_options?: Uint8Array[]
209
+ }
210
+
211
+ export interface NetworkAdapterLike {
212
+ bus: { pair?: { send(name: string, value: unknown): void } }
213
+ router_mac: Uint8Array
214
+ router_ip: Uint8Array
215
+ vm_ip: Uint8Array
216
+ vm_mac: Uint8Array
217
+ masquerade: boolean
218
+ dns_method: string
219
+ doh_server?: string
220
+ tcp_conn: Record<string, TCPConnection>
221
+ mtu?: number
222
+ eth_encoder_buf: EthEncoderBuf
223
+ receive(data: Uint8Array): void
224
+ on_tcp_connection?(conn: TCPConnection, packet: PacketSpec): boolean | void
225
+ }
226
+
227
+ function a2ethaddr(bytes: Uint8Array): string {
228
+ return [0, 1, 2, 3, 4, 5]
229
+ .map((i) => bytes[i].toString(16))
230
+ .map((x) => (x.length === 1 ? '0' + x : x))
231
+ .join(':')
232
+ }
233
+
234
+ function iptolong(parts: Uint8Array): number {
235
+ return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3]
236
+ }
237
+
238
+ class GrowableRingbuffer {
239
+ maximum_capacity: number
240
+ tail: number
241
+ head: number
242
+ length: number
243
+ buffer: Uint8Array
244
+
245
+ constructor(initial_capacity: number, maximum_capacity: number) {
246
+ initial_capacity = Math.min(initial_capacity, 16)
247
+ this.maximum_capacity = maximum_capacity
248
+ ? Math.max(maximum_capacity, initial_capacity)
249
+ : 0
250
+ this.tail = 0
251
+ this.head = 0
252
+ this.length = 0
253
+ this.buffer = new Uint8Array(initial_capacity)
254
+ }
255
+
256
+ write(src_array: Uint8Array): void {
257
+ const src_length = src_array.length
258
+ const total_length = this.length + src_length
259
+ let capacity = this.buffer.length
260
+ if (capacity < total_length) {
261
+ dbg_assert(capacity > 0)
262
+ while (capacity < total_length) {
263
+ capacity *= 2
264
+ }
265
+ if (this.maximum_capacity && capacity > this.maximum_capacity) {
266
+ throw new Error(
267
+ 'stream capacity overflow in GrowableRingbuffer.write(), package dropped',
268
+ )
269
+ }
270
+ const new_buffer = new Uint8Array(capacity)
271
+ this.peek(new_buffer)
272
+ this.tail = 0
273
+ this.head = this.length
274
+ this.buffer = new_buffer
275
+ }
276
+ const buffer = this.buffer
277
+
278
+ const new_head = this.head + src_length
279
+ if (new_head > capacity) {
280
+ const i_split = capacity - this.head
281
+ buffer.set(src_array.subarray(0, i_split), this.head)
282
+ buffer.set(src_array.subarray(i_split))
283
+ } else {
284
+ buffer.set(src_array, this.head)
285
+ }
286
+ this.head = new_head % capacity
287
+ this.length += src_length
288
+ }
289
+
290
+ peek(dst_array: Uint8Array): number {
291
+ const length = Math.min(this.length, dst_array.length)
292
+ if (length) {
293
+ const buffer = this.buffer
294
+ const capacity = buffer.length
295
+ const new_tail = this.tail + length
296
+ if (new_tail > capacity) {
297
+ const buf_len_left = new_tail % capacity
298
+ const buf_len_right = capacity - this.tail
299
+ dst_array.set(buffer.subarray(this.tail))
300
+ dst_array.set(buffer.subarray(0, buf_len_left), buf_len_right)
301
+ } else {
302
+ dst_array.set(buffer.subarray(this.tail, new_tail))
303
+ }
304
+ }
305
+ return length
306
+ }
307
+
308
+ remove(length: number): number {
309
+ if (length > this.length) {
310
+ length = this.length
311
+ }
312
+ if (length) {
313
+ this.tail = (this.tail + length) % this.buffer.length
314
+ this.length -= length
315
+ }
316
+ return length
317
+ }
318
+ }
319
+
320
+ export function create_eth_encoder_buf(
321
+ mtu: number = MTU_DEFAULT,
322
+ ): EthEncoderBuf {
323
+ const ETH_FRAME_SIZE = ETH_HEADER_SIZE + mtu + ETH_TRAILER_SIZE
324
+ const IPV4_PAYLOAD_SIZE = mtu - IPV4_HEADER_SIZE
325
+ const UDP_PAYLOAD_SIZE = IPV4_PAYLOAD_SIZE - UDP_HEADER_SIZE
326
+
327
+ const eth_frame = new Uint8Array(ETH_FRAME_SIZE)
328
+ const buffer = eth_frame.buffer
329
+ const offset = eth_frame.byteOffset
330
+ return {
331
+ eth_frame: eth_frame,
332
+ eth_frame_view: new DataView(buffer),
333
+ eth_payload_view: new DataView(
334
+ buffer,
335
+ offset + ETH_PAYLOAD_OFFSET,
336
+ mtu,
337
+ ),
338
+ ipv4_payload_view: new DataView(
339
+ buffer,
340
+ offset + IPV4_PAYLOAD_OFFSET,
341
+ IPV4_PAYLOAD_SIZE,
342
+ ),
343
+ udp_payload_view: new DataView(
344
+ buffer,
345
+ offset + UDP_PAYLOAD_OFFSET,
346
+ UDP_PAYLOAD_SIZE,
347
+ ),
348
+ text_encoder: new TextEncoder(),
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Copy given data array into view starting at offset, return number of bytes written.
354
+ */
355
+ function view_set_array(
356
+ offset: number,
357
+ data: Uint8Array | number[],
358
+ view: DataView,
359
+ out: EthEncoderBuf,
360
+ ): number {
361
+ out.eth_frame.set(data, view.byteOffset + offset)
362
+ return data.length
363
+ }
364
+
365
+ /**
366
+ * Write zeros into the view starting at offset
367
+ */
368
+ function view_set_zeros(
369
+ offset: number,
370
+ length: number,
371
+ view: DataView,
372
+ out: EthEncoderBuf,
373
+ ): void {
374
+ out.eth_frame.fill(
375
+ 0,
376
+ view.byteOffset + offset,
377
+ view.byteOffset + offset + length,
378
+ )
379
+ }
380
+
381
+ /**
382
+ * UTF8-encode given string into view starting at offset, return number of bytes written.
383
+ */
384
+ function view_set_string(
385
+ offset: number,
386
+ str: string,
387
+ view: DataView,
388
+ out: EthEncoderBuf,
389
+ ): number {
390
+ return out.text_encoder.encodeInto(
391
+ str,
392
+ out.eth_frame.subarray(view.byteOffset + offset),
393
+ ).written
394
+ }
395
+
396
+ /**
397
+ * Calculate internet checksum for view[0 : length] and return the 16-bit result.
398
+ * Source: RFC768 and RFC1071 (chapter 4.1).
399
+ */
400
+ function calc_inet_checksum(
401
+ length: number,
402
+ checksum: number,
403
+ view: DataView,
404
+ out: EthEncoderBuf,
405
+ ): number {
406
+ const uint16_end = view.byteOffset + (length & ~1)
407
+ const eth_frame = out.eth_frame
408
+ for (let i = view.byteOffset; i < uint16_end; i += 2) {
409
+ checksum += (eth_frame[i] << 8) | eth_frame[i + 1]
410
+ }
411
+ if (length & 1) {
412
+ checksum += eth_frame[uint16_end] << 8
413
+ }
414
+ while (checksum >>> 16) {
415
+ checksum = (checksum & 0xffff) + (checksum >>> 16)
416
+ }
417
+ return ~checksum & 0xffff
418
+ }
419
+
420
+ function make_packet(out: EthEncoderBuf, spec: PacketSpec): Uint8Array {
421
+ dbg_assert(!!spec.eth)
422
+ out.eth_frame.fill(0)
423
+ return out.eth_frame.subarray(0, write_eth(spec, out))
424
+ }
425
+
426
+ function handle_fake_tcp(
427
+ packet: PacketSpec,
428
+ adapter: NetworkAdapterLike,
429
+ ): boolean | undefined {
430
+ const tuple = `${packet.ipv4!.src.join('.')}:${packet.tcp!.sport}:${packet.ipv4!.dest.join('.')}:${packet.tcp!.dport}`
431
+
432
+ if (packet.tcp!.syn && !packet.tcp!.ack) {
433
+ if (adapter.tcp_conn[tuple]) {
434
+ dbg_log('SYN to already opened port', LOG_FETCH)
435
+ delete adapter.tcp_conn[tuple]
436
+ }
437
+
438
+ const conn = new TCPConnection(adapter)
439
+ conn.state = TCP_STATE_SYN_RECEIVED
440
+ conn.tuple = tuple
441
+ conn.last = packet
442
+
443
+ conn.hsrc = packet.eth.dest
444
+ conn.psrc = packet.ipv4!.dest
445
+ conn.sport = packet.tcp!.dport
446
+ conn.hdest = packet.eth.src
447
+ conn.dport = packet.tcp!.sport
448
+ conn.pdest = packet.ipv4!.src
449
+
450
+ adapter.bus.pair?.send('tcp-connection', conn)
451
+
452
+ if (adapter.on_tcp_connection) {
453
+ adapter.on_tcp_connection(conn, packet)
454
+ }
455
+ if (adapter.tcp_conn[tuple]) return
456
+ }
457
+
458
+ if (!adapter.tcp_conn[tuple]) {
459
+ dbg_log(`I dont know about ${tuple}, so resetting`, LOG_FETCH)
460
+ let bop = packet.tcp!.ackn
461
+ if (packet.tcp!.fin || packet.tcp!.syn) bop += 1
462
+ const reply: PacketSpec = {
463
+ eth: {
464
+ ethertype: ETHERTYPE_IPV4,
465
+ src: adapter.router_mac,
466
+ dest: packet.eth.src,
467
+ },
468
+ ipv4: {
469
+ proto: IPV4_PROTO_TCP,
470
+ src: packet.ipv4!.dest,
471
+ dest: packet.ipv4!.src,
472
+ },
473
+ tcp: {
474
+ sport: packet.tcp!.dport,
475
+ dport: packet.tcp!.sport,
476
+ seq: bop,
477
+ ackn: packet.tcp!.seq + (packet.tcp!.syn ? 1 : 0),
478
+ winsize: packet.tcp!.winsize,
479
+ rst: true,
480
+ ack: packet.tcp!.syn,
481
+ },
482
+ }
483
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
484
+ return true
485
+ }
486
+
487
+ adapter.tcp_conn[tuple].process(packet)
488
+ return undefined
489
+ }
490
+
491
+ function handle_fake_dns_static(
492
+ packet: PacketSpec,
493
+ adapter: NetworkAdapterLike,
494
+ ): boolean {
495
+ const reply: PacketSpec = {
496
+ eth: {
497
+ ethertype: ETHERTYPE_IPV4,
498
+ src: adapter.router_mac,
499
+ dest: packet.eth.src,
500
+ },
501
+ ipv4: {
502
+ proto: IPV4_PROTO_UDP,
503
+ src: adapter.router_ip,
504
+ dest: packet.ipv4!.src,
505
+ },
506
+ udp: { sport: 53, dport: packet.udp!.sport },
507
+ }
508
+
509
+ const answers: DnsAnswer[] = []
510
+ let flags = 0x8000 //Response,
511
+ flags |= 0x0180 // Recursion
512
+ // flags |= 0x0400; Authoritative
513
+
514
+ for (let i = 0; i < packet.dns!.questions.length; ++i) {
515
+ const q = packet.dns!.questions[i]
516
+
517
+ switch (q.type) {
518
+ case 1: // A record
519
+ answers.push({
520
+ name: q.name,
521
+ type: q.type,
522
+ class: q.class,
523
+ ttl: 600,
524
+ data: [192, 168, 87, 1],
525
+ })
526
+ break
527
+ default:
528
+ }
529
+ }
530
+
531
+ reply.dns = {
532
+ id: packet.dns!.id,
533
+ flags: flags,
534
+ questions: packet.dns!.questions,
535
+ answers: answers,
536
+ }
537
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
538
+ return true
539
+ }
540
+
541
+ function handle_fake_dns_doh(
542
+ packet: PacketSpec,
543
+ adapter: NetworkAdapterLike,
544
+ ): boolean {
545
+ const fetch_url = `https://${adapter.doh_server || DEFAULT_DOH_SERVER}/dns-query`
546
+ const udp_data = packet.udp!.data
547
+ const fetch_opts: RequestInit = {
548
+ method: 'POST',
549
+ headers: [['content-type', 'application/dns-message']],
550
+ body: udp_data ? udp_data.slice() : undefined,
551
+ }
552
+ fetch(fetch_url, fetch_opts).then(async (resp) => {
553
+ const reply: PacketSpec = {
554
+ eth: {
555
+ ethertype: ETHERTYPE_IPV4,
556
+ src: adapter.router_mac,
557
+ dest: packet.eth.src,
558
+ },
559
+ ipv4: {
560
+ proto: IPV4_PROTO_UDP,
561
+ src: adapter.router_ip,
562
+ dest: packet.ipv4!.src,
563
+ },
564
+ udp: {
565
+ sport: 53,
566
+ dport: packet.udp!.sport,
567
+ data: new Uint8Array(await resp.arrayBuffer()),
568
+ },
569
+ }
570
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
571
+ })
572
+ return true
573
+ }
574
+
575
+ function handle_fake_dns(
576
+ packet: PacketSpec,
577
+ adapter: NetworkAdapterLike,
578
+ ): boolean {
579
+ if (adapter.dns_method === 'static') {
580
+ return handle_fake_dns_static(packet, adapter)
581
+ } else {
582
+ return handle_fake_dns_doh(packet, adapter)
583
+ }
584
+ }
585
+
586
+ function handle_fake_ntp(
587
+ packet: PacketSpec,
588
+ adapter: NetworkAdapterLike,
589
+ ): boolean {
590
+ const now = Date.now() // - 1000 * 60 * 60 * 24 * 7;
591
+ const now_n = now + NTP_EPOC_DIFF
592
+ const now_n_f = TWO_TO_32 * ((now_n % 1000) / 1000)
593
+
594
+ const reply: PacketSpec = {
595
+ eth: {
596
+ ethertype: ETHERTYPE_IPV4,
597
+ src: adapter.router_mac,
598
+ dest: packet.eth.src,
599
+ },
600
+ ipv4: {
601
+ proto: IPV4_PROTO_UDP,
602
+ src: packet.ipv4!.dest,
603
+ dest: packet.ipv4!.src,
604
+ },
605
+ udp: { sport: 123, dport: packet.udp!.sport },
606
+ }
607
+ const flags = (0 << 6) | (4 << 3) | 4
608
+ reply.ntp = Object.assign({}, packet.ntp!)
609
+ reply.ntp.flags = flags
610
+ reply.ntp.poll = 10
611
+ reply.ntp.ori_ts_i = packet.ntp!.trans_ts_i
612
+ reply.ntp.ori_ts_f = packet.ntp!.trans_ts_f
613
+
614
+ reply.ntp.rec_ts_i = now_n / 1000
615
+ reply.ntp.rec_ts_f = now_n_f
616
+
617
+ reply.ntp.trans_ts_i = now_n / 1000
618
+ reply.ntp.trans_ts_f = now_n_f
619
+
620
+ reply.ntp.stratum = 2
621
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
622
+ return true
623
+ }
624
+
625
+ function handle_fake_dhcp(
626
+ packet: PacketSpec,
627
+ adapter: NetworkAdapterLike,
628
+ ): void {
629
+ const reply: PacketSpec = {
630
+ eth: {
631
+ ethertype: ETHERTYPE_IPV4,
632
+ src: adapter.router_mac,
633
+ dest: packet.eth.src,
634
+ },
635
+ ipv4: {
636
+ proto: IPV4_PROTO_UDP,
637
+ src: adapter.router_ip,
638
+ dest: adapter.vm_ip,
639
+ },
640
+ udp: { sport: 67, dport: 68 },
641
+ }
642
+ reply.dhcp = {
643
+ htype: 1,
644
+ hlen: 6,
645
+ hops: 0,
646
+ xid: packet.dhcp!.xid,
647
+ secs: 0,
648
+ flags: 0,
649
+ ciaddr: 0,
650
+ yiaddr: iptolong(adapter.vm_ip),
651
+ siaddr: iptolong(adapter.router_ip),
652
+ giaddr: iptolong(adapter.router_ip),
653
+ chaddr: packet.dhcp!.chaddr,
654
+ op: 0,
655
+ options: [],
656
+ }
657
+
658
+ const options: Uint8Array[] = []
659
+
660
+ // idk, it seems like op should be 3, but udhcpc sends 1
661
+ const fix = packet.dhcp!.options.find(function (x: Uint8Array) {
662
+ return x[0] === 53
663
+ })
664
+ if (fix && fix[2] === 3) packet.dhcp!.op = 3
665
+
666
+ if (packet.dhcp!.op === 1) {
667
+ reply.dhcp.op = 2
668
+ options.push(new Uint8Array([53, 1, 2]))
669
+ }
670
+
671
+ if (packet.dhcp!.op === 3) {
672
+ reply.dhcp.op = 2
673
+ options.push(new Uint8Array([53, 1, 5]))
674
+ options.push(new Uint8Array([51, 4, 8, 0, 0, 0])) // Lease Time
675
+ }
676
+
677
+ const router_ip: number[] = [
678
+ adapter.router_ip[0],
679
+ adapter.router_ip[1],
680
+ adapter.router_ip[2],
681
+ adapter.router_ip[3],
682
+ ]
683
+ options.push(new Uint8Array([1, 4, 255, 255, 255, 0])) // Netmask
684
+ if (adapter.masquerade) {
685
+ options.push(new Uint8Array([3, 4].concat(router_ip))) // Router
686
+ options.push(new Uint8Array([6, 4].concat(router_ip))) // DNS
687
+ }
688
+ options.push(new Uint8Array([54, 4].concat(router_ip))) // DHCP Server
689
+ options.push(new Uint8Array([60, 3].concat(V86_ASCII))) // Vendor
690
+ options.push(new Uint8Array([255, 0]))
691
+
692
+ reply.dhcp.options = options
693
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
694
+ }
695
+
696
+ export function handle_fake_networking(
697
+ data: Uint8Array,
698
+ adapter: NetworkAdapterLike,
699
+ ): void {
700
+ const packet: PacketSpec = {
701
+ eth: { ethertype: 0, src: new Uint8Array(6), dest: new Uint8Array(6) },
702
+ }
703
+ parse_eth(data, packet)
704
+
705
+ if (packet.ipv4) {
706
+ if (packet.tcp) {
707
+ handle_fake_tcp(packet, adapter)
708
+ } else if (packet.udp) {
709
+ if (packet.dns) {
710
+ handle_fake_dns(packet, adapter)
711
+ } else if (packet.dhcp) {
712
+ handle_fake_dhcp(packet, adapter)
713
+ } else if (packet.ntp) {
714
+ handle_fake_ntp(packet, adapter)
715
+ } else if (packet.udp.dport === 8) {
716
+ handle_udp_echo(packet, adapter)
717
+ }
718
+ } else if (packet.icmp && packet.icmp.type === 8) {
719
+ handle_fake_ping(packet, adapter)
720
+ }
721
+ } else if (
722
+ packet.arp &&
723
+ packet.arp.oper === 1 &&
724
+ packet.arp.ptype === ETHERTYPE_IPV4
725
+ ) {
726
+ arp_whohas(packet, adapter)
727
+ }
728
+ }
729
+
730
+ function parse_eth(data: Uint8Array, o: PacketSpec): void {
731
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
732
+
733
+ const ethertype = view.getUint16(12)
734
+ const eth: EthHeader = {
735
+ ethertype: ethertype,
736
+ dest: data.subarray(0, 6),
737
+ dest_s: a2ethaddr(data.subarray(0, 6)),
738
+ src: data.subarray(6, 12),
739
+ src_s: a2ethaddr(data.subarray(6, 12)),
740
+ }
741
+
742
+ o.eth = eth
743
+
744
+ // TODO: Remove CRC from the end of the packet maybe?
745
+ const payload = data.subarray(ETH_HEADER_SIZE, data.length)
746
+
747
+ if (ethertype === ETHERTYPE_IPV4) {
748
+ parse_ipv4(payload, o)
749
+ } else if (ethertype === ETHERTYPE_ARP) {
750
+ parse_arp(payload, o)
751
+ } else if (ethertype === ETHERTYPE_IPV6) {
752
+ dbg_log('Unimplemented: ipv6')
753
+ } else {
754
+ dbg_log('Unknown ethertype: ' + h(ethertype), LOG_FETCH)
755
+ }
756
+ }
757
+
758
+ function write_eth(spec: PacketSpec, out: EthEncoderBuf): number {
759
+ const view = out.eth_frame_view
760
+ view_set_array(0, spec.eth.dest, view, out)
761
+ view_set_array(6, spec.eth.src, view, out)
762
+ view.setUint16(12, spec.eth.ethertype)
763
+ let len = ETH_HEADER_SIZE
764
+ if (spec.arp) {
765
+ len += write_arp(spec, out)
766
+ } else if (spec.ipv4) {
767
+ len += write_ipv4(spec, out)
768
+ }
769
+ return len
770
+ }
771
+
772
+ function parse_arp(data: Uint8Array, o: PacketSpec): void {
773
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
774
+
775
+ const arp: ArpHeader = {
776
+ htype: view.getUint16(0),
777
+ ptype: view.getUint16(2),
778
+ oper: view.getUint16(6),
779
+ sha: data.subarray(8, 14),
780
+ spa: data.subarray(14, 18),
781
+ tha: data.subarray(18, 24),
782
+ tpa: data.subarray(24, 28),
783
+ }
784
+ o.arp = arp
785
+ }
786
+
787
+ function write_arp(spec: PacketSpec, out: EthEncoderBuf): number {
788
+ const view = out.eth_payload_view
789
+ view.setUint16(0, spec.arp!.htype)
790
+ view.setUint16(2, spec.arp!.ptype)
791
+ view.setUint8(4, spec.arp!.sha.length)
792
+ view.setUint8(5, spec.arp!.spa.length)
793
+ view.setUint16(6, spec.arp!.oper)
794
+ view_set_array(8, spec.arp!.sha, view, out)
795
+ view_set_array(14, spec.arp!.spa, view, out)
796
+ view_set_array(18, spec.arp!.tha, view, out)
797
+ view_set_array(24, spec.arp!.tpa, view, out)
798
+ return 28
799
+ }
800
+
801
+ function parse_ipv4(data: Uint8Array, o: PacketSpec): void {
802
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
803
+
804
+ const version = (data[0] >> 4) & 0x0f
805
+ const ihl = data[0] & 0x0f
806
+
807
+ const tos = view.getUint8(1)
808
+ const len = view.getUint16(2)
809
+
810
+ const ttl = view.getUint8(8)
811
+ const proto = view.getUint8(9)
812
+ const ip_checksum = view.getUint16(10)
813
+
814
+ const ipv4: Ipv4Header = {
815
+ version,
816
+ ihl,
817
+ tos,
818
+ len,
819
+ ttl,
820
+ proto,
821
+ ip_checksum,
822
+ src: data.subarray(12, 12 + 4),
823
+ dest: data.subarray(16, 16 + 4),
824
+ }
825
+
826
+ // Ethernet minmum packet size.
827
+ if (Math.max(len, 46) !== data.length) {
828
+ dbg_log(`ipv4 Length mismatch: ${len} != ${data.length}`, LOG_FETCH)
829
+ }
830
+
831
+ o.ipv4 = ipv4
832
+ const ipdata = data.subarray(ihl * 4, len)
833
+ if (proto === IPV4_PROTO_ICMP) {
834
+ parse_icmp(ipdata, o)
835
+ } else if (proto === IPV4_PROTO_TCP) {
836
+ parse_tcp(ipdata, o)
837
+ } else if (proto === IPV4_PROTO_UDP) {
838
+ parse_udp(ipdata, o)
839
+ }
840
+ }
841
+
842
+ function write_ipv4(spec: PacketSpec, out: EthEncoderBuf): number {
843
+ const view = out.eth_payload_view
844
+ const ihl = IPV4_HEADER_SIZE >> 2 // header length in 32-bit words
845
+ const version = 4
846
+
847
+ let len = IPV4_HEADER_SIZE
848
+ if (spec.icmp) {
849
+ len += write_icmp(spec, out)
850
+ } else if (spec.udp) {
851
+ len += write_udp(spec, out)
852
+ } else if (spec.tcp) {
853
+ len += write_tcp(spec, out)
854
+ }
855
+
856
+ view.setUint8(0, (version << 4) | (ihl & 0x0f))
857
+ view.setUint8(1, spec.ipv4!.tos || 0)
858
+ view.setUint16(2, len)
859
+ view.setUint16(4, spec.ipv4!.id || 0)
860
+ view.setUint8(6, 2 << 5) // DF Flag
861
+ view.setUint8(8, spec.ipv4!.ttl || 32)
862
+ view.setUint8(9, spec.ipv4!.proto)
863
+ view.setUint16(10, 0) // checksum initially zero before calculation
864
+ view_set_array(12, spec.ipv4!.src, view, out)
865
+ view_set_array(16, spec.ipv4!.dest, view, out)
866
+ view.setUint16(10, calc_inet_checksum(IPV4_HEADER_SIZE, 0, view, out))
867
+ return len
868
+ }
869
+
870
+ function parse_icmp(data: Uint8Array, o: PacketSpec): void {
871
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
872
+ const icmp: IcmpHeader = {
873
+ type: view.getUint8(0),
874
+ code: view.getUint8(1),
875
+ checksum: view.getUint16(2),
876
+ data: data.subarray(4),
877
+ }
878
+ o.icmp = icmp
879
+ }
880
+
881
+ function write_icmp(spec: PacketSpec, out: EthEncoderBuf): number {
882
+ const view = out.ipv4_payload_view
883
+ view.setUint8(0, spec.icmp!.type)
884
+ view.setUint8(1, spec.icmp!.code)
885
+ view.setUint16(2, 0) // checksum initially zero before calculation
886
+ const data_length = view_set_array(
887
+ ICMP_HEADER_SIZE,
888
+ spec.icmp!.data,
889
+ view,
890
+ out,
891
+ )
892
+ const total_length = ICMP_HEADER_SIZE + data_length
893
+ view.setUint16(2, calc_inet_checksum(total_length, 0, view, out))
894
+ return total_length
895
+ }
896
+
897
+ function parse_udp(data: Uint8Array, o: PacketSpec): void {
898
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
899
+ const udp: UdpHeader = {
900
+ sport: view.getUint16(0),
901
+ dport: view.getUint16(2),
902
+ len: view.getUint16(4),
903
+ checksum: view.getUint16(6),
904
+ data: data.subarray(8),
905
+ data_s: new TextDecoder().decode(data.subarray(8)),
906
+ }
907
+
908
+ //dbg_assert(udp.data.length + 8 == udp.len);
909
+ if (udp.dport === 67 || udp.sport === 67) {
910
+ //DHCP
911
+ parse_dhcp(data.subarray(8), o)
912
+ } else if (udp.dport === 53 || udp.sport === 53) {
913
+ parse_dns(data.subarray(8), o)
914
+ } else if (udp.dport === 123) {
915
+ parse_ntp(data.subarray(8), o)
916
+ }
917
+ o.udp = udp
918
+ }
919
+
920
+ function write_udp(spec: PacketSpec, out: EthEncoderBuf): number {
921
+ const view = out.ipv4_payload_view
922
+ let total_length = UDP_HEADER_SIZE
923
+ if (spec.dhcp) {
924
+ total_length += write_dhcp(spec, out)
925
+ } else if (spec.dns) {
926
+ total_length += write_dns(spec, out)
927
+ } else if (spec.ntp) {
928
+ total_length += write_ntp(spec, out)
929
+ } else {
930
+ total_length += view_set_array(
931
+ 0,
932
+ spec.udp!.data!,
933
+ out.udp_payload_view,
934
+ out,
935
+ )
936
+ }
937
+
938
+ view.setUint16(0, spec.udp!.sport)
939
+ view.setUint16(2, spec.udp!.dport)
940
+ view.setUint16(4, total_length)
941
+ view.setUint16(6, 0) // checksum initially zero before calculation
942
+
943
+ const pseudo_header =
944
+ ((spec.ipv4!.src[0] << 8) | spec.ipv4!.src[1]) +
945
+ ((spec.ipv4!.src[2] << 8) | spec.ipv4!.src[3]) +
946
+ ((spec.ipv4!.dest[0] << 8) | spec.ipv4!.dest[1]) +
947
+ ((spec.ipv4!.dest[2] << 8) | spec.ipv4!.dest[3]) +
948
+ IPV4_PROTO_UDP +
949
+ total_length
950
+ view.setUint16(
951
+ 6,
952
+ calc_inet_checksum(total_length, pseudo_header, view, out),
953
+ )
954
+ return total_length
955
+ }
956
+
957
+ function parse_dns(data: Uint8Array, o: PacketSpec): void {
958
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
959
+ const dns: DnsHeader = {
960
+ id: view.getUint16(0),
961
+ flags: view.getUint16(2),
962
+ questions: [],
963
+ answers: [],
964
+ }
965
+
966
+ const qdcount = view.getUint16(4)
967
+ const ancount = view.getUint16(6)
968
+
969
+ let offset = 12
970
+ function read_dstr(): string[] {
971
+ const o: string[] = []
972
+ let len: number
973
+ do {
974
+ len = view.getUint8(offset)
975
+ o.push(
976
+ new TextDecoder().decode(
977
+ data.subarray(offset + 1, offset + 1 + len),
978
+ ),
979
+ )
980
+ offset += len + 1
981
+ } while (len > 0)
982
+ return o
983
+ }
984
+
985
+ for (let i = 0; i < qdcount; i++) {
986
+ dns.questions.push({
987
+ name: read_dstr(),
988
+ type: view.getInt16(offset),
989
+ class: view.getInt16(offset + 2),
990
+ })
991
+ offset += 4
992
+ }
993
+ for (let i = 0; i < ancount; i++) {
994
+ const ans: DnsAnswer = {
995
+ name: read_dstr(),
996
+ type: view.getInt16(offset),
997
+ class: view.getUint16(offset + 2),
998
+ ttl: view.getUint32(offset + 4),
999
+ data: new Uint8Array(0),
1000
+ }
1001
+ offset += 8
1002
+ const rdlen = view.getUint16(offset)
1003
+ offset += 2
1004
+ ans.data = data.subarray(offset, offset + rdlen)
1005
+ offset += rdlen
1006
+ dns.answers.push(ans)
1007
+ }
1008
+ o.dns = dns
1009
+ }
1010
+
1011
+ function write_dns(spec: PacketSpec, out: EthEncoderBuf): number {
1012
+ const view = out.udp_payload_view
1013
+ view.setUint16(0, spec.dns!.id)
1014
+ view.setUint16(2, spec.dns!.flags)
1015
+ view.setUint16(4, spec.dns!.questions.length)
1016
+ view.setUint16(6, spec.dns!.answers.length)
1017
+
1018
+ let offset = 12
1019
+ for (let i = 0; i < spec.dns!.questions.length; ++i) {
1020
+ const q = spec.dns!.questions[i]
1021
+ for (const s of q.name) {
1022
+ const n_written = view_set_string(offset + 1, s, view, out)
1023
+ view.setUint8(offset, n_written)
1024
+ offset += 1 + n_written
1025
+ }
1026
+ view.setUint16(offset, q.type)
1027
+ offset += 2
1028
+ view.setUint16(offset, q.class)
1029
+ offset += 2
1030
+ }
1031
+
1032
+ function write_reply(a: DnsAnswer): void {
1033
+ for (const s of a.name) {
1034
+ const n_written = view_set_string(offset + 1, s, view, out)
1035
+ view.setUint8(offset, n_written)
1036
+ offset += 1 + n_written
1037
+ }
1038
+ view.setUint16(offset, a.type)
1039
+ offset += 2
1040
+ view.setUint16(offset, a.class)
1041
+ offset += 2
1042
+ view.setUint32(offset, a.ttl)
1043
+ offset += 4
1044
+ view.setUint16(offset, a.data.length)
1045
+ offset += 2
1046
+ offset += view_set_array(offset, a.data, view, out)
1047
+ }
1048
+
1049
+ for (let i = 0; i < spec.dns!.answers.length; ++i) {
1050
+ const a = spec.dns!.answers[i]
1051
+ write_reply(a)
1052
+ }
1053
+
1054
+ return offset
1055
+ }
1056
+
1057
+ function parse_dhcp(data: Uint8Array, o: PacketSpec): void {
1058
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
1059
+ const dhcp: DhcpHeader = {
1060
+ op: view.getUint8(0),
1061
+ htype: view.getUint8(1),
1062
+ hlen: view.getUint8(2),
1063
+ hops: view.getUint8(3),
1064
+ xid: view.getUint32(4),
1065
+ secs: view.getUint16(8),
1066
+ flags: view.getUint16(10),
1067
+ ciaddr: view.getUint32(12),
1068
+ yiaddr: view.getUint32(16),
1069
+ siaddr: view.getUint32(20),
1070
+ giaddr: view.getUint32(24),
1071
+ chaddr: data.subarray(28, 28 + 16),
1072
+ magic: view.getUint32(236),
1073
+ options: [],
1074
+ }
1075
+
1076
+ const options = data.subarray(240)
1077
+ for (let i = 0; i < options.length; ++i) {
1078
+ const start = i
1079
+ const op = options[i]
1080
+ if (op === 0) continue
1081
+ ++i
1082
+ const len = options[i]
1083
+ i += len
1084
+ dhcp.options.push(options.subarray(start, start + len + 2))
1085
+ }
1086
+
1087
+ o.dhcp = dhcp
1088
+ o.dhcp_options = dhcp.options
1089
+ }
1090
+
1091
+ function write_dhcp(spec: PacketSpec, out: EthEncoderBuf): number {
1092
+ const view = out.udp_payload_view
1093
+ view.setUint8(0, spec.dhcp!.op)
1094
+ view.setUint8(1, spec.dhcp!.htype)
1095
+ view.setUint8(2, spec.dhcp!.hlen)
1096
+ view.setUint8(3, spec.dhcp!.hops)
1097
+ view.setUint32(4, spec.dhcp!.xid)
1098
+ view.setUint16(8, spec.dhcp!.secs)
1099
+ view.setUint16(10, spec.dhcp!.flags)
1100
+ view.setUint32(12, spec.dhcp!.ciaddr)
1101
+ view.setUint32(16, spec.dhcp!.yiaddr)
1102
+ view.setUint32(20, spec.dhcp!.siaddr)
1103
+ view.setUint32(24, spec.dhcp!.giaddr)
1104
+ view_set_array(28, spec.dhcp!.chaddr, view, out)
1105
+
1106
+ view.setUint32(236, DHCP_MAGIC_COOKIE)
1107
+
1108
+ let offset = 240
1109
+ for (const o of spec.dhcp!.options) {
1110
+ offset += view_set_array(offset, o, view, out)
1111
+ }
1112
+ return offset
1113
+ }
1114
+
1115
+ function parse_ntp(data: Uint8Array, o: PacketSpec): void {
1116
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
1117
+ o.ntp = {
1118
+ flags: view.getUint8(0),
1119
+ stratum: view.getUint8(1),
1120
+ poll: view.getUint8(2),
1121
+ precision: view.getUint8(3),
1122
+ root_delay: view.getUint32(4),
1123
+ root_disp: view.getUint32(8),
1124
+ ref_id: view.getUint32(12),
1125
+ ref_ts_i: view.getUint32(16),
1126
+ ref_ts_f: view.getUint32(20),
1127
+ ori_ts_i: view.getUint32(24),
1128
+ ori_ts_f: view.getUint32(28),
1129
+ rec_ts_i: view.getUint32(32),
1130
+ rec_ts_f: view.getUint32(36),
1131
+ trans_ts_i: view.getUint32(40),
1132
+ trans_ts_f: view.getUint32(44),
1133
+ }
1134
+ }
1135
+
1136
+ function write_ntp(spec: PacketSpec, out: EthEncoderBuf): number {
1137
+ const view = out.udp_payload_view
1138
+ view.setUint8(0, spec.ntp!.flags)
1139
+ view.setUint8(1, spec.ntp!.stratum)
1140
+ view.setUint8(2, spec.ntp!.poll)
1141
+ view.setUint8(3, spec.ntp!.precision)
1142
+ view.setUint32(4, spec.ntp!.root_delay)
1143
+ view.setUint32(8, spec.ntp!.root_disp)
1144
+ view.setUint32(12, spec.ntp!.ref_id)
1145
+ view.setUint32(16, spec.ntp!.ref_ts_i)
1146
+ view.setUint32(20, spec.ntp!.ref_ts_f)
1147
+ view.setUint32(24, spec.ntp!.ori_ts_i)
1148
+ view.setUint32(28, spec.ntp!.ori_ts_f)
1149
+ view.setUint32(32, spec.ntp!.rec_ts_i)
1150
+ view.setUint32(36, spec.ntp!.rec_ts_f)
1151
+ view.setUint32(40, spec.ntp!.trans_ts_i)
1152
+ view.setUint32(44, spec.ntp!.trans_ts_f)
1153
+ return 48
1154
+ }
1155
+
1156
+ function parse_tcp(data: Uint8Array, o: PacketSpec): void {
1157
+ const view = new DataView(data.buffer, data.byteOffset, data.byteLength)
1158
+
1159
+ const tcp: TcpHeader = {
1160
+ sport: view.getUint16(0),
1161
+ dport: view.getUint16(2),
1162
+ seq: view.getUint32(4),
1163
+ ackn: view.getUint32(8),
1164
+ doff: view.getUint8(12) >> 4,
1165
+ winsize: view.getUint16(14),
1166
+ checksum: view.getUint16(16),
1167
+ urgent: view.getUint16(18),
1168
+ }
1169
+
1170
+ const flags = view.getUint8(13)
1171
+
1172
+ tcp.fin = !!(flags & 0x01)
1173
+ tcp.syn = !!(flags & 0x02)
1174
+ tcp.rst = !!(flags & 0x04)
1175
+ tcp.psh = !!(flags & 0x08)
1176
+ tcp.ack = !!(flags & 0x10)
1177
+ tcp.urg = !!(flags & 0x20)
1178
+ tcp.ece = !!(flags & 0x40)
1179
+ tcp.cwr = !!(flags & 0x80)
1180
+
1181
+ o.tcp = tcp
1182
+
1183
+ const offset = tcp.doff! * 4
1184
+ o.tcp_data = data.subarray(offset)
1185
+ }
1186
+
1187
+ function write_tcp(spec: PacketSpec, out: EthEncoderBuf): number {
1188
+ const view = out.ipv4_payload_view
1189
+ let flags = 0
1190
+ const tcp = spec.tcp!
1191
+
1192
+ if (tcp.fin) flags |= 0x01
1193
+ if (tcp.syn) flags |= 0x02
1194
+ if (tcp.rst) flags |= 0x04
1195
+ if (tcp.psh) flags |= 0x08
1196
+ if (tcp.ack) flags |= 0x10
1197
+ if (tcp.urg) flags |= 0x20
1198
+ if (tcp.ece) flags |= 0x40
1199
+ if (tcp.cwr) flags |= 0x80
1200
+
1201
+ let doff = TCP_HEADER_SIZE
1202
+ if (tcp.options) {
1203
+ if (tcp.options.mss) {
1204
+ view.setUint8(doff, 0x02) //mss option type
1205
+ view.setUint8(doff + 1, 0x04) //option length
1206
+ view.setUint16(doff + 2, tcp.options.mss)
1207
+ doff += 4
1208
+ }
1209
+ }
1210
+
1211
+ let total_length = Math.ceil(doff / 4) * 4 // needs to a multiple of 4 bytes
1212
+ if (tcp.options && total_length - doff > 0) {
1213
+ view_set_zeros(doff, total_length - doff, view, out) //write zeros into remaining space for options
1214
+ }
1215
+
1216
+ view.setUint16(0, tcp.sport)
1217
+ view.setUint16(2, tcp.dport)
1218
+ view.setUint32(4, tcp.seq)
1219
+ view.setUint32(8, tcp.ackn)
1220
+ view.setUint8(12, (total_length >> 2) << 4) // header length in 32-bit words
1221
+ view.setUint8(13, flags)
1222
+ view.setUint16(14, tcp.winsize)
1223
+ view.setUint16(16, 0) // checksum initially zero before calculation
1224
+ view.setUint16(18, tcp.urgent || 0)
1225
+
1226
+ if (spec.tcp_data) {
1227
+ total_length += view_set_array(
1228
+ TCP_HEADER_SIZE,
1229
+ spec.tcp_data,
1230
+ view,
1231
+ out,
1232
+ )
1233
+ }
1234
+
1235
+ const pseudo_header =
1236
+ ((spec.ipv4!.src[0] << 8) | spec.ipv4!.src[1]) +
1237
+ ((spec.ipv4!.src[2] << 8) | spec.ipv4!.src[3]) +
1238
+ ((spec.ipv4!.dest[0] << 8) | spec.ipv4!.dest[1]) +
1239
+ ((spec.ipv4!.dest[2] << 8) | spec.ipv4!.dest[3]) +
1240
+ IPV4_PROTO_TCP +
1241
+ total_length
1242
+ view.setUint16(
1243
+ 16,
1244
+ calc_inet_checksum(total_length, pseudo_header, view, out),
1245
+ )
1246
+ return total_length
1247
+ }
1248
+
1249
+ export function fake_tcp_connect(
1250
+ dport: number,
1251
+ adapter: NetworkAdapterLike,
1252
+ ): TCPConnection {
1253
+ const vm_ip_str = adapter.vm_ip.join('.')
1254
+ const router_ip_str = adapter.router_ip.join('.')
1255
+ const sport_0 = (Math.random() * TCP_DYNAMIC_PORT_RANGE) | 0
1256
+ let sport: number,
1257
+ tuple: string,
1258
+ sport_i = 0
1259
+ do {
1260
+ sport =
1261
+ TCP_DYNAMIC_PORT_START +
1262
+ ((sport_0 + sport_i) % TCP_DYNAMIC_PORT_RANGE)
1263
+ tuple = `${vm_ip_str}:${dport}:${router_ip_str}:${sport}`
1264
+ } while (++sport_i < TCP_DYNAMIC_PORT_RANGE && adapter.tcp_conn[tuple])
1265
+ if (adapter.tcp_conn[tuple]) {
1266
+ throw new Error(
1267
+ 'pool of dynamic TCP port numbers exhausted, connection aborted',
1268
+ )
1269
+ }
1270
+
1271
+ const conn = new TCPConnection(adapter)
1272
+
1273
+ conn.tuple = tuple
1274
+ conn.hsrc = adapter.router_mac
1275
+ conn.psrc = adapter.router_ip
1276
+ conn.sport = sport
1277
+ conn.hdest = adapter.vm_mac
1278
+ conn.dport = dport
1279
+ conn.pdest = adapter.vm_ip
1280
+ adapter.tcp_conn[tuple] = conn
1281
+ conn.connect()
1282
+ return conn
1283
+ }
1284
+
1285
+ export function fake_tcp_probe(
1286
+ dport: number,
1287
+ adapter: NetworkAdapterLike,
1288
+ ): Promise<boolean> {
1289
+ return new Promise((res, _rej) => {
1290
+ const handle = fake_tcp_connect(dport, adapter)
1291
+ handle.state = TCP_STATE_SYN_PROBE
1292
+ handle.on('probe', res)
1293
+ })
1294
+ }
1295
+
1296
+ export class TCPConnection {
1297
+ mtu: number
1298
+ state: string
1299
+ net: NetworkAdapterLike
1300
+ send_buffer: GrowableRingbuffer
1301
+ send_chunk_buf: Uint8Array
1302
+ in_active_close: boolean
1303
+ delayed_send_fin: boolean
1304
+ delayed_state: string | undefined
1305
+
1306
+ events_handlers: Record<string, (...args: any[]) => void>
1307
+ tuple: string = ''
1308
+ last: PacketSpec | undefined = undefined
1309
+ hsrc: Uint8Array = new Uint8Array(6)
1310
+ psrc: Uint8Array = new Uint8Array(4)
1311
+ sport: number = 0
1312
+ hdest: Uint8Array = new Uint8Array(6)
1313
+ dport: number = 0
1314
+ pdest: Uint8Array = new Uint8Array(4)
1315
+ seq: number = 0
1316
+ ack: number = 0
1317
+ start_seq: number = 0
1318
+ winsize: number = 0
1319
+ last_received_ackn: number | undefined = undefined
1320
+ pending: boolean = false
1321
+ name: string = ''
1322
+
1323
+ read: any = undefined
1324
+ stream_id: number = 0
1325
+ on_close: () => void = () => {
1326
+ this.emit('close')
1327
+ }
1328
+ on_shutdown: () => void = () => {
1329
+ this.emit('shutdown')
1330
+ }
1331
+
1332
+ constructor(adapter: NetworkAdapterLike) {
1333
+ this.mtu = adapter.mtu || MTU_DEFAULT
1334
+ const IPV4_PAYLOAD_SIZE = this.mtu - IPV4_HEADER_SIZE
1335
+ const TCP_PAYLOAD_SIZE = IPV4_PAYLOAD_SIZE - TCP_HEADER_SIZE
1336
+
1337
+ this.state = TCP_STATE_CLOSED
1338
+ this.net = adapter
1339
+ this.send_buffer = new GrowableRingbuffer(2048, 0)
1340
+ this.send_chunk_buf = new Uint8Array(TCP_PAYLOAD_SIZE)
1341
+ this.in_active_close = false
1342
+ this.delayed_send_fin = false
1343
+ this.delayed_state = undefined
1344
+ this.events_handlers = {}
1345
+ }
1346
+
1347
+ on(event: string, handler: (...args: any[]) => void): void {
1348
+ this.events_handlers[event] = handler
1349
+ }
1350
+
1351
+ emit(event: string, ...args: any[]): void {
1352
+ if (!this.events_handlers[event]) return
1353
+ this.events_handlers[event].apply(this, args)
1354
+ }
1355
+
1356
+ ipv4_reply(): PacketSpec {
1357
+ const reply: PacketSpec = {
1358
+ eth: {
1359
+ ethertype: ETHERTYPE_IPV4,
1360
+ src: this.hsrc,
1361
+ dest: this.hdest,
1362
+ },
1363
+ ipv4: {
1364
+ proto: IPV4_PROTO_TCP,
1365
+ src: this.psrc,
1366
+ dest: this.pdest,
1367
+ },
1368
+ tcp: {
1369
+ sport: this.sport,
1370
+ dport: this.dport,
1371
+ winsize: this.winsize,
1372
+ ackn: this.ack,
1373
+ seq: this.seq,
1374
+ ack: true,
1375
+ },
1376
+ }
1377
+ return reply
1378
+ }
1379
+
1380
+ packet_reply(
1381
+ packet: PacketSpec,
1382
+ tcp_options: Partial<TcpHeader>,
1383
+ ): PacketSpec {
1384
+ const reply_tcp: TcpHeader = {
1385
+ sport: packet.tcp!.dport,
1386
+ dport: packet.tcp!.sport,
1387
+ winsize: packet.tcp!.winsize,
1388
+ ackn: this.ack,
1389
+ seq: this.seq,
1390
+ }
1391
+ for (const opt in tcp_options) {
1392
+ ;(reply_tcp as Record<string, any>)[opt] = (
1393
+ tcp_options as Record<string, any>
1394
+ )[opt]
1395
+ }
1396
+ const reply = this.ipv4_reply()
1397
+ reply.tcp = reply_tcp
1398
+ return reply
1399
+ }
1400
+
1401
+ connect(): void {
1402
+ // dbg_log(`TCP[${this.tuple}]: connect(): sending SYN+ACK in state "${this.state}", next "${TCP_STATE_SYN_SENT}"`, LOG_FETCH);
1403
+ this.seq = 1338
1404
+ this.ack = 1
1405
+ this.start_seq = 0
1406
+ this.winsize = 64240
1407
+ if (this.state !== TCP_STATE_SYN_PROBE) {
1408
+ this.state = TCP_STATE_SYN_SENT
1409
+ }
1410
+
1411
+ const reply = this.ipv4_reply()
1412
+ reply.ipv4!.id = 2345
1413
+ reply.tcp = {
1414
+ sport: this.sport,
1415
+ dport: this.dport,
1416
+ seq: 1337,
1417
+ ackn: 0,
1418
+ winsize: 0,
1419
+ syn: true,
1420
+ }
1421
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1422
+ }
1423
+
1424
+ accept(packet?: PacketSpec): void {
1425
+ packet = packet || this.last!
1426
+ this.net.tcp_conn[this.tuple] = this
1427
+ this.seq = 1338
1428
+ this.ack = packet.tcp!.seq + 1
1429
+ this.start_seq = packet.tcp!.seq
1430
+ this.winsize = packet.tcp!.winsize
1431
+
1432
+ const reply = this.ipv4_reply()
1433
+ reply.tcp = {
1434
+ sport: this.sport,
1435
+ dport: this.dport,
1436
+ seq: 1337,
1437
+ ackn: this.ack,
1438
+ winsize: packet.tcp!.winsize,
1439
+ syn: true,
1440
+ ack: true,
1441
+ options: {
1442
+ mss: this.mtu - TCP_HEADER_SIZE - IPV4_HEADER_SIZE,
1443
+ },
1444
+ }
1445
+ // dbg_log(`TCP[${this.tuple}]: accept(): sending SYN+ACK in state "${this.state}", next "${TCP_STATE_ESTABLISHED}"`, LOG_FETCH);
1446
+ this.state = TCP_STATE_ESTABLISHED
1447
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1448
+ }
1449
+
1450
+ process(packet: PacketSpec): void {
1451
+ this.last = packet
1452
+ if (this.state === TCP_STATE_CLOSED) {
1453
+ // dbg_log(`TCP[${this.tuple}]: WARNING: connection already closed, packet dropped`, LOG_FETCH);
1454
+ const reply = this.packet_reply(packet, { rst: true })
1455
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1456
+ return
1457
+ } else if (packet.tcp!.rst) {
1458
+ if (this.state === TCP_STATE_SYN_PROBE) {
1459
+ this.emit('probe', false)
1460
+ this.release()
1461
+ return
1462
+ }
1463
+ // dbg_log(`TCP[${this.tuple}]: received RST in state "${this.state}"`, LOG_FETCH);
1464
+ this.on_close()
1465
+ this.release()
1466
+ return
1467
+ } else if (packet.tcp!.syn) {
1468
+ if (this.state === TCP_STATE_SYN_SENT && packet.tcp!.ack) {
1469
+ this.ack = packet.tcp!.seq + 1
1470
+ this.start_seq = packet.tcp!.seq
1471
+ this.last_received_ackn = packet.tcp!.ackn
1472
+
1473
+ const reply = this.ipv4_reply()
1474
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1475
+ // dbg_log(`TCP[${this.tuple}]: received SYN+ACK in state "${this.state}", next "${TCP_STATE_ESTABLISHED}"`, LOG_FETCH);
1476
+ this.state = TCP_STATE_ESTABLISHED
1477
+ this.emit('connect')
1478
+ } else if (this.state === TCP_STATE_SYN_PROBE && packet.tcp!.ack) {
1479
+ this.emit('probe', true)
1480
+ const reply = this.packet_reply(packet, { rst: true })
1481
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1482
+ this.release()
1483
+ } else {
1484
+ dbg_log(
1485
+ `TCP[${this.tuple}]: WARNING: unexpected SYN packet dropped`,
1486
+ LOG_FETCH,
1487
+ )
1488
+ }
1489
+ if (packet.tcp_data!.length) {
1490
+ dbg_log(
1491
+ `TCP[${this.tuple}]: WARNING: ${packet.tcp_data!.length} bytes of unexpected SYN packet payload dropped`,
1492
+ LOG_FETCH,
1493
+ )
1494
+ }
1495
+ return
1496
+ }
1497
+
1498
+ if (packet.tcp!.ack) {
1499
+ if (this.state === TCP_STATE_SYN_RECEIVED) {
1500
+ // dbg_log(`TCP[${this.tuple}]: received ACK in state "${this.state}", next "${TCP_STATE_ESTABLISHED}"`, LOG_FETCH);
1501
+ this.state = TCP_STATE_ESTABLISHED
1502
+ } else if (this.state === TCP_STATE_FIN_WAIT_1) {
1503
+ if (!packet.tcp!.fin) {
1504
+ // handle FIN+ACK in FIN_WAIT_1 separately further down below
1505
+ // dbg_log(`TCP[${this.tuple}]: received ACK in state "${this.state}", next "${TCP_STATE_FIN_WAIT_2}"`, LOG_FETCH);
1506
+ this.state = TCP_STATE_FIN_WAIT_2
1507
+ }
1508
+ } else if (
1509
+ this.state === TCP_STATE_CLOSING ||
1510
+ this.state === TCP_STATE_LAST_ACK
1511
+ ) {
1512
+ // dbg_log(`TCP[${this.tuple}]: received ACK in state "${this.state}"`, LOG_FETCH);
1513
+ this.release()
1514
+ return
1515
+ }
1516
+ }
1517
+
1518
+ if (this.last_received_ackn === undefined) {
1519
+ this.last_received_ackn = packet.tcp!.ackn
1520
+ } else {
1521
+ const n_ack = packet.tcp!.ackn - this.last_received_ackn
1522
+ //console.log("Read ", n_ack, "(", this.last_received_ackn, ") ", packet.tcp.ackn, packet.tcp.winsize)
1523
+ if (n_ack > 0) {
1524
+ this.last_received_ackn = packet.tcp!.ackn
1525
+ this.send_buffer.remove(n_ack)
1526
+ this.seq += n_ack
1527
+ this.pending = false
1528
+
1529
+ if (this.delayed_send_fin && !this.send_buffer.length) {
1530
+ // dbg_log(`TCP[${this.tuple}]: sending delayed FIN from active close in state "${this.state}", next "${this.delayed_state}"`, LOG_FETCH);
1531
+ this.delayed_send_fin = false
1532
+ this.state = this.delayed_state!
1533
+ const reply = this.ipv4_reply()
1534
+ reply.tcp!.fin = true
1535
+ this.net.receive(
1536
+ make_packet(this.net.eth_encoder_buf, reply),
1537
+ )
1538
+ return
1539
+ }
1540
+ } else if (n_ack < 0) {
1541
+ // TODO: could this just be a 32-bit sequence number overflow?
1542
+ dbg_log(
1543
+ `TCP[${this.tuple}]: ERROR: ack underflow (pkt=${packet.tcp!.ackn} last=${this.last_received_ackn}), resetting`,
1544
+ LOG_FETCH,
1545
+ )
1546
+ const reply = this.packet_reply(packet, { rst: true })
1547
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1548
+ this.on_close()
1549
+ this.release()
1550
+ return
1551
+ }
1552
+ }
1553
+
1554
+ if (packet.tcp!.fin) {
1555
+ if (this.ack !== packet.tcp!.seq) {
1556
+ dbg_log(
1557
+ `TCP[${this.tuple}]: WARNING: closing connection in state "${this.state}" with invalid seq (${this.ack} != ${packet.tcp!.seq})`,
1558
+ LOG_FETCH,
1559
+ )
1560
+ }
1561
+ ++this.ack // FIN increases seqnr
1562
+ const reply = this.packet_reply(packet, {})
1563
+ if (this.state === TCP_STATE_ESTABLISHED) {
1564
+ // dbg_log(`TCP[${this.tuple}]: received FIN in state "${this.state}, next "${TCP_STATE_CLOSE_WAIT}""`, LOG_FETCH);
1565
+ reply.tcp!.ack = true
1566
+ this.state = TCP_STATE_CLOSE_WAIT
1567
+ this.on_shutdown()
1568
+ } else if (this.state === TCP_STATE_FIN_WAIT_1) {
1569
+ if (packet.tcp!.ack) {
1570
+ // dbg_log(`TCP[${this.tuple}]: received ACK+FIN in state "${this.state}"`, LOG_FETCH);
1571
+ this.release()
1572
+ } else {
1573
+ // dbg_log(`TCP[${this.tuple}]: received ACK in state "${this.state}", next "${TCP_STATE_CLOSING}"`, LOG_FETCH);
1574
+ this.state = TCP_STATE_CLOSING
1575
+ }
1576
+ reply.tcp!.ack = true
1577
+ } else if (this.state === TCP_STATE_FIN_WAIT_2) {
1578
+ // dbg_log(`TCP[${this.tuple}]: received FIN in state "${this.state}"`, LOG_FETCH);
1579
+ this.release()
1580
+ reply.tcp!.ack = true
1581
+ } else {
1582
+ // dbg_log(`TCP[${this.tuple}]: ERROR: received FIN in unexpected TCP state "${this.state}", resetting`, LOG_FETCH);
1583
+ this.release()
1584
+ this.on_close()
1585
+ reply.tcp!.rst = true
1586
+ }
1587
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1588
+ } else if (this.ack !== packet.tcp!.seq) {
1589
+ // Handle TCP Keep-Alives silently.
1590
+ // Excerpt from RFC 9293, 3.8.4. TCP Keep-Alives:
1591
+ // To confirm that an idle connection is still active, these
1592
+ // implementations send a probe segment designed to elicit a response
1593
+ // from the TCP peer. Such a segment generally contains SEG.SEQ =
1594
+ // SND.NXT-1 and may or may not contain one garbage octet of data.
1595
+ if (this.ack !== packet.tcp!.seq + 1) {
1596
+ dbg_log(
1597
+ `Packet seq was wrong ex: ${this.ack} ~${this.ack - this.start_seq} ` +
1598
+ `pk: ${packet.tcp!.seq} ~${this.start_seq - packet.tcp!.seq} ` +
1599
+ `(${this.ack - packet.tcp!.seq}) = ${this.name}`,
1600
+ LOG_FETCH,
1601
+ )
1602
+ }
1603
+ const reply = this.packet_reply(packet, { ack: true })
1604
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1605
+ } else if (packet.tcp!.ack && packet.tcp_data!.length > 0) {
1606
+ this.ack += packet.tcp_data!.length
1607
+ const reply = this.ipv4_reply()
1608
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1609
+ this.emit('data', packet.tcp_data)
1610
+ }
1611
+
1612
+ this.pump()
1613
+ }
1614
+
1615
+ write(data: Uint8Array): void {
1616
+ if (!this.in_active_close) {
1617
+ this.send_buffer.write(data)
1618
+ }
1619
+ this.pump()
1620
+ }
1621
+
1622
+ writev(data_array: Uint8Array[]): void {
1623
+ if (!this.in_active_close) {
1624
+ for (const data of data_array) {
1625
+ this.send_buffer.write(data)
1626
+ }
1627
+ }
1628
+ this.pump()
1629
+ }
1630
+
1631
+ close(): void {
1632
+ if (!this.in_active_close) {
1633
+ this.in_active_close = true
1634
+ let next_state: string
1635
+ if (
1636
+ this.state === TCP_STATE_ESTABLISHED ||
1637
+ this.state === TCP_STATE_SYN_RECEIVED
1638
+ ) {
1639
+ next_state = TCP_STATE_FIN_WAIT_1
1640
+ } else if (this.state === TCP_STATE_CLOSE_WAIT) {
1641
+ next_state = TCP_STATE_LAST_ACK
1642
+ } else {
1643
+ if (this.state !== TCP_STATE_SYN_SENT) {
1644
+ dbg_log(
1645
+ `TCP[${this.tuple}]: active close in unexpected state "${this.state}"`,
1646
+ LOG_FETCH,
1647
+ )
1648
+ }
1649
+ this.release()
1650
+ return
1651
+ }
1652
+
1653
+ if (this.send_buffer.length || this.pending) {
1654
+ // dbg_log(`TCP[${this.tuple}]: active close, delaying FIN in state "${this.state}", delayed next "${next_state}"`, LOG_FETCH);
1655
+ this.delayed_send_fin = true
1656
+ this.delayed_state = next_state
1657
+ } else {
1658
+ // dbg_log(`TCP[${this.tuple}]: active close, sending FIN in state "${this.state}", next "${next_state}"`, LOG_FETCH);
1659
+ this.state = next_state
1660
+ const reply = this.ipv4_reply()
1661
+ reply.tcp!.fin = true
1662
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1663
+ }
1664
+ }
1665
+ this.pump()
1666
+ }
1667
+
1668
+ release(): void {
1669
+ if (this.net.tcp_conn[this.tuple]) {
1670
+ // dbg_log(`TCP[${this.tuple}]: connection closed in state "${this.state}"`, LOG_FETCH);
1671
+ this.state = TCP_STATE_CLOSED
1672
+ delete this.net.tcp_conn[this.tuple]
1673
+ }
1674
+ }
1675
+
1676
+ pump(): void {
1677
+ if (this.send_buffer.length && !this.pending) {
1678
+ const data = this.send_chunk_buf
1679
+ const n_ready = this.send_buffer.peek(data)
1680
+ const reply = this.ipv4_reply()
1681
+ reply.tcp!.psh = true
1682
+ reply.tcp_data = data.subarray(0, n_ready)
1683
+ this.net.receive(make_packet(this.net.eth_encoder_buf, reply))
1684
+ this.pending = true
1685
+ }
1686
+ }
1687
+ }
1688
+
1689
+ function arp_whohas(packet: PacketSpec, adapter: NetworkAdapterLike): void {
1690
+ const packet_subnet = iptolong(packet.arp!.tpa) & 0xffffff00
1691
+ const router_subnet = iptolong(adapter.router_ip) & 0xffffff00
1692
+
1693
+ if (!adapter.masquerade) {
1694
+ if (packet_subnet !== router_subnet) {
1695
+ return
1696
+ }
1697
+ }
1698
+
1699
+ if (packet_subnet === router_subnet) {
1700
+ // Ignore the DHCP client area
1701
+ if (packet.arp!.tpa[3] > 99) return
1702
+ }
1703
+
1704
+ // Reply to ARP Whohas
1705
+ const reply: PacketSpec = {
1706
+ eth: {
1707
+ ethertype: ETHERTYPE_ARP,
1708
+ src: adapter.router_mac,
1709
+ dest: packet.eth.src,
1710
+ },
1711
+ arp: {
1712
+ htype: 1,
1713
+ ptype: ETHERTYPE_IPV4,
1714
+ oper: 2,
1715
+ sha: adapter.router_mac,
1716
+ spa: packet.arp!.tpa,
1717
+ tha: packet.eth.src,
1718
+ tpa: packet.arp!.spa,
1719
+ },
1720
+ }
1721
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
1722
+ }
1723
+
1724
+ function handle_fake_ping(
1725
+ packet: PacketSpec,
1726
+ adapter: NetworkAdapterLike,
1727
+ ): void {
1728
+ const reply: PacketSpec = {
1729
+ eth: {
1730
+ ethertype: ETHERTYPE_IPV4,
1731
+ src: adapter.router_mac,
1732
+ dest: packet.eth.src,
1733
+ },
1734
+ ipv4: {
1735
+ proto: IPV4_PROTO_ICMP,
1736
+ src: packet.ipv4!.dest,
1737
+ dest: packet.ipv4!.src,
1738
+ },
1739
+ icmp: {
1740
+ type: 0,
1741
+ code: packet.icmp!.code,
1742
+ data: packet.icmp!.data,
1743
+ },
1744
+ }
1745
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
1746
+ }
1747
+
1748
+ function handle_udp_echo(
1749
+ packet: PacketSpec,
1750
+ adapter: NetworkAdapterLike,
1751
+ ): void {
1752
+ // UDP Echo Server
1753
+ const reply: PacketSpec = {
1754
+ eth: {
1755
+ ethertype: ETHERTYPE_IPV4,
1756
+ src: adapter.router_mac,
1757
+ dest: packet.eth.src,
1758
+ },
1759
+ ipv4: {
1760
+ proto: IPV4_PROTO_UDP,
1761
+ src: packet.ipv4!.dest,
1762
+ dest: packet.ipv4!.src,
1763
+ },
1764
+ udp: {
1765
+ sport: packet.udp!.dport,
1766
+ dport: packet.udp!.sport,
1767
+ data: new TextEncoder().encode(packet.udp!.data_s),
1768
+ },
1769
+ }
1770
+ adapter.receive(make_packet(adapter.eth_encoder_buf, reply))
1771
+ }