mirror of
https://github.com/Astatin3/rust-scan-mc.git
synced 2026-06-09 00:18:02 -06:00
Add services
This commit is contained in:
@@ -4,11 +4,14 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.12.15", features = ["blocking"] }
|
||||
byteorder = "1.5.0"
|
||||
indicatif = "0.17.11"
|
||||
lazy_static = "1.5.0"
|
||||
memchr = "2.7.4"
|
||||
pnet = "0.35.0"
|
||||
rand = "0.9.0"
|
||||
regex = "1.11.1"
|
||||
rocksdb = "0.23.0"
|
||||
serde = { version = "1.0.219", features = ["derive"] }
|
||||
serde_json = "1.0.140"
|
||||
|
||||
+160
-99
@@ -1,9 +1,9 @@
|
||||
use std::{net::IpAddr, sync::Arc, time::Instant};
|
||||
|
||||
use rocksdb::{Cache, ColumnFamily, IteratorMode, Options, WriteBatch, DB};
|
||||
use rocksdb::{Cache, ColumnFamily, DB, IteratorMode, Options, WriteBatch};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::port_scan::port_scan::ScanResult;
|
||||
use crate::{port_scan::port_scan::PortScanResult, service_scan::service_scan::ServiceScanResult};
|
||||
|
||||
// Global settings for optimal performance
|
||||
const BLOCK_CACHE_SIZE_MB: usize = 512; // 512MB block cache
|
||||
@@ -18,19 +18,93 @@ pub struct ResultDatabase {
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct StringRow {
|
||||
pub id: String, // Row identifier
|
||||
pub ports: Vec<i32>, // Array of string values
|
||||
pub struct DatabaseResult {
|
||||
pub id: String, // Row identifier
|
||||
pub ports: Vec<i32>, // Array of string values
|
||||
pub services: String, // json services
|
||||
}
|
||||
|
||||
impl StringRow {
|
||||
impl DatabaseResult {
|
||||
pub fn to_string(&self) -> String {
|
||||
let mut str = "".to_string();
|
||||
|
||||
str += format!("{} - ports: [{}]", self.id, join_nums(&self.ports, ",")).as_str();
|
||||
str += format!(
|
||||
"{} - ports: [{}] services: [{}]",
|
||||
self.id,
|
||||
join_nums(&self.ports, ","),
|
||||
&self.services
|
||||
)
|
||||
.as_str();
|
||||
|
||||
str
|
||||
}
|
||||
pub fn encode(&self, buf: &mut Vec<u8>) {
|
||||
let values = vec![self.ports_to_string(), self.services.clone()];
|
||||
|
||||
// Write number of values
|
||||
buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
|
||||
|
||||
// Write each value
|
||||
for value in values {
|
||||
let value_bytes = value.as_bytes();
|
||||
buf.extend_from_slice(&(value_bytes.len() as u32).to_le_bytes());
|
||||
buf.extend_from_slice(value_bytes);
|
||||
}
|
||||
}
|
||||
// Binary decoding of row data
|
||||
pub fn decode(key: &str, data: &[u8]) -> Option<Self> {
|
||||
println!("{}", data.len());
|
||||
if data.len() < 8 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut pos = 0;
|
||||
|
||||
if pos + 4 > data.len() {
|
||||
return None;
|
||||
}
|
||||
let mut values_count_bytes = [0u8; 4];
|
||||
values_count_bytes.copy_from_slice(&data[pos..pos + 4]);
|
||||
let values_count = u32::from_le_bytes(values_count_bytes) as usize;
|
||||
pos += 4;
|
||||
|
||||
// Read values
|
||||
let mut values = Vec::with_capacity(values_count);
|
||||
for _ in 0..values_count {
|
||||
if pos + 4 > data.len() {
|
||||
println!("error1!");
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut value_len_bytes = [0u8; 4];
|
||||
value_len_bytes.copy_from_slice(&data[pos..pos + 4]);
|
||||
let value_len = u32::from_le_bytes(value_len_bytes) as usize;
|
||||
pos += 4;
|
||||
|
||||
if pos + value_len > data.len() {
|
||||
println!("error!");
|
||||
return None;
|
||||
}
|
||||
|
||||
let value = String::from_utf8_lossy(&data[pos..pos + value_len]).to_string();
|
||||
values.push(value);
|
||||
pos += value_len;
|
||||
}
|
||||
|
||||
Some(DatabaseResult {
|
||||
id: key.to_string(),
|
||||
ports: if 1 > 0 {
|
||||
split_nums(values[0].as_str(), ",")
|
||||
} else {
|
||||
Vec::new()
|
||||
},
|
||||
services: if 1 > 1 {
|
||||
values[1].clone()
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
})
|
||||
}
|
||||
pub fn ports_to_string(&self) -> String {
|
||||
return join_nums(&self.ports, ",");
|
||||
}
|
||||
@@ -96,7 +170,11 @@ impl ResultDatabase {
|
||||
|
||||
// Define column families for different indexes
|
||||
|
||||
let column_families = vec!["default".to_string(), "ports".to_string()];
|
||||
let column_families = vec![
|
||||
"default".to_string(),
|
||||
"ports".to_string(),
|
||||
"services".to_string(),
|
||||
];
|
||||
|
||||
Self {
|
||||
path: path.to_string(),
|
||||
@@ -112,9 +190,10 @@ impl ResultDatabase {
|
||||
let mut string_rows = Vec::with_capacity(results.len()); // Pre-allocate capacity
|
||||
|
||||
for result in results {
|
||||
string_rows.push(StringRow {
|
||||
string_rows.push(DatabaseResult {
|
||||
id: result.to_string(),
|
||||
ports: vec![],
|
||||
services: String::new(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,27 +202,46 @@ impl ResultDatabase {
|
||||
|
||||
pub fn add_tcp_results(
|
||||
&self,
|
||||
results: &Vec<ScanResult>,
|
||||
results: &Vec<PortScanResult>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut string_rows = Vec::with_capacity(results.len()); // Pre-allocate capacity
|
||||
|
||||
for result in results {
|
||||
string_rows.push(result.to_string_row());
|
||||
string_rows.push(result.to_database());
|
||||
}
|
||||
|
||||
return self.save_rows(string_rows);
|
||||
}
|
||||
|
||||
pub fn save_rows(&self, string_rows: Vec<StringRow>) -> Result<(), Box<dyn std::error::Error>> {
|
||||
pub fn add_service_results(
|
||||
&self,
|
||||
results: &Vec<ServiceScanResult>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut string_rows = Vec::with_capacity(results.len()); // Pre-allocate capacity
|
||||
|
||||
for result in results {
|
||||
let e = result.to_database();
|
||||
print!("{}", e.services);
|
||||
string_rows.push(e);
|
||||
}
|
||||
|
||||
return self.save_rows(string_rows);
|
||||
}
|
||||
|
||||
pub fn save_rows(
|
||||
&self,
|
||||
string_rows: Vec<DatabaseResult>,
|
||||
) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let db = Arc::new(DB::open_cf(&self.options, &self.path, &self.columns)?);
|
||||
let cf_default = db.cf_handle(&self.columns[0]).unwrap();
|
||||
let cf_ports = db.cf_handle(&self.columns[1]).unwrap();
|
||||
let cf_services = db.cf_handle(&self.columns[2]).unwrap();
|
||||
|
||||
let start = Instant::now();
|
||||
let length = string_rows.len();
|
||||
|
||||
// Split the rows into chunks for parallel processing
|
||||
let chunks: Vec<Vec<StringRow>> = string_rows
|
||||
let chunks: Vec<Vec<DatabaseResult>> = string_rows
|
||||
.chunks(BATCH_SIZE)
|
||||
.map(|chunk| chunk.to_vec())
|
||||
.collect();
|
||||
@@ -160,19 +258,17 @@ impl ResultDatabase {
|
||||
let mut batch = WriteBatch::default();
|
||||
|
||||
for row in chunk {
|
||||
// Use optimized binary format for the main data
|
||||
let mut data = Vec::with_capacity(256);
|
||||
batch.put_cf(cf_default_ref, row.id.as_bytes(), &vec![]);
|
||||
|
||||
// Format: id_len + id + count + (len + str) for each value
|
||||
// Binary format: direct encoding without JSON overhead
|
||||
encode_row_binary(&mut data, &row);
|
||||
// Ports
|
||||
batch.put_cf(
|
||||
cf_ports,
|
||||
row.id.as_bytes(),
|
||||
row.ports_to_string().as_bytes(),
|
||||
);
|
||||
|
||||
// Store in main column family
|
||||
batch.put_cf(cf_default_ref, row.id.as_bytes(), &data);
|
||||
|
||||
let idx_key =
|
||||
format!("{}:{}", fast_escape(row.ports_to_string().as_str()), row.id);
|
||||
batch.put_cf(cf_ports, idx_key.as_bytes(), row.id.as_bytes());
|
||||
// Services
|
||||
batch.put_cf(cf_services, row.id.as_bytes(), row.services.as_bytes());
|
||||
}
|
||||
|
||||
batch
|
||||
@@ -195,19 +291,32 @@ impl ResultDatabase {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_row_by_host(&self, row: &str) -> Option<StringRow> {
|
||||
pub fn get_row_by_host(&self, row: &str) -> Option<DatabaseResult> {
|
||||
let db = DB::open_cf(&self.options, &self.path, &self.columns);
|
||||
if db.is_err() {
|
||||
return None;
|
||||
};
|
||||
let db = db.unwrap();
|
||||
let cf_default = db.cf_handle("default").unwrap();
|
||||
|
||||
return fetch_row(&db, cf_default, row);
|
||||
let cfs = vec![
|
||||
db.cf_handle(&self.columns[0]).unwrap(),
|
||||
db.cf_handle(&self.columns[1]).unwrap(),
|
||||
db.cf_handle(&self.columns[2]).unwrap(),
|
||||
];
|
||||
|
||||
return self.fetch_row(&db, row, &cfs);
|
||||
}
|
||||
|
||||
pub fn get_rows_by_port(&self, port: &str) -> Vec<StringRow> {
|
||||
if let Ok(result) = self.search_substring_in_column(self.columns[0].as_str(), port) {
|
||||
pub fn get_rows_by_port(&self, port: &str) -> Vec<DatabaseResult> {
|
||||
if let Ok(result) = self.search_substring_in_column(self.columns[1].as_str(), port) {
|
||||
return result;
|
||||
} else {
|
||||
return Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_rows_by_service(&self, port: &str) -> Vec<DatabaseResult> {
|
||||
if let Ok(result) = self.search_substring_in_column(self.columns[2].as_str(), port) {
|
||||
return result;
|
||||
} else {
|
||||
return Vec::new();
|
||||
@@ -218,12 +327,17 @@ impl ResultDatabase {
|
||||
&self,
|
||||
column: &str,
|
||||
substring: &str,
|
||||
) -> Result<Vec<StringRow>, rocksdb::Error> {
|
||||
) -> Result<Vec<DatabaseResult>, rocksdb::Error> {
|
||||
let db = Arc::new(DB::open_cf(&self.options, &self.path, &self.columns)?);
|
||||
|
||||
let cf = db.cf_handle(column).unwrap();
|
||||
let cfs = vec![
|
||||
db.cf_handle(&self.columns[0]).unwrap(),
|
||||
db.cf_handle(&self.columns[1]).unwrap(),
|
||||
db.cf_handle(&self.columns[2]).unwrap(),
|
||||
];
|
||||
|
||||
let mut matching_keys: Vec<StringRow> = Vec::new();
|
||||
let mut matching_keys: Vec<DatabaseResult> = Vec::new();
|
||||
|
||||
// Use RocksDB's iterator for efficient scanning
|
||||
let iter = db.iterator_cf(cf, IteratorMode::Start);
|
||||
@@ -238,7 +352,7 @@ impl ResultDatabase {
|
||||
if value_str.contains(substring) {
|
||||
// Convert key to string and add to results
|
||||
if let Ok(key_str) = std::str::from_utf8(&key_bytes) {
|
||||
if let Some(row) = decode_row_binary(key_str, &value_bytes) {
|
||||
if let Some(row) = self.fetch_row(&db, key_str, &cfs) {
|
||||
matching_keys.push(row);
|
||||
}
|
||||
}
|
||||
@@ -248,76 +362,23 @@ impl ResultDatabase {
|
||||
|
||||
Ok(matching_keys)
|
||||
}
|
||||
}
|
||||
|
||||
// Fast minimal escaping for key values
|
||||
#[inline]
|
||||
fn fast_escape(s: &str) -> String {
|
||||
// Only escape the colon character which is our separator
|
||||
s.replace(":", "\\:")
|
||||
}
|
||||
|
||||
// Fast unescaping for key values
|
||||
#[inline]
|
||||
|
||||
// Fast direct row fetch by ID
|
||||
fn fetch_row(db: &DB, cf_default: &ColumnFamily, row_id: &str) -> Option<StringRow> {
|
||||
match db.get_cf(cf_default, row_id.as_bytes()) {
|
||||
Ok(Some(value)) => decode_row_binary(row_id, &value),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
// Binary decoding of row data
|
||||
fn decode_row_binary(key: &str, data: &[u8]) -> Option<StringRow> {
|
||||
if data.len() < 8 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut pos = 0;
|
||||
|
||||
let mut values_count_bytes = [0u8; 4];
|
||||
values_count_bytes.copy_from_slice(&data[pos..pos + 4]);
|
||||
let values_count = u32::from_le_bytes(values_count_bytes) as usize;
|
||||
pos += 4;
|
||||
|
||||
// Read values
|
||||
let mut values = Vec::with_capacity(values_count);
|
||||
for _ in 0..values_count {
|
||||
if pos + 4 > data.len() {
|
||||
return None;
|
||||
fn fetch_row(&self, db: &DB, row_id: &str, cfs: &Vec<&ColumnFamily>) -> Option<DatabaseResult> {
|
||||
match db.get_cf(&cfs[0], row_id.as_bytes()) {
|
||||
Ok(Some(_)) => Some(DatabaseResult {
|
||||
id: row_id.to_string(),
|
||||
ports: split_nums(&self.row_to_string(db, row_id, &cfs[1]), ","),
|
||||
services: self.row_to_string(db, row_id, &cfs[2]),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
let mut value_len_bytes = [0u8; 4];
|
||||
value_len_bytes.copy_from_slice(&data[pos..pos + 4]);
|
||||
let value_len = u32::from_le_bytes(value_len_bytes) as usize;
|
||||
pos += 4;
|
||||
|
||||
if pos + value_len > data.len() {
|
||||
return None;
|
||||
fn row_to_string(&self, db: &DB, row_id: &str, cf: &ColumnFamily) -> String {
|
||||
if let Ok(Some(data)) = &db.get_cf(cf, row_id) {
|
||||
String::from_utf8_lossy(data).to_string()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
|
||||
let value = String::from_utf8_lossy(&data[pos..pos + value_len]).to_string();
|
||||
values.push(value);
|
||||
pos += value_len;
|
||||
}
|
||||
|
||||
Some(StringRow {
|
||||
id: key.to_string(),
|
||||
ports: split_nums(values[0].as_str(), ","),
|
||||
})
|
||||
}
|
||||
|
||||
// Binary encoding of row data for maximum performance
|
||||
fn encode_row_binary(buf: &mut Vec<u8>, row: &StringRow) {
|
||||
let values = vec![row.ports_to_string()];
|
||||
|
||||
// Write number of values
|
||||
buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
|
||||
|
||||
// Write each value
|
||||
for value in vec![row.ports_to_string()] {
|
||||
let value_bytes = value.as_bytes();
|
||||
buf.extend_from_slice(&(value_bytes.len() as u32).to_le_bytes());
|
||||
buf.extend_from_slice(value_bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ pub mod database;
|
||||
pub mod online_scan;
|
||||
pub mod parse_ip_range;
|
||||
pub mod port_scan;
|
||||
pub mod service_scan;
|
||||
|
||||
+121
-92
@@ -1,7 +1,10 @@
|
||||
use std::{env, net::IpAddr, time::Duration};
|
||||
use std::{cmp::min, env, net::IpAddr, time::Duration};
|
||||
|
||||
use parse_ip_range::parse_ip_targets;
|
||||
use untitled::{database::ResultDatabase, online_scan, parse_ip_range, port_scan::tcp_scan};
|
||||
use untitled::{
|
||||
database::ResultDatabase, online_scan, parse_ip_range, port_scan::tcp_scan,
|
||||
service_scan::service_scan::scan_services,
|
||||
};
|
||||
|
||||
const BATCH_SIZE: usize = 4096;
|
||||
|
||||
@@ -47,6 +50,74 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const PORTS: [i32; 1000] = [
|
||||
25565, 3, 4, 6, 7, 9, 13, 17, 19, 20, 21, 22, 23, 24, 25, 26, 30, 32, 33, 37, 42, 43, 49, 53,
|
||||
70, 79, 80, 81, 82, 83, 84, 85, 88, 89, 90, 99, 100, 106, 109, 110, 111, 113, 119, 125, 135,
|
||||
139, 143, 144, 146, 161, 163, 179, 199, 211, 212, 222, 254, 255, 256, 259, 264, 280, 301, 306,
|
||||
311, 340, 366, 389, 406, 407, 416, 417, 425, 427, 443, 444, 445, 458, 464, 465, 481, 497, 500,
|
||||
512, 513, 514, 515, 524, 541, 543, 544, 545, 548, 554, 555, 563, 587, 593, 616, 617, 625, 631,
|
||||
636, 646, 648, 666, 667, 668, 683, 687, 691, 700, 705, 711, 714, 720, 722, 726, 749, 765, 777,
|
||||
783, 787, 800, 801, 808, 843, 873, 880, 888, 898, 900, 901, 902, 903, 911, 912, 981, 987, 990,
|
||||
992, 993, 995, 999, 1000, 1001, 1002, 1007, 1009, 1010, 1011, 1021, 1022, 1023, 1024, 1025,
|
||||
1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041,
|
||||
1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057,
|
||||
1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073,
|
||||
1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089,
|
||||
1090, 1091, 1092, 1093, 1094, 1095, 1096, 1097, 1098, 1099, 1100, 1102, 1104, 1105, 1106, 1107,
|
||||
1108, 1110, 1111, 1112, 1113, 1114, 1117, 1119, 1121, 1122, 1123, 1124, 1126, 1130, 1131, 1132,
|
||||
1137, 1138, 1141, 1145, 1147, 1148, 1149, 1151, 1152, 1154, 1163, 1164, 1165, 1166, 1169, 1174,
|
||||
1175, 1183, 1185, 1186, 1187, 1192, 1198, 1199, 1201, 1213, 1216, 1217, 1218, 1233, 1234, 1236,
|
||||
1244, 1247, 1248, 1259, 1271, 1272, 1277, 1287, 1296, 1300, 1301, 1309, 1310, 1311, 1322, 1328,
|
||||
1334, 1352, 1417, 1433, 1434, 1443, 1455, 1461, 1494, 1500, 1501, 1503, 1521, 1524, 1533, 1556,
|
||||
1580, 1583, 1594, 1600, 1641, 1658, 1666, 1687, 1688, 1700, 1717, 1718, 1719, 1720, 1721, 1723,
|
||||
1755, 1761, 1782, 1783, 1801, 1805, 1812, 1839, 1840, 1862, 1863, 1864, 1875, 1900, 1914, 1935,
|
||||
1947, 1971, 1972, 1974, 1984, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
|
||||
2009, 2010, 2013, 2020, 2021, 2022, 2030, 2033, 2034, 2035, 2038, 2040, 2041, 2042, 2043, 2045,
|
||||
2046, 2047, 2048, 2049, 2065, 2068, 2099, 2100, 2103, 2105, 2106, 2107, 2111, 2119, 2121, 2126,
|
||||
2135, 2144, 2160, 2161, 2170, 2179, 2190, 2191, 2196, 2200, 2222, 2251, 2260, 2288, 2301, 2323,
|
||||
2366, 2381, 2382, 2383, 2393, 2394, 2399, 2401, 2492, 2500, 2522, 2525, 2557, 2601, 2602, 2604,
|
||||
2605, 2607, 2608, 2638, 2701, 2702, 2710, 2717, 2718, 2725, 2800, 2809, 2811, 2869, 2875, 2909,
|
||||
2910, 2920, 2967, 2968, 2998, 3000, 3001, 3003, 3005, 3006, 3007, 3011, 3013, 3017, 3030, 3031,
|
||||
3052, 3071, 3077, 3128, 3168, 3211, 3221, 3260, 3261, 3268, 3269, 3283, 3300, 3301, 3306, 3322,
|
||||
3323, 3324, 3325, 3333, 3351, 3367, 3369, 3370, 3371, 3372, 3389, 3390, 3404, 3476, 3493, 3517,
|
||||
3527, 3546, 3551, 3580, 3659, 3689, 3690, 3703, 3737, 3766, 3784, 3800, 3801, 3809, 3814, 3826,
|
||||
3827, 3828, 3851, 3869, 3871, 3878, 3880, 3889, 3905, 3914, 3918, 3920, 3945, 3971, 3986, 3995,
|
||||
3998, 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4045, 4111, 4125, 4126, 4129, 4224, 4242, 4279,
|
||||
4321, 4343, 4443, 4444, 4445, 4446, 4449, 4550, 4567, 4662, 4848, 4899, 4900, 4998, 5000, 5001,
|
||||
5002, 5003, 5004, 5009, 5030, 5033, 5050, 5051, 5054, 5060, 5061, 5080, 5087, 5100, 5101, 5102,
|
||||
5120, 5190, 5200, 5214, 5221, 5222, 5225, 5226, 5269, 5280, 5298, 5357, 5405, 5414, 5431, 5432,
|
||||
5440, 5500, 5510, 5544, 5550, 5555, 5560, 5566, 5631, 5633, 5666, 5678, 5679, 5718, 5730, 5800,
|
||||
5801, 5802, 5810, 5811, 5815, 5822, 5825, 5850, 5859, 5862, 5877, 5900, 5901, 5902, 5903, 5904,
|
||||
5906, 5907, 5910, 5911, 5915, 5922, 5925, 5950, 5952, 5959, 5960, 5961, 5962, 5963, 5987, 5988,
|
||||
5989, 5998, 5999, 6000, 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6009, 6025, 6059, 6100, 6101,
|
||||
6106, 6112, 6123, 6129, 6156, 6346, 6389, 6502, 6510, 6543, 6547, 6565, 6566, 6567, 6580, 6646,
|
||||
6666, 6667, 6668, 6669, 6689, 6692, 6699, 6779, 6788, 6789, 6792, 6839, 6881, 6901, 6969, 7000,
|
||||
7001, 7002, 7004, 7007, 7019, 7025, 7070, 7100, 7103, 7106, 7200, 7201, 7402, 7435, 7443, 7496,
|
||||
7512, 7625, 7627, 7676, 7741, 7777, 7778, 7800, 7911, 7920, 7921, 7937, 7938, 7999, 8000, 8001,
|
||||
8002, 8007, 8008, 8009, 8010, 8011, 8021, 8022, 8031, 8042, 8045, 8080, 8081, 8082, 8083, 8084,
|
||||
8085, 8086, 8087, 8088, 8089, 8090, 8093, 8099, 8100, 8180, 8181, 8192, 8193, 8194, 8200, 8222,
|
||||
8254, 8290, 8291, 8292, 8300, 8333, 8383, 8400, 8402, 8443, 8500, 8600, 8649, 8651, 8652, 8654,
|
||||
8701, 8800, 8873, 8888, 8899, 8994, 9000, 9001, 9002, 9003, 9009, 9010, 9011, 9040, 9050, 9071,
|
||||
9080, 9081, 9090, 9091, 9099, 9100, 9101, 9102, 9103, 9110, 9111, 9200, 9207, 9220, 9290, 9415,
|
||||
9418, 9485, 9500, 9502, 9503, 9535, 9575, 9593, 9594, 9595, 9618, 9666, 9876, 9877, 9878, 9898,
|
||||
9900, 9917, 9929, 9943, 9944, 9968, 9998, 9999, 10000, 10001, 10002, 10003, 10004, 10009,
|
||||
10010, 10012, 10024, 10025, 10082, 10180, 10215, 10243, 10566, 10616, 10617, 10621, 10626,
|
||||
10628, 10629, 10778, 11110, 11111, 11967, 12000, 12174, 12265, 12345, 13456, 13722, 13782,
|
||||
13783, 14000, 14238, 14441, 14442, 15000, 15002, 15003, 15004, 15660, 15742, 16000, 16001,
|
||||
16012, 16016, 16018, 16080, 16113, 16992, 16993, 17877, 17988, 18040, 18101, 18988, 19101,
|
||||
19283, 19315, 19350, 19780, 19801, 19842, 20000, 20005, 20031, 20221, 20222, 20828, 21571,
|
||||
22939, 23502, 24444, 24800, 25734, 25735, 26214, 27000, 27352, 27353, 27355, 27356, 27715,
|
||||
28201, 30000, 30718, 30951, 31038, 31337, 32768, 32769, 32770, 32771, 32772, 32773, 32774,
|
||||
32775, 32776, 32777, 32778, 32779, 32780, 32781, 32782, 32783, 32784, 32785, 33354, 33899,
|
||||
34571, 34572, 34573, 35500, 38292, 40193, 40911, 41511, 42510, 44176, 44442, 44443, 44501,
|
||||
45100, 48080, 49152, 49153, 49154, 49155, 49156, 49157, 49158, 49159, 49160, 49161, 49163,
|
||||
49165, 49167, 49175, 49176, 49400, 49999, 50000, 50001, 50002, 50003, 50006, 50300, 50389,
|
||||
50500, 50636, 50800, 51103, 51493, 52673, 52822, 52848, 52869, 54045, 54328, 55055, 55056,
|
||||
55555, 55600, 56737, 56738, 57294, 57797, 58080, 60020, 60443, 61532, 61900, 62078, 63331,
|
||||
64623, 64680, 65000, 65129, 65389,
|
||||
];
|
||||
|
||||
fn scan(
|
||||
database: ResultDatabase,
|
||||
search_type: String,
|
||||
@@ -83,91 +154,41 @@ fn scan(
|
||||
);
|
||||
let _ = database.add_ping_results(&up_hosts);
|
||||
|
||||
let tcp_ports = vec![
|
||||
25565, 3, 4, 6, 7, 9, 13, 17, 19, 20, 21, 22, 23, 24, 25, 26, 30, 32, 33, 37,
|
||||
42, 43, 49, 53, 70, 79, 80, 81, 82, 83, 84, 85, 88, 89, 90, 99, 100, 106, 109,
|
||||
110, 111, 113, 119, 125, 135, 139, 143, 144, 146, 161, 163, 179, 199, 211, 212,
|
||||
222, 254, 255, 256, 259, 264, 280, 301, 306, 311, 340, 366, 389, 406, 407, 416,
|
||||
417, 425, 427, 443, 444, 445, 458, 464, 465, 481, 497, 500, 512, 513, 514, 515,
|
||||
524, 541, 543, 544, 545, 548, 554, 555, 563, 587, 593, 616, 617, 625, 631, 636,
|
||||
646, 648, 666, 667, 668, 683, 687, 691, 700, 705, 711, 714, 720, 722, 726, 749,
|
||||
765, 777, 783, 787, 800, 801, 808, 843, 873, 880, 888, 898, 900, 901, 902, 903,
|
||||
911, 912, 981, 987, 990, 992, 993, 995, 999, 1000, 1001, 1002, 1007, 1009,
|
||||
1010, 1011, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031,
|
||||
1032, 1033, 1034, 1035, 1036, 1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044,
|
||||
1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056, 1057,
|
||||
1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070,
|
||||
1071, 1072, 1073, 1074, 1075, 1076, 1077, 1078, 1079, 1080, 1081, 1082, 1083,
|
||||
1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096,
|
||||
1097, 1098, 1099, 1100, 1102, 1104, 1105, 1106, 1107, 1108, 1110, 1111, 1112,
|
||||
1113, 1114, 1117, 1119, 1121, 1122, 1123, 1124, 1126, 1130, 1131, 1132, 1137,
|
||||
1138, 1141, 1145, 1147, 1148, 1149, 1151, 1152, 1154, 1163, 1164, 1165, 1166,
|
||||
1169, 1174, 1175, 1183, 1185, 1186, 1187, 1192, 1198, 1199, 1201, 1213, 1216,
|
||||
1217, 1218, 1233, 1234, 1236, 1244, 1247, 1248, 1259, 1271, 1272, 1277, 1287,
|
||||
1296, 1300, 1301, 1309, 1310, 1311, 1322, 1328, 1334, 1352, 1417, 1433, 1434,
|
||||
1443, 1455, 1461, 1494, 1500, 1501, 1503, 1521, 1524, 1533, 1556, 1580, 1583,
|
||||
1594, 1600, 1641, 1658, 1666, 1687, 1688, 1700, 1717, 1718, 1719, 1720, 1721,
|
||||
1723, 1755, 1761, 1782, 1783, 1801, 1805, 1812, 1839, 1840, 1862, 1863, 1864,
|
||||
1875, 1900, 1914, 1935, 1947, 1971, 1972, 1974, 1984, 1998, 1999, 2000, 2001,
|
||||
2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013, 2020, 2021, 2022,
|
||||
2030, 2033, 2034, 2035, 2038, 2040, 2041, 2042, 2043, 2045, 2046, 2047, 2048,
|
||||
2049, 2065, 2068, 2099, 2100, 2103, 2105, 2106, 2107, 2111, 2119, 2121, 2126,
|
||||
2135, 2144, 2160, 2161, 2170, 2179, 2190, 2191, 2196, 2200, 2222, 2251, 2260,
|
||||
2288, 2301, 2323, 2366, 2381, 2382, 2383, 2393, 2394, 2399, 2401, 2492, 2500,
|
||||
2522, 2525, 2557, 2601, 2602, 2604, 2605, 2607, 2608, 2638, 2701, 2702, 2710,
|
||||
2717, 2718, 2725, 2800, 2809, 2811, 2869, 2875, 2909, 2910, 2920, 2967, 2968,
|
||||
2998, 3000, 3001, 3003, 3005, 3006, 3007, 3011, 3013, 3017, 3030, 3031, 3052,
|
||||
3071, 3077, 3128, 3168, 3211, 3221, 3260, 3261, 3268, 3269, 3283, 3300, 3301,
|
||||
3306, 3322, 3323, 3324, 3325, 3333, 3351, 3367, 3369, 3370, 3371, 3372, 3389,
|
||||
3390, 3404, 3476, 3493, 3517, 3527, 3546, 3551, 3580, 3659, 3689, 3690, 3703,
|
||||
3737, 3766, 3784, 3800, 3801, 3809, 3814, 3826, 3827, 3828, 3851, 3869, 3871,
|
||||
3878, 3880, 3889, 3905, 3914, 3918, 3920, 3945, 3971, 3986, 3995, 3998, 4000,
|
||||
4001, 4002, 4003, 4004, 4005, 4006, 4045, 4111, 4125, 4126, 4129, 4224, 4242,
|
||||
4279, 4321, 4343, 4443, 4444, 4445, 4446, 4449, 4550, 4567, 4662, 4848, 4899,
|
||||
4900, 4998, 5000, 5001, 5002, 5003, 5004, 5009, 5030, 5033, 5050, 5051, 5054,
|
||||
5060, 5061, 5080, 5087, 5100, 5101, 5102, 5120, 5190, 5200, 5214, 5221, 5222,
|
||||
5225, 5226, 5269, 5280, 5298, 5357, 5405, 5414, 5431, 5432, 5440, 5500, 5510,
|
||||
5544, 5550, 5555, 5560, 5566, 5631, 5633, 5666, 5678, 5679, 5718, 5730, 5800,
|
||||
5801, 5802, 5810, 5811, 5815, 5822, 5825, 5850, 5859, 5862, 5877, 5900, 5901,
|
||||
5902, 5903, 5904, 5906, 5907, 5910, 5911, 5915, 5922, 5925, 5950, 5952, 5959,
|
||||
5960, 5961, 5962, 5963, 5987, 5988, 5989, 5998, 5999, 6000, 6001, 6002, 6003,
|
||||
6004, 6005, 6006, 6007, 6009, 6025, 6059, 6100, 6101, 6106, 6112, 6123, 6129,
|
||||
6156, 6346, 6389, 6502, 6510, 6543, 6547, 6565, 6566, 6567, 6580, 6646, 6666,
|
||||
6667, 6668, 6669, 6689, 6692, 6699, 6779, 6788, 6789, 6792, 6839, 6881, 6901,
|
||||
6969, 7000, 7001, 7002, 7004, 7007, 7019, 7025, 7070, 7100, 7103, 7106, 7200,
|
||||
7201, 7402, 7435, 7443, 7496, 7512, 7625, 7627, 7676, 7741, 7777, 7778, 7800,
|
||||
7911, 7920, 7921, 7937, 7938, 7999, 8000, 8001, 8002, 8007, 8008, 8009, 8010,
|
||||
8011, 8021, 8022, 8031, 8042, 8045, 8080, 8081, 8082, 8083, 8084, 8085, 8086,
|
||||
8087, 8088, 8089, 8090, 8093, 8099, 8100, 8180, 8181, 8192, 8193, 8194, 8200,
|
||||
8222, 8254, 8290, 8291, 8292, 8300, 8333, 8383, 8400, 8402, 8443, 8500, 8600,
|
||||
8649, 8651, 8652, 8654, 8701, 8800, 8873, 8888, 8899, 8994, 9000, 9001, 9002,
|
||||
9003, 9009, 9010, 9011, 9040, 9050, 9071, 9080, 9081, 9090, 9091, 9099, 9100,
|
||||
9101, 9102, 9103, 9110, 9111, 9200, 9207, 9220, 9290, 9415, 9418, 9485, 9500,
|
||||
9502, 9503, 9535, 9575, 9593, 9594, 9595, 9618, 9666, 9876, 9877, 9878, 9898,
|
||||
9900, 9917, 9929, 9943, 9944, 9968, 9998, 9999, 10000, 10001, 10002, 10003,
|
||||
10004, 10009, 10010, 10012, 10024, 10025, 10082, 10180, 10215, 10243, 10566,
|
||||
10616, 10617, 10621, 10626, 10628, 10629, 10778, 11110, 11111, 11967, 12000,
|
||||
12174, 12265, 12345, 13456, 13722, 13782, 13783, 14000, 14238, 14441, 14442,
|
||||
15000, 15002, 15003, 15004, 15660, 15742, 16000, 16001, 16012, 16016, 16018,
|
||||
16080, 16113, 16992, 16993, 17877, 17988, 18040, 18101, 18988, 19101, 19283,
|
||||
19315, 19350, 19780, 19801, 19842, 20000, 20005, 20031, 20221, 20222, 20828,
|
||||
21571, 22939, 23502, 24444, 24800, 25734, 25735, 26214, 27000, 27352, 27353,
|
||||
27355, 27356, 27715, 28201, 30000, 30718, 30951, 31038, 31337, 32768, 32769,
|
||||
32770, 32771, 32772, 32773, 32774, 32775, 32776, 32777, 32778, 32779, 32780,
|
||||
32781, 32782, 32783, 32784, 32785, 33354, 33899, 34571, 34572, 34573, 35500,
|
||||
38292, 40193, 40911, 41511, 42510, 44176, 44442, 44443, 44501, 45100, 48080,
|
||||
49152, 49153, 49154, 49155, 49156, 49157, 49158, 49159, 49160, 49161, 49163,
|
||||
49165, 49167, 49175, 49176, 49400, 49999, 50000, 50001, 50002, 50003, 50006,
|
||||
50300, 50389, 50500, 50636, 50800, 51103, 51493, 52673, 52822, 52848, 52869,
|
||||
54045, 54328, 55055, 55056, 55555, 55600, 56737, 56738, 57294, 57797, 58080,
|
||||
60020, 60443, 61532, 61900, 62078, 63331, 64623, 64680, 65000, 65129, 65389,
|
||||
];
|
||||
|
||||
let tcp_results = tcp_scan::tcp_scan(up_hosts, tcp_ports, Duration::from_secs(3));
|
||||
let tcp_results =
|
||||
tcp_scan::tcp_scan(up_hosts, PORTS.to_vec(), Duration::from_secs(3));
|
||||
println!("Saving Data...");
|
||||
let _ = database.add_tcp_results(&tcp_results);
|
||||
}
|
||||
}
|
||||
"service" => {
|
||||
let chunks = hosts.chunks(BATCH_SIZE);
|
||||
let num_chunks = chunks.len();
|
||||
for (i, hosts) in chunks.enumerate() {
|
||||
let hosts = hosts.to_vec();
|
||||
let length = hosts.len();
|
||||
|
||||
println!("Scanning chunk {}/{} ({} hosts)", i + 1, num_chunks, length);
|
||||
|
||||
let up_hosts: Vec<IpAddr> = online_scan::ping_scanner::ping_scan(hosts).unwrap();
|
||||
let up_len = up_hosts.len();
|
||||
println!(
|
||||
"Finished Pinging! {} Scanned, {} Up",
|
||||
length,
|
||||
up_hosts.len()
|
||||
);
|
||||
let _ = database.add_ping_results(&up_hosts);
|
||||
|
||||
let tcp_results =
|
||||
tcp_scan::tcp_scan(up_hosts, PORTS.to_vec(), Duration::from_secs(3));
|
||||
println!("Finished port scan");
|
||||
let _ = database.add_tcp_results(&tcp_results);
|
||||
|
||||
let service_results =
|
||||
scan_services(tcp_results, min(500, up_len), Duration::from_secs(3));
|
||||
println!("Finished service scan");
|
||||
let _ = database.add_service_results(&service_results);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Invalid search type!");
|
||||
}
|
||||
@@ -194,6 +215,14 @@ fn search(database: ResultDatabase, search_type: String, arg: String) {
|
||||
println!("{}", row.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
"service" => {
|
||||
let rows = database.get_rows_by_service(&arg);
|
||||
|
||||
for row in rows {
|
||||
println!("{}", row.to_string());
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
println!("Invalid search type!");
|
||||
}
|
||||
@@ -207,18 +236,18 @@ fn print_help(arg: Option<&str>) {
|
||||
None => {
|
||||
"rust-scan help menu
|
||||
Commands:
|
||||
scan <type> <argument> - scan a block of addresses and check for online using icmp echo
|
||||
search <type> <argument> - Search database
|
||||
help (command) - Print help"
|
||||
}
|
||||
Some("pingscan") => {
|
||||
"pingscan <addresses>
|
||||
scan a block of addresses and check for online using icmp echo
|
||||
Usage: pingscan 10.42.0.1,12.34.0.0-12.34.56.78,127.0.0.0/8"
|
||||
scan <type> <hosts> (arguments) - scan a block of addresses and check for online using icmp echo
|
||||
search <type> <arguments> - Search database
|
||||
help (command) - Print help"
|
||||
}
|
||||
// Some("scan") => {
|
||||
// "pingscan <addresses>
|
||||
// scan a block of addresses and check for online using icmp echo
|
||||
// Usage: pingscan 10.42.0.1,12.34.0.0-12.34.56.78,127.0.0.0/8"
|
||||
// }
|
||||
Some(_) => {
|
||||
print_help(None);
|
||||
""
|
||||
"Invalid Command!"
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::{net::IpAddr, time::Duration};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::database::StringRow;
|
||||
use crate::database::DatabaseResult;
|
||||
|
||||
// Structure to hold ping results
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
@@ -21,10 +21,11 @@ impl PingResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_string_row(&self) -> StringRow {
|
||||
StringRow {
|
||||
pub fn to_database(&self) -> DatabaseResult {
|
||||
DatabaseResult {
|
||||
id: self.host.to_string(),
|
||||
ports: vec![],
|
||||
services: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
use std::net::IpAddr;
|
||||
|
||||
use crate::database::StringRow;
|
||||
use crate::database::DatabaseResult;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScanResult {
|
||||
pub struct PortScanResult {
|
||||
pub ip: IpAddr,
|
||||
pub open_ports: Vec<i32>,
|
||||
// pub data: HashMap<i32, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl ScanResult {
|
||||
impl PortScanResult {
|
||||
pub fn new(ip: IpAddr) -> Self {
|
||||
ScanResult {
|
||||
PortScanResult {
|
||||
ip,
|
||||
open_ports: Vec::new(),
|
||||
// data: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn to_string_row(&self) -> StringRow {
|
||||
StringRow {
|
||||
pub fn to_database(&self) -> DatabaseResult {
|
||||
DatabaseResult {
|
||||
id: self.ip.to_string(),
|
||||
ports: (*self.open_ports).to_vec(),
|
||||
services: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-20
@@ -6,28 +6,23 @@ use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use indicatif::ProgressBar;
|
||||
use pnet::datalink::linux::interfaces;
|
||||
use pnet::datalink::{self};
|
||||
use pnet::packet::ip::IpNextHeaderProtocols;
|
||||
use pnet::packet::tcp::{MutableTcpPacket, TcpFlags, TcpPacket};
|
||||
use pnet::packet::{tcp, Packet};
|
||||
use pnet::packet::{Packet, tcp};
|
||||
use pnet::transport::{self, TransportChannelType, TransportSender};
|
||||
use rand::random_range;
|
||||
|
||||
use super::port_scan::ScanResult;
|
||||
use super::port_scan::PortScanResult;
|
||||
|
||||
fn std_to_pnet_ipv4(previous: &IpAddr) -> Ipv4Addr {
|
||||
Ipv4Addr::from_str(previous.to_string().as_str()).unwrap()
|
||||
}
|
||||
|
||||
// Main scanning function
|
||||
pub fn tcp_scan(targets: Vec<IpAddr>, ports: Vec<i32>, timeout: Duration) -> Vec<ScanResult> {
|
||||
// Find network interface
|
||||
let interfaces = datalink::interfaces();
|
||||
|
||||
println!("{}", interfaces.len());
|
||||
|
||||
let interface = interfaces
|
||||
pub fn tcp_scan(targets: Vec<IpAddr>, ports: Vec<i32>, timeout: Duration) -> Vec<PortScanResult> {
|
||||
// Search for VPN connection and fall back to regular
|
||||
let interface = datalink::interfaces()
|
||||
.into_iter()
|
||||
.find(|iface| {
|
||||
iface.is_up()
|
||||
@@ -37,6 +32,14 @@ pub fn tcp_scan(targets: Vec<IpAddr>, ports: Vec<i32>, timeout: Duration) -> Vec
|
||||
&& iface.is_running()
|
||||
&& iface.is_point_to_point()
|
||||
})
|
||||
.or(datalink::interfaces().into_iter().find(|iface| {
|
||||
iface.is_up()
|
||||
&& !iface.is_loopback()
|
||||
&& !iface.ips.is_empty()
|
||||
&& !iface.is_dormant()
|
||||
&& iface.is_running()
|
||||
&& !iface.is_point_to_point()
|
||||
}))
|
||||
.expect("No valid network interface found");
|
||||
|
||||
// Create transport channel for sending and receiving
|
||||
@@ -97,19 +100,21 @@ pub fn tcp_scan(targets: Vec<IpAddr>, ports: Vec<i32>, timeout: Duration) -> Vec
|
||||
|
||||
let pb = ProgressBar::new((targets.len() * ports.len()) as u64);
|
||||
|
||||
println!("{:?}", interface.ips);
|
||||
// println!("{:?}", interface.ips);
|
||||
|
||||
let source_ip = interface
|
||||
.ips
|
||||
.iter()
|
||||
.find(|ip| ip.is_ipv4())
|
||||
.expect("No IPv4 address found")
|
||||
.ip();
|
||||
let source_ip = std_to_pnet_ipv4(
|
||||
&interface
|
||||
.ips
|
||||
.iter()
|
||||
.find(|ip| ip.is_ipv4())
|
||||
.expect("No IPv4 address found")
|
||||
.ip(),
|
||||
);
|
||||
|
||||
// let source_ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
||||
// let source_ip = IpAddr::V4(Ipv4Addr::new(10, 0, 70, 4));
|
||||
|
||||
println!("Using IP: {}", source_ip.to_string());
|
||||
// println!("Using IP: {}", source_ip.to_string());
|
||||
|
||||
for target in &targets {
|
||||
for port in &ports {
|
||||
@@ -134,7 +139,11 @@ pub fn tcp_scan(targets: Vec<IpAddr>, ports: Vec<i32>, timeout: Duration) -> Vec
|
||||
// Calculate checksum
|
||||
let checksum = tcp::ipv4_checksum(
|
||||
&tcp_header.to_immutable(),
|
||||
&std_to_pnet_ipv4(&source_ip),
|
||||
if !target.is_loopback() {
|
||||
&source_ip
|
||||
} else {
|
||||
&Ipv4Addr::LOCALHOST
|
||||
},
|
||||
&std_to_pnet_ipv4(&target),
|
||||
);
|
||||
tcp_header.set_checksum(checksum);
|
||||
@@ -158,7 +167,7 @@ pub fn tcp_scan(targets: Vec<IpAddr>, ports: Vec<i32>, timeout: Duration) -> Vec
|
||||
let mut open_ports = results_map.get(ip).cloned().unwrap_or_default();
|
||||
open_ports.sort();
|
||||
open_ports.dedup();
|
||||
ScanResult {
|
||||
PortScanResult {
|
||||
ip: *ip,
|
||||
open_ports,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod service_scan;
|
||||
pub mod services;
|
||||
pub mod tcp_http;
|
||||
pub mod tcp_https;
|
||||
@@ -0,0 +1,241 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::{Read, Write},
|
||||
net::{IpAddr, SocketAddr, TcpStream},
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use indicatif::ProgressBar;
|
||||
|
||||
use crate::{
|
||||
database::DatabaseResult, port_scan::port_scan::PortScanResult, service_scan::tcp_http,
|
||||
};
|
||||
|
||||
use super::{services::SERVICE_PATTERNS, tcp_https};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServiceScanResult {
|
||||
pub ip: IpAddr,
|
||||
pub open_ports: Vec<i32>,
|
||||
pub services: HashMap<i32, (String, String)>,
|
||||
}
|
||||
|
||||
impl ServiceScanResult {
|
||||
fn new(ip: IpAddr) -> Self {
|
||||
ServiceScanResult {
|
||||
ip,
|
||||
open_ports: Vec::new(),
|
||||
services: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn to_database(&self) -> DatabaseResult {
|
||||
DatabaseResult {
|
||||
id: self.ip.to_string(),
|
||||
ports: self.open_ports.clone(),
|
||||
services: serde_json::to_string(&self.services).unwrap_or(String::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identify(ip: IpAddr, port: &i32, timeout: Duration) -> (String, String) {
|
||||
let e = || basic_identify(ip, port, timeout).unwrap_or(("tcp".to_string(), "".to_string()));
|
||||
|
||||
match port {
|
||||
80 | 8080 | 8081 | 8082 | 8083 | 8084 | 8085 | 8086 | 8087 | 8088 | 8089 => {
|
||||
tuple_or_none("http", tcp_http::scan(ip, port, timeout)).unwrap_or(e())
|
||||
}
|
||||
443 | 8443 => tuple_or_none("https", tcp_https::scan(ip, port, timeout)).unwrap_or(e()),
|
||||
_ => e(),
|
||||
}
|
||||
}
|
||||
|
||||
fn tuple_or_none(
|
||||
tag: &str,
|
||||
data: Result<String, Box<dyn std::error::Error>>,
|
||||
) -> Option<(String, String)> {
|
||||
if let Ok(data) = data {
|
||||
Some((tag.to_string(), data))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scan_services(
|
||||
port_scan_results: Vec<PortScanResult>,
|
||||
num_threads: usize,
|
||||
timeout: Duration,
|
||||
) -> Vec<ServiceScanResult> {
|
||||
let mut host_port_count: u64 = 0;
|
||||
let results: Arc<Mutex<Vec<ServiceScanResult>>> = Arc::new(Mutex::new(
|
||||
port_scan_results
|
||||
.iter()
|
||||
.map(|result| {
|
||||
host_port_count += result.open_ports.len() as u64;
|
||||
ServiceScanResult::new(result.ip)
|
||||
})
|
||||
.collect(),
|
||||
));
|
||||
|
||||
let mut handles = Vec::new();
|
||||
let pb = Arc::new(ProgressBar::new(host_port_count));
|
||||
|
||||
// Create a thread for each chunk of IPs
|
||||
let chunks = split_ips_into_chunks(port_scan_results, num_threads);
|
||||
for chunk in chunks {
|
||||
let chunk_hosts = chunk.clone();
|
||||
let thread_results = Arc::clone(&results);
|
||||
let thread_timeout = timeout;
|
||||
let thread_pb = Arc::clone(&pb);
|
||||
handles.push(thread::spawn(move || {
|
||||
for host in chunk_hosts {
|
||||
let ports = &host.open_ports;
|
||||
for port in ports {
|
||||
// Try to identify the service on the port
|
||||
let (service_name, banner) = identify(host.ip, port, thread_timeout);
|
||||
|
||||
let mut results_guard = thread_results.lock().unwrap();
|
||||
if let Some(result) = results_guard.iter_mut().find(|r| r.ip == host.ip) {
|
||||
result.open_ports.push(*port);
|
||||
result.services.insert(*port, (service_name, banner));
|
||||
}
|
||||
|
||||
thread_pb.inc(1);
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for handle in handles {
|
||||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
pb.clone().finish_and_clear();
|
||||
|
||||
Arc::try_unwrap(results)
|
||||
.expect("Arc still has multiple owners")
|
||||
.into_inner()
|
||||
.expect("Mutex poisoned")
|
||||
.into_iter()
|
||||
.map(|a| {
|
||||
println!("{:?}", a);
|
||||
a
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Helper function to split the IPs into roughly equal chunks for threading
|
||||
fn split_ips_into_chunks(ips: Vec<PortScanResult>, num_chunks: usize) -> Vec<Vec<PortScanResult>> {
|
||||
let chunk_size = (ips.len() + num_chunks - 1) / num_chunks;
|
||||
let mut chunks = Vec::new();
|
||||
|
||||
for chunk_idx in 0..num_chunks {
|
||||
let start = chunk_idx * chunk_size;
|
||||
if start >= ips.len() {
|
||||
break;
|
||||
}
|
||||
let end = (start + chunk_size).min(ips.len());
|
||||
chunks.push(ips[start..end].to_vec());
|
||||
}
|
||||
|
||||
chunks
|
||||
}
|
||||
|
||||
// Connect to an IP:port and send a probe
|
||||
fn try_connect(ip: IpAddr, port: &i32, timeout: Duration, probe: &[u8]) -> Option<Vec<u8>> {
|
||||
let addr = SocketAddr::new(ip, *port as u16);
|
||||
|
||||
match TcpStream::connect_timeout(&addr, timeout) {
|
||||
Ok(mut stream) => {
|
||||
// Set read/write timeouts
|
||||
let _ = stream.set_read_timeout(Some(timeout));
|
||||
let _ = stream.set_write_timeout(Some(timeout));
|
||||
|
||||
// Send the probe if it's not empty
|
||||
if !probe.is_empty() {
|
||||
if stream.write(probe).is_err() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
// Read the response
|
||||
let mut buffer = [0; 4096]; // Larger buffer for service banners
|
||||
let mut response = Vec::new();
|
||||
|
||||
// Try to read multiple times to get a complete banner
|
||||
for _ in 0..3 {
|
||||
match stream.read(&mut buffer) {
|
||||
Ok(0) => break, // End of stream
|
||||
Ok(bytes_read) => {
|
||||
response.extend_from_slice(&buffer[0..bytes_read]);
|
||||
if bytes_read < buffer.len() {
|
||||
break; // Likely got all data if we read less than buffer size
|
||||
}
|
||||
}
|
||||
Err(_) => break, // Error reading
|
||||
}
|
||||
|
||||
// Small delay between reads
|
||||
thread::sleep(Duration::from_millis(50));
|
||||
}
|
||||
|
||||
Some(response)
|
||||
}
|
||||
Err(_) => None, // Connection failed
|
||||
}
|
||||
}
|
||||
|
||||
fn basic_identify(ip: IpAddr, port: &i32, timeout: Duration) -> Option<(String, String)> {
|
||||
// Try a simple connection with no probe as last resort
|
||||
if let Some(response) = try_connect(ip, port, timeout, b"\x00\n") {
|
||||
if !response.is_empty() {
|
||||
if let Some(service_name) = identify_service_from_response(&response) {
|
||||
return Some((
|
||||
service_name.to_string(),
|
||||
String::from_utf8_lossy(response.as_slice()).to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Port is open but service couldn't be identified
|
||||
return Some(("tcp".to_string(), "".to_string()));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn identify_service_from_response(response: &[u8]) -> Option<&str> {
|
||||
// Convert response to string if possible
|
||||
if let Ok(response_str) = std::str::from_utf8(response) {
|
||||
// Try to match against known patterns
|
||||
for (pattern, service_name) in SERVICE_PATTERNS.iter() {
|
||||
if pattern.is_match(response_str) {
|
||||
return Some(service_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For binary responses, check for pattern matches
|
||||
// Check for SSL/TLS
|
||||
if response.len() >= 3 && response[0] == 0x16 && (response[1] == 0x03 || response[1] == 0x02) {
|
||||
return Some("ssl/tls");
|
||||
}
|
||||
|
||||
// Check for MySQL protocol
|
||||
if response.len() >= 5 && response[0] == 0x4a && response[1] == 0x00 {
|
||||
return Some("mysql");
|
||||
}
|
||||
|
||||
// Check for MongoDB wire protocol
|
||||
if response.len() >= 4
|
||||
&& response[0] == 0x02
|
||||
&& response[1] == 0x00
|
||||
&& response[2] == 0x00
|
||||
&& response[3] == 0x00
|
||||
{
|
||||
return Some("mongodb");
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
@@ -0,0 +1,664 @@
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SERVICE_PATTERNS: Vec<(Regex, &'static str)> = vec![
|
||||
// HTTP and Web Services
|
||||
(Regex::new(r"^HTTP/\d").unwrap(), "http"),
|
||||
(Regex::new(r"Server:").unwrap(), "http"),
|
||||
(Regex::new(r"<html.*>").unwrap(), "http"),
|
||||
(Regex::new(r"<title>.*</title>").unwrap(), "http"),
|
||||
(Regex::new(r"^HTTP/\d+\.\d+ 4\d\d").unwrap(), "http"),
|
||||
(Regex::new(r"^HTTP/\d+\.\d+ 5\d\d").unwrap(), "http"),
|
||||
(Regex::new(r"404 Not Found").unwrap(), "http"),
|
||||
(Regex::new(r"301 Moved Permanently").unwrap(), "http"),
|
||||
(Regex::new(r"Content-Type: text/html").unwrap(), "http"),
|
||||
(Regex::new(r"WebSocket").unwrap(), "websocket"),
|
||||
(Regex::new(r"^WebSphere Application Server").unwrap(), "websphere"),
|
||||
(Regex::new(r"Apache Tomcat").unwrap(), "tomcat"),
|
||||
(Regex::new(r"JBoss").unwrap(), "jboss"),
|
||||
(Regex::new(r"nginx").unwrap(), "nginx"),
|
||||
(Regex::new(r"Ruby on Rails").unwrap(), "rails"),
|
||||
(Regex::new(r"Django").unwrap(), "django"),
|
||||
(Regex::new(r"Express").unwrap(), "express"),
|
||||
(Regex::new(r"Microsoft-IIS").unwrap(), "iis"),
|
||||
(Regex::new(r"Litespeed").unwrap(), "litespeed"),
|
||||
(Regex::new(r"lighttpd").unwrap(), "lighttpd"),
|
||||
(Regex::new(r"^Jetty").unwrap(), "jetty"),
|
||||
(Regex::new(r"^GlassFish Server").unwrap(), "glassfish"),
|
||||
(Regex::new(r"^Oracle-Application-Server").unwrap(), "oracle-as"),
|
||||
(Regex::new(r"WAF/\d").unwrap(), "waf"),
|
||||
(Regex::new(r"Resin/\d").unwrap(), "resin"),
|
||||
|
||||
// SSH
|
||||
(Regex::new(r"^SSH-\d").unwrap(), "ssh"),
|
||||
(Regex::new(r"^SSH-1\.").unwrap(), "ssh1"),
|
||||
(Regex::new(r"^SSH-2\.").unwrap(), "ssh2"),
|
||||
(Regex::new(r"OpenSSH").unwrap(), "openssh"),
|
||||
(Regex::new(r"Dropbear").unwrap(), "dropbear-ssh"),
|
||||
(Regex::new(r"libssh").unwrap(), "libssh"),
|
||||
|
||||
// Email Protocols
|
||||
(Regex::new(r"^220.*SMTP").unwrap(), "smtp"),
|
||||
(Regex::new(r"^220.*ESMTP").unwrap(), "smtp"),
|
||||
(Regex::new(r"^220.*mail").unwrap(), "smtp"),
|
||||
(Regex::new(r"^220.*Email").unwrap(), "smtp"),
|
||||
(Regex::new(r"^220.*Simple Mail").unwrap(), "smtp"),
|
||||
(Regex::new(r"^250[ -]").unwrap(), "smtp"),
|
||||
(Regex::new(r"^554 ").unwrap(), "smtp"),
|
||||
(Regex::new(r"^550 ").unwrap(), "smtp"),
|
||||
(Regex::new(r"^220 .*Exchange").unwrap(), "smtp-exchange"),
|
||||
(Regex::new(r"^220 .*Postfix").unwrap(), "smtp-postfix"),
|
||||
(Regex::new(r"^220 .*Sendmail").unwrap(), "smtp-sendmail"),
|
||||
(Regex::new(r"^\+OK").unwrap(), "pop3"),
|
||||
(Regex::new(r"^\* OK").unwrap(), "imap"),
|
||||
(Regex::new(r"^\* OK .*IMAP").unwrap(), "imap"),
|
||||
(Regex::new(r"^\* OK .*Courier-IMAP").unwrap(), "courier-imap"),
|
||||
(Regex::new(r"^\* OK .*Dovecot").unwrap(), "dovecot-imap"),
|
||||
(Regex::new(r"^\* OK .*UW IMAP").unwrap(), "uw-imap"),
|
||||
(Regex::new(r"^\* PREAUTH").unwrap(), "imap"),
|
||||
(Regex::new(r"^OK LOGIN").unwrap(), "pop3"),
|
||||
(Regex::new(r"^OK CAPA").unwrap(), "pop3"),
|
||||
(Regex::new(r"^\+OK Dovecot").unwrap(), "dovecot-pop3"),
|
||||
(Regex::new(r"^\+OK Courier").unwrap(), "courier-pop3"),
|
||||
(Regex::new(r"^501 5\.5\.4").unwrap(), "smtp"),
|
||||
|
||||
// FTP
|
||||
(Regex::new(r"^220.*FTP").unwrap(), "ftp"),
|
||||
(Regex::new(r"^220 .*FileZilla").unwrap(), "filezilla-ftp"),
|
||||
(Regex::new(r"^220 .*ProFTPD").unwrap(), "proftpd"),
|
||||
(Regex::new(r"^220 .*Pure-FTPd").unwrap(), "pure-ftpd"),
|
||||
(Regex::new(r"^220 .*vsFTPd").unwrap(), "vsftpd"),
|
||||
(Regex::new(r"^220 .*WU-FTPD").unwrap(), "wu-ftpd"),
|
||||
(Regex::new(r"^220 Welcome to Pure-FTPd").unwrap(), "pure-ftpd"),
|
||||
(Regex::new(r"^220-FileZilla Server").unwrap(), "filezilla-ftp"),
|
||||
(Regex::new(r"^220 Microsoft FTP").unwrap(), "microsoft-ftp"),
|
||||
(Regex::new(r"^220 .*FRITZ!Box").unwrap(), "fritzbox-ftp"),
|
||||
(Regex::new(r"^220 .*IIS .* FTP").unwrap(), "iis-ftp"),
|
||||
(Regex::new(r"^220 .*FTP server \(GNU").unwrap(), "gnu-inetutils-ftpd"),
|
||||
(Regex::new(r"^220 .*FTP server ready").unwrap(), "generic-ftp"),
|
||||
(Regex::new(r"^331 ").unwrap(), "ftp"),
|
||||
(Regex::new(r"^530 ").unwrap(), "ftp"),
|
||||
|
||||
// Database Servers
|
||||
(Regex::new(r"^S\x00\x00\x01\x55\x00\x00").unwrap(), "mysql"),
|
||||
(Regex::new(r"^\x5b\x00\x00\x00").unwrap(), "postgres"),
|
||||
(Regex::new(r"^220 PostgreSQL").unwrap(), "postgres"),
|
||||
(Regex::new(r"PostgreSQL SCRAM-SHA-256").unwrap(), "postgres"),
|
||||
(Regex::new(r"^@REDICULOUS").unwrap(), "redis"),
|
||||
(Regex::new(r"^@REDISJSON").unwrap(), "redis"),
|
||||
(Regex::new(r"^-ERR\sERROR").unwrap(), "redis"),
|
||||
(Regex::new(r"^-ERR\s").unwrap(), "redis"),
|
||||
(Regex::new(r"^-DENIED\s").unwrap(), "redis"),
|
||||
(Regex::new(r"^\\-ERR").unwrap(), "redis"),
|
||||
(Regex::new(r"^\\+OK").unwrap(), "redis"),
|
||||
(Regex::new(r"^[+]PONG").unwrap(), "redis"),
|
||||
(Regex::new(r"^-NOAUTH\s").unwrap(), "redis"),
|
||||
(Regex::new(r"^-BUSY\s").unwrap(), "redis"),
|
||||
(Regex::new(r"^[$]").unwrap(), "redis"),
|
||||
(Regex::new(r"^(\*)").unwrap(), "redis"),
|
||||
(Regex::new(r"^redis_version").unwrap(), "redis"),
|
||||
(Regex::new(r"Oracle Transparent Network Substrate Protocol").unwrap(), "oracle-tns"),
|
||||
(Regex::new(r"^\x00\x00\x00\x00\x04\x00\x00\x00").unwrap(), "oracle-tns"),
|
||||
(Regex::new(r"^@\(#\)sybase").unwrap(), "sybase"),
|
||||
(Regex::new(r"^\x04\x01\x00").unwrap(), "sybase-ase"),
|
||||
(Regex::new(r"^MongoDB").unwrap(), "mongodb"),
|
||||
(Regex::new(r"^\x02\x00\x00\x00").unwrap(), "mongodb"),
|
||||
// (Regex::new(r#"^{\"ok\":"#).unwrap(), "mongodb-rest"),
|
||||
(Regex::new(r"^3 ").unwrap(), "mongodb-shell"),
|
||||
(Regex::new(r"^MemCache").unwrap(), "memcached"),
|
||||
(Regex::new(r"^VERSION ").unwrap(), "memcached"),
|
||||
(Regex::new(r"^(?:ERROR|CLIENT_ERROR|SERVER_ERROR)$").unwrap(), "memcached"),
|
||||
// (Regex::new(r"^\\(\\s+\(\\s+FLUSHDB").unwrap(), "db2"),
|
||||
(Regex::new(r"^SQLite format 3\x00").unwrap(), "sqlite"),
|
||||
(Regex::new(r"CouchDB").unwrap(), "couchdb"),
|
||||
(Regex::new(r"^(?:HBase|ZooKeeper)").unwrap(), "hbase"),
|
||||
(Regex::new(r"^Cassandra").unwrap(), "cassandra"),
|
||||
(Regex::new(r"^\\x00\\x58\\x01\\x00\\x19\\x00\\x00\\x00\\x11\\x00\\x00\\x00\\x00").unwrap(), "cassandra"),
|
||||
(Regex::new(r"^DSN=").unwrap(), "odbc"),
|
||||
(Regex::new(r"^DLPX-").unwrap(), "delphix"),
|
||||
(Regex::new(r"^RIAK").unwrap(), "riak"),
|
||||
(Regex::new(r"^neo4j").unwrap(), "neo4j"),
|
||||
(Regex::new(r"^\\x00\\x00\\x00\\x78\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00").unwrap(), "influxdb"),
|
||||
|
||||
// Telnet and Terminal Servers
|
||||
(Regex::new(r"^220.*telnet").unwrap(), "telnet"),
|
||||
(Regex::new(r"^\xff\xfb\x01\xff\xfb\x03").unwrap(), "telnet"),
|
||||
(Regex::new(r"^\xff\xfb").unwrap(), "telnet"),
|
||||
// (Regex::new(r"^\\x1B\\[").unwrap(), "telnet"),
|
||||
(Regex::new(r"Welcome to the Telnet Server").unwrap(), "telnet"),
|
||||
(Regex::new(r"BusyBox v").unwrap(), "busybox-telnet"),
|
||||
(Regex::new(r"^Login:").unwrap(), "telnet"),
|
||||
(Regex::new(r"^\r\nlogin: ").unwrap(), "telnet"),
|
||||
(Regex::new(r"username:").unwrap(), "telnet"),
|
||||
(Regex::new(r"password:").unwrap(), "terminal"),
|
||||
(Regex::new(r"You are on a Router").unwrap(), "router-terminal"),
|
||||
(Regex::new(r"^\r\n\r\nRTSP/1.0").unwrap(), "rtsp"),
|
||||
|
||||
// Remote Desktop and VNC
|
||||
(Regex::new(r"^RFB \d").unwrap(), "vnc"),
|
||||
(Regex::new(r"^RFB 003.").unwrap(), "vnc"),
|
||||
(Regex::new(r"^RFB 004.").unwrap(), "vnc"),
|
||||
(Regex::new(r"^\x03\x00\x00").unwrap(), "rdp"),
|
||||
(Regex::new(r"^\x03\x00\x00\x0b\x06").unwrap(), "rdp"),
|
||||
(Regex::new(r"^\x03\x00\x00\x13").unwrap(), "rdp"),
|
||||
(Regex::new(r"^\x03\x00\x00\x03\x0e\x00\x00\x00").unwrap(), "rdp"),
|
||||
(Regex::new(r"^Remote Desktop Protocol").unwrap(), "rdp"),
|
||||
(Regex::new(r"Microsoft Terminal Server").unwrap(), "rdp"),
|
||||
(Regex::new(r"^\x30\x64\xa0").unwrap(), "pcAnywhere"),
|
||||
(Regex::new(r"^CONNECTREQUEST").unwrap(), "teamviewer"),
|
||||
|
||||
// LDAP and Directory Services
|
||||
(Regex::new(r"^\x30\x0c\x02\x01\x01\x61").unwrap(), "ldap"),
|
||||
(Regex::new(r"^\x30\x84").unwrap(), "ldap"),
|
||||
(Regex::new(r"Microsoft Active Directory LDAP").unwrap(), "active-directory"),
|
||||
(Regex::new(r"^objectClass").unwrap(), "ldap"),
|
||||
(Regex::new(r"OpenLDAP").unwrap(), "openldap"),
|
||||
(Regex::new(r"389 Directory Server").unwrap(), "389-ds"),
|
||||
(Regex::new(r"^NDS version").unwrap(), "novell-nds"),
|
||||
|
||||
// Web Services and APIs
|
||||
(Regex::new(r#"^\{"jsonrpc"#).unwrap(), "jsonrpc"),
|
||||
(Regex::new(r#"^\{"result"#).unwrap(), "json-api"),
|
||||
(Regex::new(r"^<\?xml").unwrap(), "xml-service"),
|
||||
(Regex::new(r"<SOAP").unwrap(), "soap"),
|
||||
(Regex::new(r"<soap").unwrap(), "soap"),
|
||||
(Regex::new(r"<wsdl").unwrap(), "wsdl"),
|
||||
(Regex::new(r"^<\\?xml version").unwrap(), "xml-rpc"),
|
||||
(Regex::new(r"xmlns:soap").unwrap(), "soap"),
|
||||
(Regex::new(r"<faultcode>").unwrap(), "soap"),
|
||||
(Regex::new(r"graphql").unwrap(), "graphql"),
|
||||
(Regex::new(r"<GraphQLResponse>").unwrap(), "graphql"),
|
||||
(Regex::new(r"REST API").unwrap(), "rest-api"),
|
||||
(Regex::new(r"Swagger").unwrap(), "swagger-api"),
|
||||
(Regex::new(r"OpenAPI").unwrap(), "openapi"),
|
||||
(Regex::new(r"^\\d{3} MCom_Perl").unwrap(), "perl-webservice"),
|
||||
|
||||
// Message Queues and Streaming
|
||||
(Regex::new(r"^AMQP").unwrap(), "amqp"),
|
||||
(Regex::new(r"^AMQP\x00\x01\x00\x00").unwrap(), "amqp-0-10"),
|
||||
(Regex::new(r"^AMQP\x01\x01\x00\x0A").unwrap(), "amqp-1-0"),
|
||||
(Regex::new(r"^AMQP\x00\x00\x09\x01").unwrap(), "amqp-0-9-1"),
|
||||
(Regex::new(r"RabbitMQ").unwrap(), "rabbitmq"),
|
||||
(Regex::new(r"Apache Kafka").unwrap(), "kafka"),
|
||||
(Regex::new(r"^JMQ").unwrap(), "jms"),
|
||||
(Regex::new(r"ActiveMQ").unwrap(), "activemq"),
|
||||
(Regex::new(r"Apache ActiveMQ").unwrap(), "activemq"),
|
||||
(Regex::new(r"MQTT").unwrap(), "mqtt"),
|
||||
(Regex::new(r"^\\x10\\x").unwrap(), "mqtt"),
|
||||
(Regex::new(r"^\\x20\\x").unwrap(), "mqtt"),
|
||||
(Regex::new(r"Redis Pub/Sub").unwrap(), "redis-pubsub"),
|
||||
(Regex::new(r"ZeroMQ").unwrap(), "zeromq"),
|
||||
(Regex::new(r"Apache Pulsar").unwrap(), "pulsar"),
|
||||
(Regex::new(r"NSQ").unwrap(), "nsq"),
|
||||
|
||||
// SSL/TLS
|
||||
(Regex::new(r"^\x16\x03").unwrap(), "ssl/tls"),
|
||||
(Regex::new(r"^\x16\x03\x00").unwrap(), "ssl-3.0"),
|
||||
(Regex::new(r"^\x16\x03\x01").unwrap(), "tls-1.0"),
|
||||
(Regex::new(r"^\x16\x03\x02").unwrap(), "tls-1.1"),
|
||||
(Regex::new(r"^\x16\x03\x03").unwrap(), "tls-1.2"),
|
||||
(Regex::new(r"^\x16\x03\x04").unwrap(), "tls-1.3"),
|
||||
(Regex::new(r"^\x80\x80").unwrap(), "ssl-2.0"),
|
||||
(Regex::new(r"^SSL").unwrap(), "ssl"),
|
||||
(Regex::new(r"TLSv1").unwrap(), "tls"),
|
||||
(Regex::new(r"StartTLS").unwrap(), "starttls"),
|
||||
|
||||
// RTSP/SIP/Media Streaming
|
||||
(Regex::new(r"^RTSP/\d").unwrap(), "rtsp"),
|
||||
(Regex::new(r"^SIP/\d").unwrap(), "sip"),
|
||||
(Regex::new(r"^INVITE sip:").unwrap(), "sip"),
|
||||
(Regex::new(r"^REGISTER sip:").unwrap(), "sip"),
|
||||
(Regex::new(r"User-Agent: .*Asterisk").unwrap(), "asterisk-sip"),
|
||||
(Regex::new(r"User-Agent: .*FreeSWITCH").unwrap(), "freeswitch-sip"),
|
||||
(Regex::new(r"Server: .*Asterisk").unwrap(), "asterisk"),
|
||||
(Regex::new(r"Server: .*FreeSWITCH").unwrap(), "freeswitch"),
|
||||
(Regex::new(r"^ICY \d").unwrap(), "shoutcast"),
|
||||
(Regex::new(r"^ICE/1\.0").unwrap(), "icecast"),
|
||||
(Regex::new(r"Server: Icecast").unwrap(), "icecast"),
|
||||
(Regex::new(r"Server: Shoutcast").unwrap(), "shoutcast"),
|
||||
(Regex::new(r"^\$\$\$\$\$:").unwrap(), "rtmp"),
|
||||
(Regex::new(r"^RTMP/\d").unwrap(), "rtmp"),
|
||||
|
||||
// Network and Routing
|
||||
(Regex::new(r"^RIP").unwrap(), "rip"),
|
||||
(Regex::new(r"^OSPF").unwrap(), "ospf"),
|
||||
(Regex::new(r"^BGP").unwrap(), "bgp"),
|
||||
(Regex::new(r"^220.*SNMP").unwrap(), "snmp"),
|
||||
(Regex::new(r"public\x02\x01\x00\x02\x01\x00").unwrap(), "snmp"),
|
||||
(Regex::new(r"^\x30\x2c\x02\x01\x00\x04").unwrap(), "snmp"),
|
||||
(Regex::new(r"X-Openstackinternaltoken").unwrap(), "openstack"),
|
||||
(Regex::new(r"zabbix").unwrap(), "zabbix-agent"),
|
||||
(Regex::new(r"^\\x00\\x00\\x00\\x00\\x00\\x07\\x72\\x").unwrap(), "elasticsearch"),
|
||||
|
||||
// File Sharing
|
||||
(Regex::new(r"^\\x00\\x00.*SAMBA").unwrap(), "samba"),
|
||||
(Regex::new(r"^SMB").unwrap(), "smb"),
|
||||
(Regex::new(r"^\\xff\\x53\\x4d\\x42").unwrap(), "smb"),
|
||||
(Regex::new(r"NFS server").unwrap(), "nfs"),
|
||||
(Regex::new(r"^\\x80\\x00\\x00\\x18").unwrap(), "nfs"),
|
||||
(Regex::new(r"^\\x80\\x00\\x00\\x28").unwrap(), "nfs"),
|
||||
(Regex::new(r"^\\x05\\x00\\x0b\\x03\\x10\\x00\\x00\\x00").unwrap(), "dcerpc"),
|
||||
(Regex::new(r"AFP").unwrap(), "afp"),
|
||||
(Regex::new(r"AFPX").unwrap(), "afp"),
|
||||
(Regex::new(r"Apple Filing Protocol").unwrap(), "afp"),
|
||||
(Regex::new(r"^\\x00\\x00\\x00\\d.\\xc2\\x80\\x80\\x80").unwrap(), "webdav"),
|
||||
|
||||
// Version Control
|
||||
(Regex::new(r"^git://").unwrap(), "git"),
|
||||
(Regex::new(r"git version").unwrap(), "git"),
|
||||
(Regex::new(r"\\x30\\x30").unwrap(), "git"),
|
||||
(Regex::new(r"git-upload-pack").unwrap(), "git"),
|
||||
(Regex::new(r"^\\d{3} <SVN").unwrap(), "svn"),
|
||||
// (Regex::new(r"^\\( success").unwrap(), "svn"),
|
||||
(Regex::new(r"Subversion").unwrap(), "svn"),
|
||||
(Regex::new(r"Mercurial").unwrap(), "mercurial"),
|
||||
|
||||
// Gaming and Game Servers
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*cstrikeHalf-Life").unwrap(), "counter-strike"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xffinfo").unwrap(), "quake"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xffstatusResponse").unwrap(), "minecraft"),
|
||||
(Regex::new(r"^\\x01splitnum").unwrap(), "doom"),
|
||||
(Regex::new(r"^\\xa1\\x12\\xa1\\x00").unwrap(), "doom"),
|
||||
(Regex::new(r"MineCraft").unwrap(), "minecraft"),
|
||||
(Regex::new(r"^MC|").unwrap(), "minecraft"),
|
||||
(Regex::new(r"^\\x01player_").unwrap(), "minecraft"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*SourceEngine").unwrap(), "source-engine"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*Team Fortress").unwrap(), "team-fortress"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*Left 4 Dead").unwrap(), "left-4-dead"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*Portal").unwrap(), "portal"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*Half-Life").unwrap(), "half-life"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*Day of Defeat").unwrap(), "day-of-defeat"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*L\\.A\\. Noire").unwrap(), "la-noire"),
|
||||
(Regex::new(r"^\\xff\\xff\\xff\\xff.*Dota 2").unwrap(), "dota2"),
|
||||
(Regex::new(r"^\\x01ping").unwrap(), "arma"),
|
||||
(Regex::new(r"^\\x01pong").unwrap(), "arma"),
|
||||
|
||||
// IoT and Smart Home
|
||||
(Regex::new(r"CoAP").unwrap(), "coap"),
|
||||
(Regex::new(r"^\\x40\\x01").unwrap(), "coap"),
|
||||
(Regex::new(r"^\\x44\\x01").unwrap(), "coap"),
|
||||
(Regex::new(r"MQTT").unwrap(), "mqtt"),
|
||||
(Regex::new(r"^\\x10\\x..\\x00\\x04MQTT").unwrap(), "mqtt"),
|
||||
(Regex::new(r"Sonos").unwrap(), "sonos"),
|
||||
(Regex::new(r"Phillips Hue").unwrap(), "philips-hue"),
|
||||
(Regex::new(r"Nest").unwrap(), "nest"),
|
||||
(Regex::new(r"Z-Wave").unwrap(), "zwave"),
|
||||
(Regex::new(r"ZigBee").unwrap(), "zigbee"),
|
||||
(Regex::new(r"^\\x01\\x00\\x5e").unwrap(), "hue-api"),
|
||||
(Regex::new(r"^\\xd0\\x00\\x01\\x04").unwrap(), "insteon"),
|
||||
|
||||
// Time Protocols
|
||||
(Regex::new(r"^\\xd3").unwrap(), "ntp"),
|
||||
(Regex::new(r"NTP").unwrap(), "ntp"),
|
||||
(Regex::new(r"Stratum").unwrap(), "ntp"),
|
||||
(Regex::new(r"^\\xe3").unwrap(), "ntp"),
|
||||
(Regex::new(r"^\\x24").unwrap(), "ntp-control"),
|
||||
(Regex::new(r"chronyd").unwrap(), "chrony"),
|
||||
(Regex::new(r"timedatectl").unwrap(), "systemd-timesyncd"),
|
||||
|
||||
// Blockchain and Cryptocurrency
|
||||
(Regex::new(r"Bitcoin").unwrap(), "bitcoin"),
|
||||
(Regex::new(r"\\xf9\\xbe\\xb4\\xd9").unwrap(), "bitcoin"),
|
||||
(Regex::new(r"blockchain").unwrap(), "blockchain"),
|
||||
(Regex::new(r"Ethereum").unwrap(), "ethereum"),
|
||||
(Regex::new(r"geth").unwrap(), "ethereum"),
|
||||
(Regex::new(r"Ripple").unwrap(), "ripple"),
|
||||
(Regex::new(r"XRP").unwrap(), "ripple"),
|
||||
(Regex::new(r"Monero").unwrap(), "monero"),
|
||||
(Regex::new(r"Litecoin").unwrap(), "litecoin"),
|
||||
|
||||
// Machine Learning and AI Services
|
||||
(Regex::new(r"TensorFlow").unwrap(), "tensorflow-serving"),
|
||||
(Regex::new(r"PyTorch").unwrap(), "pytorch-serving"),
|
||||
(Regex::new(r"ONNX").unwrap(), "onnx-runtime"),
|
||||
(Regex::new(r"MLFlow").unwrap(), "mlflow"),
|
||||
(Regex::new(r"Jupyter").unwrap(), "jupyter"),
|
||||
|
||||
// Storage and Backup
|
||||
(Regex::new(r"Ceph").unwrap(), "ceph"),
|
||||
(Regex::new(r"GlusterFS").unwrap(), "glusterfs"),
|
||||
(Regex::new(r"Hadoop").unwrap(), "hadoop"),
|
||||
(Regex::new(r"HDFS").unwrap(), "hdfs"),
|
||||
(Regex::new(r"Rsync").unwrap(), "rsync"),
|
||||
(Regex::new(r"\\x40\\x52\\x53\\x59\\x4e\\x43\\x44").unwrap(), "rsync"),
|
||||
(Regex::new(r"BackupPC").unwrap(), "backuppc"),
|
||||
(Regex::new(r"Bacula").unwrap(), "bacula"),
|
||||
(Regex::new(r"^Hello Bacula").unwrap(), "bacula"),
|
||||
(Regex::new(r"Borg Backup").unwrap(), "borg"),
|
||||
(Regex::new(r"Veeam").unwrap(), "veeam"),
|
||||
(Regex::new(r"Amanda Backup").unwrap(), "amanda"),
|
||||
(Regex::new(r"ZFS").unwrap(), "zfs"),
|
||||
(Regex::new(r"^\\x00\\x00\\x00\\x2c\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x01\\x00\\x00\\x00\\x14rquota").unwrap(), "rquota"),
|
||||
|
||||
// Monitoring and Management
|
||||
(Regex::new(r"Nagios").unwrap(), "nagios"),
|
||||
(Regex::new(r"Zabbix").unwrap(), "zabbix"),
|
||||
(Regex::new(r"Prometheus").unwrap(), "prometheus"),
|
||||
(Regex::new(r"Grafana").unwrap(), "grafana"),
|
||||
(Regex::new(r"Munin").unwrap(), "munin"),
|
||||
(Regex::new(r"Cacti").unwrap(), "cacti"),
|
||||
(Regex::new(r"PRTG").unwrap(), "prtg"),
|
||||
(Regex::new(r"^\\x00bgp").unwrap(), "bgp"),
|
||||
(Regex::new(r"^\\xff\\xff.*BGP").unwrap(), "bgp"),
|
||||
(Regex::new(r"Icinga").unwrap(), "icinga"),
|
||||
(Regex::new(r"collectd").unwrap(), "collectd"),
|
||||
(Regex::new(r"netdata").unwrap(), "netdata"),
|
||||
(Regex::new(r"Elastic").unwrap(), "elasticsearch"),
|
||||
(Regex::new(r"opentsdb").unwrap(), "opentsdb"),
|
||||
|
||||
// News and Discussion
|
||||
(Regex::new(r"220 .*(NNTP|Network News)").unwrap(), "nntp"),
|
||||
(Regex::new(r"^200 .*NNTP").unwrap(), "nntp"),
|
||||
(Regex::new(r"^200 .*news").unwrap(), "nntp"),
|
||||
(Regex::new(r"^200 .*ready").unwrap(), "nntp"),
|
||||
(Regex::new(r"^201 ").unwrap(), "nntp"),
|
||||
(Regex::new(r"^IHAVE ").unwrap(), "nntp"),
|
||||
(Regex::new(r"^GROUP ").unwrap(), "nntp"),
|
||||
(Regex::new(r"^MODE READER").unwrap(), "nntp"),
|
||||
(Regex::new(r"NNTP-Posting-").unwrap(), "nntp"),
|
||||
(Regex::new(r"^502 ").unwrap(), "nntp"),
|
||||
|
||||
// Additional service detection patterns for various protocols
|
||||
// Add these patterns to your existing SERVICE_PATTERNS vector
|
||||
|
||||
// Container Orchestration
|
||||
(Regex::new(r"Kubernetes").unwrap(), "kubernetes"),
|
||||
(Regex::new(r"^apiVersion: v\d+").unwrap(), "kubernetes-api"),
|
||||
(Regex::new(r"Docker").unwrap(), "docker"),
|
||||
(Regex::new(r"docker-registry").unwrap(), "docker-registry"),
|
||||
(Regex::new(r"Swarm").unwrap(), "docker-swarm"),
|
||||
(Regex::new(r"Mesos").unwrap(), "mesos"),
|
||||
(Regex::new(r"Nomad").unwrap(), "nomad"),
|
||||
(Regex::new(r"containerd").unwrap(), "containerd"),
|
||||
(Regex::new(r"OpenShift").unwrap(), "openshift"),
|
||||
|
||||
// Cloud Providers
|
||||
(Regex::new(r"AmazonS3").unwrap(), "aws-s3"),
|
||||
(Regex::new(r"EC2").unwrap(), "aws-ec2"),
|
||||
(Regex::new(r"Lambda").unwrap(), "aws-lambda"),
|
||||
(Regex::new(r"Azure").unwrap(), "azure"),
|
||||
(Regex::new(r"Blob Storage").unwrap(), "azure-blob"),
|
||||
(Regex::new(r"Google Cloud").unwrap(), "gcp"),
|
||||
(Regex::new(r"Compute Engine").unwrap(), "gcp-compute"),
|
||||
(Regex::new(r"Cloud Storage").unwrap(), "gcp-storage"),
|
||||
(Regex::new(r"Firebase").unwrap(), "firebase"),
|
||||
(Regex::new(r"Heroku").unwrap(), "heroku"),
|
||||
(Regex::new(r"Digital Ocean").unwrap(), "digitalocean"),
|
||||
|
||||
// CI/CD Systems
|
||||
(Regex::new(r"Jenkins").unwrap(), "jenkins"),
|
||||
(Regex::new(r"GitLab").unwrap(), "gitlab"),
|
||||
(Regex::new(r"Travis CI").unwrap(), "travis-ci"),
|
||||
(Regex::new(r"CircleCI").unwrap(), "circleci"),
|
||||
(Regex::new(r"TeamCity").unwrap(), "teamcity"),
|
||||
(Regex::new(r"Bamboo").unwrap(), "bamboo"),
|
||||
(Regex::new(r"Drone").unwrap(), "drone-ci"),
|
||||
(Regex::new(r"Buildkite").unwrap(), "buildkite"),
|
||||
|
||||
// Search Engines
|
||||
(Regex::new(r"Elasticsearch").unwrap(), "elasticsearch"),
|
||||
// (Regex::new(r#"^{"cluster_name":"#).unwrap(), "elasticsearch"),
|
||||
// (Regex::new(r#"^{"name":"[^"]+","cluster_name":"#).unwrap(), "elasticsearch"),
|
||||
(Regex::new(r"Solr").unwrap(), "solr"),
|
||||
(Regex::new(r"Lucene").unwrap(), "lucene"),
|
||||
(Regex::new(r"Sphinx").unwrap(), "sphinx"),
|
||||
// (Regex::new(r#"^{"took":d+,"timed_out":"#).unwrap(), "elasticsearch"),
|
||||
(Regex::new(r"OpenSearch").unwrap(), "opensearch"),
|
||||
|
||||
// Additional Databases
|
||||
(Regex::new(r"InfluxDB").unwrap(), "influxdb"),
|
||||
(Regex::new(r"CrateDB").unwrap(), "cratedb"),
|
||||
(Regex::new(r"Cockroach").unwrap(), "cockroachdb"),
|
||||
(Regex::new(r"TimescaleDB").unwrap(), "timescaledb"),
|
||||
(Regex::new(r"MariaDB").unwrap(), "mariadb"),
|
||||
(Regex::new(r"SingleStore").unwrap(), "singlestore"),
|
||||
(Regex::new(r"TiDB").unwrap(), "tidb"),
|
||||
(Regex::new(r"Fauna").unwrap(), "faunadb"),
|
||||
(Regex::new(r"DynamoDB").unwrap(), "dynamodb"),
|
||||
(Regex::new(r"Clickhouse").unwrap(), "clickhouse"),
|
||||
(Regex::new(r"ArangoDB").unwrap(), "arangodb"),
|
||||
(Regex::new(r"ScyllaDB").unwrap(), "scylladb"),
|
||||
(Regex::new(r"^\x83h\x03").unwrap(), "riak"),
|
||||
(Regex::new(r"^\x83h\x02").unwrap(), "riak"),
|
||||
|
||||
// Advanced Network Protocols
|
||||
(Regex::new(r"QUIC").unwrap(), "quic"),
|
||||
(Regex::new(r"HTTP/3").unwrap(), "http3"),
|
||||
(Regex::new(r"gRPC").unwrap(), "grpc"),
|
||||
(Regex::new(r"^PRI \* HTTP/2").unwrap(), "http2"),
|
||||
(Regex::new(r"Thrift").unwrap(), "thrift"),
|
||||
(Regex::new(r"^\x00\x00\x00\x13\x06\x01").unwrap(), "tftp"),
|
||||
(Regex::new(r"SCTP").unwrap(), "sctp"),
|
||||
(Regex::new(r"DTLS").unwrap(), "dtls"),
|
||||
(Regex::new(r"^\x17\xfe").unwrap(), "dtls"),
|
||||
(Regex::new(r"^\x16\xfe").unwrap(), "dtls"),
|
||||
|
||||
// Identity and Access Management
|
||||
(Regex::new(r"OAuth").unwrap(), "oauth"),
|
||||
(Regex::new(r"SAML").unwrap(), "saml"),
|
||||
(Regex::new(r"Keycloak").unwrap(), "keycloak"),
|
||||
(Regex::new(r"^\\x30\\x84.*starttls").unwrap(), "ldaps"),
|
||||
(Regex::new(r"Okta").unwrap(), "okta"),
|
||||
(Regex::new(r"Auth0").unwrap(), "auth0"),
|
||||
(Regex::new(r"OpenID").unwrap(), "openid"),
|
||||
(Regex::new(r"Kerberos").unwrap(), "kerberos"),
|
||||
(Regex::new(r"^\x60\x82").unwrap(), "kerberos"),
|
||||
(Regex::new(r"^\x05\x02").unwrap(), "gssapi"),
|
||||
|
||||
// Cache Services
|
||||
(Regex::new(r"^ERROR\r\n").unwrap(), "memcached"),
|
||||
(Regex::new(r"^STAT pid \d+").unwrap(), "memcached"),
|
||||
(Regex::new(r"^END\r\n").unwrap(), "memcached"),
|
||||
(Regex::new(r"^CLIENT_ERROR").unwrap(), "memcached"),
|
||||
(Regex::new(r"^SERVER_ERROR").unwrap(), "memcached"),
|
||||
(Regex::new(r"Varnish").unwrap(), "varnish"),
|
||||
(Regex::new(r"Squid").unwrap(), "squid"),
|
||||
(Regex::new(r"HAProxy").unwrap(), "haproxy"),
|
||||
(Regex::new(r"^\\*\\d+\\r\\n\\$\\d+\\r\\n").unwrap(), "redis-resp"),
|
||||
|
||||
// IoT/Industrial Protocols
|
||||
(Regex::new(r"Modbus").unwrap(), "modbus"),
|
||||
(Regex::new(r"BACnet").unwrap(), "bacnet"),
|
||||
(Regex::new(r"MQTT-SN").unwrap(), "mqtt-sn"),
|
||||
(Regex::new(r"DNP3").unwrap(), "dnp3"),
|
||||
(Regex::new(r"^\x05\x64").unwrap(), "dnp3"),
|
||||
(Regex::new(r"^\x0a\x00").unwrap(), "modbus-tcp"),
|
||||
(Regex::new(r"OPC UA").unwrap(), "opcua"),
|
||||
(Regex::new(r"^\x47\x77").unwrap(), "bacnet"),
|
||||
(Regex::new(r"EtherNet/IP").unwrap(), "ethernet-ip"),
|
||||
(Regex::new(r"PROFINET").unwrap(), "profinet"),
|
||||
|
||||
// Security Services
|
||||
(Regex::new(r"^.*\sOPENVPN\s").unwrap(), "openvpn"),
|
||||
(Regex::new(r"Wireguard").unwrap(), "wireguard"),
|
||||
(Regex::new(r"IPsec").unwrap(), "ipsec"),
|
||||
(Regex::new(r"^\x00\x00\x00\x00\x00\x00\x00\x01").unwrap(), "isakmp"),
|
||||
(Regex::new(r"^SSH-1\.[5-9]").unwrap(), "ssh"),
|
||||
(Regex::new(r"^\\xff\\x01\\x00").unwrap(), "ipsec-isakmp"),
|
||||
(Regex::new(r"IKE").unwrap(), "ike"),
|
||||
(Regex::new(r"Fortinet").unwrap(), "fortinet-vpn"),
|
||||
(Regex::new(r"Palo Alto").unwrap(), "paloalto"),
|
||||
(Regex::new(r"CheckPoint").unwrap(), "checkpoint"),
|
||||
|
||||
// Additional Web Technologies
|
||||
(Regex::new(r"Wordpress").unwrap(), "wordpress"),
|
||||
(Regex::new(r"Drupal").unwrap(), "drupal"),
|
||||
(Regex::new(r"Joomla").unwrap(), "joomla"),
|
||||
(Regex::new(r"Magento").unwrap(), "magento"),
|
||||
(Regex::new(r"Laravel").unwrap(), "laravel"),
|
||||
(Regex::new(r"Spring Boot").unwrap(), "spring-boot"),
|
||||
(Regex::new(r"Next.js").unwrap(), "nextjs"),
|
||||
(Regex::new(r"ASP.NET").unwrap(), "aspnet"),
|
||||
(Regex::new(r"^HTTP/\\d\\.\\d 5\\d\\d .*cloudflare").unwrap(), "cloudflare"),
|
||||
(Regex::new(r"Fastly").unwrap(), "fastly"),
|
||||
(Regex::new(r"Akamai").unwrap(), "akamai"),
|
||||
|
||||
// Service Discovery
|
||||
(Regex::new(r"Consul").unwrap(), "consul"),
|
||||
(Regex::new(r"etcd").unwrap(), "etcd"),
|
||||
(Regex::new(r"ZooKeeper").unwrap(), "zookeeper"),
|
||||
(Regex::new(r"^RO,").unwrap(), "zookeeper"),
|
||||
(Regex::new(r"^Zookeeper version").unwrap(), "zookeeper"),
|
||||
(Regex::new(r"^notWatching").unwrap(), "zookeeper"),
|
||||
(Regex::new(r"Eureka").unwrap(), "eureka"),
|
||||
(Regex::new(r"Istio").unwrap(), "istio"),
|
||||
(Regex::new(r"Envoy").unwrap(), "envoy"),
|
||||
(Regex::new(r"Service Mesh").unwrap(), "service-mesh"),
|
||||
|
||||
// Embedded/IoT Systems
|
||||
(Regex::new(r"DD-WRT").unwrap(), "dd-wrt"),
|
||||
(Regex::new(r"OpenWrt").unwrap(), "openwrt"),
|
||||
(Regex::new(r"pfSense").unwrap(), "pfsense"),
|
||||
(Regex::new(r"Mikrotik").unwrap(), "mikrotik"),
|
||||
(Regex::new(r"RouterOS").unwrap(), "routeros"),
|
||||
(Regex::new(r"Ubiquiti").unwrap(), "ubiquiti"),
|
||||
(Regex::new(r"UniFi").unwrap(), "unifi"),
|
||||
(Regex::new(r"Synology").unwrap(), "synology"),
|
||||
(Regex::new(r"QNAP").unwrap(), "qnap"),
|
||||
(Regex::new(r"Netgear").unwrap(), "netgear"),
|
||||
(Regex::new(r"TP-Link").unwrap(), "tp-link"),
|
||||
(Regex::new(r"Asus").unwrap(), "asus"),
|
||||
|
||||
// Industrial Control Systems
|
||||
(Regex::new(r"Siemens").unwrap(), "siemens"),
|
||||
(Regex::new(r"S7Comm").unwrap(), "s7comm"),
|
||||
(Regex::new(r"^\x03\x00\x00\x16").unwrap(), "s7comm"),
|
||||
(Regex::new(r"Allen-Bradley").unwrap(), "allen-bradley"),
|
||||
(Regex::new(r"Rockwell").unwrap(), "rockwell"),
|
||||
(Regex::new(r"Schneider").unwrap(), "schneider"),
|
||||
(Regex::new(r"Honeywell").unwrap(), "honeywell"),
|
||||
(Regex::new(r"ABB").unwrap(), "abb"),
|
||||
(Regex::new(r"SCADA").unwrap(), "scada"),
|
||||
(Regex::new(r"PLC").unwrap(), "plc"),
|
||||
|
||||
// Additional RPC
|
||||
(Regex::new(r"^\x4e\x00\x00\x00").unwrap(), "rpc-nfs"),
|
||||
(Regex::new(r"^\x01\x86\xa0").unwrap(), "portmap-rpc"),
|
||||
(Regex::new(r"JsonRPC").unwrap(), "jsonrpc"),
|
||||
(Regex::new(r"XML-RPC").unwrap(), "xmlrpc"),
|
||||
(Regex::new(r"^content-length: ").unwrap(), "http-rpc"),
|
||||
(Regex::new(r"^POST /RPC2").unwrap(), "xmlrpc"),
|
||||
|
||||
// Distributed Systems
|
||||
(Regex::new(r"Apache Beam").unwrap(), "apache-beam"),
|
||||
(Regex::new(r"Apache Flink").unwrap(), "apache-flink"),
|
||||
(Regex::new(r"Apache Spark").unwrap(), "apache-spark"),
|
||||
(Regex::new(r"Dask").unwrap(), "dask"),
|
||||
(Regex::new(r"Ray").unwrap(), "ray"),
|
||||
(Regex::new(r"Akka").unwrap(), "akka"),
|
||||
(Regex::new(r"Actor System").unwrap(), "actor-system"),
|
||||
(Regex::new(r"Celery").unwrap(), "celery"),
|
||||
(Regex::new(r"RQ").unwrap(), "rq"),
|
||||
|
||||
// Legacy Protocols
|
||||
(Regex::new(r"^\\+OK POP").unwrap(), "pop3"),
|
||||
(Regex::new(r"^\\+OK Dovecot").unwrap(), "dovecot-pop3"),
|
||||
(Regex::new(r"^gopher:/").unwrap(), "gopher"),
|
||||
(Regex::new(r"^1Service").unwrap(), "gopher"),
|
||||
(Regex::new(r"^finger:").unwrap(), "finger"),
|
||||
(Regex::new(r"Whois").unwrap(), "whois"),
|
||||
(Regex::new(r"^%.*whois").unwrap(), "whois"),
|
||||
(Regex::new(r"^\\* rlogin").unwrap(), "rlogin"),
|
||||
(Regex::new(r"^\\* login").unwrap(), "rlogin"),
|
||||
(Regex::new(r"X-Gopher-Menu").unwrap(), "gopher"),
|
||||
(Regex::new(r"^150 Opening BINARY mode data").unwrap(), "ftp-data"),
|
||||
(Regex::new(r"^\xff\xfb\x01\xff\xfb\x03\xff\xfb\x00\xff\xfd\x18").unwrap(), "telnet"),
|
||||
|
||||
// Network Services
|
||||
(Regex::new(r"^DHCP").unwrap(), "dhcp"),
|
||||
(Regex::new(r"bootp").unwrap(), "bootp"),
|
||||
(Regex::new(r"TFTP").unwrap(), "tftp"),
|
||||
(Regex::new(r"^Domain Name Server").unwrap(), "dns"),
|
||||
(Regex::new(r"^\\x00\\x00\\x10\\x00\\x01").unwrap(), "dns-request"),
|
||||
(Regex::new(r"^\\x00\\x00\\x84\\x00\\x01").unwrap(), "dns-response"),
|
||||
(Regex::new(r"^PROXY").unwrap(), "proxy-protocol"),
|
||||
(Regex::new(r"^\x5b\x62\x69\x6e\x64").unwrap(), "dns-bind"),
|
||||
(Regex::new(r"^\\x13\\x03\\x00\\x00").unwrap(), "radius"),
|
||||
(Regex::new(r"^\\x01\\x06\\x00").unwrap(), "radius"),
|
||||
|
||||
// Calendar and Scheduling
|
||||
(Regex::new(r"^\\* OK.*CalDAV").unwrap(), "caldav"),
|
||||
(Regex::new(r"^\\* OK.*CardDAV").unwrap(), "carddav"),
|
||||
(Regex::new(r"BEGIN:VCALENDAR").unwrap(), "ical"),
|
||||
(Regex::new(r"BEGIN:VCARD").unwrap(), "vcard"),
|
||||
(Regex::new(r"iCalendar").unwrap(), "icalendar"),
|
||||
(Regex::new(r"Exchange Calendar").unwrap(), "exchange-calendar"),
|
||||
(Regex::new(r"Google Calendar").unwrap(), "google-calendar"),
|
||||
(Regex::new(r"Microsoft Exchange").unwrap(), "ms-exchange"),
|
||||
|
||||
// Instant Messaging
|
||||
(Regex::new(r"XMPP").unwrap(), "xmpp"),
|
||||
(Regex::new(r"^<\\?xml.*jabber").unwrap(), "jabber"),
|
||||
(Regex::new(r"^<stream:stream").unwrap(), "xmpp"),
|
||||
(Regex::new(r"Slack API").unwrap(), "slack-api"),
|
||||
(Regex::new(r"Discord").unwrap(), "discord"),
|
||||
(Regex::new(r"Matrix").unwrap(), "matrix"),
|
||||
// (Regex::new(r#"^\\{"errcode"#).unwrap(), "matrix"),
|
||||
(Regex::new(r"IRC").unwrap(), "irc"),
|
||||
(Regex::new(r"^:[a-zA-Z0-9.]+\\s\\d{3}").unwrap(), "irc"),
|
||||
(Regex::new(r"^ERROR :Closing Link:").unwrap(), "irc"),
|
||||
(Regex::new(r"^PING :").unwrap(), "irc"),
|
||||
(Regex::new(r"^:\\S+ NOTICE Auth :").unwrap(), "irc"),
|
||||
|
||||
// Content Management
|
||||
(Regex::new(r"Alfresco").unwrap(), "alfresco"),
|
||||
(Regex::new(r"SharePoint").unwrap(), "sharepoint"),
|
||||
(Regex::new(r"Documentum").unwrap(), "documentum"),
|
||||
(Regex::new(r"FileNet").unwrap(), "filenet"),
|
||||
(Regex::new(r"OpenText").unwrap(), "opentext"),
|
||||
(Regex::new(r"Box API").unwrap(), "box-api"),
|
||||
(Regex::new(r"Dropbox API").unwrap(), "dropbox-api"),
|
||||
(Regex::new(r"Google Drive").unwrap(), "google-drive"),
|
||||
(Regex::new(r"OneDrive").unwrap(), "onedrive"),
|
||||
|
||||
// Network Storage (Additional)
|
||||
(Regex::new(r"iSCSI").unwrap(), "iscsi"),
|
||||
(Regex::new(r"^\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00").unwrap(), "iscsi-discovery"),
|
||||
(Regex::new(r"Fibre Channel").unwrap(), "fibre-channel"),
|
||||
(Regex::new(r"NetApp").unwrap(), "netapp"),
|
||||
(Regex::new(r"EMC").unwrap(), "emc"),
|
||||
(Regex::new(r"\\x02\\x01\\x00\\x01\\x00").unwrap(), "fibrechannel"),
|
||||
(Regex::new(r"NDMP").unwrap(), "ndmp"),
|
||||
|
||||
// Systems Management
|
||||
(Regex::new(r"^\\xfe\\x54").unwrap(), "syslog"),
|
||||
(Regex::new(r"<\\d+>\\w{3}\\s+\\d+\\s\\d+:\\d+:\\d+").unwrap(), "syslog"),
|
||||
(Regex::new(r"WMI").unwrap(), "wmi"),
|
||||
(Regex::new(r"WBEM").unwrap(), "wbem"),
|
||||
(Regex::new(r"WS-Management").unwrap(), "ws-man"),
|
||||
(Regex::new(r"^M-SEARCH").unwrap(), "ssdp"),
|
||||
(Regex::new(r"NOTIFY").unwrap(), "ssdp-notify"),
|
||||
(Regex::new(r"UPnP").unwrap(), "upnp"),
|
||||
(Regex::new(r"DLNA").unwrap(), "dlna"),
|
||||
|
||||
// Print Services
|
||||
(Regex::new(r"IPP/").unwrap(), "ipp"),
|
||||
(Regex::new(r"CUPS").unwrap(), "cups"),
|
||||
(Regex::new(r"LPD").unwrap(), "lpd"),
|
||||
(Regex::new(r"JetDirect").unwrap(), "jetdirect"),
|
||||
(Regex::new(r"^\\x01\\x01\\x00\\x").unwrap(), "ipp"),
|
||||
|
||||
// Hardware Management
|
||||
(Regex::new(r"IPMI").unwrap(), "ipmi"),
|
||||
(Regex::new(r"BMC").unwrap(), "bmc"),
|
||||
(Regex::new(r"iDRAC").unwrap(), "idrac"),
|
||||
(Regex::new(r"iLO").unwrap(), "ilo"),
|
||||
(Regex::new(r"DRAC").unwrap(), "drac"),
|
||||
(Regex::new(r"Lights Out").unwrap(), "lights-out"),
|
||||
(Regex::new(r"\\x06\\x00\\xff\\x07").unwrap(), "ipmi"),
|
||||
(Regex::new(r"RMCP").unwrap(), "ipmi-rmcp"),
|
||||
|
||||
// Additional Crypto/Blockchain
|
||||
(Regex::new(r"Cardano").unwrap(), "cardano"),
|
||||
(Regex::new(r"Polkadot").unwrap(), "polkadot"),
|
||||
(Regex::new(r"Solana").unwrap(), "solana"),
|
||||
(Regex::new(r"Chainlink").unwrap(), "chainlink"),
|
||||
(Regex::new(r"Near Protocol").unwrap(), "near"),
|
||||
(Regex::new(r"Avalanche").unwrap(), "avalanche"),
|
||||
(Regex::new(r"Binance").unwrap(), "binance"),
|
||||
(Regex::new(r"Hyperledger").unwrap(), "hyperledger"),
|
||||
(Regex::new(r"Corda").unwrap(), "corda"),
|
||||
(Regex::new(r"^\\xfa\\xce\\xb0\\x0c").unwrap(), "cardano"),
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
use std::{io::Read, net::IpAddr, time::Duration};
|
||||
|
||||
use reqwest::redirect::Policy;
|
||||
|
||||
pub fn scan(
|
||||
ip: IpAddr,
|
||||
port: &i32,
|
||||
timeout: Duration,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let mut result = String::new();
|
||||
let _ = reqwest::blocking::Client::builder()
|
||||
.redirect(Policy::none())
|
||||
.timeout(timeout)
|
||||
.build()
|
||||
.unwrap()
|
||||
.get(format!("http://{}:{}", ip.to_string(), port))
|
||||
.send()?
|
||||
.read_to_string(&mut result);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
use std::{io::Read, net::IpAddr, time::Duration};
|
||||
|
||||
use reqwest::redirect::Policy;
|
||||
|
||||
pub fn scan(
|
||||
ip: IpAddr,
|
||||
port: &i32,
|
||||
timeout: Duration,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
let mut result = String::new();
|
||||
let _response = reqwest::blocking::Client::builder()
|
||||
.danger_accept_invalid_certs(true)
|
||||
.redirect(Policy::none())
|
||||
.timeout(timeout)
|
||||
.build()
|
||||
.unwrap()
|
||||
.get(format!("https://{}:{}", ip.to_string(), port))
|
||||
.send()?
|
||||
.read_to_string(&mut result);
|
||||
|
||||
// println!("{}", result);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
Reference in New Issue
Block a user