From cf1b4b944f48ddbfb864a7a2fa2b5320a4dd9eaa Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Sun, 9 Aug 2026 19:36:10 +0100 Subject: [PATCH 1/2] fix(enr): handle short port values --- packages/discv5/test/unit/util/ip.test.ts | 21 +++++++++++++++ packages/enr/src/enr.ts | 9 +++---- packages/enr/test/unit/enr.test.ts | 32 +++++++++++++++++++++++ 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/packages/discv5/test/unit/util/ip.test.ts b/packages/discv5/test/unit/util/ip.test.ts index 126bc74..3c1b8cc 100644 --- a/packages/discv5/test/unit/util/ip.test.ts +++ b/packages/discv5/test/unit/util/ip.test.ts @@ -145,6 +145,27 @@ describe("get/set SocketAddress on ENR", () => { expect(getSocketAddressOnENR(enr, {ip4: true, ip6: false})).to.deep.equal(addr); }); + it("accepts a one-byte UDP port from a remote ENR", () => { + const enr = SignableENR.createV4(generateKeypair("secp256k1").privateKey); + enr.set("ip", Uint8Array.from([127, 0, 0, 1])); + enr.set("udp", Uint8Array.from([53])); + + expect(getSocketAddressOnENR(enr, {ip4: true, ip6: false})).to.deep.equal({ + ip: {octets: Uint8Array.from([127, 0, 0, 1]), type: 4}, + port: 53, + }); + }); + + it("ignores an invalid UDP port from a remote ENR", () => { + const enr = SignableENR.createV4(generateKeypair("secp256k1").privateKey); + enr.set("ip", Uint8Array.from([127, 0, 0, 1])); + + for (const port of [new Uint8Array(), Uint8Array.from([0]), Uint8Array.from([1, 2, 3])]) { + enr.set("udp", port); + expect(getSocketAddressOnENR(enr, {ip4: true, ip6: false})).to.be.undefined; + } + }); + it("returns the requested family from the ENR", () => { const addr4: SocketAddress = { ip: { diff --git a/packages/enr/src/enr.ts b/packages/enr/src/enr.ts index ddc1abb..7f56bcc 100644 --- a/packages/enr/src/enr.ts +++ b/packages/enr/src/enr.ts @@ -181,11 +181,8 @@ export function getIPValue( } export function getProtocolValue(kvs: ReadonlyMap, key: string): number | undefined { - const raw = kvs.get(key); + const raw = normalizePortBytes(kvs.get(key)); if (raw) { - if (raw.length < 2) { - throw new Error("Encoded protocol length should be 2"); - } return (raw[0] << 8) + raw[1]; } return undefined; @@ -193,8 +190,8 @@ export function getProtocolValue(kvs: ReadonlyMap, key: string function normalizePortBytes(raw: Uint8Array | undefined): Uint8Array | undefined { if (!raw || raw.length === 0 || raw.length > 2) return undefined; - if (raw[0] === 0) return undefined; - if (raw.length === 1) return new Uint8Array([0, raw[0]]); + if (raw.length === 1) return raw[0] === 0 ? undefined : new Uint8Array([0, raw[0]]); + if (raw[0] === 0 && raw[1] === 0) return undefined; return raw; } diff --git a/packages/enr/test/unit/enr.test.ts b/packages/enr/test/unit/enr.test.ts index c822ac1..94ded0f 100644 --- a/packages/enr/test/unit/enr.test.ts +++ b/packages/enr/test/unit/enr.test.ts @@ -70,6 +70,38 @@ describe("ENR multiaddr support", () => { record = SignableENR.createV4(privateKey); }); + describe("port values", () => { + beforeEach(() => { + record.ip = "127.0.0.1"; + }); + + it("should decode a one-byte port", () => { + record.set("udp", new Uint8Array([80])); + + expect(record.udp).to.equal(80); + expect(record.getLocationMultiaddr("udp")?.toString()).to.equal("/ip4/127.0.0.1/udp/80"); + }); + + it("should decode a two-byte low port", () => { + record.udp = 80; + + expect(record.kvs.get("udp")).to.deep.equal(new Uint8Array([0, 80])); + expect(record.udp).to.equal(80); + expect(record.getLocationMultiaddr("udp")?.toString()).to.equal("/ip4/127.0.0.1/udp/80"); + }); + + it("should ignore invalid port values", () => { + const invalidPorts = [new Uint8Array(), new Uint8Array([0]), new Uint8Array([0, 0]), new Uint8Array([1, 2, 3])]; + + for (const port of invalidPorts) { + record.set("udp", port); + + expect(record.udp).to.be.undefined; + expect(record.getLocationMultiaddr("udp")).to.be.undefined; + } + }); + }); + it("should get / set UDP multiaddr", () => { const multi0 = multiaddr("/ip4/127.0.0.1/udp/30303"); const components0 = multi0.getComponents(); From 4892067ec502938eba4eb61ed5f3159e3d2028b7 Mon Sep 17 00:00:00 2001 From: Nico Flaig Date: Tue, 11 Aug 2026 07:20:37 +0100 Subject: [PATCH 2/2] refactor(enr): parse port values directly --- packages/enr/src/enr.ts | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/enr/src/enr.ts b/packages/enr/src/enr.ts index 7f56bcc..23c4734 100644 --- a/packages/enr/src/enr.ts +++ b/packages/enr/src/enr.ts @@ -181,18 +181,10 @@ export function getIPValue( } export function getProtocolValue(kvs: ReadonlyMap, key: string): number | undefined { - const raw = normalizePortBytes(kvs.get(key)); - if (raw) { - return (raw[0] << 8) + raw[1]; - } - return undefined; -} - -function normalizePortBytes(raw: Uint8Array | undefined): Uint8Array | undefined { + const raw = kvs.get(key); if (!raw || raw.length === 0 || raw.length > 2) return undefined; - if (raw.length === 1) return raw[0] === 0 ? undefined : new Uint8Array([0, raw[0]]); - if (raw[0] === 0 && raw[1] === 0) return undefined; - return raw; + const port = raw.length === 1 ? raw[0] : (raw[0] << 8) + raw[1]; + return port === 0 ? undefined : port; } export function portToBuf(port: number): Uint8Array { @@ -341,38 +333,38 @@ export abstract class BaseENR { }; if (isUdp) { - const protoVal = normalizePortBytes(isIpv6 ? this.kvs.get("udp6") : this.kvs.get("udp")); - if (!protoVal) { + const port = getProtocolValue(this.kvs, isIpv6 ? "udp6" : "udp"); + if (port === undefined) { return undefined; } const protoComponent: Component = { code: udp.code, name: udp.name, - value: udp.bytesToValue?.(toNewUint8Array(protoVal)), + value: port.toString(), }; return multiaddr([ipComponent, protoComponent]); } if (isTcp) { - const protoVal = normalizePortBytes(isIpv6 ? this.kvs.get("tcp6") : this.kvs.get("tcp")); - if (!protoVal) { + const port = getProtocolValue(this.kvs, isIpv6 ? "tcp6" : "tcp"); + if (port === undefined) { return undefined; } const protoComponent: Component = { code: tcp.code, name: tcp.name, - value: tcp.bytesToValue?.(toNewUint8Array(protoVal)), + value: port.toString(), }; return multiaddr([ipComponent, protoComponent]); } if (isQuic) { - const protoVal = normalizePortBytes(isIpv6 ? this.kvs.get("quic6") : this.kvs.get("quic")); - if (!protoVal) { + const port = getProtocolValue(this.kvs, isIpv6 ? "quic6" : "quic"); + if (port === undefined) { return undefined; } const protoComponent: Component = { code: udp.code, name: udp.name, - value: udp.bytesToValue?.(toNewUint8Array(protoVal)), + value: port.toString(), }; return multiaddr([ipComponent, protoComponent]).encapsulate("/quic-v1"); }