diff --git a/README.md b/README.md index 84bf119..18f1a06 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,7 @@ The portscanner module is an asynchronous JavaScript port scanner for Node.js. -Portscanner can check a port, -or range of ports, -for 'open' or 'closed' statuses. +Portscanner can check a port, range of ports, or array of ports for 'open' or 'closed' statuses. [Looking for maintainer](https://github.com/baalexander/node-portscanner/issues/25)! @@ -39,15 +37,28 @@ portscanner.findAPortNotInUse(3000, 3010, '127.0.0.1', function(error, port) { portscanner.findAPortInUse(3000, 3010, '127.0.0.1', function(error, port) { console.log('PORT IN USE AT: ' + port) }) + +// You can also pass array of ports to check +portscanner.findAPortInUse([3000, 3005, 3006], '127.0.0.1', function(error, port) { + console.log('PORT IN USE AT: ' + port) +}) + +// And skip host param. Default is '127.0.0.1' +portscanner.findAPortInUse([3000, 3005, 3006], function(error, port) { + console.log('PORT IN USE AT: ' + port) +}) + +// You can do this on every method +portscanner.findAPortNotInUse(3000, 4000, function(error, port) { + console.log('PORT IN USE AT: ' + port) +}) ``` The example directory contains a more detailed example. ## Test -There are currently no tests. -If you have ideas, -please open an issue. +```$ npm test``` ## Future diff --git a/lib/portscanner.js b/lib/portscanner.js index a27ba84..dfa7f5b 100644 --- a/lib/portscanner.js +++ b/lib/portscanner.js @@ -8,59 +8,83 @@ var portscanner = exports * Finds the first port with a status of 'open', implying the port is in use and * there is likely a service listening on it. * - * @param {Number} startPort - Port to begin status check on (inclusive). - * @param {Number} endPort - Last port to check status on (inclusive). - * Defaults to 65535. - * @param {String} host - Where to scan. Defaults to '127.0.0.1'. - * @param {Function} callback - function (error, port) { ... } + * portsArray - Array of ports to check + * startPort - Port to begin status check on (inclusive). + * endPort - Last port to check status on (inclusive). + * host - Where to scan. Defaults to '127.0.0.1'. + * callback - function (error, port) { ... } * - {Object|null} error - Any errors that occurred while port scanning. * - {Number|Boolean} port - The first open port found. Note, this is the - * first port that returns status as 'open', not - * necessarily the first open port checked. If no - * open port is found, the value is false. + * first port that returns status as 'open', not + * necessarily the first open port checked. If no + * open port is found, the value is false. + * + * + * @param {Array |Number} params portsArray or startPort + * @param {String |Number} params host or endPort + * @param {Function|String} params callback or host + * @param {* |Function} params empty or callback + * + * @examples + * portscanner.findAPortInUse([3000, 3001, 3002], '127.0.0.1', callback) + * portscanner.findAPortInUse(3000, 3002, '127.0.0.1', callback) */ -portscanner.findAPortInUse = function(startPort, endPort, host, callback) { - findAPortWithStatus('open', startPort, endPort, host, callback) -} +portscanner.findAPortInUse = function(params) { + findAPortWithStatus('open', arguments) +}; /** * Finds the first port with a status of 'closed', implying the port is not in * use. * - * @param {Number} startPort - Port to begin status check on (inclusive). - * @param {Number} endPort - Last port to check status on (inclusive). - * Defaults to 65535. - * @param {String} host - Where to scan. Defaults to '127.0.0.1'. - * @param {Function} callback - function (error, port) { ... } + * portsArray - Array of ports to check + * startPort - Port to begin status check on (inclusive). + * endPort - Last port to check status on (inclusive). + * host - Where to scan. Defaults to '127.0.0.1'. + * callback - function (error, port) { ... } * - {Object|null} error - Any errors that occurred while port scanning. * - {Number|Boolean} port - The first closed port found. Note, this is the - * first port that returns status as 'closed', not - * necessarily the first closed port checked. If no - * closed port is found, the value is false. + * first port that returns status as 'closed', not + * necessarily the first closed port checked. If no + * closed port is found, the value is false. + * + * @param {Array |Number} params portsArray or startPort + * @param {String |Number} params host or endPort + * @param {Function|String} params callback or host + * @param {* |Function} params empty or callback + * + * @examples + * portscanner.findAPortNotInUse([3000, 3001, 3002], '127.0.0.1', callback) + * portscanner.findAPortNotInUse(3000, 3002, '127.0.0.1', callback) */ -portscanner.findAPortNotInUse = function(startPort, endPort, host, callback) { - findAPortWithStatus('closed', startPort, endPort, host, callback) +portscanner.findAPortNotInUse = function(params) { + findAPortWithStatus('closed', arguments) } /** * Checks the status of an individual port. * * @param {Number} port - Port to check status on. - * @param {String|Object} options - host or options + * @param {String|Object|Function} arg2 - host or options or function * - {String} host - Host of where to scan. Defaults to '127.0.0.1'. * - {Object} options * - {String} host - Host of where to scan. Defaults to '127.0.0.1'. * - {Number} timeout - Connection timeout. Defaults to 400ms. - * @param {Function} callback - function (error, port) { ... } + * @param {Function} arg3 - function (error, port) { ... } * - {Object|null} error - Any errors that occurred while port scanning. * - {String} status - 'open' if the port is in use. - * 'closed' if the port is available. + * 'closed' if the port is available. */ -portscanner.checkPortStatus = function(port, options, callback) { - if (typeof options === 'string') { +portscanner.checkPortStatus = function(port, arg2, arg3) { + var options = {} + , callback + + if (typeof arg2 === 'string') { // Assume this param is the host option - options = {host: options} - } + options = {host: arg2} + } + + callback = Array.prototype.slice.call(arguments).slice(-1)[0]; var host = options.host || '127.0.0.1' var timeout = options.timeout || 400 @@ -107,20 +131,56 @@ portscanner.checkPortStatus = function(port, options, callback) { socket.connect(port, host) } -function findAPortWithStatus(status, startPort, endPort, host, callback) { +/** + * + * @param {Number} from + * @param {Number} to + * @return {Array} Array of integers from @from to @to inclusive + */ +function range(from, to) { + const arr = []; + + while(from <= to) { + arr.push(~~from); + from += 1; + } + + return arr; +} + +function findAPortWithStatus(status, params) { + var host; + var callback; + + //use array of ports + if (typeof params[0] === 'object') { + var ports = params[0]; + host = typeof params[1] === 'string' ? params[1] : null; + + //use startPort and endPort + } else if (typeof params[0] === 'number') { + var startPort = params[0]; + var endPort = params[1]; + host = typeof params[2] === 'string' ? params[2] : null; + } + + //callback always at the end + callback = Array.prototype.slice.call(params).slice(-1)[0]; + endPort = endPort || 65535 var foundPort = false var numberOfPortsChecked = 0 - var port = startPort + var port = ports ? ports[0] : startPort // Returns true if a port with matching status has been found or if checked // the entire range of ports var hasFoundPort = function() { - return foundPort || numberOfPortsChecked === (endPort - startPort + 1) + return foundPort || numberOfPortsChecked === (ports ? ports.length : endPort - startPort + 1) } // Checks the status of the port var checkNextPort = function(callback) { + portscanner.checkPortStatus(port, host, function(error, statusOfPort) { numberOfPortsChecked++ if (statusOfPort === status) { @@ -128,7 +188,7 @@ function findAPortWithStatus(status, startPort, endPort, host, callback) { callback(error) } else { - port++ + port = ports ? ports[numberOfPortsChecked] : port + 1; callback(null) } }) diff --git a/package.json b/package.json index 47e9c3b..2f01d10 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "portscanner", "description": "Asynchronous port scanner for Node.js", + "scripts": { + "test": "ava" + }, "keywords": [ "portscanner", "port", @@ -29,15 +32,12 @@ "dependencies": { "async": "0.1.15" }, - "devDependencies": {}, + "devDependencies": { + "ava": "^0.4.2" + }, "engines": { "node": ">=0.4", "npm": ">=1.0.0" }, - "licenses": [ - { - "type": "MIT", - "url": "https://github.com/baalexander/node-portscanner/raw/master/LICENSE" - } - ] + "license": "MIT" } diff --git a/test.js b/test.js new file mode 100644 index 0000000..2ac4688 --- /dev/null +++ b/test.js @@ -0,0 +1,171 @@ +import path from 'path'; +import fs from 'fs'; +import net from 'net'; + +import test from 'ava'; + +import portScanner from './lib/portscanner.js' + +test.before('Set #1 test server', t => { + const server = net.createServer(); + server.listen(3000, '127.0.0.1', () => t.end()); +}); + +test.before('Set #2 test server', t => { + const server2 = net.createServer(); + server2.listen(2999, '127.0.0.1', () => t.end()); +}); + + +/* checkPortStatus */ + +test('checkPortStatus - taken', t => { + t.plan(2); + + portScanner.checkPortStatus(3000, '127.0.0.1', (error, port) => { + t.is(error, null); + t.is(port, 'open'); + }); +}); + +test('checkPortStatus - free', t => { + t.plan(2); + + portScanner.checkPortStatus(80, 'asd', (error, port) => { + t.is(error, null); + t.is(port, 'closed'); + }); +}); + +test('checkPortStatus - default host', t => { + t.plan(2); + + portScanner.checkPortStatus(3001, (error, port) => { + t.is(error, null); + t.is(port, 'closed'); + }); +}); + + +/* findPortInUse */ + +test('findPortInUse - taken port in range', t => { + t.plan(2); + + portScanner.findAPortInUse(3000, 3010, '127.0.0.1', (error, port) => { + t.is(error, null); + t.is(port, 3000); + }); +}); + +test('findPortInUse - taken port in range - ports as array', t => { + t.plan(2); + + portScanner.findAPortInUse([2999, 3000, 3001], '127.0.0.1', (error, port) => { + t.is(error, null); + t.is(port, 2999); + }); +}); + +test('findPortInUse - all ports in range free', t => { + t.plan(2); + + portScanner.findAPortInUse(3001, 3010, '127.0.0.1', (error, port) => { + t.is(error, null); + t.false(port); + }); +}); + +//If endPort is smaller than startPort - ignore provided endPort, use default +//left as is for compatibility issues +test('findPortInUse - wrong range', t => { + t.plan(2); + portScanner.findAPortInUse(2999, 30, '127.0.0.1', (error, port) => { + t.is(error, null); + t.is(port, 2999); + }); +}); + +test('findPortInUse - all ports in range free - ports as array', t => { + t.plan(2); + + portScanner.findAPortInUse([3001, 3005, 3008], '127.0.0.1', (error, port) => { + t.is(error, null); + t.false(port); + }); +}); + +test('findPortInUse - all ports in range free - default host', t => { + t.plan(2); + + portScanner.findAPortInUse(3001, 3010, (error, port) => { + t.is(error, null); + t.false(port); + }); +}); + + +/* findPortNotInUse */ + +test('findAPortNotInUse - start from free port', t => { + t.plan(2); + + portScanner.findAPortNotInUse(3001, 3010, '127.0.0.1', (error, port) => { + t.is(error, null); + t.is(port, 3001); + }); +}); + +test('findAPortNotInUse - start from taken port', t => { + t.plan(2); + + portScanner.findAPortNotInUse(3000, 3010, '127.0.0.1', (error, port) => { + t.is(error, null); + t.is(port, 3001); + }); +}); + +test('findAPortNotInUse - all ports in range taken', t => { + t.plan(2); + + portScanner.findAPortNotInUse(2999, 3000, '127.0.0.1', (error, port) => { + t.is(error, null); + t.false(port); + }); +}); + +test('findAPortNotInUse - with array as parameter', t => { + t.plan(2); + + portScanner.findAPortNotInUse([3000, 3002, 2999], '127.0.0.1', (error, port) => { + t.is(error, null); + t.is(port, 3002); + }); +}); + +test('findAPortNotInUse - with array as parameter - default host', t => { + t.plan(2); + + portScanner.findAPortNotInUse([3000, 3002, 2999], (error, port) => { + t.is(error, null); + t.is(port, 3002); + }); +}); + +test('findAPortNotInUse - default host', t => { + t.plan(2); + + portScanner.findAPortNotInUse(3000, 4000, (error, port) => { + t.is(error, null); + t.is(port, 3001); + }); +}); + +test('findAPortNotInUse - default host', t => { + t.plan(2); + + portScanner.findAPortNotInUse(3000, 3002, (error, port) => { + t.is(error, null); + t.is(port, 3001); + }); +});