@amritk/nish-x86_64-linux 0.24.0 → 0.25.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 (42) hide show
  1. package/bin/nish +0 -0
  2. package/docs/AI.md +24 -4
  3. package/package.json +1 -1
  4. package/runtime/browser.mjs +4 -3
  5. package/runtime/core/host.d.ts +8 -0
  6. package/runtime/core/host.mjs +50 -2
  7. package/runtime/nish.d.ts +100 -0
  8. package/runtime/nish.h +34 -0
  9. package/runtime/nish.mjs +21 -0
  10. package/runtime/node.mjs +57 -6
  11. package/runtime/runtime-net.c +44 -3
  12. package/runtime/runtime-os.c +115 -12
  13. package/scripts/build.sh +13 -3
  14. package/std/README.md +25 -8
  15. package/std/cli-args.ts +1216 -0
  16. package/std/cli-flags.ts +585 -0
  17. package/std/cli.ts +308 -19
  18. package/std/compress/checksum.ts +132 -20
  19. package/std/compress/deflate.ts +638 -248
  20. package/std/compress/gzip.ts +20 -6
  21. package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
  22. package/std/crypto/p256.ts +63 -1
  23. package/std/crypto/p384.ts +6044 -0
  24. package/std/crypto/pki-roots.ts +322 -0
  25. package/std/crypto/pki.ts +1207 -0
  26. package/std/crypto/rsa.ts +663 -0
  27. package/std/crypto/x509.ts +1054 -58
  28. package/std/net/dns-config.ts +734 -0
  29. package/std/net/dns-message.ts +517 -0
  30. package/std/net/dns-resolver.ts +1015 -0
  31. package/std/net/dns.ts +478 -0
  32. package/std/net/http1-client.ts +1227 -0
  33. package/std/net/http1-server.ts +7 -2
  34. package/std/net/http1.ts +9 -9
  35. package/std/net/tls/client.ts +1094 -0
  36. package/std/net/tls/codec.ts +585 -3
  37. package/std/net/tls/record-client.ts +1192 -0
  38. package/std/net/tls/verify.ts +221 -0
  39. package/std/net/tls-tcp.ts +642 -11
  40. package/std/net/tls.ts +2 -2
  41. package/std/net/url.ts +559 -4
  42. package/std/yaml-write.ts +415 -0
