mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-10-26 08:26:22 -04:00
fix: decode Latin-1 WHOIS replies
Replies were always decoded as UTF-8, so registries that answer in Latin-1
(.br, .fi) lost every accented character to U+FFFD ("N�cleo de Informação").
Fall back to windows-1252 when the reply isn't valid UTF-8; a character cut off
by a read timeout doesn't count against UTF-8. Checked live: registro.br and
traficom.fi now decode cleanly.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2 files changed
+32
-1
No files matched your search
@@ -50,3 +50,21 @@ describe("query transformation for non-English WHOIS servers", () => {
|
||||
expect(capturedQuery).toBe("hairtect.jp/e");
|
||||
});
|
||||
});
|
||||
|
||||
describe("decodeWhoisReply", () => {
|
||||
it("decodes UTF-8, and Latin-1 replies without replacement characters", async () => {
|
||||
const { decodeWhoisReply } = await import("./client");
|
||||
expect(decodeWhoisReply(Buffer.from("owner: Núcleo de Informação", "utf8"))).toBe(
|
||||
"owner: Núcleo de Informação",
|
||||
);
|
||||
// .br and .fi send Latin-1
|
||||
expect(decodeWhoisReply(Buffer.from("owner: Núcleo de Informação", "latin1"))).toBe(
|
||||
"owner: Núcleo de Informação",
|
||||
);
|
||||
// UTF-8 cut off mid-character by a read timeout stays UTF-8
|
||||
const cut = Buffer.from("name: Liikenne- ja viestintävirasto", "utf8").subarray(0, -1);
|
||||
expect(decodeWhoisReply(Buffer.concat([cut, Buffer.from([0xc3])]))).toBe(
|
||||
"name: Liikenne- ja viestintävirast",
|
||||
);
|
||||
});
|
||||
});
|
||||
+14
-1
@@ -30,6 +30,19 @@ export interface WhoisTransportOptions {
|
||||
blockPrivateAddresses?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a WHOIS reply as UTF-8, or as Latin-1 (windows-1252, its WHATWG superset) when it isn't
|
||||
* valid UTF-8: some registries (.br, .fi) answer in Latin-1, which UTF-8 decoding turns into U+FFFD
|
||||
* for good. A character cut off at the end (a read timeout) doesn't count against UTF-8.
|
||||
*/
|
||||
export function decodeWhoisReply(bytes: Uint8Array): string {
|
||||
try {
|
||||
return new TextDecoder("utf-8", { fatal: true }).decode(bytes, { stream: true });
|
||||
} catch {
|
||||
return new TextDecoder("windows-1252").decode(bytes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a WHOIS query against an RFC 3912 server over TCP 43.
|
||||
* Returns the raw text and the server used.
|
||||
@@ -95,7 +108,7 @@ async function queryTcp(
|
||||
let done = false;
|
||||
let timer: ReturnType<typeof setTimeout> | undefined;
|
||||
|
||||
const text = () => Buffer.concat(chunks).toString("utf8");
|
||||
const text = () => decodeWhoisReply(Buffer.concat(chunks));
|
||||
const finish = (settle: () => void) => {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
Reference in new issue
Block a user