@mark1russell7/netdiag 0.1.0 → 0.1.1

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 (45) hide show
  1. package/README.md +153 -1
  2. package/build/address.d.ts +56 -0
  3. package/build/address.d.ts.map +1 -0
  4. package/build/address.js +172 -0
  5. package/build/address.js.map +1 -0
  6. package/build/{diagnosis-BTa-Dfwl.js → diagnosis-a4hRzFxp.js} +113 -11
  7. package/build/diagnosis-a4hRzFxp.js.map +1 -0
  8. package/build/diagnosis.d.ts +3 -2
  9. package/build/diagnosis.js +1 -1
  10. package/build/{index-CP1hzELI.d.ts → index-K2dGiYxW.d.ts} +83 -22
  11. package/build/index-K2dGiYxW.d.ts.map +1 -0
  12. package/build/index.d.ts +253 -148
  13. package/build/index.d.ts.map +1 -1
  14. package/build/index.js +1084 -161
  15. package/build/index.js.map +1 -1
  16. package/build/kinds-CQv769lK.js +341 -0
  17. package/build/kinds-CQv769lK.js.map +1 -0
  18. package/build/kinds-_NShfZpU.d.ts +377 -0
  19. package/build/kinds-_NShfZpU.d.ts.map +1 -0
  20. package/build/layer-BNg0VYuK.d.ts +22 -0
  21. package/build/layer-BNg0VYuK.d.ts.map +1 -0
  22. package/build/tools.d.ts +206 -0
  23. package/build/tools.d.ts.map +1 -0
  24. package/build/tools.js +896 -0
  25. package/build/tools.js.map +1 -0
  26. package/package.json +17 -5
  27. package/src/address/index.ts +155 -0
  28. package/src/diagnosis/catalog.ts +8 -8
  29. package/src/diagnosis/index.ts +13 -0
  30. package/src/diagnosis/score.ts +160 -0
  31. package/src/index.ts +35 -0
  32. package/src/kinds.ts +460 -0
  33. package/src/probes/native.ts +354 -0
  34. package/src/probes/socket.ts +520 -0
  35. package/src/readers.ts +367 -0
  36. package/src/scope.ts +5 -24
  37. package/src/tools/common.ts +47 -0
  38. package/src/tools/host.ts +91 -0
  39. package/src/tools/index.ts +38 -0
  40. package/src/tools/licenses.ts +37 -0
  41. package/src/tools/mtr.ts +156 -0
  42. package/src/tools/ping.ts +231 -0
  43. package/src/tools/traceroute.ts +313 -0
  44. package/build/diagnosis-BTa-Dfwl.js.map +0 -1
  45. package/build/index-CP1hzELI.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/target.ts","../src/fact.ts","../src/probe.ts","../src/scope.ts","../src/run.ts"],"sourcesContent":["import { isIP } from \"node:net\";\nimport { err, ok, type Result } from \"@mark1russell7/optional\";\n\n/** The kind of the host of a target: a DNS name, an IPv4 address or an IPv6 address. */\nexport type HostKind = \"name\" | \"ipv4\" | \"ipv6\";\n\n/**\n * A host that a probe examines, and an optional port.\n *\n * The host is in its canonical form: lower case, with no final dot and no brackets around an IPv6 address.\n */\nexport interface Target {\n readonly host: string;\n readonly kind: HostKind;\n readonly port?: number | undefined;\n}\n\n/** The ports of the URL schemes that a target can have. */\nconst SCHEME_PORTS: ReadonlyMap<string, number> = new Map([\n [\"http:\", 80],\n [\"https:\", 443],\n [\"ws:\", 80],\n [\"wss:\", 443],\n [\"dns:\", 53],\n [\"tls:\", 443],\n]);\n\n/** This function gives the kind of a host in its canonical form. */\nexport function hostKind(host: string): HostKind {\n const family = isIP(host);\n if (family === 4) return \"ipv4\";\n if (family === 6) return \"ipv6\";\n return \"name\";\n}\n\n/** This function gives the canonical form of a host: lower case, with no final dot and no brackets. */\nexport function canonicalHost(host: string): string {\n let text = host.trim().toLowerCase();\n if (text.startsWith(\"[\") && text.endsWith(\"]\")) text = text.slice(1, -1);\n if (text.endsWith(\".\")) text = text.slice(0, -1);\n return text;\n}\n\n/** This function makes a target from a host and an optional port. */\nexport function target(host: string, port?: number): Target {\n const canonical = canonicalHost(host);\n return port === undefined ? { host: canonical, kind: hostKind(canonical) } : { host: canonical, kind: hostKind(canonical), port };\n}\n\n/** This function gives the text of a target: `host`, `host:port`, or `[address]:port` for an IPv6 address. */\nexport function formatTarget(t: Target): string {\n const host = t.kind === \"ipv6\" && t.port !== undefined ? `[${t.host}]` : t.host;\n return t.port === undefined ? host : `${host}:${t.port}`;\n}\n\n/** This function tells if two targets have the same host and the same port. */\nexport function sameTarget(a: Target, b: Target): boolean {\n return a.host === b.host && a.port === b.port;\n}\n\nconst LABEL = /^(?!-)[a-z0-9-]{1,63}(?<!-)$/;\n\nfunction validName(host: string): boolean {\n if (host.length === 0 || host.length > 253) return false;\n return host.split(\".\").every((label) => LABEL.test(label) || label === \"_\" || /^_[a-z0-9-]{1,62}$/.test(label));\n}\n\nfunction portOf(text: string): number | undefined {\n if (!/^\\d{1,5}$/.test(text)) return undefined;\n const port = Number(text);\n return port >= 1 && port <= 65535 ? port : undefined;\n}\n\n/**\n * This function reads a target from the text that a user gives.\n *\n * It accepts a URL (`https://example.com/a`), a host (`example.com`) and a host with a port (`example.com:8443`).\n * It also accepts an IPv4 address, and an IPv6 address in brackets with a port (`[2001:db8::1]:443`).\n * A URL with a known scheme and no port gets the port of its scheme.\n */\nexport function parseTarget(text: string): Result<Target, string> {\n const input = text.trim();\n if (input.length === 0) return err(\"The target is empty.\");\n if (input.includes(\"://\")) {\n let url: URL;\n try {\n url = new URL(input);\n } catch {\n return err(`\"${input}\" is not a valid URL.`);\n }\n const host = canonicalHost(url.hostname);\n if (host.length === 0) return err(`The URL \"${input}\" has no host.`);\n const port = url.port === \"\" ? SCHEME_PORTS.get(url.protocol) : Number(url.port);\n return checked(host, port, input);\n }\n if (isIP(input) === 6) return checked(canonicalHost(input), undefined, input);\n const bracket = /^\\[([^\\]]+)\\](?::(.*))?$/.exec(input);\n if (bracket !== null) {\n const host = bracket[1] ?? \"\";\n if (isIP(host) !== 6) return err(`\"${host}\" is not an IPv6 address.`);\n if (bracket[2] === undefined) return checked(canonicalHost(host), undefined, input);\n const port = portOf(bracket[2]);\n return port === undefined ? err(`\"${bracket[2]}\" is not a port from 1 to 65535.`) : checked(canonicalHost(host), port, input);\n }\n const colon = input.lastIndexOf(\":\");\n if (colon === -1) return checked(canonicalHost(input), undefined, input);\n const port = portOf(input.slice(colon + 1));\n if (port === undefined) return err(`\"${input.slice(colon + 1)}\" is not a port from 1 to 65535.`);\n return checked(canonicalHost(input.slice(0, colon)), port, input);\n}\n\nfunction checked(host: string, port: number | undefined, input: string): Result<Target, string> {\n const kind = hostKind(host);\n if (kind === \"name\" && !validName(host)) return err(`\"${input}\" has no valid host name.`);\n return ok(port === undefined ? { host, kind } : { host, kind, port });\n}\n","import type { Layer } from \"./layer.ts\";\nimport type { Target } from \"./target.ts\";\n\n/**\n * The time of a fact, in milliseconds on the monotonic clock of the runner.\n *\n * The field `origin` of the runner gives the wall-clock time of one monotonic time. Thus each fact time has a wall-clock time.\n */\nexport interface FactTime {\n readonly start: number;\n readonly end: number;\n}\n\n/** The source of a fact: the probe and its version, and the outside tool when the probe starts one. */\nexport interface FactSource {\n readonly probe: string;\n readonly version: string;\n readonly tool?: string | undefined;\n}\n\n/**\n * A typed fact of one layer of the network stack.\n *\n * A fact has a layer, a time, a source and a value. Its kind names the type of its value, for example `tcp.connect`.\n * The subject is the target of the fact. A fact is plain data, so a record of facts is JSON.\n */\nexport interface Fact<V = unknown> {\n /** The id is unique in the facts of one runner, for example `f12`. */\n readonly id: string;\n readonly kind: string;\n readonly layer: Layer;\n readonly subject: Target;\n readonly time: FactTime;\n readonly source: FactSource;\n readonly value: V;\n}\n\n/**\n * The definition of a kind of fact: its name, its layer and the check of its value.\n *\n * The check is the contract of the value. The runner refuses a value that does not pass it, thus each fact of a kind has a\n * value of the type `V`.\n */\nexport interface FactKind<V> {\n readonly kind: string;\n readonly layer: Layer;\n readonly summary: string;\n readonly check: (value: unknown) => value is V;\n}\n\n/** The value type of a fact kind. */\nexport type ValueOf<K> = K extends FactKind<infer V> ? V : never;\n\nconst KIND_NAME = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/;\n\n/** This function makes a fact kind. The name has lower-case words with dots, for example `tls.handshake`. */\nexport function defineFactKind<V>(spec: FactKind<V>): FactKind<V> {\n if (!KIND_NAME.test(spec.kind)) throw new RangeError(`\"${spec.kind}\" is not a valid name of a fact kind`);\n return Object.freeze({ ...spec });\n}\n\n/** This function tells if a fact is of a kind, and if its value passes the check of the kind. */\nexport function isFactOf<V>(kind: FactKind<V>, fact: Fact): fact is Fact<V> {\n return fact.kind === kind.kind && fact.layer === kind.layer && kind.check(fact.value);\n}\n\n/** This function gives the facts of one kind, in their order. */\nexport function factsOf<V>(kind: FactKind<V>, facts: readonly Fact[]): Fact<V>[] {\n return facts.filter((fact): fact is Fact<V> => isFactOf(kind, fact));\n}\n\n/**\n * An observation is a fact that a probe parsed, before the runner gives it an id, a layer and a source.\n *\n * The time is optional. Without a time, the fact gets the time of the run of the probe.\n */\nexport interface Observation<V = unknown> {\n readonly kind: FactKind<V>;\n readonly subject: Target;\n readonly value: V;\n readonly time?: FactTime | undefined;\n}\n\n/** This function makes an observation with a type check of its value against its kind. */\nexport function observe<V>(kind: FactKind<V>, subject: Target, value: V, time?: FactTime): Observation<V> {\n return time === undefined ? { kind, subject, value } : { kind, subject, value, time };\n}\n","import type { Clock } from \"@mark1russell7/algebra/clock\";\nimport type { FactKind, Observation } from \"./fact.ts\";\nimport type { Layer } from \"./layer.ts\";\nimport type { Target } from \"./target.ts\";\n\n/**\n * The mode of a probe.\n *\n * - `active`: the probe sends packets to its peers, for example a TCP connect.\n * - `passive`: the probe reads only the state of this computer, for example its routes. It has no peers.\n */\nexport type ProbeMode = \"active\" | \"passive\";\n\n/** The context of a run: the clock of the runner, and the signal that stops the run. */\nexport interface ProbeContext {\n readonly clock: Clock;\n readonly signal: AbortSignal;\n}\n\n/** The availability of a probe on this computer. An unavailable probe tells why, for example a tool that is not installed. */\nexport type Availability =\n | { readonly available: true; readonly version?: string | undefined }\n | { readonly available: false; readonly reason: string };\n\n/**\n * The contract of a probe. A probe is an adapter: it tells if it is available, it does its work, and it parses its raw output into\n * observations.\n *\n * - `peers` gives each host that `run` sends packets to. A passive probe gives no peers. The runner examines each peer\n * against the scope before `run` starts.\n * - `run` does the effects and gives the raw output. A network failure is an observation, not an error. For example, a\n * TCP connect that times out gives the outcome `timeout`. `run` throws only for a fault of the probe.\n * - `parse` is a pure function of the raw output and the input. Thus a record of a raw output gives the same facts again,\n * also after a correction of the parser.\n *\n * The input and the raw output are plain JSON data, so a record can keep them.\n */\nexport interface Probe<I, R> {\n /** The id has lower-case words with dots, for example `tcp.connect`. */\n readonly id: string;\n /** The version of the probe changes when its facts change. */\n readonly version: string;\n readonly summary: string;\n readonly layer: Layer;\n readonly mode: ProbeMode;\n /** The kinds of the facts that the probe gives. The runner refuses an observation of a different kind. */\n readonly kinds: readonly FactKind<unknown>[];\n /** The outside tool that the probe starts, for example `ping`. A native probe has no tool. */\n readonly tool?: string | undefined;\n peers(input: I): readonly Target[];\n available(context: ProbeContext): Promise<Availability>;\n run(input: I, context: ProbeContext): Promise<R>;\n parse(raw: R, input: I): readonly Observation[];\n}\n\n/** This function makes a probe, and examines its fields. */\nexport function defineProbe<I, R>(probe: Probe<I, R>): Probe<I, R> {\n if (!/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/.test(probe.id)) throw new RangeError(`\"${probe.id}\" is not a valid probe id`);\n if (probe.kinds.length === 0) throw new RangeError(`The probe \"${probe.id}\" gives no kind of fact`);\n return probe;\n}\n","import { BlockList, isIP } from \"node:net\";\nimport { err, ok, type Result } from \"@mark1russell7/optional\";\nimport { canonicalHost, formatTarget, type Target } from \"./target.ts\";\n\n/**\n * The basis of a grant: why a target is in the scope.\n *\n * - `named`: the user named the target for this diagnosis.\n * - `owned`: the user declares that the user owns the network or the host.\n * - `configured`: the configuration of the computer of the user names it, for example the gateway or the DNS resolver.\n * - `resolved`: a DNS answer for a granted name gave this address.\n */\nexport type GrantBasis = \"named\" | \"owned\" | \"configured\" | \"resolved\";\n\n/**\n * A grant puts targets in the scope.\n *\n * A `host` grant puts a DNS name in the scope, and with `subdomains` each name under it. A `range` grant puts the\n * addresses of a CIDR range in the scope, for example `192.0.2.0/24` or `2001:db8::/32`.\n */\nexport type Grant = HostGrant | RangeGrant;\n\n/** A grant of a DNS name, and with `subdomains` of each name under it. */\nexport interface HostGrant {\n readonly kind: \"host\";\n readonly host: string;\n readonly subdomains?: boolean | undefined;\n readonly basis: GrantBasis;\n readonly note?: string | undefined;\n}\n\n/** A grant of the addresses of a CIDR range, for example `192.0.2.0/24` or `2001:db8::/32`. */\nexport interface RangeGrant {\n readonly kind: \"range\";\n readonly cidr: string;\n readonly basis: GrantBasis;\n readonly note?: string | undefined;\n}\n\n/**\n * The scope of a diagnosis: the grants, and the largest number of ports for each host.\n *\n * An active probe sends packets only to a peer in the scope. The limit of ports stops a port scan: netdiag does no\n * vulnerability scan.\n */\nexport interface Scope {\n readonly grants: readonly Grant[];\n readonly maxPortsPerHost: number;\n}\n\n/** The default limit of the different ports of one host in one runner. */\nexport const DEFAULT_MAX_PORTS_PER_HOST = 8;\n\n/** The decision of the scope for one peer of an active probe. A refusal has the reason in plain words. */\nexport type ScopeDecision =\n | { readonly allowed: true; readonly grant: Grant }\n | { readonly allowed: false; readonly reason: string };\n\n/** The ports that a runner sent packets to, for each host. */\nexport type PortLedger = ReadonlyMap<string, ReadonlySet<number>>;\n\ninterface Range {\n readonly address: string;\n readonly prefix: number;\n readonly family: \"ipv4\" | \"ipv6\";\n}\n\nfunction parseRange(cidr: string): Result<Range, string> {\n const slash = cidr.indexOf(\"/\");\n const address = canonicalHost(slash === -1 ? cidr : cidr.slice(0, slash));\n const family = isIP(address);\n if (family === 0) return err(`\"${cidr}\" is not an IP address or a CIDR range.`);\n const max = family === 4 ? 32 : 128;\n const prefixText = slash === -1 ? String(max) : cidr.slice(slash + 1);\n if (!/^\\d{1,3}$/.test(prefixText) || Number(prefixText) > max) return err(`\"${cidr}\" has no prefix from 0 to ${max}.`);\n return ok({ address, prefix: Number(prefixText), family: family === 4 ? \"ipv4\" : \"ipv6\" });\n}\n\n/** This function gives the problems of a grant, or an empty list for a correct grant. */\nexport function grantProblems(grant: Grant): string[] {\n if (grant.kind === \"range\") {\n const range = parseRange(grant.cidr);\n return range.tag === \"err\" ? [range.error] : [];\n }\n const host = canonicalHost(grant.host);\n if (host.length === 0) return [\"A host grant has an empty host.\"];\n if (isIP(host) !== 0) return [`The host grant \"${grant.host}\" is an IP address. Use a range grant for an address.`];\n return [];\n}\n\n/**\n * This function makes a scope from grants. It refuses a grant with a problem, so a typing error cannot widen the scope.\n *\n * The default limit of ports is `DEFAULT_MAX_PORTS_PER_HOST`.\n */\nexport function scopeOf(grants: readonly Grant[], options: { readonly maxPortsPerHost?: number } = {}): Result<Scope, string[]> {\n const problems = grants.flatMap(grantProblems);\n const maxPortsPerHost = options.maxPortsPerHost ?? DEFAULT_MAX_PORTS_PER_HOST;\n if (!Number.isInteger(maxPortsPerHost) || maxPortsPerHost < 1) problems.push(\"The limit of ports for each host is not a positive integer.\");\n if (problems.length > 0) return err(problems);\n return ok({ grants: [...grants], maxPortsPerHost });\n}\n\n/**\n * This function gives the grant for a target that the user names. A name gets a host grant. An IP address gets a\n * range grant of one address.\n */\nexport function namedGrant(t: Target, note?: string): Grant {\n if (t.kind === \"name\") return { kind: \"host\", host: t.host, basis: \"named\", note };\n return { kind: \"range\", cidr: `${t.host}/${t.kind === \"ipv4\" ? 32 : 128}`, basis: \"named\", note };\n}\n\n/** This function gives the grant of one address that a DNS answer for a granted name gave. */\nexport function resolvedGrant(name: string, address: string, factId?: string): RangeGrant {\n const host = canonicalHost(address);\n const family = isIP(host);\n if (family === 0) throw new RangeError(`\"${address}\" is not an IP address`);\n const note = factId === undefined ? `${canonicalHost(name)} resolved to it` : `${canonicalHost(name)} resolved to it (fact ${factId})`;\n return { kind: \"range\", cidr: `${host}/${family === 4 ? 32 : 128}`, basis: \"resolved\", note };\n}\n\n/** This function gives a scope with more grants. The limit of ports does not change. */\nexport function widen(scope: Scope, grants: readonly Grant[]): Result<Scope, string[]> {\n return scopeOf([...scope.grants, ...grants], { maxPortsPerHost: scope.maxPortsPerHost });\n}\n\nfunction grantFor(scope: Scope, peer: Target): Grant | undefined {\n const host = canonicalHost(peer.host);\n const family = isIP(host);\n for (const grant of scope.grants) {\n if (grant.kind === \"host\") {\n if (family !== 0) continue;\n const granted = canonicalHost(grant.host);\n if (host === granted || (grant.subdomains === true && host.endsWith(`.${granted}`))) return grant;\n continue;\n }\n if (family === 0) continue;\n const range = parseRange(grant.cidr);\n if (range.tag === \"err\" || (range.value.family === \"ipv4\") !== (family === 4)) continue;\n const list = new BlockList();\n list.addSubnet(range.value.address, range.value.prefix, range.value.family);\n if (list.check(host, family === 4 ? \"ipv4\" : \"ipv6\")) return grant;\n }\n return undefined;\n}\n\n/**\n * This function decides if an active probe can send packets to a peer.\n *\n * The peer must match a grant of the scope. A new port of a host must also stay in the limit of ports for each host.\n * The ledger has the ports that the runner sent packets to before.\n */\nexport function decideScope(scope: Scope, peer: Target, ledger: PortLedger = new Map()): ScopeDecision {\n const grant = grantFor(scope, peer);\n if (grant === undefined) {\n return {\n allowed: false,\n reason: `${formatTarget(peer)} is not in the scope. Name it, or add a grant for a network that you own.`,\n };\n }\n if (peer.port !== undefined) {\n const ports = ledger.get(canonicalHost(peer.host)) ?? new Set<number>();\n if (!ports.has(peer.port) && ports.size >= scope.maxPortsPerHost) {\n return {\n allowed: false,\n reason: `${formatTarget(peer)} is a new port after ${ports.size} ports of the same host. netdiag does no port scan.`,\n };\n }\n }\n return { allowed: true, grant };\n}\n","import type { Clock } from \"@mark1russell7/algebra/clock\";\nimport type { Fact, FactTime, Observation } from \"./fact.ts\";\nimport type { Probe, ProbeContext } from \"./probe.ts\";\nimport { decideScope, widen, type Grant, type PortLedger, type Scope } from \"./scope.ts\";\nimport { canonicalHost, formatTarget, type Target } from \"./target.ts\";\n\n/**\n * The outcome of one run of a probe.\n *\n * - `done`: the probe did its work. The facts include each network failure that it saw.\n * - `refused`: a peer is not in the scope, so the probe did not start.\n * - `unavailable`: the probe cannot operate on this computer.\n * - `failed`: the probe or its parser has a fault. The raw output is present when the run ended.\n * - `aborted`: the signal stopped the run.\n */\nexport type ProbeOutcome<R> =\n | { readonly status: \"done\"; readonly time: FactTime; readonly raw: R; readonly facts: readonly Fact[] }\n | { readonly status: \"refused\"; readonly peer: Target; readonly reason: string }\n | { readonly status: \"unavailable\"; readonly reason: string }\n | { readonly status: \"failed\"; readonly stage: \"probe\" | \"run\" | \"parse\"; readonly error: string; readonly raw?: R | undefined }\n | { readonly status: \"aborted\"; readonly reason: string };\n\n/** The wall-clock time of one monotonic time of the runner. */\nexport interface ClockOrigin {\n readonly wall: string;\n readonly monotonic: number;\n}\n\n/** The options of a runner. */\nexport interface RunnerOptions {\n readonly scope: Scope;\n readonly clock: Clock;\n /** The wall-clock time of the start, as an ISO 8601 text. The default is the time of the computer. */\n readonly wall?: string | undefined;\n}\n\n/**\n * A runner starts probes in one scope, and keeps the facts of the diagnosis.\n *\n * It gives each fact an id in sequence. It keeps the ledger of the ports of each host, for the limit of ports of the scope.\n */\nexport interface Runner {\n readonly origin: ClockOrigin;\n scope(): Scope;\n facts(): readonly Fact[];\n /** This method adds grants to the scope, for example the addresses of a granted name. */\n grant(grants: readonly Grant[]): void;\n run<I, R>(probe: Probe<I, R>, input: I, signal?: AbortSignal): Promise<ProbeOutcome<R>>;\n}\n\nfunction message(thrown: unknown): string {\n if (thrown instanceof Error) return thrown.message;\n return String(thrown);\n}\n\nfunction abortReason(signal: AbortSignal): string {\n const reason: unknown = signal.reason;\n return reason === undefined ? \"The signal stopped the run.\" : message(reason);\n}\n\n/** This function makes a runner. */\nexport function createRunner(options: RunnerOptions): Runner {\n const { clock } = options;\n const origin: ClockOrigin = { wall: options.wall ?? new Date().toISOString(), monotonic: clock.now() };\n let scope = options.scope;\n const facts: Fact[] = [];\n let ledger: PortLedger = new Map();\n let next = 1;\n\n /** This function gives the ledger with the ports of the peers, or the refusal of the first peer out of the scope. */\n function reserve(peers: readonly Target[]): PortLedger | { readonly peer: Target; readonly reason: string } {\n const planned = new Map([...ledger].map(([host, ports]) => [host, new Set(ports)]));\n for (const peer of peers) {\n const decision = decideScope(scope, peer, planned);\n if (!decision.allowed) return { peer, reason: decision.reason };\n if (peer.port === undefined) continue;\n const host = canonicalHost(peer.host);\n planned.set(host, (planned.get(host) ?? new Set<number>()).add(peer.port));\n }\n return planned;\n }\n\n function stamp<I, R>(probe: Probe<I, R>, observations: readonly Observation[], time: FactTime): Fact[] | string {\n const stamped: Fact[] = [];\n for (const observation of observations) {\n const { kind } = observation;\n if (!probe.kinds.some((known) => known.kind === kind.kind)) return `The probe gave the kind \"${kind.kind}\", which it does not declare.`;\n if (!kind.check(observation.value)) return `The value of a \"${kind.kind}\" fact does not pass the check of its kind.`;\n stamped.push({\n id: `f${next + stamped.length}`,\n kind: kind.kind,\n layer: kind.layer,\n subject: observation.subject,\n time: observation.time ?? time,\n source: probe.tool === undefined ? { probe: probe.id, version: probe.version } : { probe: probe.id, version: probe.version, tool: probe.tool },\n value: observation.value,\n });\n }\n return stamped;\n }\n\n async function run<I, R>(probe: Probe<I, R>, input: I, signal: AbortSignal = new AbortController().signal): Promise<ProbeOutcome<R>> {\n if (signal.aborted) return { status: \"aborted\", reason: abortReason(signal) };\n let peers: readonly Target[];\n try {\n peers = probe.peers(input);\n } catch (thrown) {\n return { status: \"failed\", stage: \"probe\", error: message(thrown) };\n }\n if (probe.mode === \"passive\" && peers.length > 0) {\n return { status: \"failed\", stage: \"probe\", error: `The passive probe \"${probe.id}\" gives peers: ${peers.map(formatTarget).join(\", \")}.` };\n }\n if (probe.mode === \"active\" && peers.length === 0) {\n return { status: \"failed\", stage: \"probe\", error: `The active probe \"${probe.id}\" gives no peer.` };\n }\n // The decision and the reservation of the ports happen with no await between them. Thus two runs at the same time\n // cannot both pass the limit of ports. A run that does not start keeps its reservation: the ledger only grows.\n const reserved = reserve(peers);\n if (\"reason\" in reserved) return { status: \"refused\", peer: reserved.peer, reason: reserved.reason };\n ledger = reserved;\n const context: ProbeContext = { clock, signal };\n let availability;\n try {\n availability = await probe.available(context);\n } catch (thrown) {\n return { status: \"failed\", stage: \"probe\", error: message(thrown) };\n }\n if (!availability.available) return { status: \"unavailable\", reason: availability.reason };\n if (signal.aborted) return { status: \"aborted\", reason: abortReason(signal) };\n const start = clock.now();\n let raw: R;\n try {\n raw = await probe.run(input, context);\n } catch (thrown) {\n if (signal.aborted) return { status: \"aborted\", reason: abortReason(signal) };\n return { status: \"failed\", stage: \"run\", error: message(thrown) };\n }\n const time: FactTime = { start, end: clock.now() };\n let observations: readonly Observation[];\n try {\n observations = probe.parse(raw, input);\n } catch (thrown) {\n return { status: \"failed\", stage: \"parse\", error: message(thrown), raw };\n }\n const stamped = stamp(probe, observations, time);\n if (typeof stamped === \"string\") return { status: \"failed\", stage: \"parse\", error: stamped, raw };\n facts.push(...stamped);\n next += stamped.length;\n return { status: \"done\", time, raw, facts: stamped };\n }\n\n return {\n origin,\n scope: () => scope,\n facts: () => [...facts],\n grant(grants) {\n const widened = widen(scope, grants);\n if (widened.tag === \"err\") throw new RangeError(widened.error.join(\" \"));\n scope = widened.value;\n },\n run,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["netConnect"],"sources":["../src/scope.ts","../src/run.ts","../src/probes/socket.ts","../src/probes/native.ts","../src/readers.ts"],"sourcesContent":["import { isIP } from \"node:net\";\nimport { err, ok, type Result } from \"@mark1russell7/optional\";\nimport { cidrContains, parseCidr } from \"./address/index.ts\";\nimport { canonicalHost, formatTarget, type Target } from \"./target.ts\";\n\n/**\n * The basis of a grant: why a target is in the scope.\n *\n * - `named`: the user named the target for this diagnosis.\n * - `owned`: the user declares that the user owns the network or the host.\n * - `configured`: the configuration of the computer of the user names it, for example the gateway or the DNS resolver.\n * - `resolved`: a DNS answer for a granted name gave this address.\n */\nexport type GrantBasis = \"named\" | \"owned\" | \"configured\" | \"resolved\";\n\n/**\n * A grant puts targets in the scope.\n *\n * A `host` grant puts a DNS name in the scope, and with `subdomains` each name under it. A `range` grant puts the\n * addresses of a CIDR range in the scope, for example `192.0.2.0/24` or `2001:db8::/32`.\n */\nexport type Grant = HostGrant | RangeGrant;\n\n/** A grant of a DNS name, and with `subdomains` of each name under it. */\nexport interface HostGrant {\n readonly kind: \"host\";\n readonly host: string;\n readonly subdomains?: boolean | undefined;\n readonly basis: GrantBasis;\n readonly note?: string | undefined;\n}\n\n/** A grant of the addresses of a CIDR range, for example `192.0.2.0/24` or `2001:db8::/32`. */\nexport interface RangeGrant {\n readonly kind: \"range\";\n readonly cidr: string;\n readonly basis: GrantBasis;\n readonly note?: string | undefined;\n}\n\n/**\n * The scope of a diagnosis: the grants, and the largest number of ports for each host.\n *\n * An active probe sends packets only to a peer in the scope. The limit of ports stops a port scan: netdiag does no\n * vulnerability scan.\n */\nexport interface Scope {\n readonly grants: readonly Grant[];\n readonly maxPortsPerHost: number;\n}\n\n/** The default limit of the different ports of one host in one runner. */\nexport const DEFAULT_MAX_PORTS_PER_HOST = 8;\n\n/** The decision of the scope for one peer of an active probe. A refusal has the reason in plain words. */\nexport type ScopeDecision =\n | { readonly allowed: true; readonly grant: Grant }\n | { readonly allowed: false; readonly reason: string };\n\n/** The ports that a runner sent packets to, for each host. */\nexport type PortLedger = ReadonlyMap<string, ReadonlySet<number>>;\n\n/** This function gives the problems of a grant, or an empty list for a correct grant. */\nexport function grantProblems(grant: Grant): string[] {\n if (grant.kind === \"range\") {\n const range = parseCidr(grant.cidr);\n return range.tag === \"err\" ? [range.error] : [];\n }\n const host = canonicalHost(grant.host);\n if (host.length === 0) return [\"A host grant has an empty host.\"];\n if (isIP(host) !== 0) return [`The host grant \"${grant.host}\" is an IP address. Use a range grant for an address.`];\n return [];\n}\n\n/**\n * This function makes a scope from grants. It refuses a grant with a problem, so a typing error cannot widen the scope.\n *\n * The default limit of ports is `DEFAULT_MAX_PORTS_PER_HOST`.\n */\nexport function scopeOf(grants: readonly Grant[], options: { readonly maxPortsPerHost?: number } = {}): Result<Scope, string[]> {\n const problems = grants.flatMap(grantProblems);\n const maxPortsPerHost = options.maxPortsPerHost ?? DEFAULT_MAX_PORTS_PER_HOST;\n if (!Number.isInteger(maxPortsPerHost) || maxPortsPerHost < 1) problems.push(\"The limit of ports for each host is not a positive integer.\");\n if (problems.length > 0) return err(problems);\n return ok({ grants: [...grants], maxPortsPerHost });\n}\n\n/**\n * This function gives the grant for a target that the user names. A name gets a host grant. An IP address gets a\n * range grant of one address.\n */\nexport function namedGrant(t: Target, note?: string): Grant {\n if (t.kind === \"name\") return { kind: \"host\", host: t.host, basis: \"named\", note };\n return { kind: \"range\", cidr: `${t.host}/${t.kind === \"ipv4\" ? 32 : 128}`, basis: \"named\", note };\n}\n\n/** This function gives the grant of one address that a DNS answer for a granted name gave. */\nexport function resolvedGrant(name: string, address: string, factId?: string): RangeGrant {\n const host = canonicalHost(address);\n const family = isIP(host);\n if (family === 0) throw new RangeError(`\"${address}\" is not an IP address`);\n const note = factId === undefined ? `${canonicalHost(name)} resolved to it` : `${canonicalHost(name)} resolved to it (fact ${factId})`;\n return { kind: \"range\", cidr: `${host}/${family === 4 ? 32 : 128}`, basis: \"resolved\", note };\n}\n\n/** This function gives a scope with more grants. The limit of ports does not change. */\nexport function widen(scope: Scope, grants: readonly Grant[]): Result<Scope, string[]> {\n return scopeOf([...scope.grants, ...grants], { maxPortsPerHost: scope.maxPortsPerHost });\n}\n\nfunction grantFor(scope: Scope, peer: Target): Grant | undefined {\n const host = canonicalHost(peer.host);\n const family = isIP(host);\n for (const grant of scope.grants) {\n if (grant.kind === \"host\") {\n if (family !== 0) continue;\n const granted = canonicalHost(grant.host);\n if (host === granted || (grant.subdomains === true && host.endsWith(`.${granted}`))) return grant;\n continue;\n }\n if (family === 0) continue;\n const range = parseCidr(grant.cidr);\n if (range.tag === \"ok\" && cidrContains(range.value, host)) return grant;\n }\n return undefined;\n}\n\n/**\n * This function decides if an active probe can send packets to a peer.\n *\n * The peer must match a grant of the scope. A new port of a host must also stay in the limit of ports for each host.\n * The ledger has the ports that the runner sent packets to before.\n */\nexport function decideScope(scope: Scope, peer: Target, ledger: PortLedger = new Map()): ScopeDecision {\n const grant = grantFor(scope, peer);\n if (grant === undefined) {\n return {\n allowed: false,\n reason: `${formatTarget(peer)} is not in the scope. Name it, or add a grant for a network that you own.`,\n };\n }\n if (peer.port !== undefined) {\n const ports = ledger.get(canonicalHost(peer.host)) ?? new Set<number>();\n if (!ports.has(peer.port) && ports.size >= scope.maxPortsPerHost) {\n return {\n allowed: false,\n reason: `${formatTarget(peer)} is a new port after ${ports.size} ports of the same host. netdiag does no port scan.`,\n };\n }\n }\n return { allowed: true, grant };\n}\n","import type { Clock } from \"@mark1russell7/algebra/clock\";\nimport type { Fact, FactTime, Observation } from \"./fact.ts\";\nimport type { Probe, ProbeContext } from \"./probe.ts\";\nimport { decideScope, widen, type Grant, type PortLedger, type Scope } from \"./scope.ts\";\nimport { canonicalHost, formatTarget, type Target } from \"./target.ts\";\n\n/**\n * The outcome of one run of a probe.\n *\n * - `done`: the probe did its work. The facts include each network failure that it saw.\n * - `refused`: a peer is not in the scope, so the probe did not start.\n * - `unavailable`: the probe cannot operate on this computer.\n * - `failed`: the probe or its parser has a fault. The raw output is present when the run ended.\n * - `aborted`: the signal stopped the run.\n */\nexport type ProbeOutcome<R> =\n | { readonly status: \"done\"; readonly time: FactTime; readonly raw: R; readonly facts: readonly Fact[] }\n | { readonly status: \"refused\"; readonly peer: Target; readonly reason: string }\n | { readonly status: \"unavailable\"; readonly reason: string }\n | { readonly status: \"failed\"; readonly stage: \"probe\" | \"run\" | \"parse\"; readonly error: string; readonly raw?: R | undefined }\n | { readonly status: \"aborted\"; readonly reason: string };\n\n/** The wall-clock time of one monotonic time of the runner. */\nexport interface ClockOrigin {\n readonly wall: string;\n readonly monotonic: number;\n}\n\n/** The options of a runner. */\nexport interface RunnerOptions {\n readonly scope: Scope;\n readonly clock: Clock;\n /** The wall-clock time of the start, as an ISO 8601 text. The default is the time of the computer. */\n readonly wall?: string | undefined;\n}\n\n/**\n * A runner starts probes in one scope, and keeps the facts of the diagnosis.\n *\n * It gives each fact an id in sequence. It keeps the ledger of the ports of each host, for the limit of ports of the scope.\n */\nexport interface Runner {\n readonly origin: ClockOrigin;\n scope(): Scope;\n facts(): readonly Fact[];\n /** This method adds grants to the scope, for example the addresses of a granted name. */\n grant(grants: readonly Grant[]): void;\n run<I, R>(probe: Probe<I, R>, input: I, signal?: AbortSignal): Promise<ProbeOutcome<R>>;\n}\n\nfunction message(thrown: unknown): string {\n if (thrown instanceof Error) return thrown.message;\n return String(thrown);\n}\n\nfunction abortReason(signal: AbortSignal): string {\n const reason: unknown = signal.reason;\n return reason === undefined ? \"The signal stopped the run.\" : message(reason);\n}\n\n/** This function makes a runner. */\nexport function createRunner(options: RunnerOptions): Runner {\n const { clock } = options;\n const origin: ClockOrigin = { wall: options.wall ?? new Date().toISOString(), monotonic: clock.now() };\n let scope = options.scope;\n const facts: Fact[] = [];\n let ledger: PortLedger = new Map();\n let next = 1;\n\n /** This function gives the ledger with the ports of the peers, or the refusal of the first peer out of the scope. */\n function reserve(peers: readonly Target[]): PortLedger | { readonly peer: Target; readonly reason: string } {\n const planned = new Map([...ledger].map(([host, ports]) => [host, new Set(ports)]));\n for (const peer of peers) {\n const decision = decideScope(scope, peer, planned);\n if (!decision.allowed) return { peer, reason: decision.reason };\n if (peer.port === undefined) continue;\n const host = canonicalHost(peer.host);\n planned.set(host, (planned.get(host) ?? new Set<number>()).add(peer.port));\n }\n return planned;\n }\n\n function stamp<I, R>(probe: Probe<I, R>, observations: readonly Observation[], time: FactTime): Fact[] | string {\n const stamped: Fact[] = [];\n for (const observation of observations) {\n const { kind } = observation;\n if (!probe.kinds.some((known) => known.kind === kind.kind)) return `The probe gave the kind \"${kind.kind}\", which it does not declare.`;\n if (!kind.check(observation.value)) return `The value of a \"${kind.kind}\" fact does not pass the check of its kind.`;\n stamped.push({\n id: `f${next + stamped.length}`,\n kind: kind.kind,\n layer: kind.layer,\n subject: observation.subject,\n time: observation.time ?? time,\n source: probe.tool === undefined ? { probe: probe.id, version: probe.version } : { probe: probe.id, version: probe.version, tool: probe.tool },\n value: observation.value,\n });\n }\n return stamped;\n }\n\n async function run<I, R>(probe: Probe<I, R>, input: I, signal: AbortSignal = new AbortController().signal): Promise<ProbeOutcome<R>> {\n if (signal.aborted) return { status: \"aborted\", reason: abortReason(signal) };\n let peers: readonly Target[];\n try {\n peers = probe.peers(input);\n } catch (thrown) {\n return { status: \"failed\", stage: \"probe\", error: message(thrown) };\n }\n if (probe.mode === \"passive\" && peers.length > 0) {\n return { status: \"failed\", stage: \"probe\", error: `The passive probe \"${probe.id}\" gives peers: ${peers.map(formatTarget).join(\", \")}.` };\n }\n if (probe.mode === \"active\" && peers.length === 0) {\n return { status: \"failed\", stage: \"probe\", error: `The active probe \"${probe.id}\" gives no peer.` };\n }\n // The decision and the reservation of the ports happen with no await between them. Thus two runs at the same time\n // cannot both pass the limit of ports. A run that does not start keeps its reservation: the ledger only grows.\n const reserved = reserve(peers);\n if (\"reason\" in reserved) return { status: \"refused\", peer: reserved.peer, reason: reserved.reason };\n ledger = reserved;\n const context: ProbeContext = { clock, signal };\n let availability;\n try {\n availability = await probe.available(context);\n } catch (thrown) {\n return { status: \"failed\", stage: \"probe\", error: message(thrown) };\n }\n if (!availability.available) return { status: \"unavailable\", reason: availability.reason };\n if (signal.aborted) return { status: \"aborted\", reason: abortReason(signal) };\n const start = clock.now();\n let raw: R;\n try {\n raw = await probe.run(input, context);\n } catch (thrown) {\n if (signal.aborted) return { status: \"aborted\", reason: abortReason(signal) };\n return { status: \"failed\", stage: \"run\", error: message(thrown) };\n }\n const time: FactTime = { start, end: clock.now() };\n let observations: readonly Observation[];\n try {\n observations = probe.parse(raw, input);\n } catch (thrown) {\n return { status: \"failed\", stage: \"parse\", error: message(thrown), raw };\n }\n const stamped = stamp(probe, observations, time);\n if (typeof stamped === \"string\") return { status: \"failed\", stage: \"parse\", error: stamped, raw };\n facts.push(...stamped);\n next += stamped.length;\n return { status: \"done\", time, raw, facts: stamped };\n }\n\n return {\n origin,\n scope: () => scope,\n facts: () => [...facts],\n grant(grants) {\n const widened = widen(scope, grants);\n if (widened.tag === \"err\") throw new RangeError(widened.error.join(\" \"));\n scope = widened.value;\n },\n run,\n };\n}\n","import { X509Certificate } from \"node:crypto\";\nimport { request } from \"node:http\";\nimport { connect as netConnect, isIP, type Socket, type TcpNetConnectOpts } from \"node:net\";\nimport * as tls from \"node:tls\";\nimport type { Clock } from \"@mark1russell7/algebra/clock\";\nimport { observe, type FactTime, type Observation } from \"../fact.ts\";\nimport {\n HTTP_RESPONSE,\n TCP_CONNECT,\n TLS_HANDSHAKE,\n type CertProblem,\n type ConnectOutcome,\n type HandshakeOutcome,\n type HttpOutcome,\n type TlsCertificate,\n} from \"../kinds.ts\";\nimport type { ProbeContext } from \"../probe.ts\";\nimport { target, type Target } from \"../target.ts\";\n\n/** The function that opens a TCP socket. A test can give a function that gives a socket with no connection. */\nexport type Dial = (options: TcpNetConnectOpts) => Socket;\n\n/** The options of the native probes that connect. */\nexport interface ConnectOptions {\n /** The function that opens a TCP socket. The default is `connect` of `node:net`. */\n readonly dial?: Dial | undefined;\n}\n\n/** The time limit of a probe run, when its input gives none: 5 seconds. */\nexport const DEFAULT_TIMEOUT_MS = 5000;\n\nconst defaultDial: Dial = (options) => netConnect(options);\n\n/** This function gives the dial of the options. */\nexport function dialOf(options: ConnectOptions): Dial {\n return options.dial ?? defaultDial;\n}\n\n/** This function gives the error code of a thrown value, for example `ECONNREFUSED`. */\nexport function codeOf(thrown: unknown): string | undefined {\n if (typeof thrown !== \"object\" || thrown === null) return undefined;\n const code: unknown = (thrown as { code?: unknown }).code;\n return typeof code === \"string\" && code.length > 0 ? code : undefined;\n}\n\nfunction syscallOf(thrown: unknown): string | undefined {\n if (typeof thrown !== \"object\" || thrown === null) return undefined;\n const syscall: unknown = (thrown as { syscall?: unknown }).syscall;\n return typeof syscall === \"string\" ? syscall : undefined;\n}\n\n/** This function gives the peer of a probe that connects. It throws for an address that is not an IP address. */\nexport function peerOf(address: string, port: number): Target {\n if (isIP(address) === 0) {\n throw new TypeError(`\"${address}\" is not an IP address. Get the addresses of a name with the DNS probe first.`);\n }\n if (!Number.isInteger(port) || port < 1 || port > 65535) throw new RangeError(`${port} is not a port from 1 to 65535.`);\n return target(address, port);\n}\n\n/** This function gives the time limit of an input. It throws for a limit that is not a positive number. */\nexport function timeoutOf(timeoutMs: number | undefined): number {\n const limit = timeoutMs ?? DEFAULT_TIMEOUT_MS;\n if (!Number.isFinite(limit) || limit <= 0) throw new RangeError(`The time limit ${limit} is not a positive number of milliseconds.`);\n return limit;\n}\n\n/**\n * The deadline and the signal of one probe run.\n *\n * The watch destroys the current socket when the deadline passes or when the signal stops the run. The phase that\n * waits on the socket then gives the outcome `timeout`.\n */\nexport interface Watch {\n expired(): boolean;\n guard(socket: Socket): void;\n close(): void;\n}\n\n/** This function starts the watch of a probe run. */\nexport function watch(context: ProbeContext, timeoutMs: number): Watch {\n let expired = false;\n let current: Socket | undefined;\n const stop = (): void => {\n current?.destroy();\n };\n const cancel = context.clock.after(timeoutMs, () => {\n expired = true;\n stop();\n });\n context.signal.addEventListener(\"abort\", stop, { once: true });\n return {\n expired: () => expired,\n guard(socket) {\n current = socket;\n if (expired || context.signal.aborted) socket.destroy();\n },\n close() {\n cancel();\n context.signal.removeEventListener(\"abort\", stop);\n },\n };\n}\n\nconst ignore = (): void => undefined;\n\n/** This function maps an error code of a TCP connect to its outcome. */\nexport function connectOutcome(code: string | undefined): ConnectOutcome {\n switch (code) {\n case \"ECONNREFUSED\":\n return \"refused\";\n case \"ETIMEDOUT\":\n return \"timeout\";\n case \"EHOSTUNREACH\":\n case \"ENETUNREACH\":\n case \"EHOSTDOWN\":\n case \"ENETDOWN\":\n return \"unreachable\";\n case \"ECONNRESET\":\n return \"reset\";\n default:\n return \"error\";\n }\n}\n\n/**\n * This function tells how a stream ended before its phase was complete.\n *\n * Node.js gives the code `ECONNRESET` for a TCP reset, with the system call that read it. It gives the same code with\n * no system call when the peer closed the connection with a FIN.\n */\nexport function endOf(thrown: unknown, expired: boolean): \"timeout\" | \"reset\" | \"closed\" | undefined {\n if (expired) return \"timeout\";\n const code = codeOf(thrown);\n if (code === \"EPIPE\") return \"reset\";\n if (code !== \"ECONNRESET\") return undefined;\n return syscallOf(thrown) === undefined ? \"closed\" : \"reset\";\n}\n\nconst NOT_TLS = new Set([\"ERR_SSL_WRONG_VERSION_NUMBER\", \"ERR_SSL_PACKET_LENGTH_TOO_LONG\", \"ERR_SSL_HTTP_REQUEST\", \"ERR_SSL_RECORD_LAYER_FAILURE\"]);\nconst MISMATCH = new Set([\"ERR_SSL_UNSUPPORTED_PROTOCOL\", \"ERR_SSL_NO_PROTOCOLS_AVAILABLE\", \"ERR_SSL_NO_SHARED_CIPHER\", \"ERR_SSL_WRONG_SSL_VERSION\"]);\n\n/** This function maps an error of a TLS handshake to its outcome. */\nexport function handshakeOutcome(thrown: unknown, expired: boolean): HandshakeOutcome {\n const end = endOf(thrown, expired);\n if (end !== undefined) return end;\n const code = codeOf(thrown) ?? \"\";\n if (NOT_TLS.has(code)) return \"not-tls\";\n if (code.includes(\"_ALERT_\") || MISMATCH.has(code)) return \"alert\";\n return \"error\";\n}\n\n/** This function maps the authorization error of a TLS socket to the problem of the certificate. */\nexport function certProblem(code: string): CertProblem {\n switch (code) {\n case \"CERT_HAS_EXPIRED\":\n return \"expired\";\n case \"CERT_NOT_YET_VALID\":\n return \"not-yet-valid\";\n case \"ERR_TLS_CERT_ALTNAME_INVALID\":\n case \"HOSTNAME_MISMATCH\":\n return \"name\";\n case \"DEPTH_ZERO_SELF_SIGNED_CERT\":\n case \"SELF_SIGNED_CERT_IN_CHAIN\":\n case \"UNABLE_TO_GET_ISSUER_CERT\":\n case \"UNABLE_TO_GET_ISSUER_CERT_LOCALLY\":\n case \"UNABLE_TO_VERIFY_LEAF_SIGNATURE\":\n case \"CERT_UNTRUSTED\":\n return \"untrusted\";\n case \"CERT_REVOKED\":\n return \"revoked\";\n default:\n return \"other\";\n }\n}\n\n/** The TCP phase of a probe run. The times are readings of the clock of the runner. */\nexport interface TcpPhase {\n readonly address: string;\n readonly port: number;\n readonly family: 4 | 6;\n readonly start: number;\n readonly end: number;\n readonly outcome: ConnectOutcome;\n readonly code?: string | undefined;\n}\n\n/** This function connects to an address and a port, and gives the socket when it connected. */\nexport function connectPhase(dial: Dial, address: string, port: number, w: Watch, clock: Clock): Promise<{ socket?: Socket; phase: TcpPhase }> {\n const family = isIP(address) === 6 ? 6 : 4;\n const start = clock.now();\n return new Promise((resolve) => {\n const socket = dial({ host: address, port, family });\n socket.on(\"error\", ignore);\n let done = false;\n const finish = (outcome: ConnectOutcome, code?: string): void => {\n if (done) return;\n done = true;\n const phase: TcpPhase = { address, port, family, start, end: clock.now(), outcome, ...(code === undefined ? {} : { code }) };\n if (outcome === \"connected\") resolve({ socket, phase });\n else {\n socket.destroy();\n resolve({ phase });\n }\n };\n socket.once(\"connect\", () => finish(\"connected\"));\n socket.once(\"error\", (error) => (w.expired() ? finish(\"timeout\") : finish(connectOutcome(codeOf(error)), codeOf(error))));\n socket.once(\"close\", () => (w.expired() ? finish(\"timeout\") : finish(\"error\", \"ECLOSED\")));\n w.guard(socket);\n });\n}\n\n/** This function gives the observation of a TCP phase. */\nexport function tcpObservation(phase: TcpPhase): Observation {\n const { start, end, ...rest } = phase;\n return observe(TCP_CONNECT, target(phase.address, phase.port), { ...rest, ms: end - start }, { start, end });\n}\n\n/** The trust of the certificates of a TLS probe: the context, and the SHA-256 fingerprints of its roots. */\nexport interface TrustStore {\n readonly context: tls.SecureContext;\n readonly publicRoots: ReadonlySet<string>;\n /** The roots of the computer and of the user that are not public roots. */\n readonly privateRoots: ReadonlySet<string>;\n}\n\nfunction systemRoots(): readonly string[] {\n // `getCACertificates` is in Node.js 22.15 and later.\n const get: unknown = (tls as { getCACertificates?: unknown }).getCACertificates;\n if (typeof get !== \"function\") return [];\n try {\n const roots: unknown = get.call(tls, \"system\");\n return Array.isArray(roots) ? roots.filter((root): root is string => typeof root === \"string\") : [];\n } catch {\n return [];\n }\n}\n\n/**\n * This function makes the store of trust of a TLS probe. It has the public roots of Node.js, the roots of the computer\n * and the roots that the user gives. A chain that ends at a public root has the trust `public`. A chain that ends at a\n * different root of the store has the trust `private`, for example the root of a proxy that examines TLS. The roots\n * in `extraPublic` count as public roots.\n */\nexport function trustStore(extraRoots: readonly string[] = [], extraPublic: readonly string[] = []): TrustStore {\n const local = [...systemRoots(), ...extraRoots];\n const publicRoots = new Set([...tls.rootCertificates, ...extraPublic].flatMap(fingerprintOf));\n const privateRoots = new Set(local.flatMap(fingerprintOf).filter((fingerprint) => !publicRoots.has(fingerprint)));\n const context = tls.createSecureContext({ ca: [...tls.rootCertificates, ...extraPublic, ...local] });\n return { context, publicRoots, privateRoots };\n}\n\nfunction fingerprintOf(pem: string): string[] {\n try {\n return [new X509Certificate(pem).fingerprint256];\n } catch {\n return [];\n }\n}\n\nfunction text(value: string | string[] | undefined): string {\n if (value === undefined) return \"\";\n return Array.isArray(value) ? value.join(\", \") : value;\n}\n\nfunction isoTime(value: string): string {\n const time = new Date(value);\n return Number.isNaN(time.getTime()) ? value : time.toISOString();\n}\n\nfunction namesOf(subjectAltName: string | undefined): string[] {\n if (subjectAltName === undefined || subjectAltName.length === 0) return [];\n return subjectAltName\n .split(\", \")\n .map((entry) => entry.replace(/^(?:DNS|IP Address):/, \"\"))\n .filter((entry) => entry.length > 0);\n}\n\n// Node.js links a certificate to itself only when the check of the chain found it as a root. Thus this function\n// examines the certificate: its issuer is its subject, and its own key verifies its signature. It does not use\n// `checkIssued`, because OpenSSL then also requires the key usage `keyCertSign`, and a server certificate has none.\nfunction isSelfSigned(raw: Buffer): boolean {\n try {\n const certificate = new X509Certificate(raw);\n return certificate.issuer === certificate.subject && certificate.verify(certificate.publicKey);\n } catch {\n return false;\n }\n}\n\nfunction certificateOf(socket: tls.TLSSocket, store: TrustStore): TlsCertificate | undefined {\n const leaf = socket.getPeerCertificate(true);\n if (Object.keys(leaf).length === 0) return undefined;\n let top: tls.DetailedPeerCertificate = leaf;\n let chain = 1;\n const seen = new Set([leaf.fingerprint256]);\n while (top.issuerCertificate !== undefined && !seen.has(top.issuerCertificate.fingerprint256)) {\n top = top.issuerCertificate;\n seen.add(top.fingerprint256);\n chain += 1;\n }\n const authorized = socket.authorized;\n // The trust names the root of the chain, also when the check of the certificate fails. Thus an expired certificate of\n // a public authority keeps the trust `public`, and the certificate of a captive portal gets `none`.\n const trust = store.publicRoots.has(top.fingerprint256) ? \"public\" : store.privateRoots.has(top.fingerprint256) || authorized ? \"private\" : \"none\";\n const reason: unknown = socket.authorizationError;\n return {\n subject: text(leaf.subject.CN) || text(leaf.subject.O),\n issuer: text(leaf.issuer.CN) || text(leaf.issuer.O),\n names: namesOf(leaf.subjectaltname),\n validFrom: isoTime(leaf.valid_from),\n validTo: isoTime(leaf.valid_to),\n fingerprint256: leaf.fingerprint256,\n selfSigned: isSelfSigned(leaf.raw),\n chain,\n trust,\n ...(authorized ? {} : { problem: certProblem(reason instanceof Error ? (codeOf(reason) ?? reason.message) : String(reason)) }),\n };\n}\n\n/** The TLS phase of a probe run. */\nexport interface TlsPhase {\n readonly servername?: string | undefined;\n readonly start: number;\n readonly end: number;\n readonly outcome: HandshakeOutcome;\n readonly protocol?: string | undefined;\n readonly cipher?: string | undefined;\n readonly alpn?: string | undefined;\n readonly code?: string | undefined;\n readonly certificate?: TlsCertificate | undefined;\n}\n\n/** The input of a TLS handshake. */\nexport interface HandshakeInput {\n readonly address: string;\n /** The name for SNI and for the check of the certificate. With no name, the check uses the address. */\n readonly servername?: string | undefined;\n readonly alpn?: readonly string[] | undefined;\n}\n\n/**\n * This function does a TLS handshake on a connected socket. It does not refuse a certificate that is not valid: it\n * gives the outcome `cert-error` and the problem of the certificate.\n */\nexport function handshakePhase(\n socket: Socket,\n input: HandshakeInput,\n store: TrustStore,\n w: Watch,\n clock: Clock,\n): Promise<{ secure?: tls.TLSSocket; phase: TlsPhase }> {\n const start = clock.now();\n const servername = input.servername !== undefined && isIP(input.servername) === 0 ? input.servername : undefined;\n return new Promise((resolve) => {\n const secure = tls.connect({\n socket,\n host: input.address,\n ...(servername === undefined ? {} : { servername }),\n ...(input.alpn === undefined ? {} : { ALPNProtocols: [...input.alpn] }),\n secureContext: store.context,\n // The probe examines a certificate that is not valid, so the handshake does not stop at it. The probe then writes\n // no data. The HTTP probe sends its request only when the check of the certificate passed.\n // codeql[js/disabling-certificate-validation]\n rejectUnauthorized: false,\n });\n secure.on(\"error\", ignore);\n let done = false;\n const base = { ...(servername === undefined ? {} : { servername }), start };\n const finish = (phase: TlsPhase, ok: boolean): void => {\n if (done) return;\n done = true;\n if (ok) resolve({ secure, phase });\n else {\n secure.destroy();\n resolve({ phase });\n }\n };\n secure.once(\"secureConnect\", () => {\n const cipher = secure.getCipher().name;\n const protocol = secure.getProtocol() ?? undefined;\n const alpn = typeof secure.alpnProtocol === \"string\" ? secure.alpnProtocol : undefined;\n const certificate = certificateOf(secure, store);\n finish(\n {\n ...base,\n end: clock.now(),\n outcome: secure.authorized ? \"ok\" : \"cert-error\",\n ...(protocol === undefined ? {} : { protocol }),\n ...(cipher.length === 0 ? {} : { cipher }),\n ...(alpn === undefined ? {} : { alpn }),\n ...(certificate === undefined ? {} : { certificate }),\n },\n true,\n );\n });\n secure.once(\"error\", (error) => {\n const code = codeOf(error);\n finish({ ...base, end: clock.now(), outcome: handshakeOutcome(error, w.expired()), ...(code === undefined ? {} : { code }) }, false);\n });\n secure.once(\"close\", () => finish({ ...base, end: clock.now(), outcome: w.expired() ? \"timeout\" : \"closed\" }, false));\n w.guard(secure);\n });\n}\n\n/** This function gives the observation of a TLS phase. */\nexport function tlsObservation(address: string, port: number, phase: TlsPhase): Observation {\n const { start, end, ...rest } = phase;\n return observe(TLS_HANDSHAKE, target(address, port), { address, port, ...rest, ms: end - start }, { start, end });\n}\n\n/** The fields of a response head that an HTTP fact keeps. */\nexport const KEPT_FIELDS: readonly string[] = [\"server\", \"via\", \"location\", \"content-type\", \"content-length\", \"alt-svc\", \"x-cache\", \"cf-ray\"];\n\n/** The HTTP phase of a probe run. */\nexport interface HttpPhase {\n readonly method: \"GET\" | \"HEAD\";\n readonly host: string;\n readonly path: string;\n readonly start: number;\n /** The time of the head of the response. */\n readonly head?: number | undefined;\n readonly end: number;\n readonly outcome: HttpOutcome;\n readonly status?: number | undefined;\n readonly version?: string | undefined;\n readonly fields: Readonly<Record<string, string>>;\n readonly bytes: number;\n readonly truncated: boolean;\n readonly code?: string | undefined;\n}\n\n/** The input of an HTTP exchange. */\nexport interface ExchangeInput {\n readonly method: \"GET\" | \"HEAD\";\n readonly host: string;\n readonly path: string;\n readonly maxBytes: number;\n}\n\nfunction httpOutcome(thrown: unknown, expired: boolean): HttpOutcome {\n const end = endOf(thrown, expired);\n if (end !== undefined) return end;\n return (codeOf(thrown) ?? \"\").startsWith(\"HPE_\") ? \"protocol\" : \"error\";\n}\n\n/** This function sends one HTTP/1.1 request on a connected socket, and reads the response up to a limit of bytes. */\nexport function exchangePhase(socket: Socket, input: ExchangeInput, w: Watch, clock: Clock): Promise<HttpPhase> {\n return new Promise((resolve) => {\n let done = false;\n let head: number | undefined;\n let status: number | undefined;\n let version: string | undefined;\n let fields: Record<string, string> = {};\n let bytes = 0;\n let truncated = false;\n const start = clock.now();\n const finish = (outcome: HttpOutcome, code?: string): void => {\n if (done) return;\n done = true;\n resolve({\n method: input.method,\n host: input.host,\n path: input.path,\n start,\n ...(head === undefined ? {} : { head }),\n end: clock.now(),\n outcome,\n ...(status === undefined ? {} : { status }),\n ...(version === undefined ? {} : { version }),\n fields,\n bytes,\n truncated,\n ...(code === undefined ? {} : { code }),\n });\n socket.destroy();\n };\n const req = request({\n createConnection: () => socket,\n method: input.method,\n path: input.path,\n headers: { host: input.host, \"user-agent\": \"netdiag\", accept: \"*/*\", connection: \"close\" },\n });\n req.on(\"error\", (error) => finish(httpOutcome(error, w.expired()), codeOf(error)));\n req.on(\"close\", () => finish(w.expired() ? \"timeout\" : \"closed\"));\n req.on(\"response\", (res) => {\n head = clock.now();\n status = res.statusCode;\n version = res.httpVersion;\n fields = Object.fromEntries(\n KEPT_FIELDS.flatMap((name) => {\n const value = res.headers[name];\n return value === undefined ? [] : [[name, Array.isArray(value) ? value.join(\", \") : value]];\n }),\n );\n res.on(\"data\", (chunk: Buffer) => {\n bytes += chunk.length;\n if (bytes >= input.maxBytes) {\n truncated = true;\n finish(\"response\");\n }\n });\n res.on(\"end\", () => finish(\"response\"));\n res.on(\"error\", (error) => finish(httpOutcome(error, w.expired()), codeOf(error)));\n });\n req.end();\n });\n}\n\n/** This function gives the observation of an HTTP phase. */\nexport function httpObservation(address: string, port: number, scheme: \"http\" | \"https\", phase: HttpPhase): Observation {\n const { start, end, head, ...rest } = phase;\n const time: FactTime = { start, end };\n return observe(\n HTTP_RESPONSE,\n target(address, port),\n { address, port, scheme, ...rest, ...(head === undefined ? {} : { ttfbMs: head - start }), ms: end - start },\n time,\n );\n}\n","import { Resolver } from \"node:dns/promises\";\nimport { isIP } from \"node:net\";\nimport { observe, type Observation } from \"../fact.ts\";\nimport { DNS_ANSWER, TCP_CONNECT, TLS_HANDSHAKE, HTTP_RESPONSE, type DnsOutcome, type RecordType } from \"../kinds.ts\";\nimport { defineProbe, type Availability, type Probe } from \"../probe.ts\";\nimport { formatTarget, target } from \"../target.ts\";\nimport {\n connectPhase,\n dialOf,\n exchangePhase,\n handshakePhase,\n httpObservation,\n peerOf,\n tcpObservation,\n timeoutOf,\n tlsObservation,\n trustStore,\n watch,\n codeOf,\n type ConnectOptions,\n type HttpPhase,\n type TcpPhase,\n type TlsPhase,\n type TrustStore,\n} from \"./socket.ts\";\n\n// The native probes use only the `node:` modules. Each network failure is an outcome in the raw output, not an error.\n\nconst NATIVE: Availability = { available: true, version: process.versions.node };\n\nfunction native(): Promise<Availability> {\n return Promise.resolve(NATIVE);\n}\n\n/** The input of the DNS probe: one query for the addresses of a name, to one resolver. */\nexport interface DnsInput {\n readonly name: string;\n /** The record type. The default is `A`. */\n readonly record?: RecordType | undefined;\n /** The IP address of the resolver. */\n readonly resolver: string;\n /** The port of the resolver. The default is 53. */\n readonly port?: number | undefined;\n readonly timeoutMs?: number | undefined;\n}\n\n/** The raw output of the DNS probe. The times are readings of the clock of the runner. */\nexport interface DnsRaw {\n readonly name: string;\n readonly record: RecordType;\n readonly resolver: string;\n readonly start: number;\n readonly end: number;\n readonly outcome: DnsOutcome;\n readonly addresses: readonly string[];\n readonly ttl?: number | undefined;\n readonly code?: string | undefined;\n}\n\n/** This function maps an error code of a DNS query to its outcome. */\nexport function dnsOutcome(code: string | undefined): DnsOutcome {\n switch (code) {\n case \"ENOTFOUND\":\n return \"nxdomain\";\n case \"ENODATA\":\n return \"nodata\";\n case \"ESERVFAIL\":\n return \"servfail\";\n case \"EREFUSED\":\n return \"refused\";\n case \"ETIMEOUT\":\n return \"timeout\";\n case \"ECONNREFUSED\":\n return \"unreachable\";\n default:\n return \"error\";\n }\n}\n\n/**\n * The DNS probe sends one query for the addresses of a name to one resolver. Its peer is the resolver, not the name.\n * The query uses no search domain.\n */\nexport const DNS_PROBE: Probe<DnsInput, DnsRaw> = defineProbe<DnsInput, DnsRaw>({\n id: \"dns.query\",\n version: \"1\",\n summary: \"One DNS query for the A or AAAA records of a name, to one resolver.\",\n layer: \"dns\",\n mode: \"active\",\n kinds: [DNS_ANSWER],\n peers(input) {\n if (isIP(input.name) !== 0) throw new TypeError(`\"${input.name}\" is an address, not a name.`);\n return [peerOf(input.resolver, input.port ?? 53)];\n },\n available: native,\n async run(input, context) {\n const record = input.record ?? \"A\";\n const limit = timeoutOf(input.timeoutMs);\n const server = formatTarget(target(input.resolver, input.port ?? 53));\n const resolver = new Resolver({ timeout: limit, tries: 1 });\n resolver.setServers([server]);\n let expired = false;\n const stop = (): void => resolver.cancel();\n const cancel = context.clock.after(limit, () => {\n expired = true;\n stop();\n });\n context.signal.addEventListener(\"abort\", stop, { once: true });\n const start = context.clock.now();\n const base = { name: input.name, record, resolver: server, start };\n try {\n const answers = record === \"A\" ? await resolver.resolve4(input.name, { ttl: true }) : await resolver.resolve6(input.name, { ttl: true });\n const raw: DnsRaw = {\n ...base,\n end: context.clock.now(),\n outcome: \"ok\",\n addresses: answers.map((answer) => answer.address),\n ...(answers.length === 0 ? {} : { ttl: Math.min(...answers.map((answer) => answer.ttl)) }),\n };\n context.signal.throwIfAborted();\n return raw;\n } catch (thrown) {\n context.signal.throwIfAborted();\n const code = codeOf(thrown);\n return { ...base, end: context.clock.now(), outcome: expired ? \"timeout\" : dnsOutcome(code), addresses: [], ...(code === undefined ? {} : { code }) };\n } finally {\n cancel();\n context.signal.removeEventListener(\"abort\", stop);\n }\n },\n parse(raw) {\n const { start, end, ...rest } = raw;\n return [observe(DNS_ANSWER, target(raw.name), { ...rest, ms: end - start }, { start, end })];\n },\n});\n\n/** The input of the TCP probe: one connect to an IP address and a port. */\nexport interface TcpInput {\n readonly address: string;\n readonly port: number;\n readonly timeoutMs?: number | undefined;\n}\n\n/** The raw output of the TCP probe. */\nexport interface TcpRaw {\n readonly tcp: TcpPhase;\n}\n\n/** This function makes the TCP probe. It connects, and then it closes the connection. */\nexport function tcpProbe(options: ConnectOptions = {}): Probe<TcpInput, TcpRaw> {\n const dial = dialOf(options);\n return defineProbe<TcpInput, TcpRaw>({\n id: \"tcp.connect\",\n version: \"1\",\n summary: \"One TCP connect to an IP address and a port.\",\n layer: \"tcp\",\n mode: \"active\",\n kinds: [TCP_CONNECT],\n peers: (input) => [peerOf(input.address, input.port)],\n available: native,\n async run(input, context) {\n const w = watch(context, timeoutOf(input.timeoutMs));\n try {\n const { socket, phase } = await connectPhase(dial, input.address, input.port, w, context.clock);\n socket?.destroy();\n context.signal.throwIfAborted();\n return { tcp: phase };\n } finally {\n w.close();\n }\n },\n parse: (raw) => [tcpObservation(raw.tcp)],\n });\n}\n\n/** The TCP probe with the `connect` of `node:net`. */\nexport const TCP_PROBE: Probe<TcpInput, TcpRaw> = tcpProbe();\n\n/** The input of the TLS probe: a TCP connect and a TLS handshake. */\nexport interface TlsInput {\n readonly address: string;\n /** The port. The default is 443. */\n readonly port?: number | undefined;\n /** The name for SNI and for the check of the certificate. */\n readonly servername?: string | undefined;\n readonly alpn?: readonly string[] | undefined;\n readonly timeoutMs?: number | undefined;\n}\n\n/** The raw output of the TLS probe. It has no TLS phase when the TCP connect failed. */\nexport interface TlsRaw {\n readonly tcp: TcpPhase;\n readonly tls?: TlsPhase | undefined;\n}\n\n/** The options of the TLS and HTTP probes. */\nexport interface TlsProbeOptions extends ConnectOptions {\n /** More roots of trust, as PEM text: the roots that the user trusts, for example the root of a company. */\n readonly roots?: readonly string[] | undefined;\n /**\n * More public roots, as PEM text. A chain that ends at one of them gets the trust `public`. A lab gives its own\n * root here, so that its servers stand for servers on the internet.\n */\n readonly publicRoots?: readonly string[] | undefined;\n}\n\nfunction lazyStore(options: TlsProbeOptions): () => TrustStore {\n let store: TrustStore | undefined;\n return () => (store ??= trustStore(options.roots ?? [], options.publicRoots ?? []));\n}\n\n/**\n * This function makes the TLS probe. It measures the TCP connect and the TLS handshake apart. It examines the\n * certificate, and it does not refuse a certificate that is not valid.\n */\nexport function tlsProbe(options: TlsProbeOptions = {}): Probe<TlsInput, TlsRaw> {\n const dial = dialOf(options);\n const store = lazyStore(options);\n return defineProbe<TlsInput, TlsRaw>({\n id: \"tls.handshake\",\n version: \"1\",\n summary: \"One TCP connect and one TLS handshake, with the certificate of the server.\",\n layer: \"tls\",\n mode: \"active\",\n kinds: [TCP_CONNECT, TLS_HANDSHAKE],\n peers: (input) => [peerOf(input.address, input.port ?? 443)],\n available: native,\n async run(input, context) {\n const port = input.port ?? 443;\n const w = watch(context, timeoutOf(input.timeoutMs));\n try {\n const { socket, phase } = await connectPhase(dial, input.address, port, w, context.clock);\n if (socket === undefined) {\n context.signal.throwIfAborted();\n return { tcp: phase };\n }\n const handshake = await handshakePhase(socket, { address: input.address, servername: input.servername, alpn: input.alpn }, store(), w, context.clock);\n handshake.secure?.destroy();\n context.signal.throwIfAborted();\n return { tcp: phase, tls: handshake.phase };\n } finally {\n w.close();\n }\n },\n parse(raw) {\n const observations: Observation[] = [tcpObservation(raw.tcp)];\n if (raw.tls !== undefined) observations.push(tlsObservation(raw.tcp.address, raw.tcp.port, raw.tls));\n return observations;\n },\n });\n}\n\n/** The TLS probe with the public roots and the roots of the computer. */\nexport const TLS_PROBE: Probe<TlsInput, TlsRaw> = tlsProbe();\n\n/** The input of the HTTP probe: one HTTP/1.1 request over TCP, or over TLS for `https`. */\nexport interface HttpInput {\n readonly address: string;\n readonly scheme: \"http\" | \"https\";\n /** The port. The default is 80 for `http` and 443 for `https`. */\n readonly port?: number | undefined;\n /** The name of the host for the `Host` field and for SNI. The default is the address. */\n readonly host?: string | undefined;\n /** The path of the request. The default is `/`. */\n readonly path?: string | undefined;\n /** The method. The default is `GET`. */\n readonly method?: \"GET\" | \"HEAD\" | undefined;\n readonly timeoutMs?: number | undefined;\n /** The limit of the bytes of the body that the probe reads. The default is 65536. */\n readonly maxBytes?: number | undefined;\n}\n\n/** The raw output of the HTTP probe. */\nexport interface HttpRaw {\n readonly scheme: \"http\" | \"https\";\n readonly tcp: TcpPhase;\n readonly tls?: TlsPhase | undefined;\n readonly http?: HttpPhase | undefined;\n}\n\n/** The limit of the bytes of a body that the HTTP probe reads, when its input gives none: 64 KiB. */\nexport const DEFAULT_MAX_BYTES = 65536;\n\nfunction hostField(input: HttpInput, port: number): string {\n const host = input.host ?? (isIP(input.address) === 6 ? `[${input.address}]` : input.address);\n const standard = input.scheme === \"https\" ? 443 : 80;\n return port === standard ? host : `${host}:${port}`;\n}\n\n/**\n * This function makes the HTTP probe. It sends one request, follows no redirect, and reads the body up to a limit. It\n * gives the facts of each phase: TCP, TLS for `https`, and HTTP. For `https`, it sends the request only when the\n * certificate passes its check.\n */\nexport function httpProbe(options: TlsProbeOptions = {}): Probe<HttpInput, HttpRaw> {\n const dial = dialOf(options);\n const store = lazyStore(options);\n const portOf = (input: HttpInput): number => input.port ?? (input.scheme === \"https\" ? 443 : 80);\n return defineProbe<HttpInput, HttpRaw>({\n id: \"http.request\",\n version: \"1\",\n summary: \"One HTTP/1.1 request, with the time of each phase: TCP, TLS and the first byte.\",\n layer: \"http\",\n mode: \"active\",\n kinds: [TCP_CONNECT, TLS_HANDSHAKE, HTTP_RESPONSE],\n peers(input) {\n const path = input.path ?? \"/\";\n if (!path.startsWith(\"/\") || /[\\s]/.test(path)) throw new TypeError(`\"${path}\" is not a path that starts with \"/\" and has no space.`);\n return [peerOf(input.address, portOf(input))];\n },\n available: native,\n async run(input, context) {\n const port = portOf(input);\n const maxBytes = input.maxBytes ?? DEFAULT_MAX_BYTES;\n if (!Number.isInteger(maxBytes) || maxBytes < 1) throw new RangeError(`The limit of bytes ${maxBytes} is not a positive integer.`);\n const w = watch(context, timeoutOf(input.timeoutMs));\n try {\n const { socket, phase: tcp } = await connectPhase(dial, input.address, port, w, context.clock);\n if (socket === undefined) {\n context.signal.throwIfAborted();\n return { scheme: input.scheme, tcp };\n }\n let stream = socket;\n let secured: TlsPhase | undefined;\n if (input.scheme === \"https\") {\n const handshake = await handshakePhase(socket, { address: input.address, servername: input.host, alpn: [\"http/1.1\"] }, store(), w, context.clock);\n secured = handshake.phase;\n // A request on a session with a certificate that is not valid can send the path to an interceptor.\n if (handshake.secure === undefined || secured.outcome !== \"ok\") {\n handshake.secure?.destroy();\n context.signal.throwIfAborted();\n return { scheme: input.scheme, tcp, tls: secured };\n }\n stream = handshake.secure;\n }\n const http = await exchangePhase(stream, { method: input.method ?? \"GET\", host: hostField(input, port), path: input.path ?? \"/\", maxBytes }, w, context.clock);\n context.signal.throwIfAborted();\n return { scheme: input.scheme, tcp, ...(secured === undefined ? {} : { tls: secured }), http };\n } finally {\n w.close();\n }\n },\n parse(raw) {\n const { address, port } = raw.tcp;\n const observations: Observation[] = [tcpObservation(raw.tcp)];\n if (raw.tls !== undefined) observations.push(tlsObservation(address, port, raw.tls));\n if (raw.http !== undefined) observations.push(httpObservation(address, port, raw.scheme, raw.http));\n return observations;\n },\n });\n}\n\n/** The HTTP probe with the public roots and the roots of the computer. */\nexport const HTTP_PROBE: Probe<HttpInput, HttpRaw> = httpProbe();\n","import { addressKind, canonicalAddress } from \"./address/index.ts\";\nimport type { Evidence } from \"./diagnosis/model.ts\";\nimport { isFactOf, type Fact, type FactKind } from \"./fact.ts\";\nimport {\n DNS_ANSWER,\n HTTP_RESPONSE,\n ICMP_ECHO,\n PATH_REPORT,\n PATH_TRACE,\n TCP_CONNECT,\n TLS_HANDSHAKE,\n type ConnectOutcome,\n type DnsAnswer,\n type DnsOutcome,\n type EchoOutcome,\n type HandshakeOutcome,\n type HttpResponse,\n type IcmpEcho,\n type PathReport,\n type PathTrace,\n type TcpConnect,\n type TlsHandshake,\n type Trust,\n} from \"./kinds.ts\";\n\n// The readers change facts into evidence for the model. Each value that they give is a value of a feature of the\n// standard catalog, and a test holds it.\n\n/** A fact that gave no evidence, with the reason in plain words. */\nexport interface Skipped {\n readonly fact: string;\n readonly reason: string;\n}\n\n/** The evidence of a list of facts, and the facts that gave no evidence. */\nexport interface Reading {\n readonly evidence: readonly Evidence[];\n readonly skipped: readonly Skipped[];\n}\n\n/** The options of `readEvidence`. */\nexport interface ReadOptions {\n /** This function tells if a fact is about the target (`true`) or about a reference host (`false`). The default is `true`. */\n readonly primary?: ((fact: Fact) => boolean) | undefined;\n /**\n * The IP address of the default gateway. The ICMP echo facts of this address give the feature `gateway.reply`. The\n * ICMP echo facts of the other addresses give the feature `icmp.reply`.\n */\n readonly gateway?: string | undefined;\n}\n\n/** A bin of a time or a ratio. A number below `below` gets `value`. The last bin has no limit. */\nexport interface Bin {\n readonly below?: number | undefined;\n readonly value: string;\n}\n\n/** The bins of the feature `tcp.time`, in milliseconds. A connect of 900 ms or more sent its SYN again. */\nexport const TCP_TIME_BINS: readonly Bin[] = [{ below: 150, value: \"short\" }, { below: 900, value: \"long\" }, { value: \"retry\" }];\n\n/** The bins of the feature `tls.ratio`: the time of the TLS handshake divided by the time of the TCP connect. */\nexport const TLS_RATIO_BINS: readonly Bin[] = [{ below: 3, value: \"low\" }, { below: 8, value: \"mid\" }, { value: \"high\" }];\n\n/** The bins of the feature `http.ttfb`: the time to the first byte divided by the time of the TCP connect. */\nexport const HTTP_TTFB_BINS: readonly Bin[] = [{ below: 4, value: \"low\" }, { below: 20, value: \"mid\" }, { value: \"high\" }];\n\n/**\n * The smallest time of a TCP connect in a ratio, in milliseconds. On a local link, a ratio with a smaller time measures\n * only the cost of the cryptography.\n */\nexport const MIN_CONNECT_MS = 1;\n\n/** The value of the feature `dns.answer` for each outcome of a DNS query. `nodata` tells that the name exists. */\nexport const DNS_ANSWER_VALUES: Readonly<Partial<Record<DnsOutcome, string>>> = {\n ok: \"ok\",\n nodata: \"ok\",\n nxdomain: \"nxdomain\",\n servfail: \"servfail\",\n refused: \"refused\",\n timeout: \"timeout\",\n unreachable: \"unreachable\",\n};\n\n/** The value of the feature `tcp.connect` for each outcome of a TCP connect. */\nexport const TCP_CONNECT_VALUES: Readonly<Partial<Record<ConnectOutcome, string>>> = {\n connected: \"connected\",\n refused: \"refused\",\n timeout: \"timeout\",\n unreachable: \"unreachable\",\n reset: \"reset\",\n};\n\n/**\n * The value of the feature `tls.handshake` for each outcome of a TLS handshake. The catalog has no value for `not-tls`\n * and `closed` yet. The lab runs (ND-7) tell which faults cause them.\n */\nexport const TLS_HANDSHAKE_VALUES: Readonly<Partial<Record<HandshakeOutcome, string>>> = {\n ok: \"ok\",\n \"cert-error\": \"cert-error\",\n alert: \"alert\",\n timeout: \"timeout\",\n reset: \"reset\",\n};\n\n/** The value of the feature `tls.trust` for each trust. A chain that ends at no root of the store has no public root. */\nexport const TLS_TRUST_VALUES: Readonly<Record<Trust, string>> = { public: \"public\", private: \"private\", none: \"private\" };\n\n/** The value of the feature `icmp.reply` for each outcome of the ICMP echo requests to an address. */\nexport const ICMP_REPLY_VALUES: Readonly<Partial<Record<EchoOutcome, string>>> = { reply: \"reply\", none: \"none\", unreachable: \"unreachable\" };\n\n/**\n * The value of the feature `gateway.reply` for each outcome of the ICMP echo requests to the gateway. When this computer\n * tells that the gateway is unreachable, the gateway gives no reply.\n */\nexport const GATEWAY_REPLY_VALUES: Readonly<Partial<Record<EchoOutcome, string>>> = { reply: \"reply\", none: \"none\", unreachable: \"none\" };\n\n/**\n * The bins of the feature `path.loss`, on the share of the echo requests with a reply. A share of 1 has no loss. A share\n * of 0.95 or more has a loss of up to 5 %.\n */\nexport const PATH_LOSS_BINS: readonly Bin[] = [{ below: 0.95, value: \"high\" }, { below: 1, value: \"some\" }, { value: \"none\" }];\n\n/**\n * The smallest number of echo requests that gives the feature `path.loss`. With 20 requests, one lost request is a loss\n * of 5 %. With no reply, the feature `icmp.reply` tells the result, not `path.loss`.\n */\nexport const LOSS_MIN_SENT = 20;\n\n/** The suffixes of the names of local networks and of the special-use names (RFC 6761, RFC 6762 and RFC 8375). */\nexport const LOCAL_SUFFIXES: readonly string[] = [\"local\", \"localhost\", \"test\", \"invalid\", \"example\", \"home.arpa\", \"internal\", \"lan\", \"home\", \"corp\", \"intranet\", \"private\"];\n\n/** This function gives the value of the first bin that holds `x`. */\nexport function binOf(bins: readonly Bin[], x: number): string {\n for (const bin of bins) if (bin.below === undefined || x < bin.below) return bin.value;\n throw new RangeError(\"The last bin has a limit.\");\n}\n\n/** This function tells if a name is a local name: a name with one label, or a name with a local suffix. */\nexport function isLocalName(name: string): boolean {\n const lower = name.toLowerCase().replace(/\\.$/, \"\");\n if (!lower.includes(\".\")) return true;\n return LOCAL_SUFFIXES.some((suffix) => lower === suffix || lower.endsWith(`.${suffix}`));\n}\n\ninterface Slot {\n readonly feature: string;\n readonly primary: boolean;\n value: string;\n readonly facts: string[];\n}\n\ninterface Facts {\n readonly dns: Fact<DnsAnswer>[];\n readonly tcp: Fact<TcpConnect>[];\n readonly tls: Fact<TlsHandshake>[];\n readonly http: Fact<HttpResponse>[];\n readonly icmp: Fact<IcmpEcho>[];\n readonly report: Fact<PathReport>[];\n readonly trace: Fact<PathTrace>[];\n}\n\nconst READ_KINDS: readonly [keyof Facts, FactKind<unknown>][] = [\n [\"dns\", DNS_ANSWER],\n [\"tcp\", TCP_CONNECT],\n [\"tls\", TLS_HANDSHAKE],\n [\"http\", HTTP_RESPONSE],\n [\"icmp\", ICMP_ECHO],\n [\"report\", PATH_REPORT],\n [\"trace\", PATH_TRACE],\n];\n\n/**\n * This function changes facts into evidence for the model.\n *\n * - The reader gives one evidence for each feature, subject and role. When a subject has more facts, the last fact decides.\n * - For `dns.answer`, one answer `ok` of a name and a resolver decides. For `dns.address`, one private address decides.\n * - The order of the facts is the order of the runner. A ratio uses the last TCP connect before the handshake or the request.\n * - An ICMP echo fact gives `gateway.reply` for the gateway, and `icmp.reply` for each other address.\n * - An ICMP echo fact with replies to `LOSS_MIN_SENT` requests or more gives `path.loss`. A report of `mtr` gives it from\n * the last hop, when the address answered there.\n * - A trace of `traceroute` gives no evidence. The catalog has no feature for the hops of a path.\n */\nexport function readEvidence(facts: readonly Fact[], options: ReadOptions = {}): Reading {\n const primaryOf = options.primary ?? (() => true);\n const slots = new Map<string, Slot>();\n const skipped: Skipped[] = [];\n const sorted: Facts = { dns: [], tcp: [], tls: [], http: [], icmp: [], report: [], trace: [] };\n\n for (const fact of facts) {\n const entry = READ_KINDS.find(([, kind]) => kind.kind === fact.kind);\n if (entry === undefined) skipped.push({ fact: fact.id, reason: `netdiag has no reader for the kind ${fact.kind}.` });\n else if (!entry[1].check(fact.value)) skipped.push({ fact: fact.id, reason: `The value does not pass the check of the kind ${fact.kind}.` });\n else (sorted[entry[0]] as Fact[]).push(fact);\n }\n\n /** This function puts a value in the slot of a feature and a group. A `sticky` value stays when a later fact disagrees. */\n const note = (feature: string, group: string, fact: Fact, value: string, sticky?: string): void => {\n const primary = primaryOf(fact);\n const key = `${feature}\\u0000${primary ? 1 : 0}\\u0000${group}`;\n const slot = slots.get(key);\n if (slot === undefined) slots.set(key, { feature, primary, value, facts: [fact.id] });\n else {\n if (slot.value !== sticky) slot.value = value;\n slot.facts.push(fact.id);\n }\n };\n const skip = (fact: Fact, feature: string, outcome: string): void => {\n skipped.push({ fact: fact.id, reason: `The outcome \"${outcome}\" of ${fact.kind} has no value of the feature ${feature}.` });\n };\n\n for (const fact of sorted.dns) {\n const answer = fact.value;\n const group = `${answer.name.toLowerCase()}\\u0000${answer.resolver}`;\n const value = DNS_ANSWER_VALUES[answer.outcome];\n if (value === undefined) {\n skip(fact, \"dns.answer\", answer.outcome);\n continue;\n }\n note(\"dns.answer\", group, fact, value, \"ok\");\n if (answer.addresses.length > 0 && !isLocalName(answer.name)) {\n const kinds = answer.addresses.map(addressKind);\n note(\"dns.address\", group, fact, kinds.includes(\"private\") ? \"private\" : \"public\", \"private\");\n }\n }\n\n readFamily(sorted, primaryOf, note);\n\n const subject = (value: { readonly address: string; readonly port: number }): string => `${value.address}\\u0000${value.port}`;\n for (const fact of sorted.tcp) {\n const connect = fact.value;\n const value = TCP_CONNECT_VALUES[connect.outcome];\n if (value === undefined) {\n skip(fact, \"tcp.connect\", connect.outcome);\n continue;\n }\n note(\"tcp.connect\", subject(connect), fact, value);\n if (connect.outcome === \"connected\") note(\"tcp.time\", subject(connect), fact, binOf(TCP_TIME_BINS, connect.ms));\n }\n\n const connectBefore = (fact: Fact, value: { readonly address: string; readonly port: number }): Fact<TcpConnect> | undefined => {\n let best: Fact<TcpConnect> | undefined;\n for (const tcp of sorted.tcp) {\n const same = tcp.value.address === value.address && tcp.value.port === value.port && primaryOf(tcp) === primaryOf(fact);\n if (!same || tcp.value.outcome !== \"connected\" || tcp.time.end > fact.time.start) continue;\n if (best === undefined || tcp.time.end >= best.time.end) best = tcp;\n }\n return best;\n };\n const ratio = (ms: number, tcp: Fact<TcpConnect>): number => ms / Math.max(tcp.value.ms, MIN_CONNECT_MS);\n\n for (const fact of sorted.tls) {\n const handshake = fact.value;\n const group = `${subject(handshake)}\\u0000${handshake.servername ?? \"\"}`;\n const value = TLS_HANDSHAKE_VALUES[handshake.outcome];\n if (value === undefined) {\n skip(fact, \"tls.handshake\", handshake.outcome);\n continue;\n }\n note(\"tls.handshake\", group, fact, value);\n if (handshake.certificate !== undefined) note(\"tls.trust\", group, fact, TLS_TRUST_VALUES[handshake.certificate.trust]);\n const tcp = handshake.outcome === \"ok\" || handshake.outcome === \"cert-error\" ? connectBefore(fact, handshake) : undefined;\n if (tcp !== undefined) note(\"tls.ratio\", group, fact, binOf(TLS_RATIO_BINS, ratio(handshake.ms, tcp)));\n }\n\n for (const fact of sorted.http) {\n const response = fact.value;\n const group = [subject(response), response.scheme, response.host, response.method, response.path].join(\"\\u0000\");\n const status = statusValue(response);\n if (status === undefined) {\n skip(fact, \"http.status\", response.outcome === \"response\" ? `response ${response.status ?? \"\"}`.trim() : response.outcome);\n continue;\n }\n note(\"http.status\", group, fact, status);\n const tcp = response.ttfbMs === undefined ? undefined : connectBefore(fact, response);\n if (tcp !== undefined && response.ttfbMs !== undefined) note(\"http.ttfb\", group, fact, binOf(HTTP_TTFB_BINS, ratio(response.ttfbMs, tcp)));\n }\n\n const gateway = options.gateway === undefined ? undefined : canonicalAddress(options.gateway);\n for (const fact of sorted.icmp) {\n const echo = fact.value;\n const address = canonicalAddress(echo.address);\n const isGateway = address === gateway;\n const feature = isGateway ? \"gateway.reply\" : \"icmp.reply\";\n const value = (isGateway ? GATEWAY_REPLY_VALUES : ICMP_REPLY_VALUES)[echo.outcome];\n if (value === undefined) {\n skip(fact, feature, echo.outcome);\n continue;\n }\n note(feature, address, fact, value);\n }\n\n // The feature path.loss comes from ping and from mtr. The facts of both kinds go in the order of the runner.\n const lossFacts = new Set<Fact>([...sorted.icmp, ...sorted.report]);\n for (const fact of facts) {\n if (!lossFacts.has(fact)) continue;\n const loss = lossOf(fact);\n if (typeof loss === \"string\") skipped.push({ fact: fact.id, reason: loss });\n else if (loss !== undefined) note(\"path.loss\", loss.address, fact, binOf(PATH_LOSS_BINS, loss.share));\n }\n\n for (const fact of sorted.trace) {\n skipped.push({ fact: fact.id, reason: \"The catalog has no feature for the hops of a path. The lab runs fit such a feature later.\" });\n }\n\n const evidence = [...slots.values()].map(({ feature, value, primary, facts: ids }) => ({ feature, value, primary, facts: ids }));\n return { evidence, skipped };\n}\n\n/**\n * This function gives the share of the probes with an answer, for the feature `path.loss`. It gives a reason when a report\n * of `mtr` gives no evidence, and `undefined` when an echo fact gives no share.\n */\nfunction lossOf(fact: Fact): { readonly address: string; readonly share: number } | string | undefined {\n if (isFactOf(ICMP_ECHO, fact)) {\n const echo = fact.value;\n return echo.received > 0 && echo.sent >= LOSS_MIN_SENT ? { address: canonicalAddress(echo.address), share: echo.received / echo.sent } : undefined;\n }\n if (!isFactOf(PATH_REPORT, fact)) return undefined;\n const report = fact.value;\n const last = report.hops.at(-1);\n if (report.outcome !== \"reached\" || last === undefined) return `The outcome \"${report.outcome}\" of ${fact.kind} has no value of the feature path.loss.`;\n if (last.sent < LOSS_MIN_SENT) return `The last hop of the report has ${last.sent} probes. The feature path.loss needs ${LOSS_MIN_SENT} probes or more.`;\n return { address: canonicalAddress(report.address), share: last.received / last.sent };\n}\n\nfunction statusValue(response: HttpResponse): string | undefined {\n if (response.outcome === \"error\") return undefined;\n if (response.outcome !== \"response\") return \"none\";\n const status = response.status ?? 0;\n if (status >= 200 && status < 400) return \"ok\";\n if (status >= 400 && status < 500) return \"client-error\";\n if (status >= 500 && status < 600) return \"server-error\";\n return undefined;\n}\n\n/**\n * This function gives the feature `ip.family` for a name with IPv4 and IPv6 addresses. It needs a TCP connect to an\n * address of each family.\n */\nfunction readFamily(sorted: Facts, primaryOf: (fact: Fact) => boolean, note: (feature: string, group: string, fact: Fact, value: string) => void): void {\n const names = new Map<string, { v4: Set<string>; v6: Set<string>; facts: Fact[]; primary: boolean }>();\n for (const fact of sorted.dns) {\n if (fact.value.outcome !== \"ok\") continue;\n const key = `${primaryOf(fact) ? 1 : 0}\\u0000${fact.value.name.toLowerCase()}`;\n const entry = names.get(key) ?? { v4: new Set<string>(), v6: new Set<string>(), facts: [], primary: primaryOf(fact) };\n for (const address of fact.value.addresses) (fact.value.record === \"A\" ? entry.v4 : entry.v6).add(address);\n entry.facts.push(fact);\n names.set(key, entry);\n }\n for (const [key, entry] of names) {\n if (entry.v4.size === 0 || entry.v6.size === 0) continue;\n const tried = sorted.tcp.filter((tcp) => primaryOf(tcp) === entry.primary && (entry.v4.has(tcp.value.address) || entry.v6.has(tcp.value.address)));\n const works = (family: Set<string>): boolean | undefined => {\n const connects = tried.filter((tcp) => family.has(tcp.value.address));\n return connects.length === 0 ? undefined : connects.some((tcp) => tcp.value.outcome === \"connected\");\n };\n const v4 = works(entry.v4);\n const v6 = works(entry.v6);\n if (v4 === undefined || v6 === undefined || (!v4 && !v6)) continue;\n const value = v4 && v6 ? \"both\" : v4 ? \"ipv4-only\" : \"ipv6-only\";\n const evidenceFacts = [...entry.facts, ...tried];\n const [first, ...rest] = evidenceFacts;\n if (first === undefined) continue;\n note(\"ip.family\", key, first, value);\n for (const fact of rest) note(\"ip.family\", key, fact, value);\n }\n}\n"],"mappings":"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"}