@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.
- package/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- 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
|
+
}
|