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
}