25 Commits

Author SHA1 Message Date
Robert Whitney
e1379038a4 derp 2024-12-23 02:52:38 -08:00
Robert Whitney
d522d490eb Merge pull request #8 from rileyscool/master
fix package.json
2024-12-22 18:58:14 -08:00
Riley
625567ae1a Update scanner.js
fix bug where there is no output if you select show-desc
2024-12-23 14:53:29 +13:00
Riley
344b583499 fix package.json
zlib package tries to use an old thing to build and you cant install dependencies
2024-12-23 14:12:14 +13:00
Robert Whitney
2451ec794a Update README.md 2024-12-22 06:20:25 -08:00
Robert Whitney
a5dc543cf4 Update README.md 2024-12-22 00:53:23 -08:00
Robert Whitney
b13e690eda Merge pull request #6 from etianl/master 2023-01-06 04:34:50 -06:00
etianl
73afc74074 forgot a +
derp
2023-01-06 01:55:43 -08:00
etianl
f05b17ee9a Option for how many concurrent connections to use.
Can increase scan efficiency with higher numbers, or you can reduce the amount of connections for use on terrible internet connections. Default is 256
2023-01-06 01:49:31 -08:00
Robert Whitney
0a73fb77af create an actual package.json 2022-09-25 11:54:55 -05:00
Robert Whitney
a801231a84 version filter. Closes issue #1 2022-09-25 10:44:43 -05:00
Rob
d060605253 output file append, not overwrite. 2022-03-06 02:23:56 -06:00
Rob
1be7bcda44 bug fix? 2022-03-06 01:52:00 -06:00
Rob
2bc8640c0b should be 256. 2022-03-02 02:55:32 -06:00
Rob
e7a59b2e97 color parsing 2022-02-27 00:27:29 -06:00
Rob
4a08a5ce14 normalize? 2022-02-27 00:21:49 -06:00
Rob
cbfea276f3 fix file output not including all results. Resource busy, so created output stream to write to instead of appending sync. 2022-02-27 00:13:12 -06:00
Rob
c14d2fbb8c add missing dependency 2022-02-26 23:58:05 -06:00
Rob
c6e5f6fb25 bug fixes, final touches 2022-02-26 23:32:59 -06:00
Rob
8a68e61c0f bug fix to output, don't add new line until end of string 2022-02-26 23:18:08 -06:00
Rob
dddb5fbba9 remove some comments, not needed. 2022-02-26 23:09:28 -06:00
Rob
aac11cd570 GeoIP database stuff 2022-02-26 23:08:16 -06:00
Rob
4d9542e268 update README 2022-02-16 14:32:10 -06:00
Rob
a9e7e3b230 add --format option 2022-02-16 14:29:23 -06:00
Rob
0d2f83db70 update readme 2022-02-16 07:48:27 -06:00
3 changed files with 155 additions and 15 deletions

View File

