Skip to content
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions submissions/Chikus/port-sniffer/sniffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
const Net = require('net');

function sniffer(arg) {
if (arg.length > 6) {
throw new Error(
'Bad usage to know the manual type: node sniffer.js --help'
);
}
const help = `
TCP sniffer parameters:
--host Mandatory parameter which define the host address for a TCP
port, It can be specified as IP address or URL.
--ports Optional parameter which specifies port range, from min (1) to
max (65536) with a separator '-' between them, if this is not specified will
take the min and max values.

Examples: node sniffer.js --host google.com --ports 100-2345
node sniffer.js --ports 1-1500 --host www.google.com
node sniffer.js --host 171.217.3.110
node sniffer.js --ports 70-80 --host 171.217.3.110
node sniffer.js --host www.google.com
`;

function checkServer(host, port) {
return new Promise(function prom(resolve) {
const socket = Net.createConnection(port, host);
socket.setTimeout(300);
socket.on('timeout', function timer() {
socket.destroy();
resolve(null);
});
socket.on('connect', function success() {
process.stdout.write('.');
socket.destroy();
resolve(port);
});
socket.on('error', function fail() {
throw new Error('Error in Socket, wrong host \n');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use reject for error handling.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

});
}).catch(e => e);
}

async function runPorts(host, lowerPort, higherPort) {
const portList = [];
for (let i = lowerPort; i <= higherPort; i += 1) {
/* eslint-disable */

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use

/* eslint-disable no-await-in-loop */

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

const validPort = await checkServer(host, i);
/* eslint-enable */
if (validPort) {
portList.push(validPort);
}
}
if (portList.length) {
process.stdout.write(`\n${portList.join()} ports are opened \n`);
return process.exit(0);
}
process.stdout.write('No ports were found \n');
return process.exit(0);
}

function checkPorts(userPorts) {
if (userPorts) {
const ports = userPorts.split('-').map(elem => Number(elem));
if (
ports[0] > ports[1] ||
ports[0] < 0 ||
ports[1] > 65535 ||
!ports[1]
) {
throw new Error(' Check your ports, range from 1-65535\n');
}
return [ports[0], ports[1]];
}
throw new Error('Ports no defined \n');
}

function checkHost(host) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to declare this functions inside sniffer function. Move functions to module scope (checkPorts, runPorts, checkHost, checkServer).

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if import the function sniffer, I will not hava access to this others functions, I just did that for comfort from the user he just execute the funcion, but Ok i will change the scope.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You won't import sniffer function and any other function here, since they are not exported.

if (host) {
const domains = host.split('.');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use dns node package to validate domain

@lempiy lempiy Nov 1, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const dns = require('dns').promises;
async function checkHost(host) {
    try {
       return (await dns.resolve(host)).address;
    } catch (err) {
      throw new Error('Wrong host');
    }
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool and Done

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used this function but I consider it is not necessary to add, for example if you set in argument --host 10.4 this function will not throw any error, it will fill with zeros the IP 10.0.0.4, also I had some issues with my node version, but when I maked it work, I see it is doing the same that net.socket does, so for this consideration I didn't implemented.

@lempiy lempiy Nov 4, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DNS is not the same as socket. Socket uses TCP connection or IPC streaming. DNS is domain resolve system built on top of UDP with fallback to TCP.
If you don't want to use it - it's ok, but your current host validation is incorrect and looks ugly. Here example of correct (RFC) regexp for hostname (https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address). As you see its pretty big, you may use this if you want or check for Invalid host error in socket.error event to break the loop on first connect to wrong host.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, Socket doesnt do the same as DNS, but what I tried to said is that socket
= required('net') inside of socket they are doing DNS validation, You can checked in this repo. https://github.com/nodejs/node/blob/master/lib/net.js , other wise how socket can work when you provide www.google.com here im sure they first contact DNS to get the IP add.

@lempiy lempiy Nov 4, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it does. Generally, all you need is to check host validity once, at the start of your job.
I think that using dns for this purpose appears to be more clear then doing it through socket.connect. Otherwise, Its like using a bicycle when you need a wheel.

if (host.replace(/[^.]/g, '').length === 3) {
if (
domains.every(x => Number.isNaN(x)) ||
domains.every(x => !Number.isNaN(x))
) {
return host;
}
} else if (host.replace(/[^.]/g, '').length <= 2) {
if (domains.every(x => !Number.isNaN(x))) {
return host;
}
}
}
return null;
}

switch (arg[2]) {

@lempiy lempiy Nov 1, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be great to create parseArgument function, that will return an object with parsed args. parseArgument may be called from sniffer (root) function and depending on returned object we will decide how to call runPorts .
It will make you code more flexible and extendable.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, Done

case '--ports':

@lempiy lempiy Nov 1, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using imperative switch case which will grow very rapidly if you gonna add more params to your program, it's better to create more universal solution to parse arguments. Maybe something like this (simplified):

function parseArguments(args) {
   const parsedArgs = args.slice(2).reduce((acc, arg) => {
         if (arg.startsWith('--') && !acc.lastKey) {
              acc.lastKey = arg.replace('--', '')
         } else if (acc.lastKey) {
              acc.result = {...acc.result, [acc.lastKey]: arg}
              acc.lastKey = null
         } else {
               throw new Error(`Unexpected argument '${arg}'`)
         }
         return acc
    }, {result: {}, lastKey: null}).result
    if (parsedArgs.lastKey) throw new Error(`Unexpected key '${acc.lastKey}' without value`)
    return parsedArgs.result
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const args = ['node',
  '/home/index.js',
  '--one',
  'two',
  '--three',
  'four']
parseArguments(args)
// {one: "two", three: "four"}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hehe you gave me the solution, thanks, I will make the changes.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a very challenging function, this reduce is kind of crazy, but I like it and it has a bunch of functionalities. I hope in the future I can use it again :D

@lempiy lempiy Nov 4, 2019

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In a real world apps you might be using external package for that. (like yargs) My implementation is very straightforward.

if (arg[4] === '--host') {
const [portMin, portMax] = checkPorts(arg[3]);
runPorts(checkHost(arg[5]), portMin, portMax);
} else {
process.stdout.write('Bad usage type: node sniffer.js --help \n');
}
break;
case '--host':
if (arg[4] === '--ports') {
const [portMin, portMax] = checkPorts(arg[5]);
runPorts(checkHost(arg[3]), portMin, portMax);
} else if (typeof arg[4] === 'undefined') {
process.stdout.write('Ports checking from 1 to 65535 \n');
runPorts(checkHost(arg[3]), 1, 65535);
} else {
process.stdout.write('Bad usage type: node sniffer.js --help \n');
}
break;
case '--help':
process.stdout.write(help);
break;
default:
process.stdout.write('Bad usage type: node sniffer.js --help \n');
}
}
sniffer(process.argv);