diff --git a/lib/portscanner.js b/lib/portscanner.js index 8784a93..5b7d2b5 100644 --- a/lib/portscanner.js +++ b/lib/portscanner.js @@ -129,7 +129,28 @@ function checkPortStatus (port) { // Return after the socket has closed socket.on('close', function (exception) { if (exception && !connectionRefused) { error = error || exception } else { error = null } - callback(error, status) + + // For windows localhost, we should check if the port is in excluded range by actually binding to it + if ((host === '127.0.0.1' || host === 'localhost') && process.platform === 'win32' && status === 'closed') { + var server = net.createServer() + server.on('error', function (e) { + if (e.code === 'EACCES') { + status = 'reserved' + error = null + } + server.close() + }) + // on close, we should call the callback + server.on('close', function () { + callback(error, status) + }) + server.listen(port, host, function () { + server.close() + }) + + } else { + callback(error, status) + } }) socket.connect(port, host) @@ -138,7 +159,7 @@ function checkPortStatus (port) { * Callback for {@link checkPortStatus} * @callback checkPortCallback * @param {Error|null} error - Any error that occurred while port scanning, or null. - * @param {String} status - Status: 'open' if the port is in use, 'closed' if the port is available. + * @param {String} status - Status: 'open' if the port is in use, 'closed' if the port is available, or 'reserved' if the port is reserved (Windows only). */ /** diff --git a/test.js b/test.js index 4f47d84..146ae41 100644 --- a/test.js +++ b/test.js @@ -1,4 +1,5 @@ import net from 'net' +import { execSync } from 'child_process' import test from 'ava' import portScanner from '.' @@ -9,6 +10,7 @@ findPortNotInUse() promise() reverseOrder() portsAsStrings() +findExcludedPorts() function initialize () { test.before.cb('Set #1 test server', t => { @@ -487,3 +489,37 @@ function portsAsStrings () { }) }) } + +function findExcludedPorts () { + if (process.platform !== 'win32') return + const output = execSync( + 'netsh interface ipv4 show excludedportrange protocol=tcp' + ).toString() + const ports = output + .split('\n') + .filter((line) => line.match(/^ +[0-9]+ +[0-9]+/)) + .map((line) => line.match(/ +([0-9]+) +([0-9]+)/).slice(1, 3)) + console.log(`ports:`, ports) + const portsToCheck = [] + for (const [startPort, endPort] of ports) { + for ( + let i = Number(startPort); + i <= Math.min(Number(startPort) + 2, endPort); + i++ + ) { + portsToCheck.push(i) + } + } + if (!portsToCheck.length) return + console.log(`portsToCheck:`, portsToCheck) + test.cb('Test reserved ports on Windows', (t) => { + t.plan(portsToCheck.length) + for (const port of portsToCheck) { + console.log(`testing:`, port) + portScanner.checkPortStatus(port, function (error, status) { + console.log(port, { error, status }) + t.is(status, 'reserved') + }) + } + }) +}