@amritk/nish-x86_64-linux 0.24.0 → 0.26.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 (43) hide show
  1. package/bin/nish +0 -0
  2. package/docs/AI.md +45 -4
  3. package/package.json +1 -1
  4. package/runtime/browser.mjs +4 -3
  5. package/runtime/core/host.d.ts +12 -0
  6. package/runtime/core/host.mjs +74 -2
  7. package/runtime/nish.d.ts +148 -0
  8. package/runtime/nish.h +59 -0
  9. package/runtime/nish.mjs +25 -0
  10. package/runtime/node.mjs +136 -6
  11. package/runtime/runtime-net.c +44 -3
  12. package/runtime/runtime-os.c +351 -18
  13. package/scripts/build.sh +13 -3
  14. package/std/README.md +27 -10
  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/postgres.ts +259 -26
  36. package/std/net/tls/client.ts +1094 -0
  37. package/std/net/tls/codec.ts +585 -3
  38. package/std/net/tls/record-client.ts +1192 -0
  39. package/std/net/tls/verify.ts +221 -0
  40. package/std/net/tls-tcp.ts +642 -11
  41. package/std/net/tls.ts +2 -2
  42. package/std/net/url.ts +559 -4
  43. package/std/yaml-write.ts +415 -0
@@ -0,0 +1,1015 @@
1
+ /**
2
+ * `nish/net/dns-resolver` — a DNS stub resolver with no I/O of its own: it
3
+ * says which bytes to send to which server, reads the bytes that came back,
4
+ * and answers each lookup's addresses, while the program's own loop owns the
5
+ * sockets and the clock.
6
+ *
7
+ * const resolver = new DnsResolver(config, hosts, 4);
8
+ * const lookup = resolver.start("example.com", 443, now);
9
+ * // in the loop: send what nextOutgoing gives, feed replies to receive,
10
+ * // call tick(now) when nextDeadlineNanos() passes, then read result.
11
+ *
12
+ * `nish/net/dns` drives it over `nish:net` sockets and adds a blocking
13
+ * `resolveHost`; this module never imports `nish:net`, never reads a clock
14
+ * and never reads a file, so it compiles for `wasm32` and a test drives it
15
+ * with scripted packets.
16
+ *
17
+ * A lookup asks A and AAAA at once, one query each. Each query tries the
18
+ * servers of its `DnsConfig` in turn, `attempts` rounds of `timeout` seconds
19
+ * a try, follows a CNAME the reply leaves unanswered with a query of its own,
20
+ * at most `DNS_RESOLVER_MAX_CNAME_HOPS` of them, and asks again over TCP when
21
+ * a reply comes back truncated. A name goes through the search list as
22
+ * resolv.conf(5) describes. `localhost`, an address literal and a hosts entry
23
+ * answer at once with no packet.
24
+ *
25
+ * A reply from any address or port but the server's, or with another ID or
26
+ * question, is dropped and counted, and the query keeps waiting for the true
27
+ * one: a spoofer can lose a race, never end a lookup. The ID of each send is
28
+ * the caller's, given to `nextOutgoing`: `nish/net/dns` draws it from
29
+ * `crypto.getRandomValues`, which a `wasm32` build cannot reach, so this
30
+ * module asks for none and a test can script its IDs.
31
+ *
32
+ * Every buffer is made in the constructor; a lookup allocates only the
33
+ * sixteen transient bytes `dnsParseAddress` takes to test a name for an
34
+ * address literal.
35
+ * Private helpers share the importing program's flat symbol namespace
36
+ * (`docs/wp26-stdlib.md` §3e), which is why each one carries the module's name.
37
+ */
38
+
39
+ import {
40
+ DNS_ADDRESS_BYTES,
41
+ DNS_ERR_ID,
42
+ DNS_ERR_QUESTION,
43
+ DNS_ERR_RCODE,
44
+ DNS_MAX_ANSWERS,
45
+ DNS_MAX_LABEL_BYTES,
46
+ DNS_MAX_NAME_BYTES,
47
+ DNS_NOERROR,
48
+ DNS_NXDOMAIN,
49
+ DNS_QUERY_MAX_BYTES,
50
+ DNS_TYPE_A,
51
+ DNS_TYPE_AAAA,
52
+ DnsReply,
53
+ dnsBuildQuery,
54
+ dnsParseReply,
55
+ } from "nish/net/dns-message"
56
+ import { setU16BE } from "nish/bytes"
57
+ import { DnsConfig, DnsHosts, dnsHostsFind, dnsParseAddress } from "nish/net/dns-config"
58
+
59
+ /** `result` for a lookup that has not finished. */
60
+ export const DNS_RESOLVE_PENDING: i32 = -40
61
+ /** The name does not exist, or has no address: NXDOMAIN or NODATA from every candidate. */
62
+ export const DNS_RESOLVE_NOT_FOUND: i32 = -41
63
+ /** No server gave an answer: each timed out, failed or was refused, `attempts` rounds over. */
64
+ export const DNS_RESOLVE_NO_ANSWER: i32 = -42
65
+ /**
66
+ * The CNAME chain loops across queries back to a name already asked, or needs
67
+ * more than `DNS_RESOLVER_MAX_CNAME_HOPS` queries. A loop closed inside one
68
+ * reply is `DNS_RESOLVE_NOT_FOUND`, as that reply names no address.
69
+ */
70
+ export const DNS_RESOLVE_CNAME: i32 = -43
71
+ /** `start` found every lookup slot in use. */
72
+ export const DNS_RESOLVE_BUSY: i32 = -44
73
+ /** `resolveHost`'s time ran out before the lookup finished. */
74
+ export const DNS_RESOLVE_TIMEOUT: i32 = -45
75
+
76
+ /** The most queries a CNAME chain may add to one query before it is `DNS_RESOLVE_CNAME`. */
77
+ export const DNS_RESOLVER_MAX_CNAME_HOPS: i32 = 8
78
+ /** The longest reply read over TCP; a longer length prefix fails the server. */
79
+ export const DNS_TCP_MAX_REPLY: i32 = 16384
80
+ /** The most addresses one lookup answers: `DNS_MAX_ANSWERS` A and as many AAAA. */
81
+ export const DNS_RESOLVE_MAX_ADDRESSES: i32 = 2 * DNS_MAX_ANSWERS
82
+ /** The most lookups one resolver holds at once. */
83
+ export const DNS_RESOLVER_MAX_LOOKUPS: i32 = 64
84
+ /** `nextOutgoing`'s `meta[0]`: send `out` as one datagram to `to` from a new socket. */
85
+ export const DNS_SEND_UDP: i32 = 1
86
+ /** `nextOutgoing`'s `meta[0]`: connect a new TCP socket to `to` and write `out`. */
87
+ export const DNS_SEND_TCP: i32 = 2
88
+ /** `nextOutgoing`'s `meta[0]`: close the socket the query held, if any. */
89
+ export const DNS_CLOSE: i32 = 3
90
+ /** The room `nextOutgoing` needs in `out`: a query and TCP's two-byte length. */
91
+ export const DNS_OUTGOING_MAX_BYTES: i32 = DNS_QUERY_MAX_BYTES + 2
92
+
93
+ // A query's state. A lookup's: free, running, or finished with its result held.
94
+ const DNS_Q_IDLE: i32 = 0
95
+ const DNS_Q_UDP: i32 = 1
96
+ const DNS_Q_TCP: i32 = 2
97
+ const DNS_Q_DONE: i32 = 3
98
+ const DNS_L_FREE: i32 = 0
99
+ const DNS_L_RUNNING: i32 = 1
100
+ const DNS_L_DONE: i32 = 2
101
+
102
+ /**
103
+ * A stub resolver of `maxLookups` concurrent lookups over `config`'s servers,
104
+ * answering from `hosts` first when it is given. Query `q` belongs to lookup
105
+ * `q / 2`, and asks A when `q` is even and AAAA when it is odd.
106
+ */
107
+ export class DnsResolver {
108
+ config: DnsConfig
109
+ hosts: DnsHosts | null
110
+ /** Each query's bytes, from offset 0: the header, the name and the question's type and class. */
111
+ queries: u8[][]
112
+ /** Each query's name in wire form, labels and root, as asked; and that name lowercased, the start of its chain. */
113
+ names: u8[][]
114
+ starts: u8[][]
115
+ /** Each query's TCP reply as it arrives, without its two-byte length. */
116
+ streams: u8[][]
117
+ /** Each query's addresses and their TTLs, once it has its answer. */
118
+ answers: u8[][]
119
+ answerTtls: i32[][]
120
+ /** Each lookup's name in wire form, without its root byte, as `start` gave it. */
121
+ bases: u8[][]
122
+ /** Each lookup's answer, A first. */
123
+ lookupAddresses: u8[][]
124
+ lookupTtls: i32[][]
125
+ reply: DnsReply
126
+ scratch: u8[]
127
+ queryLength: i32[]
128
+ nameLength: i32[]
129
+ state: i32[]
130
+ /** The outcome of a finished query: its address count, or `DNS_RESOLVE_*`. */
131
+ outcome: i32[]
132
+ /** The action `nextOutgoing` owes the driver for a query: 0 or a `DNS_SEND_*`/`DNS_CLOSE`. */
133
+ pending: i32[]
134
+ server: i32[]
135
+ round: i32[]
136
+ hops: i32[]
137
+ deadline: i64[]
138
+ /** Bytes of the TCP reply read so far, its two-byte length included, and that length once read. */
139
+ streamHave: i32[]
140
+ streamWant: i32[]
141
+ baseLength: i32[]
142
+ /** Whether a finished query's name was NXDOMAIN, which moves the search on, rather than NODATA, which ends it. */
143
+ nxdomain: boolean[]
144
+ /** Whether a lookup's name ended in a dot, so no search suffix applies. */
145
+ absolute: boolean[]
146
+ /** The candidate a lookup is asking, by `dnsResolverCandidate`'s order, and how many it has. */
147
+ candidate: i32[]
148
+ candidates: i32[]
149
+ /** The error a lookup ends with if no later candidate answers: a family that reached no server while the other was NXDOMAIN, or 0. */
150
+ fallback: i32[]
151
+ dots: i32[]
152
+ lookupState: i32[]
153
+ lookupResult: i32[]
154
+ lookupPort: i32[]
155
+ maxLookups: i32 = 0
156
+ /** Where `nextOutgoing` looks first, so no query waits behind another forever. */
157
+ cursor: i32 = 0
158
+ /** Datagrams dropped for coming from another address or port than the server's. */
159
+ droppedSource: i32 = 0
160
+ /** Replies dropped for another ID. */
161
+ droppedId: i32 = 0
162
+ /** Replies dropped for another question. */
163
+ droppedQuestion: i32 = 0
164
+ /** Datagrams dropped as malformed, or arriving for a query not waiting on UDP. */
165
+ droppedMalformed: i32 = 0
166
+ /** Tries that timed out. */
167
+ timeouts: i32 = 0
168
+ /** Tries that ended in SERVFAIL, REFUSED, a bad TCP reply or a socket failure. */
169
+ serverFailures: i32 = 0
170
+ /** Truncated replies asked again over TCP. */
171
+ tcpRetries: i32 = 0
172
+ /** Queries a CNAME chain added. */
173
+ cnameQueries: i32 = 0
174
+ /** Queries handed to the driver, UDP and TCP. */
175
+ sent: i32 = 0
176
+
177
+ /**
178
+ * A resolver with room for `maxLookups` lookups at once, every buffer made
179
+ * now. Panics unless `maxLookups` is 1 to `DNS_RESOLVER_MAX_LOOKUPS`.
180
+ * `config` and `hosts` are read at each `start`, so a program may parse
181
+ * them again between lookups.
182
+ */
183
+ constructor(config: DnsConfig, hosts: DnsHosts | null, maxLookups: i32) {
184
+ if (maxLookups < 1 || maxLookups > DNS_RESOLVER_MAX_LOOKUPS) {
185
+ panic(`DnsResolver: ${maxLookups} lookups is outside 1 to ${DNS_RESOLVER_MAX_LOOKUPS}`)
186
+ }
187
+ this.config = config
188
+ this.hosts = hosts
189
+ this.maxLookups = maxLookups
190
+ const n: i32 = maxLookups * 2
191
+ this.queries = []
192
+ this.names = []
193
+ this.starts = []
194
+ this.streams = []
195
+ this.answers = []
196
+ this.answerTtls = []
197
+ this.bases = []
198
+ this.lookupAddresses = []
199
+ this.lookupTtls = []
200
+ let q: i32 = 0
201
+ while (q < n) {
202
+ this.queries.push(new Array<u8>(DNS_QUERY_MAX_BYTES))
203
+ this.names.push(new Array<u8>(DNS_MAX_NAME_BYTES))
204
+ this.starts.push(new Array<u8>(DNS_MAX_NAME_BYTES))
205
+ this.streams.push(new Array<u8>(DNS_TCP_MAX_REPLY))
206
+ this.answers.push(new Array<u8>(DNS_MAX_ANSWERS * DNS_ADDRESS_BYTES))
207
+ this.answerTtls.push(new Array<i32>(DNS_MAX_ANSWERS))
208
+ q += 1
209
+ }
210
+ let l: i32 = 0
211
+ while (l < maxLookups) {
212
+ this.bases.push(new Array<u8>(DNS_MAX_NAME_BYTES))
213
+ this.lookupAddresses.push(new Array<u8>(DNS_RESOLVE_MAX_ADDRESSES * DNS_ADDRESS_BYTES))
214
+ this.lookupTtls.push(new Array<i32>(DNS_RESOLVE_MAX_ADDRESSES))
215
+ l += 1
216
+ }
217
+ this.reply = new DnsReply()
218
+ this.scratch = new Array<u8>(DNS_QUERY_MAX_BYTES)
219
+ this.queryLength = new Array<i32>(n)
220
+ this.nameLength = new Array<i32>(n)
221
+ this.state = new Array<i32>(n)
222
+ this.outcome = new Array<i32>(n)
223
+ this.pending = new Array<i32>(n)
224
+ this.server = new Array<i32>(n)
225
+ this.round = new Array<i32>(n)
226
+ this.hops = new Array<i32>(n)
227
+ this.deadline = new Array<i64>(n)
228
+ this.streamHave = new Array<i32>(n)
229
+ this.streamWant = new Array<i32>(n)
230
+ this.baseLength = new Array<i32>(maxLookups)
231
+ this.nxdomain = new Array<boolean>(n)
232
+ this.absolute = new Array<boolean>(maxLookups)
233
+ this.candidate = new Array<i32>(maxLookups)
234
+ this.candidates = new Array<i32>(maxLookups)
235
+ this.fallback = new Array<i32>(maxLookups)
236
+ this.dots = new Array<i32>(maxLookups)
237
+ this.lookupState = new Array<i32>(maxLookups)
238
+ this.lookupResult = new Array<i32>(maxLookups)
239
+ this.lookupPort = new Array<i32>(maxLookups)
240
+ }
241
+
242
+ /**
243
+ * Begins a lookup of `name`, whose addresses will carry `port`, at
244
+ * `nowNanos` on the caller's monotonic clock, and answers its id, or a
245
+ * negative code: `DNS_RESOLVE_BUSY` when every slot is in use, or the
246
+ * `DNS_ERR_*` `dnsBuildQuery` gives a name that is not one.
247
+ *
248
+ * `localhost` and any name under it answer 127.0.0.1 and ::1 (RFC 6761
249
+ * §6.3), an address literal answers itself, and a name in `hosts` answers
250
+ * its entries, each at once and with no packet: `result` has them now.
251
+ * Panics when `port` is not 16 bits.
252
+ */
253
+ start(name: string, port: i32, nowNanos: i64): i32 {
254
+ if (port < 0 || port > 0xffff) {
255
+ panic(`DnsResolver.start: the port ${port} is not 16 bits`)
256
+ }
257
+ let l: i32 = 0
258
+ while (l < this.maxLookups && this.lookupState[l] !== DNS_L_FREE) {
259
+ l += 1
260
+ }
261
+ if (l === this.maxLookups) {
262
+ return DNS_RESOLVE_BUSY
263
+ }
264
+ this.state[l * 2] = DNS_Q_IDLE
265
+ this.state[l * 2 + 1] = DNS_Q_IDLE
266
+ const at: u8[] = this.lookupAddresses[l]
267
+ const ttls: i32[] = this.lookupTtls[l]
268
+ if (dnsResolverIsLocalhost(name)) {
269
+ dnsResolverLoopback(at, port)
270
+ ttls[0] = 0
271
+ ttls[1] = 0
272
+ return dnsResolverFinishNow(this, l, 2)
273
+ }
274
+ if (dnsResolverLiteral(this.scratch, name, at, port)) {
275
+ ttls[0] = 0
276
+ return dnsResolverFinishNow(this, l, 1)
277
+ }
278
+ const built: i32 = dnsBuildQuery(this.scratch, 0, name, DNS_TYPE_A)
279
+ if (built < 0) {
280
+ return built
281
+ }
282
+ const hosts: DnsHosts | null = this.hosts
283
+ if (hosts !== null) {
284
+ const v4: i32 = dnsHostsFind(hosts, name, DNS_TYPE_A, port, this.answers[l * 2], DNS_MAX_ANSWERS)
285
+ const v6: i32 = dnsHostsFind(hosts, name, DNS_TYPE_AAAA, port, this.answers[l * 2 + 1], DNS_MAX_ANSWERS)
286
+ if (v4 + v6 > 0) {
287
+ dnsResolverGather(this, l, v4, v6, false)
288
+ return dnsResolverFinishNow(this, l, v4 + v6)
289
+ }
290
+ }
291
+ // The wire name is the query's bytes after the header, less the root byte
292
+ // and the question's type and class.
293
+ const base: u8[] = this.bases[l]
294
+ const baseLength: i32 = built - 12 - 5
295
+ let i: i32 = 0
296
+ let dots: i32 = 0
297
+ while (i < baseLength && i < toI32(base.length)) {
298
+ base[i] = this.scratch[12 + i]
299
+ i += 1
300
+ }
301
+ // Every length byte past the first is a dot of the name.
302
+ let p: i32 = 0
303
+ while (p >= 0 && p < baseLength && p < toI32(base.length)) {
304
+ p += toI32(base[p]) + 1
305
+ if (p < baseLength) {
306
+ dots += 1
307
+ }
308
+ }
309
+ this.baseLength[l] = baseLength
310
+ this.absolute[l] = toI32(name.charCodeAt(toI32(name.length) - 1)) === 46
311
+ this.dots[l] = dots
312
+ this.candidate[l] = 0
313
+ this.fallback[l] = 0
314
+ this.candidates[l] = this.absolute[l] ? 1 : this.config.searchCount + 1
315
+ this.lookupPort[l] = port
316
+ this.lookupState[l] = DNS_L_RUNNING
317
+ this.lookupResult[l] = DNS_RESOLVE_PENDING
318
+ if (!dnsResolverNextCandidate(this, l, nowNanos)) {
319
+ dnsResolverFinish(this, l, DNS_RESOLVE_NOT_FOUND)
320
+ }
321
+ return l
322
+ }
323
+
324
+ /**
325
+ * The next thing the driver must do, or -1 when there is nothing: the
326
+ * query it is for, with `meta[0]` one of `DNS_SEND_UDP`, `DNS_SEND_TCP` or
327
+ * `DNS_CLOSE`. A send writes `meta[1]` bytes to `out` from offset 0 (TCP's
328
+ * with their two-byte length) and the server's address to `to`, and draws
329
+ * the query `id`, which must be new and random for each send since it is
330
+ * half of what keeps an off-path spoofer out; it also replaces whatever
331
+ * socket the query held, so the driver closes that one first. Panics when
332
+ * `out` is shorter than `DNS_OUTGOING_MAX_BYTES`, `to` shorter than 18
333
+ * bytes or `meta` than 2, or when `id` is not 16 bits.
334
+ */
335
+ nextOutgoing(out: u8[], to: u8[], meta: i32[], id: i32): i32 {
336
+ if (id < 0 || id > 0xffff) {
337
+ panic(`DnsResolver.nextOutgoing: the id ${id} is not 16 bits`)
338
+ }
339
+ if (
340
+ toI32(out.length) < DNS_OUTGOING_MAX_BYTES ||
341
+ toI32(to.length) < DNS_ADDRESS_BYTES ||
342
+ toI32(meta.length) < 2
343
+ ) {
344
+ panic("DnsResolver.nextOutgoing: out, to or meta is too short")
345
+ }
346
+ const n: i32 = this.maxLookups * 2
347
+ let k: i32 = 0
348
+ while (k < n) {
349
+ const q: i32 = (this.cursor + k) % n
350
+ const kind: i32 = this.pending[q]
351
+ if (kind !== 0) {
352
+ this.pending[q] = 0
353
+ this.cursor = (q + 1) % n
354
+ meta[0] = kind
355
+ meta[1] = 0
356
+ if (kind === DNS_CLOSE) {
357
+ return q
358
+ }
359
+ const query: u8[] = this.queries[q]
360
+ setU16BE(query, 0, toU16(id))
361
+ const length: i32 = this.queryLength[q]
362
+ const skip: i32 = kind === DNS_SEND_TCP ? 2 : 0
363
+ if (skip === 2) {
364
+ setU16BE(out, 0, toU16(length))
365
+ }
366
+ let i: i32 = 0
367
+ while (i < length && i < toI32(query.length)) {
368
+ out[skip + i] = query[i]
369
+ i += 1
370
+ }
371
+ meta[1] = skip + length
372
+ const from: i32 = this.server[q] * DNS_ADDRESS_BYTES
373
+ i = 0
374
+ while (i < DNS_ADDRESS_BYTES && i < toI32(to.length)) {
375
+ to[i] = this.config.servers[from + i]
376
+ i += 1
377
+ }
378
+ this.sent = dnsResolverCount(this.sent)
379
+ return q
380
+ }
381
+ k += 1
382
+ }
383
+ return -1
384
+ }
385
+
386
+ /**
387
+ * A datagram of `len` bytes from `buf[0]` that arrived from `from` on query
388
+ * `q`'s socket at `nowNanos`. One from another address or port than the
389
+ * server's, with another ID or question, or malformed, is dropped and
390
+ * counted, and the query keeps waiting. One that answers the query with an
391
+ * RCODE the resolver does not read, FORMERR or NOTIMP, fails the server. Panics when `q` is not a query or
392
+ * `len` is outside `buf`.
393
+ */
394
+ receive(q: i32, buf: u8[], len: i32, from: u8[], nowNanos: i64): void {
395
+ dnsResolverCheckQuery(this, q, "receive")
396
+ if (len < 0 || len > toI32(buf.length) || toI32(from.length) < DNS_ADDRESS_BYTES) {
397
+ panic("DnsResolver.receive: len is outside buf, or from is shorter than 18 bytes")
398
+ }
399
+ if (this.state[q] !== DNS_Q_UDP) {
400
+ this.droppedMalformed = dnsResolverCount(this.droppedMalformed)
401
+ return
402
+ }
403
+ const at: i32 = this.server[q] * DNS_ADDRESS_BYTES
404
+ let i: i32 = 0
405
+ while (i < DNS_ADDRESS_BYTES && i < toI32(from.length)) {
406
+ if (from[i] !== this.config.servers[at + i]) {
407
+ this.droppedSource = dnsResolverCount(this.droppedSource)
408
+ return
409
+ }
410
+ i += 1
411
+ }
412
+ const status: i32 = dnsParseReply(
413
+ buf,
414
+ len,
415
+ this.queries[q],
416
+ this.queryLength[q],
417
+ this.lookupPort[q / 2],
418
+ this.reply
419
+ )
420
+ if (status === DNS_ERR_ID) {
421
+ this.droppedId = dnsResolverCount(this.droppedId)
422
+ } else if (status === DNS_ERR_QUESTION) {
423
+ this.droppedQuestion = dnsResolverCount(this.droppedQuestion)
424
+ } else if (status === DNS_ERR_RCODE) {
425
+ dnsResolverServerFailed(this, q, nowNanos)
426
+ } else if (status < 0) {
427
+ this.droppedMalformed = dnsResolverCount(this.droppedMalformed)
428
+ } else if (this.reply.truncated) {
429
+ this.tcpRetries = dnsResolverCount(this.tcpRetries)
430
+ dnsResolverSend(this, q, DNS_Q_TCP, nowNanos)
431
+ } else {
432
+ dnsResolverAnswer(this, q, status, nowNanos)
433
+ }
434
+ }
435
+
436
+ /**
437
+ * `len` bytes from `buf[0]` read from query `q`'s TCP socket at
438
+ * `nowNanos`. A length over `DNS_TCP_MAX_REPLY`, a reply that is malformed
439
+ * or answers another question, or one still truncated fails the server,
440
+ * since a stream cannot carry a second, true reply after it. Panics as
441
+ * `receive` does.
442
+ */
443
+ receiveStream(q: i32, buf: u8[], len: i32, nowNanos: i64): void {
444
+ dnsResolverCheckQuery(this, q, "receiveStream")
445
+ if (len < 0 || len > toI32(buf.length)) {
446
+ panic("DnsResolver.receiveStream: len is outside buf")
447
+ }
448
+ if (this.state[q] !== DNS_Q_TCP) {
449
+ return
450
+ }
451
+ const stream: u8[] = this.streams[q]
452
+ let i: i32 = 0
453
+ while (i < len) {
454
+ const have: i32 = this.streamHave[q]
455
+ if (have < 2) {
456
+ this.streamWant[q] = (this.streamWant[q] << 8) | toI32(buf[i])
457
+ if (have === 1 && (this.streamWant[q] < 12 || this.streamWant[q] > DNS_TCP_MAX_REPLY)) {
458
+ dnsResolverServerFailed(this, q, nowNanos)
459
+ return
460
+ }
461
+ } else if (have - 2 < this.streamWant[q]) {
462
+ stream[have - 2] = buf[i]
463
+ }
464
+ this.streamHave[q] = have + 1
465
+ i += 1
466
+ }
467
+ const want: i32 = this.streamWant[q]
468
+ if (this.streamHave[q] < 2 || this.streamHave[q] - 2 < want) {
469
+ return
470
+ }
471
+ const status: i32 = dnsParseReply(
472
+ stream,
473
+ want,
474
+ this.queries[q],
475
+ this.queryLength[q],
476
+ this.lookupPort[q / 2],
477
+ this.reply
478
+ )
479
+ if (status < 0 || this.reply.truncated) {
480
+ dnsResolverServerFailed(this, q, nowNanos)
481
+ } else {
482
+ dnsResolverAnswer(this, q, status, nowNanos)
483
+ }
484
+ }
485
+
486
+ /**
487
+ * The driver's word that query `q`'s socket failed at `nowNanos`: it could
488
+ * not be opened or written, the connection was refused, or the stream
489
+ * ended before the reply did. The query moves to the next server. Panics
490
+ * when `q` is not a query.
491
+ */
492
+ fail(q: i32, nowNanos: i64): void {
493
+ dnsResolverCheckQuery(this, q, "fail")
494
+ if (this.state[q] === DNS_Q_UDP || this.state[q] === DNS_Q_TCP) {
495
+ dnsResolverServerFailed(this, q, nowNanos)
496
+ }
497
+ }
498
+
499
+ /** Moves every query whose try has run past its deadline at `nowNanos` to the next server. */
500
+ tick(nowNanos: i64): void {
501
+ const n: i32 = this.maxLookups * 2
502
+ let q: i32 = 0
503
+ while (q < n) {
504
+ if ((this.state[q] === DNS_Q_UDP || this.state[q] === DNS_Q_TCP) && this.deadline[q] <= nowNanos) {
505
+ this.timeouts = dnsResolverCount(this.timeouts)
506
+ dnsResolverServerFailed(this, q, nowNanos)
507
+ }
508
+ q += 1
509
+ }
510
+ }
511
+
512
+ /** The earliest deadline of any try in flight, on the caller's clock, or -1 when none is. */
513
+ nextDeadlineNanos(): i64 {
514
+ const n: i32 = this.maxLookups * 2
515
+ let soonest: i64 = -1
516
+ let q: i32 = 0
517
+ while (q < n) {
518
+ if (
519
+ (this.state[q] === DNS_Q_UDP || this.state[q] === DNS_Q_TCP) &&
520
+ (soonest < 0 || this.deadline[q] < soonest)
521
+ ) {
522
+ soonest = this.deadline[q]
523
+ }
524
+ q += 1
525
+ }
526
+ return soonest
527
+ }
528
+
529
+ /**
530
+ * Lookup `lookup`'s answer: `DNS_RESOLVE_PENDING` while it runs, or, once
531
+ * it has finished, its address count with the addresses written to `out`
532
+ * (A first, then AAAA, 18 bytes each, with the port `start` was given) and
533
+ * their TTLs in seconds to `ttls`, or a negative `DNS_RESOLVE_*` code. A
534
+ * finished lookup's slot is free again once this has answered it. Panics
535
+ * when `lookup` is not a slot that `start` gave out, or when `out` or
536
+ * `ttls` has no room for `DNS_RESOLVE_MAX_ADDRESSES`.
537
+ */
538
+ result(lookup: i32, out: u8[], ttls: i32[]): i32 {
539
+ if (lookup < 0 || lookup >= this.maxLookups || this.lookupState[lookup] === DNS_L_FREE) {
540
+ panic(`DnsResolver.result: ${lookup} is not a lookup in progress`)
541
+ }
542
+ if (
543
+ toI32(out.length) < DNS_RESOLVE_MAX_ADDRESSES * DNS_ADDRESS_BYTES ||
544
+ toI32(ttls.length) < DNS_RESOLVE_MAX_ADDRESSES
545
+ ) {
546
+ panic("DnsResolver.result: out or ttls has no room for DNS_RESOLVE_MAX_ADDRESSES")
547
+ }
548
+ if (this.lookupState[lookup] !== DNS_L_DONE) {
549
+ return DNS_RESOLVE_PENDING
550
+ }
551
+ const count: i32 = this.lookupResult[lookup]
552
+ let i: i32 = 0
553
+ while (i < count * DNS_ADDRESS_BYTES && i < toI32(out.length)) {
554
+ out[i] = this.lookupAddresses[lookup][i]
555
+ i += 1
556
+ }
557
+ i = 0
558
+ while (i < count && i < toI32(ttls.length)) {
559
+ ttls[i] = this.lookupTtls[lookup][i]
560
+ i += 1
561
+ }
562
+ this.lookupState[lookup] = DNS_L_FREE
563
+ return count
564
+ }
565
+
566
+ /**
567
+ * Ends `lookup` now, whether or not it has finished, and frees its slot;
568
+ * the sockets of its queries are owed a `DNS_CLOSE`. Panics as `result`.
569
+ */
570
+ cancel(lookup: i32): void {
571
+ if (lookup < 0 || lookup >= this.maxLookups || this.lookupState[lookup] === DNS_L_FREE) {
572
+ panic(`DnsResolver.cancel: ${lookup} is not a lookup in progress`)
573
+ }
574
+ dnsResolverStop(this, lookup * 2)
575
+ dnsResolverStop(this, lookup * 2 + 1)
576
+ this.lookupState[lookup] = DNS_L_FREE
577
+ }
578
+
579
+ /** How many lookups are in progress or hold an answer not yet read. */
580
+ active(): i32 {
581
+ let count: i32 = 0
582
+ let l: i32 = 0
583
+ while (l < this.maxLookups) {
584
+ if (this.lookupState[l] !== DNS_L_FREE) {
585
+ count += 1
586
+ }
587
+ l += 1
588
+ }
589
+ return count
590
+ }
591
+ }
592
+
593
+ /** Panics unless `q` names one of `r`'s queries. */
594
+ const dnsResolverCheckQuery = (r: DnsResolver, q: i32, who: string): void => {
595
+ if (q < 0 || q >= r.maxLookups * 2) {
596
+ panic(`DnsResolver.${who}: ${q} is not a query`)
597
+ }
598
+ }
599
+
600
+ /** `n` plus one, held at the i32 maximum, so a counter a peer drives never overflows. */
601
+ const dnsResolverCount = (n: i32): i32 => (n < 2147483647 ? n + 1 : n)
602
+
603
+ /** Whether `name` is `localhost` or a name under it, in any case, with or without the root's dot, and with no empty label before `localhost`. */
604
+ const dnsResolverIsLocalhost = (name: string): boolean => {
605
+ let n: i32 = toI32(name.length)
606
+ if (n > 0 && toI32(name.charCodeAt(n - 1)) === 46) {
607
+ n -= 1
608
+ }
609
+ const word: string = "localhost"
610
+ if (n < 9 || (n > 9 && toI32(name.charCodeAt(n - 10)) !== 46)) {
611
+ return false
612
+ }
613
+ // `.localhost` and `x..localhost` have an empty label, so are no name.
614
+ if (n === 10 || (n > 10 && toI32(name.charCodeAt(n - 11)) === 46)) {
615
+ return false
616
+ }
617
+ let i: i32 = 0
618
+ while (i < 9 && i < toI32(word.length)) {
619
+ let b: i32 = toI32(name.charCodeAt(n - 9 + i))
620
+ if (b >= 65 && b <= 90) {
621
+ b += 32
622
+ }
623
+ if (b !== toI32(word.charCodeAt(i))) {
624
+ return false
625
+ }
626
+ i += 1
627
+ }
628
+ return true
629
+ }
630
+
631
+ /** Writes 127.0.0.1 then ::1, both with `port`, to `out`. */
632
+ const dnsResolverLoopback = (out: u8[], port: i32): void => {
633
+ let i: i32 = 0
634
+ while (i < 2 * DNS_ADDRESS_BYTES && i < toI32(out.length)) {
635
+ out[i] = toU8(0)
636
+ i += 1
637
+ }
638
+ out[10] = toU8(255)
639
+ out[11] = toU8(255)
640
+ out[12] = toU8(127)
641
+ out[15] = toU8(1)
642
+ setU16BE(out, 16, toU16(port))
643
+ out[DNS_ADDRESS_BYTES + 15] = toU8(1)
644
+ setU16BE(out, DNS_ADDRESS_BYTES + 16, toU16(port))
645
+ }
646
+
647
+ /**
648
+ * Whether `name` is an IPv4 or IPv6 literal, which is then written to `out`
649
+ * with `port`. `scratch` holds the name's bytes for the parser.
650
+ */
651
+ const dnsResolverLiteral = (scratch: u8[], name: string, out: u8[], port: i32): boolean => {
652
+ const n: i32 = toI32(name.length)
653
+ if (n === 0 || n > toI32(scratch.length)) {
654
+ return false
655
+ }
656
+ let i: i32 = 0
657
+ while (i < n) {
658
+ const b: i32 = toI32(name.charCodeAt(i))
659
+ if (b > 127) {
660
+ return false
661
+ }
662
+ scratch[i] = toU8(b)
663
+ i += 1
664
+ }
665
+ return dnsParseAddress(scratch, 0, n, out, 0, port)
666
+ }
667
+
668
+ /** Finishes lookup `l` before any query was sent, with `count` addresses already in place. */
669
+ const dnsResolverFinishNow = (r: DnsResolver, l: i32, count: i32): i32 => {
670
+ r.lookupState[l] = DNS_L_DONE
671
+ r.lookupResult[l] = count
672
+ return l
673
+ }
674
+
675
+ /** Ends lookup `l` with `result`, and owes the driver a close for each of its queries still holding a socket. */
676
+ const dnsResolverFinish = (r: DnsResolver, l: i32, result: i32): void => {
677
+ dnsResolverStop(r, l * 2)
678
+ dnsResolverStop(r, l * 2 + 1)
679
+ r.lookupState[l] = DNS_L_DONE
680
+ r.lookupResult[l] = result
681
+ }
682
+
683
+ /** Stops query `q`, and owes the driver a close if a socket may be open for it. */
684
+ const dnsResolverStop = (r: DnsResolver, q: i32): void => {
685
+ if (r.state[q] === DNS_Q_UDP || r.state[q] === DNS_Q_TCP || r.pending[q] !== 0) {
686
+ r.pending[q] = DNS_CLOSE
687
+ }
688
+ if (r.state[q] !== DNS_Q_IDLE) {
689
+ r.state[q] = DNS_Q_DONE
690
+ }
691
+ }
692
+
693
+ /**
694
+ * Copies `v4` A and `v6` AAAA addresses from lookup `l`'s two queries into
695
+ * its answer, A first. From a server, every address of a type gets the
696
+ * smallest TTL among them, the TTL of the RRset (RFC 2181 §5.2); from the
697
+ * hosts file, `fromServer` false, each gets 0, since nothing says how long
698
+ * the file holds.
699
+ */
700
+ const dnsResolverGather = (r: DnsResolver, l: i32, v4: i32, v6: i32, fromServer: boolean): void => {
701
+ const out: u8[] = r.lookupAddresses[l]
702
+ const ttls: i32[] = r.lookupTtls[l]
703
+ let k: i32 = 0
704
+ let half: i32 = 0
705
+ while (half < 2) {
706
+ const q: i32 = l * 2 + half
707
+ const count: i32 = half === 0 ? v4 : v6
708
+ let least: i32 = 0
709
+ let j: i32 = 0
710
+ while (fromServer && j < count) {
711
+ const t: i32 = r.answerTtls[q][j]
712
+ if (j === 0 || t < least) {
713
+ least = t
714
+ }
715
+ j += 1
716
+ }
717
+ let i: i32 = 0
718
+ while (i < count && k >= 0 && k < toI32(ttls.length)) {
719
+ let b: i32 = 0
720
+ while (b < DNS_ADDRESS_BYTES) {
721
+ out[k * DNS_ADDRESS_BYTES + b] = r.answers[q][i * DNS_ADDRESS_BYTES + b]
722
+ b += 1
723
+ }
724
+ ttls[k] = least
725
+ k += 1
726
+ i += 1
727
+ }
728
+ half += 1
729
+ }
730
+ }
731
+
732
+ /**
733
+ * Starts lookup `l`'s current candidate name, skipping any the search list
734
+ * makes too long, and answers whether there was one to start.
735
+ *
736
+ * The order is resolv.conf(5)'s: a name with a trailing dot is only itself; a
737
+ * name with at least `ndots` dots is itself and then each search suffix; any
738
+ * other name is each suffix and then itself.
739
+ */
740
+ const dnsResolverNextCandidate = (r: DnsResolver, l: i32, nowNanos: i64): boolean => {
741
+ while (r.candidate[l] < r.candidates[l]) {
742
+ const c: i32 = r.candidate[l]
743
+ const search: i32 = r.config.searchCount
744
+ let suffix: i32 = -1
745
+ if (!r.absolute[l]) {
746
+ if (r.dots[l] >= r.config.ndots) {
747
+ suffix = c - 1
748
+ } else {
749
+ suffix = c < search ? c : -1
750
+ }
751
+ }
752
+ const a: i32 = l * 2
753
+ const length: i32 = dnsResolverCandidate(r, l, suffix, r.names[a])
754
+ if (length > 0) {
755
+ dnsResolverBegin(r, a, length, DNS_TYPE_A, nowNanos)
756
+ let i: i32 = 0
757
+ while (i < length) {
758
+ r.names[a + 1][i] = r.names[a][i]
759
+ i += 1
760
+ }
761
+ dnsResolverBegin(r, a + 1, length, DNS_TYPE_AAAA, nowNanos)
762
+ return true
763
+ }
764
+ r.candidate[l] = c + 1
765
+ }
766
+ return false
767
+ }
768
+
769
+ /**
770
+ * Writes lookup `l`'s name, with search suffix `suffix` appended unless it
771
+ * is -1, into `out` in wire form with its root byte, and answers its length,
772
+ * or 0 when the suffix is not a name of labels or makes the name over 255
773
+ * bytes.
774
+ */
775
+ const dnsResolverCandidate = (r: DnsResolver, l: i32, suffix: i32, out: u8[]): i32 => {
776
+ const base: u8[] = r.bases[l]
777
+ let n: i32 = r.baseLength[l]
778
+ let i: i32 = 0
779
+ while (i < n && i < toI32(out.length) && i < toI32(base.length)) {
780
+ out[i] = base[i]
781
+ i += 1
782
+ }
783
+ if (suffix >= 0) {
784
+ const start: i32 = r.config.searchStart[suffix]
785
+ const length: i32 = r.config.searchLength[suffix]
786
+ // The suffix's text becomes labels: each dot, and its end, closes the
787
+ // label whose length byte sits at `n`.
788
+ let label: i32 = 0
789
+ let k: i32 = 0
790
+ while (k <= length) {
791
+ const b: i32 = k < length ? toI32(r.config.search[start + k]) : 46
792
+ if (b === 46) {
793
+ if (label === 0 || label > DNS_MAX_LABEL_BYTES || n < 0 || n >= toI32(out.length)) {
794
+ return 0
795
+ }
796
+ out[n] = toU8(label)
797
+ n += label + 1
798
+ label = 0
799
+ } else if (b < 33 || b > 126 || n + 1 + label > DNS_MAX_NAME_BYTES - 2) {
800
+ return 0
801
+ } else {
802
+ out[n + 1 + label] = toU8(b)
803
+ label += 1
804
+ }
805
+ k += 1
806
+ }
807
+ }
808
+ if (n + 1 > DNS_MAX_NAME_BYTES) {
809
+ return 0
810
+ }
811
+ out[n] = toU8(0)
812
+ return n + 1
813
+ }
814
+
815
+ /**
816
+ * Makes query `q` ask `qtype` for the `length`-byte wire name in
817
+ * `r.names[q]`, as the start of a new chain, and sends it to the first
818
+ * server.
819
+ */
820
+ const dnsResolverBegin = (r: DnsResolver, q: i32, length: i32, qtype: i32, nowNanos: i64): void => {
821
+ r.hops[q] = 0
822
+ r.nameLength[q] = length
823
+ let i: i32 = 0
824
+ while (i < length) {
825
+ const b: i32 = toI32(r.names[q][i])
826
+ r.starts[q][i] = toU8(b >= 65 && b <= 90 ? b + 32 : b)
827
+ i += 1
828
+ }
829
+ dnsResolverAsk(r, q, qtype, nowNanos)
830
+ }
831
+
832
+ /** Writes query `q`'s bytes for its current name and `qtype`, ID 0 until it is sent, and sends it to the first server. */
833
+ const dnsResolverAsk = (r: DnsResolver, q: i32, qtype: i32, nowNanos: i64): void => {
834
+ const query: u8[] = r.queries[q]
835
+ const length: i32 = r.nameLength[q]
836
+ // The bytes are dnsBuildQuery's: the ID, RD, one question, then the name, type and class IN.
837
+ setU16BE(query, 0, toU16(0))
838
+ setU16BE(query, 2, toU16(0x0100))
839
+ setU16BE(query, 4, toU16(1))
840
+ setU16BE(query, 6, toU16(0))
841
+ setU16BE(query, 8, toU16(0))
842
+ setU16BE(query, 10, toU16(0))
843
+ let i: i32 = 0
844
+ while (i < length && 12 + i < toI32(query.length)) {
845
+ query[12 + i] = r.names[q][i]
846
+ i += 1
847
+ }
848
+ setU16BE(query, 12 + length, toU16(qtype))
849
+ setU16BE(query, 14 + length, toU16(1))
850
+ r.queryLength[q] = 16 + length
851
+ // Only this name's reply may say NXDOMAIN, not an earlier candidate's or lookup's.
852
+ r.nxdomain[q] = false
853
+ r.server[q] = 0
854
+ r.round[q] = 0
855
+ dnsResolverSend(r, q, DNS_Q_UDP, nowNanos)
856
+ }
857
+
858
+ /** Owes the driver a send of query `q` to its current server over `how`, with a deadline `timeout` seconds after `nowNanos`. */
859
+ const dnsResolverSend = (r: DnsResolver, q: i32, how: i32, nowNanos: i64): void => {
860
+ r.state[q] = how
861
+ r.pending[q] = how === DNS_Q_TCP ? DNS_SEND_TCP : DNS_SEND_UDP
862
+ r.streamHave[q] = 0
863
+ r.streamWant[q] = 0
864
+ r.deadline[q] = nowNanos + toI64(r.config.timeout) * toI64(1000000000)
865
+ }
866
+
867
+ /**
868
+ * Query `q`'s try failed: it moves to the next server, and after the last
869
+ * server to the next round, and after `attempts` rounds it is
870
+ * `DNS_RESOLVE_NO_ANSWER`.
871
+ */
872
+ const dnsResolverServerFailed = (r: DnsResolver, q: i32, nowNanos: i64): void => {
873
+ r.serverFailures = dnsResolverCount(r.serverFailures)
874
+ r.server[q] = r.server[q] + 1
875
+ if (r.server[q] >= r.config.serverCount) {
876
+ r.server[q] = 0
877
+ r.round[q] = r.round[q] + 1
878
+ }
879
+ if (r.round[q] >= r.config.attempts) {
880
+ dnsResolverDone(r, q, DNS_RESOLVE_NO_ANSWER, nowNanos)
881
+ return
882
+ }
883
+ dnsResolverSend(r, q, DNS_Q_UDP, nowNanos)
884
+ }
885
+
886
+ /**
887
+ * Query `q`'s reply, whose status `dnsParseReply` answered and which is in
888
+ * `r.reply`: its addresses, NXDOMAIN, NODATA, a CNAME to ask after, or a
889
+ * failure of the server.
890
+ */
891
+ const dnsResolverAnswer = (r: DnsResolver, q: i32, status: i32, nowNanos: i64): void => {
892
+ const reply: DnsReply = r.reply
893
+ r.nxdomain[q] = status === DNS_NXDOMAIN
894
+ if (status === DNS_NXDOMAIN) {
895
+ dnsResolverDone(r, q, DNS_RESOLVE_NOT_FOUND, nowNanos)
896
+ return
897
+ }
898
+ if (status !== DNS_NOERROR) {
899
+ dnsResolverServerFailed(r, q, nowNanos)
900
+ return
901
+ }
902
+ if (reply.count > 0) {
903
+ let i: i32 = 0
904
+ while (i < reply.count * DNS_ADDRESS_BYTES) {
905
+ r.answers[q][i] = reply.addresses[i]
906
+ i += 1
907
+ }
908
+ i = 0
909
+ while (i < reply.count) {
910
+ r.answerTtls[q][i] = reply.ttls[i]
911
+ i += 1
912
+ }
913
+ dnsResolverDone(r, q, reply.count, nowNanos)
914
+ return
915
+ }
916
+ if (dnsResolverSameName(reply.chain, reply.asked)) {
917
+ dnsResolverDone(r, q, DNS_RESOLVE_NOT_FOUND, nowNanos)
918
+ return
919
+ }
920
+ // The chain left the reply without an address: ask for its end.
921
+ if (r.hops[q] >= DNS_RESOLVER_MAX_CNAME_HOPS || dnsResolverSameName(reply.chain, r.starts[q])) {
922
+ dnsResolverDone(r, q, DNS_RESOLVE_CNAME, nowNanos)
923
+ return
924
+ }
925
+ r.hops[q] = r.hops[q] + 1
926
+ r.cnameQueries = dnsResolverCount(r.cnameQueries)
927
+ const length: i32 = dnsResolverNameLength(reply.chain)
928
+ let i: i32 = 0
929
+ while (i < length) {
930
+ r.names[q][i] = reply.chain[i]
931
+ i += 1
932
+ }
933
+ r.nameLength[q] = length
934
+ dnsResolverAsk(r, q, (q & 1) === 0 ? DNS_TYPE_A : DNS_TYPE_AAAA, nowNanos)
935
+ }
936
+
937
+ /** The length of the wire name in `name`, its root byte included, read label by label since a label may hold a 0 byte. */
938
+ const dnsResolverNameLength = (name: u8[]): i32 => {
939
+ let i: i32 = 0
940
+ while (i >= 0 && i < toI32(name.length) && name[i] !== toU8(0)) {
941
+ i += toI32(name[i]) + 1
942
+ }
943
+ return i + 1
944
+ }
945
+
946
+ /** Whether the lowercase wire names `a` and `b` are the same name. */
947
+ const dnsResolverSameName = (a: u8[], b: u8[]): boolean => {
948
+ const n: i32 = dnsResolverNameLength(a)
949
+ let i: i32 = 0
950
+ while (i < n && i < toI32(a.length) && i < toI32(b.length)) {
951
+ if (a[i] !== b[i]) {
952
+ return false
953
+ }
954
+ i += 1
955
+ }
956
+ return true
957
+ }
958
+
959
+ /**
960
+ * Query `q` has finished with `outcome`, its address count or a
961
+ * `DNS_RESOLVE_*` code; once its lookup's other query has too, the lookup
962
+ * finishes, or moves to its next candidate name when both found nothing.
963
+ *
964
+ * Addresses from either query make the answer. NXDOMAIN names the name,
965
+ * not a type, so NXDOMAIN from either query with the other reaching no server
966
+ * or breaking its CNAME chain sends the search to its next candidate, keeping
967
+ * that error as the lookup's if no later candidate answers: a network that
968
+ * drops AAAA queries still finds a name under a later search domain. Without
969
+ * an NXDOMAIN, such an error is the lookup's, since the name may well exist.
970
+ * Otherwise the name has no address: NXDOMAIN from both moves the search on,
971
+ * and NODATA ends it, as musl's resolver does, since the name exists and only
972
+ * lacks the type asked.
973
+ */
974
+ const dnsResolverDone = (r: DnsResolver, q: i32, outcome: i32, nowNanos: i64): void => {
975
+ if (r.state[q] === DNS_Q_UDP || r.state[q] === DNS_Q_TCP) {
976
+ r.pending[q] = DNS_CLOSE
977
+ }
978
+ r.state[q] = DNS_Q_DONE
979
+ r.outcome[q] = outcome
980
+ const l: i32 = q / 2
981
+ const a: i32 = l * 2
982
+ if (r.state[a] !== DNS_Q_DONE || r.state[a + 1] !== DNS_Q_DONE) {
983
+ return
984
+ }
985
+ const v4: i32 = r.outcome[a]
986
+ const v6: i32 = r.outcome[a + 1]
987
+ if (v4 > 0 || v6 > 0) {
988
+ const four: i32 = v4 > 0 ? v4 : 0
989
+ const six: i32 = v6 > 0 ? v6 : 0
990
+ dnsResolverGather(r, l, four, six, true)
991
+ dnsResolverFinish(r, l, four + six)
992
+ return
993
+ }
994
+ let error: i32 = 0
995
+ if (v4 === DNS_RESOLVE_NO_ANSWER || v4 === DNS_RESOLVE_CNAME) {
996
+ error = v4
997
+ } else if (v6 === DNS_RESOLVE_NO_ANSWER || v6 === DNS_RESOLVE_CNAME) {
998
+ error = v6
999
+ }
1000
+ const named: boolean = r.nxdomain[a] || r.nxdomain[a + 1]
1001
+ if (error !== 0 && !named) {
1002
+ dnsResolverFinish(r, l, error)
1003
+ return
1004
+ }
1005
+ if (error !== 0 && r.fallback[l] === 0) {
1006
+ r.fallback[l] = error
1007
+ }
1008
+ if (error !== 0 || (r.nxdomain[a] && r.nxdomain[a + 1])) {
1009
+ r.candidate[l] = r.candidate[l] + 1
1010
+ if (dnsResolverNextCandidate(r, l, nowNanos)) {
1011
+ return
1012
+ }
1013
+ }
1014
+ dnsResolverFinish(r, l, r.fallback[l] !== 0 ? r.fallback[l] : DNS_RESOLVE_NOT_FOUND)
1015
+ }