@@ -0,0 +1,517 @@
1
+ /**
2
+ * `nish/net/dns-message` — a DNS query for one A or AAAA question written into
3
+ * the caller's buffer, and the reply to it read back into addresses.
4
+ *
5
+ * A stub resolver sends one question over UDP and reads whatever datagram
6
+ * comes back to its port, so every byte of a reply is the peer's: a name
7
+ * compressed into a loop, a count of 65,535 answers in a 40-byte message, an A
8
+ * record five bytes long. This module reads a reply against the query it
9
+ * answers and refuses each of those with its own code:
10
+ *
11
+ * import { DnsReply, DNS_NOERROR, DNS_QUERY_MAX_BYTES, DNS_TYPE_A, dnsBuildQuery, dnsParseReply } from "nish/net/dns-message"
12
+ *
13
+ * const query: u8[] = new Array<u8>(DNS_QUERY_MAX_BYTES)
14
+ * const qlen: i32 = dnsBuildQuery(query, id, "example.com", DNS_TYPE_A) // < 0: a DNS_ERR_* code
15
+ * // ... send query[0, qlen), read the datagram into msg[0, mlen) ...
16
+ * const reply: DnsReply = new DnsReply()
17
+ * const status: i32 = dnsParseReply(msg, mlen, query, qlen, 443, reply)
18
+ * if (status === DNS_NOERROR && !reply.truncated) {
19
+ * // reply.addresses[i * DNS_ADDRESS_BYTES, +18) for i < reply.count, each with reply.ttls[i]
20
+ * } // DNS_NXDOMAIN, DNS_SERVFAIL, DNS_REFUSED: the server's answer; < 0: a DNS_ERR_* code
21
+ *
22
+ * **Nothing is allocated per message.** The query is written into the
23
+ * caller's array and the reply read into a `DnsReply` the caller made once and
24
+ * passes again; `dnsParseReply` resets it first. A window outside its array is
25
+ * a bug in the caller and panics; nothing a peer sends does, and the work is
26
+ * linear in the reply's length.
27
+ *
28
+ * **A reply must answer the query.** Its ID, its QR bit, opcode 0, exactly one
29
+ * question, and that question's name (compared without case, as RFC 4343 asks),
30
+ * type and class must all be the query's, or the reply is refused before any
31
+ * record is read: it is not an answer to this question.
32
+ *
33
+ * **Names are bounded.** A compression pointer must point before the start of
34
+ * the run of labels that holds it, so every hop moves backwards and a loop is
35
+ * impossible; at most `DNS_MAX_POINTER_HOPS` (16) hops are followed, a name
36
+ * is at most 255 bytes in wire form, and the label types RFC 6891 retired
37
+ * (`0x40`, `0x80`) are refused.
38
+ *
39
+ * **Only the question's name is answered.** Records are read in message
40
+ * order, and the chain starts at the question's name: a CNAME whose owner is
41
+ * the chain's end moves the end to its target, and an A or AAAA record of the
42
+ * asked type whose owner is the chain's end is kept. Every other record is
43
+ * read past, so an answer for a name nobody asked about never becomes an
44
+ * address. The first `DNS_MAX_ANSWERS` (8) addresses are kept and the rest
45
+ * counted in `dropped`.
46
+ *
47
+ * **Addresses are the 18-byte form** of `docs/LANGUAGE.md`: sixteen bytes of
48
+ * IPv6, an IPv4 address as `::ffff:a.b.c.d`, then the caller's port big-endian,
49
+ * so one can be handed to a connect as it stands.
50
+ *
51
+ * The record of what this module refuses and why is `docs/security/dns.md`.
52
+ */
53
+ import { getU16BE, getU32BE, setU16BE } from "nish/bytes"
54
+
55
+ /** The record type of an IPv4 address (RFC 1035 §3.2.2). */
56
+ export const DNS_TYPE_A: i32 = 1
57
+ /** The record type of an alias (RFC 1035 §3.2.2). */
58
+ export const DNS_TYPE_CNAME: i32 = 5
59
+ /** The record type of an IPv6 address (RFC 3596 §2.1). */
60
+ export const DNS_TYPE_AAAA: i32 = 28
61
+ /** The Internet class, the only one asked or kept. */
62
+ export const DNS_CLASS_IN: i32 = 1
63
+
64
+ /** The bytes of one address: sixteen of IPv6 and two of port. */
65
+ export const DNS_ADDRESS_BYTES: i32 = 18
66
+ /** How many addresses one reply keeps; the rest are counted in `DnsReply.dropped`. */
67
+ export const DNS_MAX_ANSWERS: i32 = 8
68
+ /** How many compression pointers one name may follow. */
69
+ export const DNS_MAX_POINTER_HOPS: i32 = 16
70
+ /** The longest name in wire form, its root byte included (RFC 1035 §2.3.4). */
71
+ export const DNS_MAX_NAME_BYTES: i32 = 255
72
+ /** The longest label (RFC 1035 §2.3.4). */
73
+ export const DNS_MAX_LABEL_BYTES: i32 = 63
74
+ /** The longest query `dnsBuildQuery` writes: the header, the longest name, the type and class. */
75
+ export const DNS_QUERY_MAX_BYTES: i32 = 12 + 255 + 4
76
+
77
+ /** The server answered: the name exists, and its addresses, if any, are in the reply. */
78
+ export const DNS_NOERROR: i32 = 0
79
+ /** The server could not answer (RCODE 2). */
80
+ export const DNS_SERVFAIL: i32 = 2
81
+ /** The name does not exist (RCODE 3). */
82
+ export const DNS_NXDOMAIN: i32 = 3
83
+ /** The server will not answer (RCODE 5). */
84
+ export const DNS_REFUSED: i32 = 5
85
+
86
+ /** `out` has no room for the query; nothing is written past `out.length`. */
87
+ export const DNS_ERR_SPACE: i32 = -1
88
+ /** The name to ask is empty, or holds an empty label (`a..b`, `.a`). */
89
+ export const DNS_ERR_EMPTY_LABEL: i32 = -2
90
+ /** A label of the name to ask is longer than 63 bytes. */
91
+ export const DNS_ERR_LABEL_LONG: i32 = -3
92
+ /** A name is longer than 255 bytes in wire form: the one to ask, or one in the reply. */
93
+ export const DNS_ERR_NAME_LONG: i32 = -4
94
+ /** The name to ask holds a byte that is not printable ASCII (a control byte, a space, or above 0x7E). */
95
+ export const DNS_ERR_NAME_BYTE: i32 = -5
96
+ /** The type to ask is not `DNS_TYPE_A` or `DNS_TYPE_AAAA`. */
97
+ export const DNS_ERR_QTYPE: i32 = -6
98
+ /** The reply is shorter than its 12-byte header. */
99
+ export const DNS_ERR_HEADER: i32 = -7
100
+ /** The reply's QR bit is clear: it is a query, not a reply. */
101
+ export const DNS_ERR_NOT_REPLY: i32 = -8
102
+ /** The reply's opcode is not 0 (a standard query). */
103
+ export const DNS_ERR_OPCODE: i32 = -9
104
+ /** The reply's ID is not the query's. */
105
+ export const DNS_ERR_ID: i32 = -10
106
+ /** The reply does not hold exactly one question, or not the query's name, type and class. */
107
+ export const DNS_ERR_QUESTION: i32 = -11
108
+ /** A name or a record's fixed fields run past the end of the reply. */
109
+ export const DNS_ERR_SHORT: i32 = -12
110
+ /** The reply's counts promise more records than its remaining bytes can hold. */
111
+ export const DNS_ERR_COUNT: i32 = -13
112
+ /** A compression pointer points at or after the run of labels that holds it, or into the header. */
113
+ export const DNS_ERR_POINTER: i32 = -14
114
+ /** A name follows more than `DNS_MAX_POINTER_HOPS` compression pointers. */
115
+ export const DNS_ERR_HOPS: i32 = -15
116
+ /** A label's length byte starts with the bits `01` or `10`, types RFC 6891 retired. */
117
+ export const DNS_ERR_LABEL_TYPE: i32 = -16
118
+ /** An A record's RDLENGTH is not 4, or an AAAA record's is not 16. */
119
+ export const DNS_ERR_RDLENGTH: i32 = -17
120
+ /** A record's RDATA runs past the end of the reply, or a CNAME's target does not fill it. */
121
+ export const DNS_ERR_RDATA: i32 = -18
122
+ /** The reply's RCODE is none of NOERROR, SERVFAIL, NXDOMAIN and REFUSED (FORMERR, NOTIMP, or one undefined). */
123
+ export const DNS_ERR_RCODE: i32 = -19
124
+
125
+ const DNS_HEADER_BYTES: i32 = 12
126
+ const DNS_FLAG_QR: i32 = 0x8000
127
+ const DNS_FLAG_TC: i32 = 0x0200
128
+ const DNS_FLAG_RD: i32 = 0x0100
129
+ /** The fewest bytes a record can take: a root name and its ten fixed bytes. */
130
+ const DNS_MIN_RECORD_BYTES: i32 = 11
131
+
132
+ /**
133
+ * One reply read by `dnsParseReply`, made once and reused.
134
+ *
135
+ * The arrays are sized for `DNS_MAX_ANSWERS`, and `owner` and `chain` hold a
136
+ * name each in lowercase wire form, so reading a reply writes only into what
137
+ * the constructor made.
138
+ */
139
+ export class DnsReply {
140
+ /** `count` addresses of `DNS_ADDRESS_BYTES` each, from offset 0. */
141
+ addresses: u8[]
142
+ /** The TTL of each kept address, in seconds; one above 2^31 - 1 reads as 0 (RFC 2181 §8). */
143
+ ttls: i32[]
144
+ /** How many addresses were kept. */
145
+ count: i32 = 0
146
+ /** How many addresses past `DNS_MAX_ANSWERS` were read and not kept. */
147
+ dropped: i32 = 0
148
+ /** The reply's RCODE, once its header was read. */
149
+ rcode: i32 = 0
150
+ /** The TC bit: the reply was cut to fit a datagram, and no record of it was read. */
151
+ truncated: boolean = false
152
+ /** The last record's owner name, lowercase, in wire form. */
153
+ owner: u8[]
154
+ /** The end of the alias chain, lowercase, in wire form: the question's name until a CNAME moves it. */
155
+ chain: u8[]
156
+ /** The question's name as the query asked it, lowercase, in wire form. */
157
+ asked: u8[]
158
+
159
+ constructor() {
160
+ this.addresses = new Array<u8>(DNS_MAX_ANSWERS * DNS_ADDRESS_BYTES)
161
+ this.ttls = new Array<i32>(DNS_MAX_ANSWERS)
162
+ this.owner = new Array<u8>(DNS_MAX_NAME_BYTES)
163
+ this.chain = new Array<u8>(DNS_MAX_NAME_BYTES)
164
+ this.asked = new Array<u8>(DNS_MAX_NAME_BYTES)
165
+ }
166
+ }
167
+
168
+ /** Panics unless `[0, len)` lies inside `bytes`. */
169
+ const dnsCheckLength = (bytes: u8[], len: i32, who: string): void => {
170
+ if (len < 0 || len > toI32(bytes.length)) {
171
+ panic(who + ": the length is outside the array")
172
+ }
173
+ }
174
+
175
+ /** `b` with A to Z read as a to z, and every other byte as it stands. */
176
+ const dnsLower = (b: i32): i32 => (b >= 65 && b <= 90 ? b + 32 : b)
177
+
178
+ /**
179
+ * Writes the query for `name` and `qtype` into `out` from offset 0: the ID,
180
+ * RD set, one question of class IN, and no EDNS record. Answers the query's
181
+ * length, or a `DNS_ERR_*` code with `out` partly written.
182
+ *
183
+ * `name` is ASCII, its labels split on `.`, and one trailing `.` is read as the
184
+ * root it names. `id` is the caller's: a resolver draws it at random, so that a
185
+ * spoofed reply must guess it, and passes the same query to `dnsParseReply`.
186
+ */
187
+ export const dnsBuildQuery = (out: u8[], id: i32, name: string, qtype: i32): i32 => {
188
+ if (id < 0 || id > 0xffff) {
189
+ panic(`dnsBuildQuery: the id ${id} is not 16 bits`)
190
+ }
191
+ if (qtype !== DNS_TYPE_A && qtype !== DNS_TYPE_AAAA) {
192
+ return DNS_ERR_QTYPE
193
+ }
194
+ let n: i32 = toI32(name.length)
195
+ if (n > 0 && name.charCodeAt(n - 1) === 46) {
196
+ n -= 1
197
+ }
198
+ if (n === 0) {
199
+ return DNS_ERR_EMPTY_LABEL
200
+ }
201
+ // n bytes of name become n + 1 of labels and one root byte.
202
+ if (n + 2 > DNS_MAX_NAME_BYTES) {
203
+ return DNS_ERR_NAME_LONG
204
+ }
205
+ const total: i32 = DNS_HEADER_BYTES + n + 2 + 4
206
+ if (total > toI32(out.length)) {
207
+ return DNS_ERR_SPACE
208
+ }
209
+ setU16BE(out, 0, toU16(id))
210
+ setU16BE(out, 2, toU16(DNS_FLAG_RD))
211
+ setU16BE(out, 4, toU16(1))
212
+ setU16BE(out, 6, toU16(0))
213
+ setU16BE(out, 8, toU16(0))
214
+ setU16BE(out, 10, toU16(0))
215
+ // `lengthAt` is the length byte of the label being written.
216
+ let lengthAt: i32 = DNS_HEADER_BYTES
217
+ let i: i32 = 0
218
+ while (i <= n) {
219
+ const b: i32 = i < n ? toI32(name.charCodeAt(i)) : 46
220
+ if (b === 46) {
221
+ const label: i32 = DNS_HEADER_BYTES + i - lengthAt
222
+ if (label === 0) {
223
+ return DNS_ERR_EMPTY_LABEL
224
+ }
225
+ if (label > DNS_MAX_LABEL_BYTES) {
226
+ return DNS_ERR_LABEL_LONG
227
+ }
228
+ if (lengthAt < 0 || lengthAt >= toI32(out.length)) {
229
+ return DNS_ERR_SPACE
230
+ }
231
+ out[lengthAt] = toU8(label)
232
+ lengthAt = DNS_HEADER_BYTES + i + 1
233
+ } else if (b < 33 || b > 126) {
234
+ return DNS_ERR_NAME_BYTE
235
+ } else {
236
+ out[DNS_HEADER_BYTES + i + 1] = toU8(b)
237
+ }
238
+ i += 1
239
+ }
240
+ out[DNS_HEADER_BYTES + n + 1] = toU8(0)
241
+ setU16BE(out, DNS_HEADER_BYTES + n + 2, toU16(qtype))
242
+ setU16BE(out, DNS_HEADER_BYTES + n + 4, toU16(DNS_CLASS_IN))
243
+ return total
244
+ }
245
+
246
+ /**
247
+ * Reads the name at `msg[off]`, following compression pointers, into `out` in
248
+ * lowercase wire form, and answers the offset just past the name where it
249
+ * stands in the message (past its first pointer, when it has one), or a
250
+ * `DNS_ERR_*` code.
251
+ *
252
+ * A pointer must point before `runStart`, the offset the labels holding it
253
+ * started at, and after the header: each hop then moves strictly backwards,
254
+ * which is what makes a loop impossible rather than merely bounded.
255
+ */
256
+ const dnsReadName = (msg: u8[], len: i32, off: i32, out: u8[]): i32 => {
257
+ let pos: i32 = off
258
+ let runStart: i32 = off
259
+ let after: i32 = -1
260
+ let hops: i32 = 0
261
+ let w: i32 = 0
262
+ const msgLen: i32 = toI32(msg.length)
263
+ const outLen: i32 = toI32(out.length)
264
+ while (true) {
265
+ if (pos < 0 || pos >= len || pos >= msgLen) {
266
+ return DNS_ERR_SHORT
267
+ }
268
+ const b: i32 = toI32(msg[pos])
269
+ if (w < 0 || w >= outLen) {
270
+ return DNS_ERR_NAME_LONG
271
+ }
272
+ if (b === 0) {
273
+ out[w] = toU8(0)
274
+ return after >= 0 ? after : pos + 1
275
+ }
276
+ const kind: i32 = b & 0xc0
277
+ if (kind === 0xc0) {
278
+ if (pos + 1 >= len || pos + 1 >= msgLen) {
279
+ return DNS_ERR_SHORT
280
+ }
281
+ const target: i32 = ((b & 0x3f) << 8) | toI32(msg[pos + 1])
282
+ if (target >= runStart || target < DNS_HEADER_BYTES) {
283
+ return DNS_ERR_POINTER
284
+ }
285
+ hops += 1
286
+ if (hops > DNS_MAX_POINTER_HOPS) {
287
+ return DNS_ERR_HOPS
288
+ }
289
+ if (after < 0) {
290
+ after = pos + 2
291
+ }
292
+ pos = target
293
+ runStart = target
294
+ } else if (kind !== 0) {
295
+ return DNS_ERR_LABEL_TYPE
296
+ } else {
297
+ if (b > len - pos - 1) {
298
+ return DNS_ERR_SHORT
299
+ }
300
+ // The label, its length byte and the root byte still to come.
301
+ if (w + b + 2 > DNS_MAX_NAME_BYTES) {
302
+ return DNS_ERR_NAME_LONG
303
+ }
304
+ out[w] = toU8(b)
305
+ let k: i32 = 1
306
+ while (k <= b) {
307
+ out[w + k] = toU8(dnsLower(toI32(msg[pos + k])))
308
+ k += 1
309
+ }
310
+ w += b + 1
311
+ pos += b + 1
312
+ }
313
+ }
314
+ }
315
+
316
+ /** Whether two names `dnsReadName` wrote are the same name. */
317
+ const dnsSameName = (a: u8[], b: u8[]): boolean => {
318
+ let i: i32 = 0
319
+ const aLen: i32 = toI32(a.length)
320
+ const bLen: i32 = toI32(b.length)
321
+ while (true) {
322
+ if (i < 0 || i >= aLen || i >= bLen) {
323
+ return false
324
+ }
325
+ const n: i32 = toI32(a[i])
326
+ if (n !== toI32(b[i])) {
327
+ return false
328
+ }
329
+ if (n === 0) {
330
+ return true
331
+ }
332
+ let k: i32 = 1
333
+ while (k <= n) {
334
+ if (a[i + k] !== b[i + k]) {
335
+ return false
336
+ }
337
+ k += 1
338
+ }
339
+ i += n + 1
340
+ }
341
+ }
342
+
343
+ /** Copies `name`, a name `dnsReadName` wrote, into `into`. */
344
+ const dnsCopyName = (name: u8[], into: u8[]): void => {
345
+ let i: i32 = 0
346
+ const nameLen: i32 = toI32(name.length)
347
+ while (true) {
348
+ if (i < 0 || i >= nameLen) {
349
+ return
350
+ }
351
+ const n: i32 = toI32(name[i])
352
+ let k: i32 = 0
353
+ while (k <= n) {
354
+ into[i + k] = name[i + k]
355
+ k += 1
356
+ }
357
+ if (n === 0) {
358
+ return
359
+ }
360
+ i += n + 1
361
+ }
362
+ }
363
+
364
+ /** Keeps the RDATA at `msg[at]`, `size` bytes, as the next address of `reply`, with `port` and `ttl`. */
365
+ const dnsKeepAddress = (msg: u8[], at: i32, size: i32, port: i32, ttl: i32, reply: DnsReply): void => {
366
+ const base: i32 = reply.count * DNS_ADDRESS_BYTES
367
+ const lead: i32 = 16 - size
368
+ let k: i32 = 0
369
+ while (k < lead) {
370
+ reply.addresses[base + k] = toU8(0)
371
+ k += 1
372
+ }
373
+ if (size === 4) {
374
+ reply.addresses[base + 10] = toU8(255)
375
+ reply.addresses[base + 11] = toU8(255)
376
+ }
377
+ k = 0
378
+ while (k < size) {
379
+ reply.addresses[base + lead + k] = msg[at + k]
380
+ k += 1
381
+ }
382
+ setU16BE(reply.addresses, base + 16, toU16(port))
383
+ reply.ttls[reply.count] = ttl
384
+ reply.count += 1
385
+ }
386
+
387
+ /**
388
+ * Reads `msg[0, len)` as the reply to `query[0, queryLen)`, a query
389
+ * `dnsBuildQuery` wrote, into `reply`, with `port` written into every address.
390
+ *
391
+ * Answers `DNS_NOERROR`, `DNS_NXDOMAIN`, `DNS_SERVFAIL` or `DNS_REFUSED` for a
392
+ * reply that answers the query, or a `DNS_ERR_*` code for one that is
393
+ * malformed or answers another. The answer records are read only under
394
+ * NOERROR and with TC clear; with TC set, `reply.truncated` says so,
395
+ * `reply.count` is 0 whatever the counts claim, and the caller asks again
396
+ * over TCP. The authority and
397
+ * additional sections are counted against the bytes left and never read.
398
+ */
399
+ export const dnsParseReply = (
400
+ msg: u8[],
401
+ len: i32,
402
+ query: u8[],
403
+ queryLen: i32,
404
+ port: i32,
405
+ reply: DnsReply
406
+ ): i32 => {
407
+ dnsCheckLength(msg, len, "dnsParseReply")
408
+ dnsCheckLength(query, queryLen, "dnsParseReply")
409
+ if (port < 0 || port > 0xffff) {
410
+ panic(`dnsParseReply: the port ${port} is not 16 bits`)
411
+ }
412
+ reply.count = 0
413
+ reply.dropped = 0
414
+ reply.rcode = 0
415
+ reply.truncated = false
416
+ if (len < DNS_HEADER_BYTES) {
417
+ return DNS_ERR_HEADER
418
+ }
419
+ const flags: i32 = toI32(getU16BE(msg, 2))
420
+ if ((flags & DNS_FLAG_QR) === 0) {
421
+ return DNS_ERR_NOT_REPLY
422
+ }
423
+ if (((flags >> 11) & 15) !== 0) {
424
+ return DNS_ERR_OPCODE
425
+ }
426
+ if (queryLen < DNS_HEADER_BYTES || msg[0] !== query[0] || msg[1] !== query[1]) {
427
+ return DNS_ERR_ID
428
+ }
429
+ if (toI32(getU16BE(msg, 4)) !== 1) {
430
+ return DNS_ERR_QUESTION
431
+ }
432
+ const asked: i32 = dnsReadName(query, queryLen, DNS_HEADER_BYTES, reply.asked)
433
+ if (asked < 0 || asked + 4 > queryLen) {
434
+ panic("dnsParseReply: the query is not one dnsBuildQuery wrote")
435
+ }
436
+ const named: i32 = dnsReadName(msg, len, DNS_HEADER_BYTES, reply.chain)
437
+ if (named < 0) {
438
+ return named
439
+ }
440
+ if (named + 4 > len) {
441
+ return DNS_ERR_SHORT
442
+ }
443
+ if (
444
+ !dnsSameName(reply.chain, reply.asked) ||
445
+ msg[named] !== query[asked] ||
446
+ msg[named + 1] !== query[asked + 1] ||
447
+ msg[named + 2] !== query[asked + 2] ||
448
+ msg[named + 3] !== query[asked + 3]
449
+ ) {
450
+ return DNS_ERR_QUESTION
451
+ }
452
+ const qtype: i32 = toI32(getU16BE(query, asked))
453
+ reply.truncated = (flags & DNS_FLAG_TC) !== 0
454
+ const rcode: i32 = flags & 15
455
+ reply.rcode = rcode
456
+ if (rcode !== DNS_NOERROR && rcode !== DNS_SERVFAIL && rcode !== DNS_NXDOMAIN && rcode !== DNS_REFUSED) {
457
+ return DNS_ERR_RCODE
458
+ }
459
+ // A truncated reply's counts describe the records the server could not fit,
460
+ // so they are not held to the bytes that arrived.
461
+ if (reply.truncated) {
462
+ return rcode
463
+ }
464
+ let pos: i32 = named + 4
465
+ const records: i32 = toI32(getU16BE(msg, 6)) + toI32(getU16BE(msg, 8)) + toI32(getU16BE(msg, 10))
466
+ if (records > (len - pos) / DNS_MIN_RECORD_BYTES) {
467
+ return DNS_ERR_COUNT
468
+ }
469
+ if (rcode !== DNS_NOERROR) {
470
+ return rcode
471
+ }
472
+ const answers: i32 = toI32(getU16BE(msg, 6))
473
+ let i: i32 = 0
474
+ while (i < answers) {
475
+ const fixed: i32 = dnsReadName(msg, len, pos, reply.owner)
476
+ if (fixed < 0) {
477
+ return fixed
478
+ }
479
+ if (fixed + 10 > len) {
480
+ return DNS_ERR_SHORT
481
+ }
482
+ const type: i32 = toI32(getU16BE(msg, fixed))
483
+ const klass: i32 = toI32(getU16BE(msg, fixed + 2))
484
+ const rawTtl: i32 = toI32(getU32BE(msg, fixed + 4))
485
+ const size: i32 = toI32(getU16BE(msg, fixed + 8))
486
+ const data: i32 = fixed + 10
487
+ if (size > len - data) {
488
+ return DNS_ERR_RDATA
489
+ }
490
+ if (klass === DNS_CLASS_IN) {
491
+ if ((type === DNS_TYPE_A && size !== 4) || (type === DNS_TYPE_AAAA && size !== 16)) {
492
+ return DNS_ERR_RDLENGTH
493
+ }
494
+ if (dnsSameName(reply.owner, reply.chain)) {
495
+ if (type === DNS_TYPE_CNAME) {
496
+ const target: i32 = dnsReadName(msg, data + size, data, reply.owner)
497
+ if (target < 0) {
498
+ return target
499
+ }
500
+ if (target !== data + size) {
501
+ return DNS_ERR_RDATA
502
+ }
503
+ dnsCopyName(reply.owner, reply.chain)
504
+ } else if (type === qtype) {
505
+ if (reply.count < DNS_MAX_ANSWERS) {
506
+ dnsKeepAddress(msg, data, size, port, rawTtl < 0 ? 0 : rawTtl, reply)
507
+ } else {
508
+ reply.dropped += 1
509
+ }
510
+ }
511
+ }
512
+ }
513
+ pos = data + size
514
+ i += 1
515
+ }
516
+ return DNS_NOERROR
517
+ }