@roflsec/fail2scan 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/daemon.js +93 -10
- package/package.json +1 -1
package/bin/daemon.js
CHANGED
|
@@ -48,23 +48,106 @@ const STATE=loadState();
|
|
|
48
48
|
// -------------------- IP extraction --------------------
|
|
49
49
|
function extractIpFromLine(line){const v4=line.match(/\b(?:\d{1,3}\.){3}\d{1,3}\b/);if(v4&&v4[0])return v4[0];const v6=line.match(/\b([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}\b/);if(v6&&v6[0])return v6[0];return null;}
|
|
50
50
|
|
|
51
|
+
// -------------------- nmap helpers (parallel support) --------------------
|
|
52
|
+
function spawnOneNmap(args, outFile) {
|
|
53
|
+
return new Promise((resolve, reject) => {
|
|
54
|
+
const proc = spawn('nmap', args, { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
55
|
+
const outStream = fs.createWriteStream(outFile, { flags: 'w' });
|
|
56
|
+
proc.stdout.pipe(outStream);
|
|
57
|
+
let stderr = '';
|
|
58
|
+
proc.stderr.on('data', c => { stderr += c.toString(); });
|
|
59
|
+
proc.on('close', code => {
|
|
60
|
+
if (stderr) {
|
|
61
|
+
try { fs.appendFileSync(outFile, '\n\nSTDERR:\n' + stderr); } catch (e) {}
|
|
62
|
+
}
|
|
63
|
+
resolve({ code, ok: code === 0 });
|
|
64
|
+
});
|
|
65
|
+
proc.on('error', err => reject(err));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async function spawnNmapParallel(ip, outDir, requestedArgs, parts) {
|
|
70
|
+
// requestedArgs: array of args
|
|
71
|
+
// if -p- not present, run single nmap
|
|
72
|
+
if (!requestedArgs.includes('-p-')) {
|
|
73
|
+
const outNmap = path.join(outDir, 'nmap.txt');
|
|
74
|
+
await spawnOneNmap([...requestedArgs, ip], outNmap);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// decide number of parts
|
|
79
|
+
const cpuCount = os.cpus() ? os.cpus().length : 1;
|
|
80
|
+
const numParts = Math.max(1, parts || CORE_OVERRIDE || cpuCount);
|
|
81
|
+
|
|
82
|
+
const portsPer = Math.floor(65535 / numParts);
|
|
83
|
+
const jobs = [];
|
|
84
|
+
for (let i = 0; i < numParts; i++) {
|
|
85
|
+
const start = 1 + i * portsPer;
|
|
86
|
+
const end = (i === numParts - 1) ? 65535 : ((i + 1) * portsPer);
|
|
87
|
+
const subdir = path.join(outDir, `part-${i}`);
|
|
88
|
+
try { fs.mkdirSync(subdir, { recursive: true, mode: 0o750 }); } catch (e) {}
|
|
89
|
+
const outNmap = path.join(subdir, 'nmap.txt');
|
|
90
|
+
const portArg = `-p${start}-${end}`;
|
|
91
|
+
// replace '-p-' with '-pstart-end' in requestedArgs
|
|
92
|
+
const args = requestedArgs.map(a => a === '-p-' ? portArg : a).concat([ip]);
|
|
93
|
+
jobs.push(spawnOneNmap(args, outNmap));
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// await all parts
|
|
97
|
+
await Promise.all(jobs);
|
|
98
|
+
|
|
99
|
+
// merge outputs into single nmap.txt (preserve order)
|
|
100
|
+
const partsContent = [];
|
|
101
|
+
for (let i = 0; i < numParts; i++) {
|
|
102
|
+
const fn = path.join(outDir, `part-${i}`, 'nmap.txt');
|
|
103
|
+
try {
|
|
104
|
+
if (fs.existsSync(fn)) partsContent.push(fs.readFileSync(fn, 'utf8'));
|
|
105
|
+
} catch (e) {}
|
|
106
|
+
}
|
|
107
|
+
try { fs.writeFileSync(path.join(outDir, 'nmap.txt'), partsContent.join('\n\n--- PART ---\n\n')); } catch (e) {}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// wrapper used by performScan
|
|
111
|
+
async function runNmap(ip, outDir, requestedArgs) {
|
|
112
|
+
// adapt -sS -> -sT if not root
|
|
113
|
+
const isRoot = (typeof process.getuid === 'function' && process.getuid() === 0);
|
|
114
|
+
const args = requestedArgs.map(a => (!isRoot && a === '-sS') ? '-sT' : a);
|
|
115
|
+
|
|
116
|
+
// if user explicitly set a small host-timeout, keep it; otherwise let parallel jobs share same requested args
|
|
117
|
+
// decide parts from CORE_OVERRIDE or cpu
|
|
118
|
+
const cpuCount = os.cpus() ? os.cpus().length : 1;
|
|
119
|
+
const parts = CORE_OVERRIDE || cpuCount;
|
|
120
|
+
|
|
121
|
+
// if args include -p- -> use parallel split, else single
|
|
122
|
+
await spawnNmapParallel(ip, outDir, args, parts);
|
|
123
|
+
}
|
|
124
|
+
|
|
51
125
|
// -------------------- scan --------------------
|
|
52
|
-
function spawnNmap(ip,outDir,args){return new Promise((res,rej)=>{const outNmap=path.join(outDir,'nmap.txt');const proc=spawn('nmap',[...args,ip],{stdio:['ignore','pipe','pipe']});const outStream=fs.createWriteStream(outNmap,{flags:'w'});proc.stdout.pipe(outStream);let err='';proc.stderr.on('data',c=>{err+=c.toString();});proc.on('close',code=>{if(err)fs.appendFileSync(outNmap,'\n\nSTDERR:\n'+err);res({code,ok:code===0});});proc.on('error',e=>rej(e));});}
|
|
53
126
|
async function performScan(ip){
|
|
54
127
|
const now=new Date(),dateDir=now.toISOString().slice(0,10),safeIp=sanitizeFilename(ip);
|
|
55
128
|
let outDir=path.join(OUT_ROOT,dateDir,`${safeIp}_${now.toISOString().replace(/[:.]/g,'-')}`);
|
|
56
129
|
outDir=path.join(safeMkdirSyncWithFallback(path.dirname(outDir)),path.basename(outDir));
|
|
57
|
-
fs.mkdirSync(outDir,{recursive:true,mode:0o750});
|
|
130
|
+
try { fs.mkdirSync(outDir,{recursive:true,mode:0o750}); } catch (e) {}
|
|
58
131
|
const summary={ip,ts:now.toISOString(),cmds:{}};
|
|
132
|
+
|
|
59
133
|
const requested=NMAP_ARGS_STR.trim().split(/\s+/).filter(Boolean);
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
134
|
+
|
|
135
|
+
try{
|
|
136
|
+
log('Running nmap on',ip,'args:',requested.join(' '));
|
|
137
|
+
await runNmap(ip, outDir, requested);
|
|
138
|
+
summary.cmds.nmap = { ok: true, args: requested.join(' '), path: 'nmap.txt' };
|
|
139
|
+
} catch(e){
|
|
140
|
+
log('nmap failed for',ip, e && e.message ? e.message : e);
|
|
141
|
+
summary.cmds.nmap = { ok: false, err: e && e.message ? e.message : String(e) };
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
try{ const dig = await runCmdCapture('dig',['-x',ip,'+short']); fs.writeFileSync(path.join(outDir,'dig.txt'),(dig.stdout||'') + (dig.stderr?'\n\nSTDERR:\n'+dig.stderr:'')); summary.cmds.dig = { ok: dig.ok, path: 'dig.txt' }; } catch(e){ summary.cmds.dig = { ok:false, err: e && e.message ? e.message : String(e) }; }
|
|
145
|
+
try{ const who = await runCmdCapture('whois',[ip]); fs.writeFileSync(path.join(outDir,'whois.txt'),(who.stdout||'') + (who.stderr?'\n\nSTDERR:\n'+who.stderr:'')); summary.cmds.whois = { ok: who.ok, path: 'whois.txt' }; } catch(e){ summary.cmds.whois = { ok:false, err: e && e.message ? e.message : String(e) }; }
|
|
146
|
+
|
|
147
|
+
try{ const nmapTxt = fs.readFileSync(path.join(outDir,'nmap.txt'),'utf8'); summary.open_ports = nmapTxt.split(/\r?\n/).filter(l=>/^\d+\/tcp\s+open/.test(l)).map(l=>l.trim()); } catch(e){ summary.open_ports = []; }
|
|
148
|
+
|
|
149
|
+
try{ fs.writeFileSync(path.join(outDir,'summary.json'),JSON.stringify(summary,null,2)); } catch (e) {}
|
|
150
|
+
try{ fs.chmodSync(outDir,0o750); } catch (e) {}
|
|
68
151
|
log('Scan written for',ip,'->',outDir);
|
|
69
152
|
}
|
|
70
153
|
|