commit 79ea3325f4231f0eea4e69f12ca6653dee17222e Author: Rob Date: Wed Feb 16 07:34:47 2022 -0600 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fed401d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +package-lock.json +node_modules/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5213dfc --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# What is this? +This scans for MineCraft servers... really really fast! + +# Getting Started +* Clone this repository somewhere. +* Run `npm install` from within this directory. + +## Usage +Run `node ./scanner.js [options] --ip ` + +### Example +`node ./scanner.js --ip 192.168.1.0/24 --port 25565-25569 --show-desc --min-players 1 --max-players 100 --out report.csv` + +### CLI Options +* `--ip ` - IP Address or Range of IP Addresses with CIDR notation (eg- 192.168.1.0/24) +* `--port ` - Ports to look for minecraft servers on. (Default: `25565-25566`) +* `--show-desc` - Enable showing of server description in output. +* `--quiet` - Silence terminal output. +* `--min-players ` - Minimum number of players to display. +* `--max-players ` - Only show servers with max player count or below. +* `--out ` - Output to CSV file (Can be opened as a spreadsheet in MS Office, Google Docs, etc.) + +## By really fast, I mean really fast! + # time node ./scanner.js --ip 135.148.60.0/24 --show-desc --quiet --out example.csv + Scanning ports 25565-25566 on 135.148.60.0/24 + Scan finished! + + real 0m3.183s + user 0m0.825s + sys 0m0.334s +At this speed, a full /16 (123.45.0.0 - 123.45.255.255) will take about 13 and a half minutes to scan. + +## Limitations +* Fails to scan more than a /16 without kicking the bucket... so you should probably stick to that or smaller ranges. +* Working on a Minecraft bot client... doesn't work though... you can see how badly it doesn't work by using `--enable-client` flag... it is totally broken. **Don't use it**. \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..eec6e79 --- /dev/null +++ b/package.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "commandos": "^0.10.1", + "evilscan": "^1.8.1", + "minecraft-status": "^1.1.0", + "mineflayer": "^4.0.0" + } +} diff --git a/scanner.js b/scanner.js new file mode 100644 index 0000000..a608a8d --- /dev/null +++ b/scanner.js @@ -0,0 +1,125 @@ +var Scanner = require('evilscan'); +var status = require('minecraft-status').MinecraftServerListPing; +var mc = require('mineflayer'); +var fs = require('fs'); +//var mcClient = require('minecraft-protocol'); +process.params = (require('commandos')).parse(process.argv); +var MINECRAFT_DEFAULT_PORT = '25565-25566'; +var SCAN_MIN_PLAYERS = (process.params['min-players'] || 0); +var SCAN_OPTS_HOSTS = (process.params['ip']||'0.0.0.0/0').toString(); +var SCAN_OPTS_PORTS = (process.params['port'] || MINECRAFT_DEFAULT_PORT).toString(); +var SCAN_OPTS_OUTPUT_CSV = (process.params['out']||null); +var CLIENT_TOKEN; + +if(process.params['quiet'] && !SCAN_OPTS_OUTPUT_CSV) +{ + console.log("Error:\tYou have asked for --quiet output, but did not specify an --out file. This scan is pointless!\nRefusing to run a pointless operation."); + process.exit(1); +} + +if(process.params['enable-client'] && !process.params['client-token']) +{ + const https = require('https'); + + https.get("https://api.thealtening.com/free/generate", res => { + let data = []; + res.on('data', chunk => { + data.push(chunk); + }); + + res.on('end', () => { + var fullBuffer = Buffer.concat(data); + var output; + try { + output = JSON.parse((fullBuffer || '{}').toString()); + } catch(e) { + console.log(fullBuffer.toString()); + } + + CLIENT_TOKEN = ((output||{}).token||null); + console.log(CLIENT_TOKEN||"No token!"); + }); + }).on('error', err => { + //console.log('Error: ', err.message); + }); +} + +console.log("Scanning ports " + SCAN_OPTS_PORTS + " on " + SCAN_OPTS_HOSTS); + +var options = { + target: SCAN_OPTS_HOSTS, + port: SCAN_OPTS_PORTS, + states: 'O', + banner: false, + concurrency: 255 +} +var scan = new Scanner(options); + +function placeTabs(string) +{ + var count = Math.floor(string.length / 4); + for(i=0; i <= count; i++) + { + string += "\t"; + } + if(count <= 1) + { + string += "\t\t"; + } + return string; +} + +scan.on('result', function(data){ + //console.log(data); + status.ping(757, data.ip, data.port, (process.params['timeout']||15)*1000).then(function(pingRes){ + if (pingRes.players.online >= SCAN_MIN_PLAYERS && (!process.params['max-players'] || (process.params['max-players'] && pingRes.players.max <= process.params['max-players']))) + { + var theText = data.ip + ":" + data.port + "\t" + pingRes.version.name + "\t" + pingRes.players.online + " of " + pingRes.players.max + " players"; + if(process.params['show-desc']) + { + theText += "\t"+pingRes.description.text; + } + if (SCAN_OPTS_OUTPUT_CSV) + { + fs.appendFileSync(SCAN_OPTS_OUTPUT_CSV, data.ip+":"+data.port+","+pingRes.version.name.replace(/\,/g, '+')+","+pingRes.players.online+"/"+pingRes.players.max+"\n"); + } + if(!process.params['quiet']) + { + console.log(theText); + } + } + if(process.params['enable-client'] && (CLIENT_TOKEN||process.params['client-token'])) + { + var client = mc.createBot({ + host: data.ip, // optional + port: data.port, // optional + token: (CLIENT_TOKEN||process.params['client-token']), + auth: 'mojang', // optional; by default uses mojang, if using a microsoft account, set to 'microsoft' + protocol: pingRes.version.protocol + }); + client.on('chat', function (packet) { + // Listen for chat messages and echo them back. + var jsonMsg = JSON.parse(packet.message); + if (jsonMsg.translate == 'chat.type.announcement' || jsonMsg.translate == 'chat.type.text') { + var username = jsonMsg.with[0].text; + var msg = jsonMsg.with[1]; + if (username === client.username) return; + console.log(msg); + //client.write('chat', { message: msg.text }); + client.end("Logout"); + } + }); + } + }).catch(function(error){ + //console.log(error); + }) +}) + +scan.on('error', err => { + //console.log(err.toString()); +}); + +scan.on('done', () => { + console.log("Scan finished!"); +}); +scan.run(); \ No newline at end of file