-
NodeJS Port Scanner
Some asshole in my new house has decided to mess with our router settings, and I was tryna figure out why I cannot access administrative page. I thought they might have changed listening port... I didn't have testing tools, so I just made a script to scan all ports on an ip address or hostname.
It works on domain names, ip addresses, private networks, anything you like. It functions by trying to open a TCP socket with an endpoint of your choice.
Usage:
npm start address={string} concurrency={number} timeout={number}
address parameter:
endpoint we're scanning. could be an IP address, hostname, or something of such nature.
concurrency parameter (OPTIONAL):
amount of con-current scans you have. The more you have, the more strain it might put on your CPU. If you're scanning a remote address that has a big latency, i suggest keeping this high (1000+), if you're scanning a local network with little latency, i suggest keeping this low (<300). The reason I suggest like this, is how the event loop in NodeJS works, and polling IO events.
timeout parameter (OPTIONAL):
the socket timeout before deeming a port closed. I suggest not allowing this go below 500 milliseconds, especially if you're scanning a host in another country. If you're scanning in another country, keep it around a 1000 or 2000. If you're scanning a host on a local network, you could probably get away with 200, but I would never suggest going under 500 to get the best results.
Code:
const net = require('net')
const LINE_DIVIDER = '----------------'
const subjectAddress = readArgument('address')
const concurrentScanLimit = parseInt(readArgument('concurrency', 1000))
const sockTimeout = parseInt(readArgument('timeout', 700))
console.log(`Subject Address\t\t${subjectAddress}`)
console.log(`Concurrent Workers\t${concurrentScanLimit}`)
console.log(`Socket Test Timeout\t${sockTimeout}`)
console.log(LINE_DIVIDER)
// CLI Argument Validation
if(typeof subjectAddress !== 'string') {
return console.error('\n\nThe \"address\" argument cannot be found. Add a process argument \"address={ip|host}\"')
}
if(typeof concurrentScanLimit !== 'number' || concurrentScanLimit <= 0) {
return console.error('\n\nThe \"concurrency\" argument is invalid. Set it with a valid integer value that is greater than 0. Example, \"concurrency={integer}\"')
}
if(typeof sockTimeout !== 'number' || sockTimeout <= 0) {
return console.error('\n\nThe \"timeout\" argument is invalid. Set it with a valid integer value that is greater than 0. It is measured in milliseconds. Argument example \"sockTimeout={integer}\"')
}
// Stores state for all of the worker statuses.
let workerStatuses = { }
let openPorts = [ ]
let closedPorts = [ ]
let activeWorkerCount = 0
// creating the concurrency for scanning ports, concurrency is determined by
// concurrentScanLimit
for(let i = 0; i < concurrentScanLimit; i++) {
workerStatuses[i] = {
id: i,
idPrint: `#${i}`,
scannedPorts: [],
}
setImmediate(
concurrencyWorker,
workerStatuses[i],
openPorts,
closedPorts
)
}
// function is run whenever a concurrencyWorker has started its duty
function concurrencyStartEvent(workerStatus)
{
activeWorkerCount++
}
// function is run whenever a concurrencyWorker has completed its duty
function concurrencyEndEvent(workerStatus)
{
activeWorkerCount--
if(activeWorkerCount === 0) {
// all concurrency workers have finished their execution
console.log('Scan Complete')
console.log(`${subjectAddress} has ${openPorts.length} open ports and ${closedPorts.length} closed ports`)
if(openPorts.length > 0) {
console.log(`Open ports: ${openPorts.join(', ')}`)
}
}
}
// this function will create a new worker for scanning ports.
async function concurrencyWorker(workerStatus, openPorts, closedPorts) {
concurrencyStartEvent(workerStatus)
while(true) {
const port = pullPort()
if(port === null) {
break
}
const result = await testPort(port)
workerStatus.scannedPorts.push(port)
if(result === port) {
openPorts.push(port)
}
else {
closedPorts.push(port)
}
}
concurrencyEndEvent(workerStatus)
}
function testPort(port) {
return new Promise((resolve, reject) => {
const sock = new net.Socket()
sock.setTimeout(sockTimeout)
sock.on('timeout', () => {
resolve(null)
sock.destroy()
})
sock.on('error', (err) => {
resolve(null)
sock.destroy()
})
sock.on('connect', () => {
resolve(port)
sock.end()
})
sock.connect(port, subjectAddress)
})
}
function pullPort() {
if(typeof global.portIncrement === 'undefined') {
return global.portIncrement = 1
}
++global.portIncrement
if(global.portIncrement > 65535) {
return null
}
return global.portIncrement
}
function readArgument(name, assumed = null)
{
for(let arg of process.argv) {
const [key, value] = arg.split('=', 2)
if(typeof value === 'undefined' && key === name) {
return true
}
else if(key === name) {
return value
}
}
return assumed
}