mirror of
https://github.com/morluto/rea.git
synced 2026-10-31 20:06:14 +00:00
* fix(pcap): retain producer contradiction diagnostics * test(pcap): align DNS fixture with empty captures
182 lines
6.3 KiB
TypeScript
182 lines
6.3 KiB
TypeScript
import { chmod, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
export const CAPINFOS_OUTPUT = [
|
|
"File name: /work/capture.pcap",
|
|
"File type: Wireshark/tcpdump/... - pcap",
|
|
"File encapsulation: Ethernet",
|
|
"Packet size limit: file hdr: 262144 bytes",
|
|
"Packet size limit: inferred: 128 bytes - 256 bytes (range)",
|
|
"Number of packets: 3",
|
|
"Capture duration: 0.123 seconds",
|
|
"Earliest packet time: 1791728020.608505",
|
|
"Latest packet time: 1791728020.981978",
|
|
].join("\n");
|
|
|
|
/** Synthetic complete UDP A response; bytes own identity, tree owns display. */
|
|
export const dnsResponseLayers = (query: string, address: string) => {
|
|
const name = Buffer.concat([
|
|
...query
|
|
.split(".")
|
|
.map((label) =>
|
|
Buffer.concat([
|
|
Buffer.from([Buffer.byteLength(label)]),
|
|
Buffer.from(label),
|
|
]),
|
|
),
|
|
Buffer.from([0]),
|
|
]);
|
|
const bytes = Buffer.concat([
|
|
Buffer.from("000181800001000100000000", "hex"),
|
|
name,
|
|
Buffer.from("00010001c00c000100010000003c0004", "hex"),
|
|
Buffer.from(address.split(".").map(Number)),
|
|
]);
|
|
return {
|
|
dns_raw: [bytes.toString("hex"), 42, bytes.length, 0, 1],
|
|
dns: {
|
|
"dns.id_raw": ["0001", 42, 2, 0, 5],
|
|
Queries: { question: { "dns.qry.name": query } },
|
|
Answers: { answer: { "dns.a": address } },
|
|
},
|
|
};
|
|
};
|
|
|
|
export const TSHARK_JSON = JSON.stringify([
|
|
{
|
|
_source: {
|
|
layers: {
|
|
"frame.number": ["1"],
|
|
"frame.protocols": ["eth:ethertype:ip:udp:dns"],
|
|
"ip.src": ["192.168.1.100"],
|
|
"ip.dst": ["8.8.8.8"],
|
|
"dns.qry.name": ["api.example.com"],
|
|
"dns.a": ["93.184.216.34"],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
_source: {
|
|
layers: {
|
|
"frame.number": ["2"],
|
|
"frame.protocols": ["eth:ethertype:ip:tcp:tls"],
|
|
"ip.src": ["192.168.1.100"],
|
|
"ip.dst": ["93.184.216.34"],
|
|
"tls.handshake.extensions_server_name": ["api.example.com"],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
_source: {
|
|
layers: {
|
|
"frame.number": ["3"],
|
|
"frame.protocols": ["eth:ethertype:ip:tcp:http"],
|
|
"ip.src": ["192.168.1.100"],
|
|
"ip.dst": ["93.184.216.34"],
|
|
"http.request.full_uri": ["http://api.example.com/v1/data"],
|
|
},
|
|
},
|
|
},
|
|
]);
|
|
|
|
export const TSHARK_DNS_JSON = JSON.stringify([
|
|
{
|
|
_source: {
|
|
layers: {
|
|
frame: { "frame.number": "1" },
|
|
...dnsResponseLayers("api.example.com", "93.184.216.34"),
|
|
},
|
|
},
|
|
},
|
|
]);
|
|
|
|
export interface PcapStubToolsOptions {
|
|
readonly failCapinfos?: boolean;
|
|
readonly malformedDnsJson?: boolean;
|
|
readonly dnsJson?: string;
|
|
readonly tsharkJson?: string;
|
|
readonly capinfosText?: string;
|
|
readonly httpAbsoluteUriOnly?: boolean;
|
|
readonly http2Uri?: boolean;
|
|
readonly invalidTsharkStream?: "stdout" | "stderr";
|
|
readonly invalidTsharkVersionStream?: "stdout" | "stderr";
|
|
readonly invalidCapinfosStream?: "stdout" | "stderr";
|
|
readonly invalidCapinfosVersionStream?: "stdout" | "stderr";
|
|
readonly literalReplacement?: boolean;
|
|
readonly literalTsharkVersion?: boolean;
|
|
}
|
|
|
|
/** Write caller-selected POSIX tool stubs for pcap provider and adapter tests. */
|
|
export const writeStubTools = async (
|
|
root: string,
|
|
options: PcapStubToolsOptions = {},
|
|
): Promise<{ tshark: string; capinfos: string }> => {
|
|
const tshark = join(root, "tshark");
|
|
const capinfos = join(root, "capinfos");
|
|
const tsharkOutput = options.http2Uri
|
|
? TSHARK_JSON.replace(
|
|
'"http.request.full_uri":["http://api.example.com/v1/data"]',
|
|
'"http2.request.full_uri":["http://example.test/h2"],"http2.headers.path":["/h2"]',
|
|
).replace('eth:ethertype:ip:tcp:http"', 'eth:ethertype:ip:tcp:http2"')
|
|
: options.literalReplacement
|
|
? TSHARK_JSON.replace(
|
|
"http://api.example.com/v1/data",
|
|
"http://api.example.com/�",
|
|
)
|
|
: options.httpAbsoluteUriOnly
|
|
? TSHARK_JSON.replace(
|
|
'"http.request.full_uri":["http://api.example.com/v1/data"]',
|
|
'"http.request.uri":["http://proxy.example.test/path"]',
|
|
)
|
|
: TSHARK_JSON;
|
|
await writeFile(
|
|
tshark,
|
|
`#!/bin/sh
|
|
if [ "$1" = "--version" ]; then
|
|
printf 'TShark (Wireshark) 4.6.6${options.literalTsharkVersion ? "�" : ""}-stub\\n'
|
|
${options.invalidTsharkVersionStream === "stdout" ? `printf '\\377'` : options.invalidTsharkVersionStream === "stderr" ? `printf '\\377' >&2` : ""}
|
|
exit 0
|
|
fi
|
|
[ "$1" = "-n" ] && [ "$2" = "-r" ] && [ "$4" = "-T" ] && [ "$5" = "json" ] || exit 2
|
|
case " $* " in
|
|
*" -x "*)
|
|
case " $* " in *" --no-duplicate-keys "*) ;; *) exit 2 ;; esac
|
|
case " $* " in *" -J frame dns mdns llmnr "*) ;; *) exit 2 ;; esac
|
|
case " $* " in *" -Y dns || mdns || llmnr "*) ;; *) exit 2 ;; esac
|
|
printf '%s' '${(options.malformedDnsJson ? "{" : (options.dnsJson ?? TSHARK_DNS_JSON)).replace(/'/gu, "'\\''")}'
|
|
exit 0
|
|
;;
|
|
*)
|
|
case " $* " in *" -e http.request.uri "*) ;; *) exit 2 ;; esac
|
|
case " $* " in *" -e http2.request.full_uri "*) ;; *) exit 2 ;; esac
|
|
case " $* " in *" -e http2.headers.path "*) ;; *) exit 2 ;; esac
|
|
;;
|
|
esac
|
|
${options.invalidTsharkStream === "stdout" ? `printf '%s' '[{"_source":{"layers":{"frame.number":["1"],"frame.protocols":["eth:ip:tcp:http"],"http.request.full_uri":["http://example.test/'; printf '\\377'; printf '%s' '"]}}}]'` : `printf '%s' '${(options.tsharkJson ?? tsharkOutput).replace(/'/gu, "'\\''")}'`}
|
|
${options.invalidTsharkStream === "stderr" ? `printf '\\377' >&2` : ""}
|
|
exit 0
|
|
`,
|
|
{ mode: 0o755 },
|
|
);
|
|
await writeFile(
|
|
capinfos,
|
|
`#!/bin/sh
|
|
if [ "$1" = "--version" ]; then
|
|
printf 'Capinfos (Wireshark) 4.6.6-stub\\n'
|
|
${options.invalidCapinfosVersionStream === "stdout" ? `printf '\\377'` : options.invalidCapinfosVersionStream === "stderr" ? `printf '\\377' >&2` : ""}
|
|
exit 0
|
|
fi
|
|
[ "$1" = "-M" ] && [ "$2" = "-S" ] && [ "$3" = "--" ] && [ "$#" = "4" ] || exit 2
|
|
if [ "${String(options.failCapinfos === true)}" = "true" ]; then
|
|
printf 'capinfos: The file is not a capture file' >&2; exit 1
|
|
fi
|
|
${options.invalidCapinfosStream === "stdout" ? `printf '\\377'` : options.invalidCapinfosStream === "stderr" ? `printf '\\377' >&2` : `printf '%s\\n' '${(options.capinfosText ?? CAPINFOS_OUTPUT).replace(/'/gu, "'\\''")}'`}
|
|
exit 0
|
|
`,
|
|
{ mode: 0o755 },
|
|
);
|
|
await chmod(tshark, 0o755);
|
|
await chmod(capinfos, 0o755);
|
|
return { tshark, capinfos };
|
|
};
|