@@ -1,24 +1,43 @@
# What is this?
This scans for MineCraft servers... really really fast!
This scans for MineCraft servers... really really fast! [but this scans even faster](https://github.com/xnite/BBCrawler)
# Getting Started
* Clone this repository somewhere.
* Run `npm install` from within this directory.
## Getting Help
You can get help through our [Discord server](https://discord.gg/3RUjaRzdKv)
## Usage
Run `node ./scanner.js [options] --ip <ip range>`
### Example
### Examples
`node ./scanner.js --ip 192.168.1.0/24 --port 25565-25569 --show-desc --min-players 1 --max-players 100 --out report.csv`
`node ./scanner.js --ip 192.168.1.0/24 --port 25565-25569 --show-desc --min-players 1 --max-players 100 --version '1.8.*' --out 1.8.x-servers.csv`
`node ./scanner.js --ip 192.168.1.0/24 --port 25565-25569 --show-desc --min-players 1 --max-players 100 --version '*forge*' --out forge-servers.csv`
### CLI Options
### Options
* `--ip <ip>` - IP Address or Range of IP Addresses with CIDR notation (eg- 192.168.1.0/24)
* `--port <ports>` - Ports to look for minecraft servers on. (Default: `25565-25566`)
* `--min-players <count>` - Minimum number of players.
* `--max-players <count>` - Maximum player count.
* `--version <glob expression>` - Glob expression to filter version (eg- `1.19.*`, or `1.1*.*`). (Default: `*`)
* `--conc <howmany>` - How many concurrent connections to use for scanning. (Default is 256)
#### Output Options
* `--show-desc` - Enable showing of server description in output.
* `--quiet` - Silence terminal output.
* `--min-players <count>` - Minimum number of players to display.
* `--max-players <count>` - Only show servers with max player count or below.
#### Output File Options
* `--out <filename>` - Output to CSV file (Can be opened as a spreadsheet in MS Office, Google Docs, etc.)
* `--format <csv|txt|txt-connect-only>` - Output format (`txt-connect-only` for `ip:port` list format)
* `--log-desc` - Output server discription to output file.
#### Geo Location
* `--geo-ip` - Use IP Geolocation database.
* `--geo-coords` - Add geo-coordinates to output.
* `--maxmind-key` - Provide a key for maxmind database download.
## By really fast, I mean really fast!
# time node ./scanner.js --ip 135.148.60.0/24 --show-desc --quiet --out example.csv
@@ -28,8 +47,11 @@ Run `node ./scanner.js [options] --ip <ip range>`
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.
# cat example.csv | wc -l
85
In this example it took `3.183` seconds to scan `255` IP addresses, and find `85` MineCraft servers. At this speed, a full `/16` (`123.45.0.0 - 123.45.255.255`) will take about `13.5` 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**.
* Fails to scan more than a /10 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**.
* Above limitations are solved by running [BBCrawler](https://github.com/xnite/BBCrawler) instead but that is more complex than most people will want.

View File

@@ -1,8 +1,18 @@
{
"name": "mcseeker",
"version": "0.0.2",
"description": "A Minecraft server scanner",
"author": "Robert Whitney <Me@Rob.cat>",
"main": "scanner.js",
"license": "GPL V3",
"dependencies": {
"commandos": "^0.10.1",
"evilscan": "^1.8.1",
"maxmind": "^4.3.5",
"minecraft-status": "^1.1.0",
"mineflayer": "^4.0.0"
"mineflayer": "^4.0.0",
"minimatch": "^3.0.4",
"node-mcpe-color-parser": "^0.1.1",
"tar-stream": "^2.2.0"
}
}

View File

@@ -1,7 +1,10 @@
var Scanner = require('evilscan');
var status = require('minecraft-status').MinecraftServerListPing;
var mc = require('mineflayer');
var minimatch = require("minimatch");
var fs = require('fs');
var maxmind;
var mcp = require('node-mcpe-color-parser');
//var mcClient = require('minecraft-protocol');
process.params = (require('commandos')).parse(process.argv);
var MINECRAFT_DEFAULT_PORT = '25565-25566';
@@ -9,8 +12,50 @@ 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 SCAN_OPTS_VERSION_FILTER = (process.params['version']||'*');
var SCAN_OPTS_CONCURRENCY = (process.params['conc'] || 256);
var CLIENT_TOKEN;
if(process.params['geo-ip'])
{
if (!fs.existsSync("./GeoLite2.mmdb"))
{
if(!process.params['maxmind-key'])
{
return console.log("NO MAXMIND DOWNLOAD KEY WAS PROVIDED! CANNOT DOWNLOAD THE DATABASE WITHOUT A KEY!");
}
return require('https').get("https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&license_key="+ process.params['maxmind-key'] +"&suffix=tar.gz", function(resp){
const zlib = require('zlib');
const tar = require('tar-stream');
const tarFile = fs.createWriteStream("./GeoLite2.tar");
const dbfile = fs.createWriteStream('./GeoLite2.mmdb');
resp.pipe(zlib.createGunzip()).pipe(tarFile);
tarFile.on("close", function(){
console.log("Wrote tar to disk. Extracting DB file...");
var extract = tar.extract();
extract.on('entry', function(header, stream, next){
if (header.name.match(/.*?\.mmdb/))
{
stream.pipe(dbfile);
dbfile.on('close', function(){
fs.unlinkSync("./GeoLite2.tar");
console.log("Extracted GeoIP database. Please rerun your scan to continue.");
return process.exit(0);
})
} else {
return next();
}
stream.resume();
});
fs.createReadStream("./GeoLite2.tar").pipe(extract);
})
});
}
maxmind = require('maxmind');
}
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.");
@@ -44,14 +89,14 @@ if(process.params['enable-client'] && !process.params['client-token'])
});
}
console.log("Scanning ports " + SCAN_OPTS_PORTS + " on " + SCAN_OPTS_HOSTS);
console.log("Scanning ports " + SCAN_OPTS_PORTS + " on " + SCAN_OPTS_HOSTS + " with " + SCAN_OPTS_CONCURRENCY + " connections.");
var options = {
target: SCAN_OPTS_HOSTS,
port: SCAN_OPTS_PORTS,
states: 'O',
banner: false,
concurrency: 255
concurrency: SCAN_OPTS_CONCURRENCY
}
var scan = new Scanner(options);
@@ -69,23 +114,86 @@ function placeTabs(string)
return string;
}
if (SCAN_OPTS_OUTPUT_CSV) {
var outStream = fs.createWriteStream(SCAN_OPTS_OUTPUT_CSV, { flags: 'a' });
}
scan.on('result', function(data){
//console.log(data);
status.ping(757, data.ip, data.port, (process.params['timeout']||15)*1000).then(function(pingRes){
if(!minimatch(pingRes.version.name, SCAN_OPTS_VERSION_FILTER)) { return; } // Does not match version filter
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;
theText += "\t"+mcp(pingRes.description).replace(/\n/g, ' ');
// pingRes.description is a string, not an object
}
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");
var line;
switch(process.params['format']||'csv')
{
case "txt":
line = data.ip + ":" + data.port + "\t" + pingRes.version.name.replace(/\,/g, '+');
if (process.params['log-desc']) {
line += "\t" + mcp(pingRes.description.text).replace(/\n/g, ' ');
}
break;
case "txt-connect-only":
line = data.ip + ":" + data.port;
break;
case "csv":
default:
line = data.ip + ":" + data.port + "," + pingRes.version.name.replace(/\,/g, '+') + "," + pingRes.players.online + "/" + pingRes.players.max;
if (process.params['log-desc']) {
line += "," + mcp(pingRes.description.text).replace(/\n/g, ' ').replace(/\,/g, ';');
}
}
if(process.params['geo-ip'])
{
maxmind.open('./GeoLite2.mmdb').then(function(geoip){
var geoLoc = geoip.get(data.ip);
var geoText = geoLoc.country.iso_code;
if (process.params['geo-coords']) {
geoText += " (" + geoLoc.location.latitude + "," + geoLoc.location.longitude + ")";
}
switch (process.params['format'] || 'csv')
{
case "txt":
line += " " + geoText;
case "csv":
line += "," + geoText;
default:
break;
}
outStream.write(line.toString().normalize() + "\n");
}).catch(function(err){
console.log(err);
});
} else {
outStream.write(line + "\n");
}
}
if(!process.params['quiet'])
{
console.log(theText);
if (process.params['geo-ip'])
{
maxmind.open('./GeoLite2.mmdb').then(function (geoip) {
var geoLoc = geoip.get(data.ip);
var geoText = geoLoc.country.iso_code;
if(process.params['geo-coords'])
{
geoText += " (" + geoLoc.location.latitude + "," + geoLoc.location.longitude + ")";
}
console.log("[" + geoText + "] " + theText);
}).catch(function (err) {
console.log(err);
});
} else {
console.log(theText);
}
}
}
if(process.params['enable-client'] && (CLIENT_TOKEN||process.params['client-token']))
@@ -122,4 +230,4 @@ scan.on('error', err => {
scan.on('done', () => {
console.log("Scan finished!");
});
scan.run();
scan.run();