scanNetwork function
Future<Map<String, Map<int, Map<String, dynamic> > > >
scanNetwork(})
Scan multiple hosts concurrently (host-level concurrency) and scan each
host's ports using scanHost. Returns a map host->(port->banner).
Implementation
Future<Map<String, Map<int, Map<String, dynamic>>>> scanNetwork(
Iterable<String> hosts,
List<int> ports, {
int hostConcurrency = 10,
int portConcurrency = 50,
Duration timeout = const Duration(milliseconds: 300),
Duration bannerTimeout = const Duration(milliseconds: 300),
void Function(String host, Map<int, Map<String, dynamic>> result)?
onHostResult,
// If provided, write per-host JSON lines to this sink as results arrive.
IOSink? jsonlSink,
}) async {
final results = <String, Map<int, Map<String, dynamic>>>{};
final hostPool = Pool(hostConcurrency);
final futures = <Future>[];
for (final h in hosts) {
futures.add(
hostPool.withResource(() async {
final r = await scanHost(
h,
ports,
portConcurrency: portConcurrency,
timeout: timeout,
bannerTimeout: bannerTimeout,
);
// Always record results (may be empty) and stream a JSONL line if requested
results[h] = r;
// Build per-host JSON object
final hostObj = <String, dynamic>{
'host': h,
'timestamp': DateTime.now().toIso8601String(),
'open_count': r.length,
};
final portsList = <Map<String, dynamic>>[];
for (final entry in r.entries) {
final p = entry.key;
final banner = entry.value['banner'];
final proto = entry.value['protocol'];
final service = entry.value['service'] ?? _wellKnownPortService(p);
final severity = entry.value['severity'];
portsList.add({
'port': p,
'banner': banner,
'protocol': proto,
'service': service,
'severity': severity,
});
}
hostObj['ports'] = portsList;
// If HTTPS likely present (443 open), try cert inspection
if (r.keys.contains(443)) {
try {
final cert = await inspectTlsCertificate(h, 443);
if (cert != null) hostObj['tls'] = cert;
} catch (_) {
// ignore cert errors
}
}
// Use the separated http probe to gather headers and resolved IP.
Map<String, String>? headersMap;
String? cnameTarget;
try {
final probe = await http_probe.httpHeadProbe(
h,
timeout: Duration(seconds: 5),
);
headersMap = probe['headers'] as Map<String, String>?;
final ip = probe['ip'] as String?;
if (ip != null) {
final geo = await geoIpLookup(ip);
if (geo != null) hostObj['geoip'] = geo;
}
try {
final trace = await traceroute(h);
if (trace.isNotEmpty) hostObj['traceroute'] = trace;
} catch (_) {}
} catch (_) {
headersMap = null;
}
// Fingerprint service and detect WAF/CDN
final fp = fingerprintService(headers: headersMap, banner: null);
final waf = detectWafCdns(headersMap, cnameTarget);
if (fp['name'] != null) hostObj['fingerprint'] = fp;
if (waf.isNotEmpty) hostObj['waf_cdns'] = waf;
if (jsonlSink != null) {
jsonlSink.writeln(JsonEncoder().convert(hostObj));
await jsonlSink.flush();
}
try {
if (onHostResult != null) onHostResult(h, r);
} catch (_) {
// ignore callback errors
}
}),
);
}
await Future.wait(futures);
// If streaming JSONL, produce a final aggregated severity summary line
if (jsonlSink != null) {
final summary = <String, dynamic>{
'timestamp': DateTime.now().toIso8601String(),
'summary': reporting.severitySummary(results),
};
jsonlSink.writeln(JsonEncoder().convert(summary));
await jsonlSink.flush();
}
return results;
}