From f5b38f9c48dfd16e7bc45aa611b9929bdd3f316a Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Thu, 22 May 2025 19:08:48 +0800 Subject: [PATCH] fix: check subnet for dhcp discovery (#12) * fix: check subnet for dhcp discovery * Add changeset --------- Co-authored-by: Phil Pluckthun --- .changeset/shaggy-walls-argue.md | 5 +++++ src/__tests__/network.test.ts | 14 ++++++++++++++ src/dhcp.ts | 8 +++++++- src/network.ts | 11 +++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 .changeset/shaggy-walls-argue.md diff --git a/.changeset/shaggy-walls-argue.md b/.changeset/shaggy-walls-argue.md new file mode 100644 index 0000000..f312409 --- /dev/null +++ b/.changeset/shaggy-walls-argue.md @@ -0,0 +1,5 @@ +--- +'lan-network': patch +--- + +Compare subnet-masked addresses before accepting DHCP discover message diff --git a/src/__tests__/network.test.ts b/src/__tests__/network.test.ts index 51a77f0..3499eda 100644 --- a/src/__tests__/network.test.ts +++ b/src/__tests__/network.test.ts @@ -6,6 +6,7 @@ import { toIpStr, interfaceAssignments, matchAssignment, + isSameSubnet, } from '../network'; describe(parseMacStr, () => { @@ -32,6 +33,19 @@ describe(toIpStr, () => { ); }); +describe(isSameSubnet, () => { + it('returns true for same subnet', () => { + expect(isSameSubnet('192.168.1.1', '192.168.1.2', '255.255.255.0')).toBe( + true + ); + }); + it('returns false for different subnet', () => { + expect(isSameSubnet('192.168.1.1', '192.168.2.1', '255.255.255.0')).toBe( + false + ); + }); +}); + describe(interfaceAssignments, () => { const networkInterfaces = vi .spyOn(os, 'networkInterfaces') diff --git a/src/dhcp.ts b/src/dhcp.ts index d4e6462..1ef2b7e 100644 --- a/src/dhcp.ts +++ b/src/dhcp.ts @@ -1,6 +1,6 @@ import { randomBytes } from 'node:crypto'; import { createSocket } from 'node:dgram'; -import { parseIpStr, toIpStr, parseMacStr } from './network'; +import { parseIpStr, toIpStr, parseMacStr, isSameSubnet } from './network'; import type { NetworkAssignment } from './types'; class DHCPTimeoutError extends TypeError { @@ -57,6 +57,12 @@ export const dhcpDiscover = ( const socket = createSocket( { type: 'udp4', reuseAddr: true }, (_msg, rinfo) => { + if ( + !isSameSubnet(rinfo.address, assignment.address, assignment.netmask) + ) { + return; + } + clearTimeout(timeout); resolve(rinfo.address); socket.close(); diff --git a/src/network.ts b/src/network.ts index fe39c6c..6c469ab 100644 --- a/src/network.ts +++ b/src/network.ts @@ -26,6 +26,17 @@ export const parseIpStr = (ipStr: string): number => { return addr[3] | (addr[2] << 8) | (addr[1] << 16) | (addr[0] << 24); }; +export const isSameSubnet = ( + addrA: string, + addrB: string, + netmask: string +): boolean => { + const rawAddrA = parseIpStr(addrA); + const rawAddrB = parseIpStr(addrB); + const rawMask = parseIpStr(netmask); + return (rawAddrA & rawMask) === (rawAddrB & rawMask); +}; + export const toIpStr = (addr: number): string => { const MASK = (1 << 8) - 1; let ipStr = ''; -- 2.51.2