mirror of
https://github.com/Astatin3/rust-scan-mc.git
synced 2026-06-08 16:08:02 -06:00
Make this better at scanning minecraft
This commit is contained in:
+403
-246
@@ -6,8 +6,6 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use rayon::prelude::*;
|
||||
|
||||
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
|
||||
const WRITE_BUFFER_SIZE_MB: usize = 64; // 64MB write buffer
|
||||
@@ -22,35 +20,84 @@ pub struct ResultDatabase {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct DatabaseResult {
|
||||
pub id: String,
|
||||
pub ports: Vec<i32>,
|
||||
pub services: Vec<String>,
|
||||
pub responses: String,
|
||||
pub ip: String,
|
||||
pub port: u16,
|
||||
|
||||
pub version: String,
|
||||
pub protocol: u32,
|
||||
pub max_players: u32,
|
||||
pub online_players: u32,
|
||||
pub players_list: Option<Vec<(String, String)>>,
|
||||
pub description: String,
|
||||
pub icon_hash: String,
|
||||
|
||||
pub mod_info: Option<(String, Vec<(String, String)>)>,
|
||||
pub forge_data: Option<(Vec<(String, String, bool)>, Vec<(String, String)>, i32)>,
|
||||
|
||||
pub enforces_secure_chat: Option<bool>,
|
||||
pub previews_chat: Option<bool>,
|
||||
}
|
||||
|
||||
pub struct MCResult {}
|
||||
|
||||
impl DatabaseResult {
|
||||
pub fn to_string(&self) -> String {
|
||||
let mut str = "".to_string();
|
||||
|
||||
str += format!(
|
||||
"\n{}\n- ports: [{}]\n- services: [{}]\n- responses: [{}]",
|
||||
self.id,
|
||||
join_nums(&self.ports, ","),
|
||||
self.services.join(", "),
|
||||
if let Ok(data) =
|
||||
serde_json::from_str::<HashMap<i32, (String, String)>>(self.responses.as_str())
|
||||
{
|
||||
format!("{:?}", data)
|
||||
} else {
|
||||
self.responses.clone()
|
||||
}
|
||||
"\n{}\n- ports: [{}]\n- version: [{}]\n- protocol: [{}]\n- max_players: [{}]\n- online_players: [{}]\n- players_list: [{:?}]\n- description: [{}]\n- icon_hash: [{}]\n- mod_info: [{:?}]\n- forge_data: [{:?}]\n- enforces_secure_chat: [{:?}]\n- previews_chat: [{:?}]",
|
||||
self.ip,
|
||||
self.port,
|
||||
self.version,
|
||||
self.protocol,
|
||||
self.max_players,
|
||||
self.online_players,
|
||||
self.players_list,
|
||||
self.description,
|
||||
self.icon_hash,
|
||||
self.mod_info,
|
||||
self.forge_data,
|
||||
self.enforces_secure_chat,
|
||||
self.previews_chat,
|
||||
)
|
||||
.as_str();
|
||||
|
||||
str
|
||||
}
|
||||
pub fn ports_to_string(&self) -> String {
|
||||
return join_nums(&self.ports, ",");
|
||||
pub fn get_addr(&self) -> String {
|
||||
format!("{}:{}", self.ip, &self.port)
|
||||
}
|
||||
pub fn decode_players_list(data: String) -> Option<Vec<(String, String)>> {
|
||||
let value = serde_json::to_value(data).unwrap();
|
||||
|
||||
// value.as_array().unwrap().iter().map(|a| a.t).collect()
|
||||
Some(vec![(value.to_string(), "".to_string())])
|
||||
}
|
||||
pub fn decode_mod_info(data: String) -> Option<(String, Vec<(String, String)>)> {
|
||||
let value = serde_json::to_value(data).unwrap();
|
||||
|
||||
// value.as_array().unwrap().iter().map(|a| a.t).collect()
|
||||
Some(("".to_string(), vec![(value.to_string(), "".to_string())]))
|
||||
}
|
||||
pub fn decode_forge_data(
|
||||
data: String,
|
||||
) -> Option<(Vec<(String, String, bool)>, Vec<(String, String)>, i32)> {
|
||||
let value = serde_json::to_value(data).unwrap();
|
||||
|
||||
// value.as_array().unwrap().iter().map(|a| a.t).collect()
|
||||
Some((
|
||||
vec![(value.to_string(), "".to_string(), false)],
|
||||
vec![("".to_string(), "".to_string())],
|
||||
5,
|
||||
))
|
||||
}
|
||||
pub fn decode_option_bool(data: String) -> Option<bool> {
|
||||
if data == "true" {
|
||||
return Some(true);
|
||||
} else if data == "false" {
|
||||
return Some(false);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,10 +162,18 @@ impl ResultDatabase {
|
||||
// Define column families for different indexes
|
||||
|
||||
let column_families = vec![
|
||||
"default".to_string(),
|
||||
"ports".to_string(),
|
||||
"services".to_string(),
|
||||
"responses".to_string(),
|
||||
"addr".to_string(),
|
||||
"version".to_string(),
|
||||
"protocol".to_string(),
|
||||
"max_players".to_string(),
|
||||
"online_players".to_string(),
|
||||
"players_list".to_string(),
|
||||
"description".to_string(),
|
||||
"icon_hash".to_string(),
|
||||
"mod_info".to_string(),
|
||||
"forge_data".to_string(),
|
||||
"enforces_secure_chat".to_string(),
|
||||
"previews_chat".to_string(),
|
||||
];
|
||||
|
||||
Self {
|
||||
@@ -128,45 +183,56 @@ impl ResultDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_ping_results(
|
||||
// pub fn add_ping_results(
|
||||
// &self,
|
||||
// results: &Vec<IpAddr>,
|
||||
// ) -> 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(DatabaseResult {
|
||||
// ip: result.to_string(),
|
||||
// ports: vec![],
|
||||
// pub version: String,
|
||||
// pub protocol: i32,
|
||||
// pub max_players: usize,
|
||||
// pub online_players: usize,
|
||||
// pub players_list: Option<Vec<(String, String)>>,
|
||||
// pub description: String,
|
||||
// pub icon_hash: String,
|
||||
|
||||
// pub mod_info: Option<Vec<(String, String, String)>>,
|
||||
// pub forge_data: Option<(Vec<(String, String, bool)>, Vec<(String, String)>, i32)>,
|
||||
|
||||
// pub enforces_secure_chat: Option<bool>,
|
||||
// pub previews_chat: Option<bool>,
|
||||
// });
|
||||
// }
|
||||
|
||||
// return self.save_rows(string_rows);
|
||||
// }
|
||||
|
||||
// pub fn add_tcp_results(
|
||||
// &self,
|
||||
// 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_database());
|
||||
// }
|
||||
|
||||
// return self.save_rows(string_rows);
|
||||
// }
|
||||
|
||||
pub fn add_data_row(
|
||||
&self,
|
||||
results: &Vec<IpAddr>,
|
||||
results: Vec<DatabaseResult>,
|
||||
) -> 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(DatabaseResult {
|
||||
id: result.to_string(),
|
||||
ports: vec![],
|
||||
services: Vec::new(),
|
||||
responses: String::new(),
|
||||
});
|
||||
}
|
||||
|
||||
return self.save_rows(string_rows);
|
||||
}
|
||||
|
||||
pub fn add_tcp_results(
|
||||
&self,
|
||||
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_database());
|
||||
}
|
||||
|
||||
return self.save_rows(string_rows);
|
||||
}
|
||||
|
||||
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 {
|
||||
string_rows.push(result.to_database());
|
||||
string_rows.push(result);
|
||||
}
|
||||
|
||||
return self.save_rows(string_rows);
|
||||
@@ -177,10 +243,19 @@ impl ResultDatabase {
|
||||
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 cf_responses = db.cf_handle(&self.columns[3]).unwrap();
|
||||
|
||||
let cf_addr = db.cf_handle(&self.columns[0]).unwrap();
|
||||
let cf_version = db.cf_handle(&self.columns[1]).unwrap();
|
||||
let cf_protocol = db.cf_handle(&self.columns[2]).unwrap();
|
||||
let cf_max_players = db.cf_handle(&self.columns[3]).unwrap();
|
||||
let cf_online_players = db.cf_handle(&self.columns[4]).unwrap();
|
||||
let cf_players_list = db.cf_handle(&self.columns[5]).unwrap();
|
||||
let cf_description = db.cf_handle(&self.columns[6]).unwrap();
|
||||
let cf_icon_hash = db.cf_handle(&self.columns[7]).unwrap();
|
||||
let cf_mod_info = db.cf_handle(&self.columns[8]).unwrap();
|
||||
let cf_forge_data = db.cf_handle(&self.columns[9]).unwrap();
|
||||
let cf_enforces_secure_chat = db.cf_handle(&self.columns[10]).unwrap();
|
||||
let cf_previews_chat = db.cf_handle(&self.columns[11]).unwrap();
|
||||
|
||||
let start = Instant::now();
|
||||
let length = string_rows.len();
|
||||
@@ -194,7 +269,6 @@ impl ResultDatabase {
|
||||
// Process chunks in parallel
|
||||
let elapsed = {
|
||||
let db_ref = Arc::clone(&db);
|
||||
let cf_default_ref = cf_default;
|
||||
|
||||
// Create batches in parallel but write them sequentially
|
||||
let batches: Vec<WriteBatch> = chunks
|
||||
@@ -203,24 +277,55 @@ impl ResultDatabase {
|
||||
let mut batch = WriteBatch::default();
|
||||
|
||||
for row in chunk {
|
||||
batch.put_cf(cf_default_ref, row.id.as_bytes(), &vec![]);
|
||||
let key = row.get_addr();
|
||||
println!("{}", key);
|
||||
let key = key.as_bytes();
|
||||
|
||||
// Ports
|
||||
batch.put_cf(cf_addr, key, key);
|
||||
batch.put_cf(cf_version, key, row.version.as_bytes());
|
||||
batch.put_cf(cf_protocol, key, row.protocol.to_string().as_bytes());
|
||||
batch.put_cf(cf_max_players, key, row.max_players.to_string().as_bytes());
|
||||
batch.put_cf(
|
||||
cf_ports,
|
||||
row.id.as_bytes(),
|
||||
row.ports_to_string().as_bytes(),
|
||||
cf_online_players,
|
||||
key,
|
||||
row.online_players.to_string().as_bytes(),
|
||||
);
|
||||
|
||||
// Services
|
||||
batch.put_cf(
|
||||
cf_services,
|
||||
row.id.as_bytes(),
|
||||
row.services.join(",").into_bytes(),
|
||||
cf_players_list,
|
||||
key,
|
||||
serde_json::to_string(&row.players_list).unwrap().as_bytes(),
|
||||
);
|
||||
batch.put_cf(
|
||||
cf_players_list,
|
||||
key,
|
||||
serde_json::to_string(&row.players_list).unwrap().as_bytes(),
|
||||
);
|
||||
batch.put_cf(cf_description, key, row.description.as_bytes());
|
||||
batch.put_cf(cf_icon_hash, key, row.icon_hash.as_bytes());
|
||||
batch.put_cf(
|
||||
cf_mod_info,
|
||||
key,
|
||||
serde_json::to_string(&row.mod_info).unwrap().as_bytes(),
|
||||
);
|
||||
batch.put_cf(
|
||||
cf_forge_data,
|
||||
key,
|
||||
serde_json::to_string(&row.forge_data).unwrap().as_bytes(),
|
||||
);
|
||||
batch.put_cf(
|
||||
cf_enforces_secure_chat,
|
||||
key,
|
||||
serde_json::to_string(&row.enforces_secure_chat)
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
);
|
||||
batch.put_cf(
|
||||
cf_previews_chat,
|
||||
key,
|
||||
serde_json::to_string(&row.previews_chat)
|
||||
.unwrap()
|
||||
.as_bytes(),
|
||||
);
|
||||
|
||||
// Responses
|
||||
batch.put_cf(cf_responses, row.id.as_bytes(), row.responses.into_bytes());
|
||||
}
|
||||
|
||||
batch
|
||||
@@ -255,6 +360,14 @@ impl ResultDatabase {
|
||||
db.cf_handle(&self.columns[1]).unwrap(),
|
||||
db.cf_handle(&self.columns[2]).unwrap(),
|
||||
db.cf_handle(&self.columns[3]).unwrap(),
|
||||
db.cf_handle(&self.columns[4]).unwrap(),
|
||||
db.cf_handle(&self.columns[5]).unwrap(),
|
||||
db.cf_handle(&self.columns[6]).unwrap(),
|
||||
db.cf_handle(&self.columns[7]).unwrap(),
|
||||
db.cf_handle(&self.columns[8]).unwrap(),
|
||||
db.cf_handle(&self.columns[9]).unwrap(),
|
||||
db.cf_handle(&self.columns[10]).unwrap(),
|
||||
db.cf_handle(&self.columns[11]).unwrap(),
|
||||
];
|
||||
|
||||
return self.fetch_row(&db, row, &cfs);
|
||||
@@ -292,6 +405,14 @@ impl ResultDatabase {
|
||||
db.cf_handle(&self.columns[1]).unwrap(),
|
||||
db.cf_handle(&self.columns[2]).unwrap(),
|
||||
db.cf_handle(&self.columns[3]).unwrap(),
|
||||
db.cf_handle(&self.columns[4]).unwrap(),
|
||||
db.cf_handle(&self.columns[5]).unwrap(),
|
||||
db.cf_handle(&self.columns[6]).unwrap(),
|
||||
db.cf_handle(&self.columns[7]).unwrap(),
|
||||
db.cf_handle(&self.columns[8]).unwrap(),
|
||||
db.cf_handle(&self.columns[9]).unwrap(),
|
||||
db.cf_handle(&self.columns[10]).unwrap(),
|
||||
db.cf_handle(&self.columns[11]).unwrap(),
|
||||
];
|
||||
|
||||
let mut matching_keys: Vec<DatabaseResult> = Vec::new();
|
||||
@@ -328,6 +449,14 @@ impl ResultDatabase {
|
||||
db.cf_handle(&self.columns[1]).unwrap(),
|
||||
db.cf_handle(&self.columns[2]).unwrap(),
|
||||
db.cf_handle(&self.columns[3]).unwrap(),
|
||||
db.cf_handle(&self.columns[4]).unwrap(),
|
||||
db.cf_handle(&self.columns[5]).unwrap(),
|
||||
db.cf_handle(&self.columns[6]).unwrap(),
|
||||
db.cf_handle(&self.columns[7]).unwrap(),
|
||||
db.cf_handle(&self.columns[8]).unwrap(),
|
||||
db.cf_handle(&self.columns[9]).unwrap(),
|
||||
db.cf_handle(&self.columns[10]).unwrap(),
|
||||
db.cf_handle(&self.columns[11]).unwrap(),
|
||||
];
|
||||
|
||||
let mut matching_keys: Vec<DatabaseResult> = Vec::new();
|
||||
@@ -361,10 +490,12 @@ impl ResultDatabase {
|
||||
if queries.len() == 1 {
|
||||
// Return host if results include host
|
||||
match queries[0] {
|
||||
QueryDataType::Host(row) => {
|
||||
QueryDataType::Host(row, port) => {
|
||||
return Ok(vec![
|
||||
self.get_row_by_host(row.to_string().as_str())
|
||||
.expect("Host Not Found"),
|
||||
self.get_row_by_host(
|
||||
format!("{}:{}", row.to_string().as_str(), port).as_str(),
|
||||
)
|
||||
.expect("Host Not Found"),
|
||||
]);
|
||||
}
|
||||
_ => {}
|
||||
@@ -378,6 +509,14 @@ impl ResultDatabase {
|
||||
db.cf_handle(&self.columns[1]).unwrap(),
|
||||
db.cf_handle(&self.columns[2]).unwrap(),
|
||||
db.cf_handle(&self.columns[3]).unwrap(),
|
||||
db.cf_handle(&self.columns[4]).unwrap(),
|
||||
db.cf_handle(&self.columns[5]).unwrap(),
|
||||
db.cf_handle(&self.columns[6]).unwrap(),
|
||||
db.cf_handle(&self.columns[7]).unwrap(),
|
||||
db.cf_handle(&self.columns[8]).unwrap(),
|
||||
db.cf_handle(&self.columns[9]).unwrap(),
|
||||
db.cf_handle(&self.columns[10]).unwrap(),
|
||||
db.cf_handle(&self.columns[11]).unwrap(),
|
||||
];
|
||||
|
||||
let matching_key_bytes = search_parallel(&db, queries, &cfs);
|
||||
@@ -397,14 +536,43 @@ impl ResultDatabase {
|
||||
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
|
||||
ip: row_id.to_string().split(":").nth(0).unwrap().to_string(),
|
||||
port: row_id
|
||||
.to_string()
|
||||
.split(":")
|
||||
.nth(1)
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.parse::<u16>()
|
||||
.unwrap(),
|
||||
version: self.row_to_string(db, row_id, &cfs[1]),
|
||||
protocol: self
|
||||
.row_to_string(db, row_id, &cfs[2])
|
||||
.split(",")
|
||||
.map(|a| a.to_string())
|
||||
.collect(),
|
||||
responses: self.row_to_string(db, row_id, &cfs[3]),
|
||||
.parse::<u32>()
|
||||
.unwrap(),
|
||||
max_players: self
|
||||
.row_to_string(db, row_id, &cfs[3])
|
||||
.parse::<u32>()
|
||||
.unwrap(),
|
||||
online_players: self
|
||||
.row_to_string(db, row_id, &cfs[4])
|
||||
.parse::<u32>()
|
||||
.unwrap(),
|
||||
players_list: DatabaseResult::decode_players_list(
|
||||
self.row_to_string(db, row_id, &cfs[5]),
|
||||
),
|
||||
description: self.row_to_string(db, row_id, &cfs[6]),
|
||||
icon_hash: self.row_to_string(db, row_id, &cfs[7]),
|
||||
mod_info: DatabaseResult::decode_mod_info(self.row_to_string(db, row_id, &cfs[8])),
|
||||
forge_data: DatabaseResult::decode_forge_data(
|
||||
self.row_to_string(db, row_id, &cfs[9]),
|
||||
),
|
||||
enforces_secure_chat: DatabaseResult::decode_option_bool(
|
||||
self.row_to_string(db, row_id, &cfs[10]),
|
||||
),
|
||||
previews_chat: DatabaseResult::decode_option_bool(
|
||||
self.row_to_string(db, row_id, &cfs[11]),
|
||||
),
|
||||
}),
|
||||
_ => None,
|
||||
}
|
||||
@@ -421,18 +589,33 @@ impl ResultDatabase {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum QueryDataType {
|
||||
Host(IpAddr),
|
||||
Port(QueryType, i32),
|
||||
Service(QueryType, String, String),
|
||||
FullTextIncludes(String),
|
||||
Host(IpAddr, u16),
|
||||
Version(QueryType, String),
|
||||
Protocol(QueryType, u32),
|
||||
MaxPlayers(QueryType, u32),
|
||||
OnlinePlayers(QueryType, u32),
|
||||
PlayersList(QueryType, String),
|
||||
Description(QueryType, String),
|
||||
IconHash(QueryType, String),
|
||||
ModInfo(QueryType, String),
|
||||
ForgeData(QueryType, String),
|
||||
SecureChat(QueryType, String),
|
||||
PreviewsChat(QueryType, String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum QueryType {
|
||||
Equals,
|
||||
|
||||
NotEquals,
|
||||
Includes,
|
||||
NotIncludes,
|
||||
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
|
||||
GreaterOrEqual,
|
||||
LessThanOrEqual,
|
||||
}
|
||||
|
||||
// /// Search function that takes query constraints and returns matching keys
|
||||
@@ -487,7 +670,6 @@ pub enum QueryType {
|
||||
// matching_keys
|
||||
// }
|
||||
|
||||
/// Collect all keys from the ports column family as potential candidates
|
||||
fn collect_all_keys(db: &DB, cf: &ColumnFamily) -> Vec<Vec<u8>> {
|
||||
let mut keys = Vec::new();
|
||||
let iter = db.iterator_cf(cf, rocksdb::IteratorMode::Start);
|
||||
@@ -501,183 +683,158 @@ fn collect_all_keys(db: &DB, cf: &ColumnFamily) -> Vec<Vec<u8>> {
|
||||
keys
|
||||
}
|
||||
|
||||
/// Optimized search implementation with parallelism for large datasets
|
||||
pub fn search_parallel(
|
||||
db: &DB,
|
||||
queries: Vec<QueryDataType>,
|
||||
cfs: &Vec<&ColumnFamily>,
|
||||
) -> Vec<Vec<u8>> {
|
||||
// Get column family handles
|
||||
let cf_ports = cfs[1];
|
||||
let cf_services = cfs[2];
|
||||
let cf_responses = cfs[3];
|
||||
|
||||
// Collect all keys as potential candidates
|
||||
let potential_keys = collect_all_keys(db, cf_ports);
|
||||
let cf_addr = cfs[0];
|
||||
let cf_version = cfs[1];
|
||||
let cf_protocol = cfs[2];
|
||||
let cf_max_players = cfs[3];
|
||||
let cf_online_players = cfs[4];
|
||||
let cf_players_list = cfs[5];
|
||||
let cf_description = cfs[6];
|
||||
let cf_icon_hash = cfs[7];
|
||||
let cf_mod_info = cfs[8];
|
||||
let cf_forge_data = cfs[9];
|
||||
let cf_secure_chat = cfs[10];
|
||||
let cf_previews_chat = cfs[11];
|
||||
|
||||
// Partition queries by type
|
||||
let port_queries: Vec<_> = queries
|
||||
.iter()
|
||||
.filter_map(|q| {
|
||||
if let QueryDataType::Port(_, _) = q {
|
||||
Some(q)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
let mut version_queries = Vec::new();
|
||||
let mut protocol_queries = Vec::new();
|
||||
let mut max_players_queries = Vec::new();
|
||||
let mut online_players_queries = Vec::new();
|
||||
let mut players_list_queries = Vec::new();
|
||||
let mut description_queries = Vec::new();
|
||||
let mut icon_hash_queries = Vec::new();
|
||||
let mut mod_info_queries = Vec::new();
|
||||
let mut forge_data_queries = Vec::new();
|
||||
let mut secure_chat_queries = Vec::new();
|
||||
let mut previews_chat_queries = Vec::new();
|
||||
|
||||
let service_queries: Vec<_> = queries
|
||||
.iter()
|
||||
.filter_map(|q| {
|
||||
if let QueryDataType::Service(_, _, _) = q {
|
||||
Some(q)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
for q in queries {
|
||||
match q {
|
||||
QueryDataType::Version(_, _) => version_queries.push(q),
|
||||
QueryDataType::Protocol(_, _) => protocol_queries.push(q),
|
||||
QueryDataType::MaxPlayers(_, _) => max_players_queries.push(q),
|
||||
QueryDataType::OnlinePlayers(_, _) => online_players_queries.push(q),
|
||||
QueryDataType::PlayersList(_, _) => players_list_queries.push(q),
|
||||
QueryDataType::Description(_, _) => description_queries.push(q),
|
||||
QueryDataType::IconHash(_, _) => icon_hash_queries.push(q),
|
||||
QueryDataType::ModInfo(_, _) => mod_info_queries.push(q),
|
||||
QueryDataType::ForgeData(_, _) => forge_data_queries.push(q),
|
||||
QueryDataType::SecureChat(_, _) => secure_chat_queries.push(q),
|
||||
QueryDataType::PreviewsChat(_, _) => previews_chat_queries.push(q),
|
||||
|
||||
let fulltext_queries: Vec<_> = queries
|
||||
.iter()
|
||||
.filter_map(|q| {
|
||||
if let QueryDataType::FullTextIncludes(_) = q {
|
||||
Some(q)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Load all data for batch processing to minimize DB reads
|
||||
let mut ports_data = HashMap::new();
|
||||
let mut services_data = HashMap::new();
|
||||
let mut responses_data = HashMap::new();
|
||||
|
||||
for key in &potential_keys {
|
||||
if let Ok(Some(value)) = db.get_cf(cf_ports, key) {
|
||||
ports_data.insert(key.clone(), value);
|
||||
_ => {} // This should never happen
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(Some(value)) = db.get_cf(cf_services, key) {
|
||||
services_data.insert(key.clone(), value);
|
||||
fn match_string_comparison(qt: &QueryType, test: &str, data: &str) -> bool {
|
||||
match qt {
|
||||
QueryType::Equals => data == test,
|
||||
QueryType::NotEquals => data != test,
|
||||
QueryType::Includes => data.contains(test),
|
||||
QueryType::NotIncludes => !data.contains(test),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(Some(value)) = db.get_cf(cf_responses, key) {
|
||||
responses_data.insert(key.clone(), value);
|
||||
fn match_num_comparison(qt: &QueryType, test: &u32, data: &str) -> bool {
|
||||
if let Ok(data) = data.parse::<u32>() {
|
||||
match qt {
|
||||
QueryType::Equals => &data == test,
|
||||
QueryType::NotEquals => &data != test,
|
||||
QueryType::GreaterThan => &data > test,
|
||||
QueryType::LessThan => &data < test,
|
||||
QueryType::GreaterOrEqual => &data >= test,
|
||||
QueryType::LessThanOrEqual => &data <= test,
|
||||
_ => false,
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
println!("{:?}", version_queries);
|
||||
|
||||
fn loop_queries(
|
||||
db: &DB,
|
||||
cf: &ColumnFamily,
|
||||
key: &Vec<u8>,
|
||||
queries: &Vec<QueryDataType>,
|
||||
) -> bool {
|
||||
if let Ok(bytes) = db.get_cf(cf, key) {
|
||||
if let Some(bytes) = bytes {
|
||||
if let Ok(data) = std::str::from_utf8(&bytes) {
|
||||
queries.iter().all(|query| match query {
|
||||
QueryDataType::Host(_, _) => false,
|
||||
QueryDataType::Version(qt, test) => match_string_comparison(qt, test, data),
|
||||
QueryDataType::Protocol(qt, test) => match_num_comparison(qt, test, data),
|
||||
QueryDataType::MaxPlayers(qt, test) => match_num_comparison(qt, test, data),
|
||||
QueryDataType::OnlinePlayers(qt, test) => {
|
||||
match_num_comparison(qt, test, data)
|
||||
}
|
||||
QueryDataType::PlayersList(qt, test) => {
|
||||
match_string_comparison(qt, test, data)
|
||||
}
|
||||
QueryDataType::Description(qt, test) => {
|
||||
match_string_comparison(qt, test, data)
|
||||
}
|
||||
QueryDataType::IconHash(qt, test) => {
|
||||
match_string_comparison(qt, test, data)
|
||||
}
|
||||
QueryDataType::ModInfo(qt, test) => match_string_comparison(qt, test, data),
|
||||
QueryDataType::ForgeData(qt, test) => {
|
||||
match_string_comparison(qt, test, data)
|
||||
}
|
||||
QueryDataType::SecureChat(qt, test) => {
|
||||
match_string_comparison(qt, test, data)
|
||||
}
|
||||
QueryDataType::PreviewsChat(qt, test) => {
|
||||
match_string_comparison(qt, test, data)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Process in parallel using rayon
|
||||
let matching_keys: Vec<Vec<u8>> = potential_keys
|
||||
let matching_keys: Vec<Vec<u8>> = collect_all_keys(db, cf_addr)
|
||||
.into_par_iter()
|
||||
.filter(|key| {
|
||||
// Check port queries
|
||||
let ports_match = port_queries.is_empty()
|
||||
|| if let Some(ports_value) = ports_data.get(key) {
|
||||
if let Ok(ports_str) = std::str::from_utf8(ports_value) {
|
||||
let ports: Vec<i32> = ports_str
|
||||
.split(',')
|
||||
.filter_map(|p| p.trim().parse::<i32>().ok())
|
||||
.collect();
|
||||
|
||||
port_queries.iter().all(|query| {
|
||||
if let QueryDataType::Port(query_type, port_num) = *query {
|
||||
match query_type {
|
||||
QueryType::Equals => ports_str == port_num.to_string(),
|
||||
QueryType::NotEquals => ports_str != port_num.to_string(),
|
||||
QueryType::Includes => ports.contains(port_num),
|
||||
QueryType::NotIncludes => !ports.contains(port_num),
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !ports_match {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check service queries
|
||||
let services_match = service_queries.is_empty()
|
||||
|| if let (Some(services_value), Some(responses_value)) =
|
||||
(services_data.get(key), responses_data.get(key))
|
||||
{
|
||||
if let (Ok(_), Ok(responses_str)) = (
|
||||
std::str::from_utf8(services_value),
|
||||
std::str::from_utf8(responses_value),
|
||||
) {
|
||||
if let Ok(responses_map) =
|
||||
serde_json::from_str::<HashMap<String, (String, String)>>(responses_str)
|
||||
{
|
||||
service_queries.iter().all(|query| {
|
||||
if let QueryDataType::Service(query_type, service_name, data_str) =
|
||||
*query
|
||||
{
|
||||
let data_str = &data_str.to_lowercase();
|
||||
responses_map
|
||||
.values()
|
||||
.any(|(service, data)| match query_type {
|
||||
QueryType::Equals => {
|
||||
&service.to_lowercase() == service_name
|
||||
&& data == data_str
|
||||
}
|
||||
QueryType::NotEquals => {
|
||||
&service.to_lowercase() != service_name
|
||||
|| data != data_str
|
||||
}
|
||||
QueryType::Includes => {
|
||||
&service.to_lowercase() == service_name
|
||||
&& data.to_lowercase().contains(data_str)
|
||||
}
|
||||
QueryType::NotIncludes => {
|
||||
&service.to_lowercase() != service_name
|
||||
|| !data.to_lowercase().contains(data_str)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if !services_match {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check fulltext queries
|
||||
let fulltext_match = fulltext_queries.is_empty()
|
||||
|| if let Some(responses_value) = responses_data.get(key) {
|
||||
if let Ok(responses_str) = std::str::from_utf8(responses_value) {
|
||||
fulltext_queries.iter().all(|query| {
|
||||
if let QueryDataType::FullTextIncludes(search_str) = *query {
|
||||
responses_str.contains(search_str)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
fulltext_match
|
||||
(version_queries.is_empty() || loop_queries(db, cf_version, key, &version_queries))
|
||||
&& (protocol_queries.is_empty()
|
||||
|| loop_queries(db, cf_protocol, key, &protocol_queries))
|
||||
&& (max_players_queries.is_empty()
|
||||
|| loop_queries(db, cf_max_players, key, &max_players_queries))
|
||||
&& (online_players_queries.is_empty()
|
||||
|| loop_queries(db, cf_online_players, key, &online_players_queries))
|
||||
&& (players_list_queries.is_empty()
|
||||
|| loop_queries(db, cf_players_list, key, &players_list_queries))
|
||||
&& (description_queries.is_empty()
|
||||
|| loop_queries(db, cf_description, key, &description_queries))
|
||||
&& (icon_hash_queries.is_empty()
|
||||
|| loop_queries(db, cf_icon_hash, key, &icon_hash_queries))
|
||||
&& (mod_info_queries.is_empty()
|
||||
|| loop_queries(db, cf_mod_info, key, &mod_info_queries))
|
||||
&& (forge_data_queries.is_empty()
|
||||
|| loop_queries(db, cf_forge_data, key, &forge_data_queries))
|
||||
&& (secure_chat_queries.is_empty()
|
||||
|| loop_queries(db, cf_secure_chat, key, &secure_chat_queries))
|
||||
&& (previews_chat_queries.is_empty()
|
||||
|| loop_queries(db, cf_previews_chat, key, &previews_chat_queries))
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
+100
-133
File diff suppressed because one or more lines are too long
@@ -21,12 +21,12 @@ impl PingResult {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_database(&self) -> DatabaseResult {
|
||||
DatabaseResult {
|
||||
id: self.host.to_string(),
|
||||
ports: vec![],
|
||||
services: Vec::new(),
|
||||
responses: String::new(),
|
||||
}
|
||||
}
|
||||
// pub fn to_database(&self) -> DatabaseResult {
|
||||
// DatabaseResult {
|
||||
// ip: self.host.to_string(),
|
||||
// ports: vec![],
|
||||
// services: Vec::new(),
|
||||
// responses: String::new(),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -27,8 +27,9 @@ pub fn parse_ip_targets(targets: &str) -> Result<Vec<IpAddr>, Box<dyn std::error
|
||||
parse_ip_range(target, &mut ips)?;
|
||||
} else {
|
||||
// Single IP
|
||||
let ip = IpAddr::from_str(target)?;
|
||||
ips.push(ip);
|
||||
if let Ok(ip) = IpAddr::from_str(target) {
|
||||
ips.push(ip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@ impl PortScanResult {
|
||||
// data: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn to_database(&self) -> DatabaseResult {
|
||||
DatabaseResult {
|
||||
id: self.ip.to_string(),
|
||||
ports: (*self.open_ports).to_vec(),
|
||||
services: Vec::new(),
|
||||
responses: String::new(),
|
||||
}
|
||||
}
|
||||
// pub fn to_database(&self) -> DatabaseResult {
|
||||
// DatabaseResult {
|
||||
// ip: self.ip.to_string(),
|
||||
// ports: (*self.open_ports).to_vec(),
|
||||
// services: Vec::new(),
|
||||
// responses: String::new(),
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
+122
-22
@@ -4,28 +4,45 @@ use regex::Regex;
|
||||
|
||||
use crate::database::{QueryDataType, QueryType, split_nums};
|
||||
|
||||
pub fn search(query: String) -> Result<Vec<QueryDataType>, Box<dyn std::error::Error>> {
|
||||
fn try_parse_host(query: &str) -> Option<QueryDataType> {
|
||||
if query.contains(":") {
|
||||
let mut split = query.split(",");
|
||||
let ip = IpAddr::from_str(split.nth(0).unwrap());
|
||||
if let Some(port) = &split.nth(1) {
|
||||
if let (Ok(ip), Ok(port)) = (ip, port.parse::<u16>()) {
|
||||
return Some(QueryDataType::Host(ip, port));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(ip) = IpAddr::from_str(&query) {
|
||||
return Ok(vec![QueryDataType::Host(ip)]);
|
||||
return Some(QueryDataType::Host(ip, 25565));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn search(query: String) -> Result<Vec<QueryDataType>, Box<dyn std::error::Error>> {
|
||||
if let Some(host) = try_parse_host(&query) {
|
||||
return Ok(vec![host]);
|
||||
}
|
||||
|
||||
let split = query.split(" ");
|
||||
|
||||
let delim = Regex::new("(?:!=|[=:+-])")?;
|
||||
let delim = Regex::new("(?:!=|<=|>=|[=:+-><])")?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
|
||||
for query in split {
|
||||
if let Ok(ip) = IpAddr::from_str(&query) {
|
||||
return Ok(vec![QueryDataType::Host(ip)]);
|
||||
if let Some(host) = try_parse_host(query) {
|
||||
return Ok(vec![host]);
|
||||
}
|
||||
|
||||
if let Some(m) = delim.find(query) {
|
||||
let tag = query[0..m.start()].to_string();
|
||||
let tag = query[0..m.start()].to_string().to_lowercase();
|
||||
let delim = query[m.start()..m.end()].to_string();
|
||||
let data = query[m.end()..query.len()].to_string();
|
||||
|
||||
fn get_equals_type(delim: &str) -> QueryType {
|
||||
fn get_equals_type_str(delim: &str) -> QueryType {
|
||||
match delim {
|
||||
":" | "+" => Some(QueryType::Includes),
|
||||
"-" => Some(QueryType::NotIncludes),
|
||||
@@ -33,27 +50,110 @@ pub fn search(query: String) -> Result<Vec<QueryDataType>, Box<dyn std::error::E
|
||||
"!=" => Some(QueryType::NotEquals),
|
||||
_ => None,
|
||||
}
|
||||
.expect("Error parsing query")
|
||||
.expect(format!("Strings cannot be determined by \"{}\"", delim).as_str())
|
||||
}
|
||||
|
||||
match tag.as_str() {
|
||||
"port" => {
|
||||
let mut ports = split_nums(&data, ",");
|
||||
fn get_equals_type_num(delim: &str) -> QueryType {
|
||||
match delim {
|
||||
"=" => Some(QueryType::Equals),
|
||||
"!=" => Some(QueryType::NotEquals),
|
||||
">" => Some(QueryType::GreaterThan),
|
||||
"<" => Some(QueryType::LessThan),
|
||||
">=" => Some(QueryType::GreaterOrEqual),
|
||||
"<=" => Some(QueryType::LessThanOrEqual),
|
||||
_ => None,
|
||||
}
|
||||
.expect(format!("Nums cannot be determined by \"{}\"", delim).as_str())
|
||||
}
|
||||
|
||||
ports.sort();
|
||||
ports.dedup();
|
||||
(match tag.as_str() {
|
||||
"version" => {
|
||||
results.push(QueryDataType::Version(get_equals_type_str(&delim), data));
|
||||
Ok(())
|
||||
}
|
||||
"protocol" => {
|
||||
results.push(QueryDataType::Protocol(
|
||||
get_equals_type_num(&delim),
|
||||
data.parse::<u32>().expect("Error parsing protocol"),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
for port in ports {
|
||||
if port == 0 {
|
||||
continue;
|
||||
}
|
||||
results.push(QueryDataType::Port(get_equals_type(&delim), port));
|
||||
"maxplayers" => {
|
||||
results.push(QueryDataType::MaxPlayers(
|
||||
get_equals_type_num(&delim),
|
||||
data.parse::<u32>().expect("Error parsing max players"),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
"onlineplayers" => {
|
||||
results.push(QueryDataType::OnlinePlayers(
|
||||
get_equals_type_num(&delim),
|
||||
data.parse::<u32>().expect("Error parsing online players"),
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
"playerslist" => {
|
||||
results.push(QueryDataType::PlayersList(
|
||||
get_equals_type_str(&delim),
|
||||
data,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
"description" => {
|
||||
results.push(QueryDataType::Description(
|
||||
get_equals_type_str(&delim),
|
||||
data,
|
||||
));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
"iconhash" => {
|
||||
results.push(QueryDataType::IconHash(get_equals_type_str(&delim), data));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
"modinfo" => {
|
||||
results.push(QueryDataType::ModInfo(get_equals_type_str(&delim), data));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
"forgedata" => {
|
||||
results.push(QueryDataType::ForgeData(get_equals_type_str(&delim), data));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
"securechat" => {
|
||||
if !vec!["true".to_string(), "false".to_string(), "None".to_string()]
|
||||
.contains(&data)
|
||||
{
|
||||
Err(())
|
||||
} else {
|
||||
results.push(QueryDataType::SecureChat(get_equals_type_str(&delim), data));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
_ => results.push(QueryDataType::Service(get_equals_type(&delim), tag, data)),
|
||||
};
|
||||
} else {
|
||||
results.push(QueryDataType::FullTextIncludes(query.to_string()));
|
||||
"previewschat" => {
|
||||
if !vec!["true".to_string(), "false".to_string(), "None".to_string()]
|
||||
.contains(&data)
|
||||
{
|
||||
Err(())
|
||||
} else {
|
||||
results.push(QueryDataType::PreviewsChat(
|
||||
get_equals_type_str(&delim),
|
||||
data,
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
})
|
||||
.expect(format!("Invalid Tag: \"{}\"", tag).as_str());
|
||||
}
|
||||
|
||||
// (host, data) =
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod service_scan;
|
||||
pub mod services;
|
||||
pub mod tcp_http;
|
||||
pub mod tcp_https;
|
||||
pub mod tcp_minecraft;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::HashMap,
|
||||
io::{Read, Write},
|
||||
net::{IpAddr, SocketAddr, TcpStream},
|
||||
@@ -14,117 +15,68 @@ use crate::{
|
||||
database::DatabaseResult, port_scan::port_scan::PortScanResult, service_scan::tcp_http,
|
||||
};
|
||||
|
||||
use super::{services::SERVICE_PATTERNS, tcp_https, tcp_minecraft};
|
||||
use super::tcp_minecraft;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ServiceScanResult {
|
||||
pub ip: IpAddr,
|
||||
pub open_ports: Vec<i32>,
|
||||
pub services: HashMap<i32, (String, String)>,
|
||||
}
|
||||
// #[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 {
|
||||
let data = serde_json::to_string(&self.services).unwrap_or(String::new());
|
||||
// impl ServiceScanResult {
|
||||
// fn new(ip: IpAddr) -> Self {
|
||||
// ServiceScanResult {
|
||||
// ip,
|
||||
// open_ports: Vec::new(),
|
||||
// services: HashMap::new(),
|
||||
// }
|
||||
// }
|
||||
// pub fn to_database(&self) -> DatabaseResult {
|
||||
// let data = serde_json::to_string(&self.services).unwrap_or(String::new());
|
||||
|
||||
let mut services = Vec::new();
|
||||
// let mut services = Vec::new();
|
||||
|
||||
for key in self.services.keys() {
|
||||
services.push(self.services.get(key).unwrap().0.clone());
|
||||
}
|
||||
// for key in self.services.keys() {
|
||||
// services.push(self.services.get(key).unwrap().0.clone());
|
||||
// }
|
||||
|
||||
services.sort();
|
||||
services.dedup();
|
||||
// services.sort();
|
||||
// services.dedup();
|
||||
|
||||
// println!("{}", data);
|
||||
DatabaseResult {
|
||||
id: self.ip.to_string(),
|
||||
ports: self.open_ports.clone(),
|
||||
services,
|
||||
responses: data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn identify(ip: IpAddr, port: &i32, timeout: Duration) -> (String, String) {
|
||||
let e = || {
|
||||
// // println!("secondary1");
|
||||
// let (service, data) =
|
||||
// basic_identify(ip, port, timeout).unwrap_or(("tcp".to_string(), "".to_string()));
|
||||
|
||||
// // println!("secondary2");
|
||||
|
||||
// (match service.as_str() {
|
||||
// "http" => tuple_or_none("http", tcp_http::scan(ip, port, timeout)),
|
||||
// "https" => tuple_or_none("https", tcp_https::scan(ip, port, timeout)),
|
||||
// "minecraft" => tuple_or_none("minecraft", tcp_minecraft::scan(ip, port, timeout)),
|
||||
// _ => None,
|
||||
// })
|
||||
// .unwrap_or((service, data))
|
||||
basic_identify(ip, port, timeout).unwrap_or(("tcp".to_string(), "".to_string()))
|
||||
};
|
||||
// // println!("{}", data);
|
||||
// DatabaseResult {
|
||||
// ip: self.ip.to_string(),
|
||||
// ports: self.open_ports.clone(),
|
||||
// services,
|
||||
// responses: data,
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn identify(ip: IpAddr, port: &i32, timeout: Duration) -> Option<DatabaseResult> {
|
||||
// println!("primary");
|
||||
|
||||
(match port {
|
||||
80 | 8080 | 8081 | 8082 | 8083 | 8084 | 8085 | 8086 | 8087 | 8088 | 8089 => {
|
||||
// println!("http");
|
||||
tuple_or_none("http", tcp_http::scan(ip, port, timeout))
|
||||
}
|
||||
443 | 8443 => {
|
||||
// println!("https");
|
||||
tuple_or_none("https", tcp_https::scan(ip, port, timeout))
|
||||
}
|
||||
25565 | 25575 => {
|
||||
// println!("minecraft");
|
||||
tuple_or_none("minecraft", tcp_minecraft::scan(ip, port, timeout))
|
||||
}
|
||||
tcp_minecraft::scan(ip, port, timeout).ok()
|
||||
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or(e())
|
||||
// basic_identify(ip, port, timeout).unwrap_or(("tcp".to_string(), "".to_string()))
|
||||
}
|
||||
|
||||
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(),
|
||||
));
|
||||
) -> Vec<DatabaseResult> {
|
||||
let mut host_port_count: usize = 0;
|
||||
|
||||
let mut host_port: Vec<(IpAddr, i32)> = Vec::with_capacity(host_port_count as usize);
|
||||
let results: Arc<Mutex<Vec<DatabaseResult>>> = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let mut host_port: Vec<(IpAddr, i32)> = Vec::new();
|
||||
for host in &port_scan_results {
|
||||
for port in &host.open_ports {
|
||||
host_port.push((host.ip, port.clone()));
|
||||
}
|
||||
host_port_count += host.open_ports.len();
|
||||
}
|
||||
|
||||
host_port.shuffle(&mut rand::rng());
|
||||
@@ -133,7 +85,7 @@ pub fn scan_services(
|
||||
|
||||
let mut handles = Vec::new();
|
||||
let pb = Arc::new(
|
||||
ProgressBar::new(host_port_count).with_style(
|
||||
ProgressBar::new(host_port_count as u64).with_style(
|
||||
ProgressStyle::with_template(
|
||||
"[{msg}] {wide_bar:.magenta/red} {pos}/{len} ({eta_precise})",
|
||||
)
|
||||
@@ -143,7 +95,7 @@ pub fn scan_services(
|
||||
|
||||
// Create a thread for each chunk of IPs
|
||||
// let chunks = split_ips_into_chunks(port_scan_results, num_threads);
|
||||
for i in 0..=num_threads {
|
||||
for i in 0..=min(num_threads, host_port_count) {
|
||||
// println!("Thread {},{}", i, chunk.len());
|
||||
// let chunk_hosts = chunk.clone();
|
||||
let thread_hosts = Arc::clone(&host_port);
|
||||
@@ -173,18 +125,20 @@ pub fn scan_services(
|
||||
let ip = host.0;
|
||||
let port = host.1;
|
||||
|
||||
println!("{}, {}, {}", i, ip, port);
|
||||
// println!("{}, {}, {}", i, ip, port);
|
||||
|
||||
// Try to identify the service on the port
|
||||
// println!("Thread {} stall 2", i);
|
||||
let (service_name, banner) = identify(ip, &port, thread_timeout);
|
||||
let result = identify(ip, &port, thread_timeout);
|
||||
// println!("Thread {} stall 3", i);
|
||||
|
||||
let mut results_guard = thread_results.lock().unwrap();
|
||||
if let Some(result) = results_guard.iter_mut().find(|r| r.ip == ip) {
|
||||
result.open_ports.push(port);
|
||||
result.services.insert(port, (service_name, banner));
|
||||
if let Some(result) = result {
|
||||
let mut results_guard = thread_results.lock().unwrap();
|
||||
println!("{}, {}", i, result.to_string());
|
||||
results_guard.push(result);
|
||||
std::mem::drop(results_guard);
|
||||
}
|
||||
|
||||
// println!("Thread {} stall 4", i);
|
||||
|
||||
thread_pb.inc(1);
|
||||
@@ -276,61 +230,26 @@ fn try_connect(ip: IpAddr, port: &i32, timeout: Duration, probe: &[u8]) -> Optio
|
||||
}
|
||||
}
|
||||
|
||||
fn basic_identify(ip: IpAddr, port: &i32, timeout: Duration) -> Option<(String, String)> {
|
||||
// println!("Start try_connect");
|
||||
// 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(),
|
||||
));
|
||||
}
|
||||
}
|
||||
// fn basic_identify(ip: IpAddr, port: &i32, timeout: Duration) -> Option<(String, String)> {
|
||||
// // println!("Start try_connect");
|
||||
// // 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(),
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
|
||||
// println!("End try_connect1");
|
||||
// // println!("End try_connect1");
|
||||
|
||||
// Port is open but service couldn't be identified
|
||||
return Some(("tcp".to_string(), "".to_string()));
|
||||
}
|
||||
// // Port is open but service couldn't be identified
|
||||
// return Some(("tcp".to_string(), "".to_string()));
|
||||
// }
|
||||
|
||||
// println!("Start try_connect2");
|
||||
// // println!("Start try_connect2");
|
||||
|
||||
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
|
||||
}
|
||||
// None
|
||||
// }
|
||||
|
||||
@@ -1,668 +0,0 @@
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
|
||||
lazy_static! {
|
||||
pub static ref SERVICE_PATTERNS: Vec<(Regex, &'static str)> = vec![
|
||||
// Unknown
|
||||
(Regex::new(r"^$").unwrap(), "tcp"),
|
||||
|
||||
// HTTP and Web Services
|
||||
(Regex::new(r"HTTP.*HTTPS").unwrap(), "https"),
|
||||
(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"),
|
||||
];
|
||||
}
|
||||
@@ -6,37 +6,93 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use crate::database::DatabaseResult;
|
||||
|
||||
pub fn scan(
|
||||
ip: IpAddr,
|
||||
port: &i32,
|
||||
timeout: Duration,
|
||||
) -> Result<String, Box<dyn std::error::Error>> {
|
||||
) -> Result<DatabaseResult, Box<dyn std::error::Error>> {
|
||||
let port = *port as u16;
|
||||
let socket = SocketAddr::new(ip, port);
|
||||
let ip = ip.to_string();
|
||||
|
||||
let mut stream = TcpStream::connect_timeout(&socket, timeout)?;
|
||||
stream.set_read_timeout(Some(timeout))?;
|
||||
stream.set_write_timeout(Some(timeout))?;
|
||||
let pong = ping(&mut stream, &ip, port)?;
|
||||
|
||||
let icon_hash = match pong.favicon {
|
||||
Some(icon) => digest(icon),
|
||||
None => "null".to_string(),
|
||||
None => "None".to_string(),
|
||||
};
|
||||
|
||||
Ok(serde_json::to_string(&json!({
|
||||
"version": pong.version,
|
||||
"protocol": pong.protocol,
|
||||
"max_players": pong.max_players,
|
||||
"online_players": pong.online_players,
|
||||
"players_list": pong.sample,
|
||||
Ok(DatabaseResult {
|
||||
ip: ip.to_string(),
|
||||
port: port as u16,
|
||||
version: pong.version,
|
||||
protocol: pong.protocol as u32,
|
||||
max_players: pong.max_players as u32,
|
||||
online_players: pong.online_players as u32,
|
||||
players_list: if let Some(sample) = pong.sample {
|
||||
Some(
|
||||
sample
|
||||
.iter()
|
||||
.map(|a| (a.id.clone(), a.name.clone()))
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
description: pong.description.unwrap_or(json!("")).to_string(),
|
||||
icon_hash: icon_hash,
|
||||
mod_info: if let Some(mod_info) = pong.mod_info {
|
||||
Some((
|
||||
mod_info.mod_type,
|
||||
mod_info
|
||||
.mod_list
|
||||
.iter()
|
||||
.map(|a| (a.mod_id.clone(), a.version.clone()))
|
||||
.collect(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
forge_data: if let Some(forge_data) = pong.forge_data {
|
||||
Some((
|
||||
forge_data
|
||||
.channels
|
||||
.iter()
|
||||
.map(|a| (a.version.clone(), a.res.clone(), a.required))
|
||||
.collect(),
|
||||
forge_data
|
||||
.mods
|
||||
.iter()
|
||||
.map(|a| (a.mod_marker.clone(), a.mod_id.clone()))
|
||||
.collect(),
|
||||
forge_data.fml_network_version,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
},
|
||||
enforces_secure_chat: pong.enforces_secure_chat,
|
||||
previews_chat: pong.previews_chat,
|
||||
})
|
||||
|
||||
"description": pong.description,
|
||||
"icon": icon_hash,
|
||||
// Ok(serde_json::to_string(&json!({
|
||||
// "version": pong.version,
|
||||
// "protocol": pong.protocol,
|
||||
// "max_players": pong.max_players,
|
||||
// "online_players": pong.online_players,
|
||||
// "players_list": pong.sample,
|
||||
|
||||
"mod_info": pong.mod_info,
|
||||
"forge_data": pong.forge_data,
|
||||
// "description": pong.description,
|
||||
// "icon": icon_hash,
|
||||
|
||||
"enforces_secure_chat": pong.enforces_secure_chat,
|
||||
"previews_chat": pong.previews_chat
|
||||
}))?)
|
||||
// "mod_info": pong.mod_info,
|
||||
// "forge_data": pong.forge_data,
|
||||
|
||||
// "enforces_secure_chat": pong.enforces_secure_chat,
|
||||
// "previews_chat": pong.previews_chat
|
||||
// }))?)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user