working on discovery

This commit is contained in:
Maya
2025-07-31 19:25:44 -04:00
parent 7774c0c152
commit d4e148a2e4
9 changed files with 432 additions and 442 deletions
+88 -88
View File
@@ -1,99 +1,99 @@
// use axum::{
// extract::{Path, State},
// http::StatusCode,
// response::{Json},
// };
// use std::sync::Arc;
// use crate::AppState;
// use crate::shared::handlers::ApiResponse;
use axum::{
extract::{Path, State},
http::StatusCode,
response::{Json},
};
use std::sync::Arc;
use crate::AppState;
use crate::shared::handlers::ApiResponse;
// use crate::components::discovery::types::{DiscoveryConfig, StartDiscoveryRequest, DiscoveryProgress, DiscoveredDevice};
// use crate::components::discovery::types::DiscoveryDepth::{Basic, Deep, Standard};
// use crate::components::nodes::types::NetworkNode;
use crate::components::discovery::types::{DiscoveryConfig, StartDiscoveryRequest, DiscoveryProgress, DiscoveredDevice};
use crate::components::discovery::types::DiscoveryDepth::{Basic, Deep, Standard};
use crate::components::nodes::types::NetworkNode;
// pub async fn start_discovery(
// State(state): State<Arc<AppState>>,
// Json(request): Json<StartDiscoveryRequest>,
// ) -> Result<Json<ApiResponse<()>>, StatusCode> {
// let config = DiscoveryConfig {
// target_subnets: request.target_subnets,
// discovery_depth: match request.discovery_depth.as_str() {
// "basic" => Basic,
// "deep" => Deep,
// _ => Standard,
// },
// include_services: request.include_services,
// snmp_communities: request.snmp_communities,
// max_concurrent: request.max_concurrent,
// timeout_ms: request.timeout,
// port_scan_enabled: true,
// common_ports: vec![22, 23, 25, 53, 80, 110, 143, 443, 993, 995, 3389, 5900, 8080, 8443],
// };
pub async fn start_discovery(
State(state): State<Arc<AppState>>,
Json(request): Json<StartDiscoveryRequest>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
let config = DiscoveryConfig {
target_subnets: request.target_subnets,
discovery_depth: match request.discovery_depth.as_str() {
"basic" => Basic,
"deep" => Deep,
_ => Standard,
},
include_services: request.include_services,
snmp_communities: request.snmp_communities,
max_concurrent: request.max_concurrent,
timeout_ms: request.timeout,
port_scan_enabled: true,
common_ports: vec![22, 23, 25, 53, 80, 110, 143, 443, 993, 995, 3389, 5900, 8080, 8443],
};
// let discovery = state.discovery.clone();
// tokio::spawn(async move {
// let discovery_guard = discovery.lock().await;
// if let Err(e) = discovery_guard.start_discovery(config).await {
// tracing::error!("Discovery failed: {}", e);
// }
// });
let discovery = state.discovery.clone();
tokio::spawn(async move {
let discovery_guard = discovery.lock().await;
if let Err(e) = discovery_guard.start_discovery(config).await {
tracing::error!("Discovery failed: {}", e);
}
});
// Ok(Json(ApiResponse::success(())))
// }
Ok(Json(ApiResponse::success(())))
}
// pub async fn stop_discovery(
// State(state): State<Arc<AppState>>,
// ) -> Result<Json<ApiResponse<()>>, StatusCode> {
// let discovery = state.discovery.lock().await;
// discovery.stop_discovery().await;
// Ok(Json(ApiResponse::success(())))
// }
pub async fn stop_discovery(
State(state): State<Arc<AppState>>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
let discovery = state.discovery.lock().await;
discovery.stop_discovery().await;
Ok(Json(ApiResponse::success(())))
}
// pub async fn get_discovery_progress(
// State(state): State<Arc<AppState>>,
// ) -> Result<Json<ApiResponse<DiscoveryProgress>>, StatusCode> {
// let discovery = state.discovery.lock().await;
// let progress = discovery.get_progress().await;
// Ok(Json(ApiResponse::success(progress)))
// }
pub async fn get_discovery_progress(
State(state): State<Arc<AppState>>,
) -> Result<Json<ApiResponse<DiscoveryProgress>>, StatusCode> {
let discovery = state.discovery.lock().await;
let progress = discovery.get_progress().await;
Ok(Json(ApiResponse::success(progress)))
}
// pub async fn get_discovered_devices(
// State(state): State<Arc<AppState>>,
// ) -> Result<Json<ApiResponse<Vec<DiscoveredDevice>>>, StatusCode> {
// let discovery = state.discovery.lock().await;
// let devices = discovery.get_discovered_devices().await;
// Ok(Json(ApiResponse::success(devices)))
// }
pub async fn get_discovered_devices(
State(state): State<Arc<AppState>>,
) -> Result<Json<ApiResponse<Vec<DiscoveredDevice>>>, StatusCode> {
let discovery = state.discovery.lock().await;
let devices = discovery.get_discovered_devices().await;
Ok(Json(ApiResponse::success(devices)))
}
// pub async fn accept_discovered_device(
// State(state): State<Arc<AppState>>,
// Path(device_id): Path<String>,
// ) -> Result<Json<ApiResponse<NetworkNode>>, StatusCode> {
// let discovery = state.discovery.lock().await;
pub async fn accept_discovered_device(
State(state): State<Arc<AppState>>,
Path(device_id): Path<String>,
) -> Result<Json<ApiResponse<NetworkNode>>, StatusCode> {
let discovery = state.discovery.lock().await;
// match discovery.accept_device(&device_id).await {
// Ok(node) => {
// // Save the new node to storage
// match state.storage.save_node(&node).await {
// Ok(_) => Ok(Json(ApiResponse::success(node))),
// Err(e) => {
// tracing::error!("Failed to save accepted device as node: {}", e);
// Ok(Json(ApiResponse::error(format!("Failed to save node: {}", e))))
// }
// }
// },
// Err(e) => Ok(Json(ApiResponse::error(e)))
// }
// }
match discovery.accept_device(&device_id).await {
Ok(node) => {
// Save the new node to storage
match state.storage.save_node(&node).await {
Ok(_) => Ok(Json(ApiResponse::success(node))),
Err(e) => {
tracing::error!("Failed to save accepted device as node: {}", e);
Ok(Json(ApiResponse::error(format!("Failed to save node: {}", e))))
}
}
},
Err(e) => Ok(Json(ApiResponse::error(e)))
}
}
// pub async fn reject_discovered_device(
// State(state): State<Arc<AppState>>,
// Path(device_id): Path<String>,
// ) -> Result<Json<ApiResponse<()>>, StatusCode> {
// let discovery = state.discovery.lock().await;
pub async fn reject_discovered_device(
State(state): State<Arc<AppState>>,
Path(device_id): Path<String>,
) -> Result<Json<ApiResponse<()>>, StatusCode> {
let discovery = state.discovery.lock().await;
// match discovery.reject_device(&device_id).await {
// Ok(_) => Ok(Json(ApiResponse::success(()))),
// Err(e) => Ok(Json(ApiResponse::error(e)))
// }
// }
match discovery.reject_device(&device_id).await {
Ok(_) => Ok(Json(ApiResponse::success(()))),
Err(e) => Ok(Json(ApiResponse::error(e)))
}
}
+130 -146
View File
@@ -1,176 +1,160 @@
// // src/discovery/scanner.rs
// use super::types::*;
// use std::collections::{HashMap, HashSet};
// use std::net::{IpAddr, Ipv4Addr};
// use std::sync::Arc;
// use std::time::{Duration, Instant};
// use tokio::net::TcpStream;
// use tokio::sync::mpsc;
// use tokio::time::timeout;
// src/discovery/scanner.rs
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use std::time::{Duration};
use tokio::net::TcpStream;
use tokio::sync::mpsc;
use tokio::time::timeout;
// pub async fn ping_host(ip: &str, timeout_ms: u64) -> Result<bool, Box<dyn std::error::Error>> {
// // Use TCP connect to port 80 as a proxy for "ping" since ICMP requires privileges
// let addr = format!("{}:80", ip);
// match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
// Ok(Ok(_)) => Ok(true),
// _ => {
// // Try port 22 as fallback
// let addr = format!("{}:22", ip);
// match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
// Ok(Ok(_)) => Ok(true),
// _ => Ok(false)
// }
// }
// }
// }
use crate::shared::utils::get_common_service_name;
use crate::components::discovery::types::{DiscoveryConfig, DeviceType};
// pub async fn test_port(ip: &str, port: u16, timeout_ms: u64) -> Result<bool, Box<dyn std::error::Error>> {
// let addr = format!("{}:{}", ip, port);
// match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
// Ok(Ok(_)) => Ok(true),
// _ => Ok(false)
// }
// }
pub async fn ping_host(ip: &str, timeout_ms: u64) -> Result<bool, Box<dyn std::error::Error>> {
// Use TCP connect to port 80 as a proxy for "ping" since ICMP requires privileges
let addr = format!("{}:80", ip);
match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
Ok(Ok(_)) => Ok(true),
_ => {
// Try port 22 as fallback
let addr = format!("{}:22", ip);
match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
Ok(Ok(_)) => Ok(true),
_ => Ok(false)
}
}
}
}
// pub async fn ping_sweep(
// target_ips: &[String],
// config: &DiscoveryConfig,
// stop_signal: Arc<tokio::sync::RwLock<bool>>
// ) -> Vec<String> {
// let (tx, mut rx) = mpsc::channel(100);
// let mut alive_hosts = Vec::new();
// let semaphore = Arc::new(tokio::sync::Semaphore::new(config.max_concurrent));
pub async fn test_port(ip: &str, port: u16, timeout_ms: u64) -> Result<bool, Box<dyn std::error::Error>> {
let addr = format!("{}:{}", ip, port);
match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
Ok(Ok(_)) => Ok(true),
_ => Ok(false)
}
}
// // Spawn tasks for each IP
// for ip in target_ips {
// let ip = ip.clone();
// let tx = tx.clone();
// let semaphore = semaphore.clone();
// let timeout_ms = config.timeout_ms;
// let stop_signal = stop_signal.clone();
pub async fn ping_sweep(
target_ips: &[String],
config: &DiscoveryConfig,
stop_signal: Arc<tokio::sync::RwLock<bool>>
) -> Vec<String> {
let (tx, mut rx) = mpsc::channel(100);
let mut alive_hosts = Vec::new();
let semaphore = Arc::new(tokio::sync::Semaphore::new(config.max_concurrent));
// tokio::spawn(async move {
// let _permit = semaphore.acquire().await.unwrap();
// Spawn tasks for each IP
for ip in target_ips {
let ip = ip.clone();
let tx = tx.clone();
let semaphore = semaphore.clone();
let timeout_ms = config.timeout_ms;
let stop_signal = stop_signal.clone();
tokio::spawn(async move {
let _permit = semaphore.acquire().await.unwrap();
// if *stop_signal.read().await {
// return;
// }
if *stop_signal.read().await {
return;
}
// if let Ok(true) = ping_host(&ip, timeout_ms).await {
// let _ = tx.send(ip).await;
// }
// });
// }
if let Ok(true) = ping_host(&ip, timeout_ms).await {
let _ = tx.send(ip).await;
}
});
}
// drop(tx); // Close the channel
drop(tx); // Close the channel
// // Collect results
// while let Some(ip) = rx.recv().await {
// alive_hosts.push(ip);
// Collect results
while let Some(ip) = rx.recv().await {
alive_hosts.push(ip);
// if *stop_signal.read().await {
// break;
// }
// }
if *stop_signal.read().await {
break;
}
}
// alive_hosts
// }
alive_hosts
}
// pub async fn port_scan(
// hosts: &[String],
// config: &DiscoveryConfig,
// stop_signal: Arc<tokio::sync::RwLock<bool>>
// ) -> HashMap<String, Vec<u16>> {
// let mut results = HashMap::new();
// let semaphore = Arc::new(tokio::sync::Semaphore::new(config.max_concurrent));
pub async fn port_scan(
hosts: &[String],
config: &DiscoveryConfig,
stop_signal: Arc<tokio::sync::RwLock<bool>>
) -> HashMap<String, Vec<u16>> {
let mut results = HashMap::new();
let semaphore = Arc::new(tokio::sync::Semaphore::new(config.max_concurrent));
// for host in hosts {
// if *stop_signal.read().await {
// break;
// }
for host in hosts {
if *stop_signal.read().await {
break;
}
// let mut open_ports = Vec::new();
let mut open_ports = Vec::new();
// for &port in &config.common_ports {
// let _permit = semaphore.acquire().await.unwrap();
for &port in &config.common_ports {
let _permit = semaphore.acquire().await.unwrap();
// if *stop_signal.read().await {
// break;
// }
if *stop_signal.read().await {
break;
}
// if let Ok(true) = test_port(host, port, config.timeout_ms).await {
// open_ports.push(port);
// }
// }
if let Ok(true) = test_port(host, port, config.timeout_ms).await {
open_ports.push(port);
}
}
// if !open_ports.is_empty() {
// results.insert(host.clone(), open_ports);
// }
// }
if !open_ports.is_empty() {
results.insert(host.clone(), open_ports);
}
}
// results
// }
results
}
// pub fn classify_device_type(ports: &[u16]) -> DeviceType {
// let port_set: HashSet<u16> = ports.iter().cloned().collect();
pub fn classify_device_type(ports: &[u16]) -> DeviceType {
let port_set: HashSet<u16> = ports.iter().cloned().collect();
// // Router/Switch indicators
// if port_set.contains(&161) && (port_set.contains(&22) || port_set.contains(&23)) {
// return DeviceType::Router;
// }
// Router/Switch indicators
if port_set.contains(&161) && (port_set.contains(&22) || port_set.contains(&23)) {
return DeviceType::Router;
}
// // Server indicators
// if port_set.contains(&22) && (port_set.contains(&80) || port_set.contains(&443)) {
// return DeviceType::Server;
// }
// Server indicators
if port_set.contains(&22) && (port_set.contains(&80) || port_set.contains(&443)) {
return DeviceType::Server;
}
// // Printer indicators
// if port_set.contains(&631) || port_set.contains(&9100) {
// return DeviceType::Printer;
// }
// Printer indicators
if port_set.contains(&631) || port_set.contains(&9100) {
return DeviceType::Printer;
}
// // NAS indicators
// if port_set.contains(&139) && port_set.contains(&445) {
// return DeviceType::NAS;
// }
// NAS indicators
if port_set.contains(&139) && port_set.contains(&445) {
return DeviceType::NAS;
}
// // IoT indicators (common IoT ports)
// if port_set.contains(&8080) || port_set.contains(&8443) {
// return DeviceType::IoT;
// }
// IoT indicators (common IoT ports)
if port_set.contains(&8080) || port_set.contains(&8443) {
return DeviceType::IoT;
}
// // Access Point indicators
// if port_set.contains(&80) && port_set.contains(&443) && ports.len() <= 3 {
// return DeviceType::AccessPoint;
// }
// Access Point indicators
if port_set.contains(&80) && port_set.contains(&443) && ports.len() <= 3 {
return DeviceType::AccessPoint;
}
// DeviceType::Unknown
// }
DeviceType::Unknown
}
// pub async fn detect_services_on_ports(ip: &str, ports: &[u16], timeout_ms: u64) -> Vec<String> {
// let mut services = Vec::new();
pub async fn detect_services_on_ports(ip: &str, ports: &[u16], _timeout_ms: u64) -> Vec<String> {
let mut services = Vec::new();
// for &port in ports {
// let service = match port {
// 22 => "SSH",
// 23 => "Telnet",
// 25 => "SMTP",
// 53 => "DNS",
// 80 => "HTTP",
// 110 => "POP3",
// 143 => "IMAP",
// 443 => "HTTPS",
// 993 => "IMAPS",
// 995 => "POP3S",
// 3389 => "RDP",
// 5900 => "VNC",
// 8080 => "HTTP-Alt",
// 8443 => "HTTPS-Alt",
// _ => continue,
// };
// services.push(service.to_string());
// }
for &port in ports {
let service = get_common_service_name(port);
services.push(service.to_string());
}
// services
// }
services
}
+150 -149
View File
@@ -1,171 +1,172 @@
// // src/discovery/snmp.rs
// use snmp2::{SyncSession, Value as SnmpValue};
// use std::collections::HashMap;
// use std::time::Duration;
// use tokio::net::TcpStream;
// use tokio::time::timeout;
// src/discovery/snmp.rs
use snmp2::{SyncSession, Value as SnmpValue};
use std::collections::HashMap;
use std::time::Duration;
use tokio::net::TcpStream;
use tokio::time::timeout;
use crate::components::discovery::types::DiscoveryConfig;
// const SYSTEM_DESCR: &str = "1.3.6.1.2.1.1.1.0";
// const SYSTEM_NAME: &str = "1.3.6.1.2.1.1.5.0";
// const SYSTEM_CONTACT: &str = "1.3.6.1.2.1.1.4.0";
// const SYSTEM_LOCATION: &str = "1.3.6.1.2.1.1.6.0";
const SYSTEM_DESCR: &str = "1.3.6.1.2.1.1.1.0";
const SYSTEM_NAME: &str = "1.3.6.1.2.1.1.5.0";
const SYSTEM_CONTACT: &str = "1.3.6.1.2.1.1.4.0";
const SYSTEM_LOCATION: &str = "1.3.6.1.2.1.1.6.0";
// fn create_snmp_session(target: &str, community: &str, timeout_ms: u64) -> Result<SyncSession, String> {
// let session = SyncSession::new(
// target,
// community.as_bytes(),
// Some(Duration::from_millis(timeout_ms)),
// 0
// ).map_err(|e| format!("Failed to create SNMP session: {}", e))?;
fn create_snmp_session(target: &str, community: &str, timeout_ms: u64) -> Result<SyncSession, String> {
let session = SyncSession::new(
target,
community.as_bytes(),
Some(Duration::from_millis(timeout_ms)),
0
).map_err(|e| format!("Failed to create SNMP session: {}", e))?;
// Ok(session)
// }
Ok(session)
}
// pub async fn test_snmp_connectivity(ip: &str, community: &str, timeout_ms: u64) -> Result<bool, Box<dyn std::error::Error>> {
// // Test SNMP connectivity by querying system description
// match query_snmp_get(ip, community, SYSTEM_DESCR, timeout_ms).await {
// Ok(_) => Ok(true),
// Err(_) => {
// // Fallback: try to connect to SNMP port
// let addr = format!("{}:161", ip);
// match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
// Ok(Ok(_)) => Ok(true),
// _ => Ok(false)
// }
// }
// }
// }
pub async fn test_snmp_connectivity(ip: &str, community: &str, timeout_ms: u64) -> Result<bool, Box<dyn std::error::Error>> {
// Test SNMP connectivity by querying system description
match query_snmp_get(ip, community, SYSTEM_DESCR, timeout_ms).await {
Ok(_) => Ok(true),
Err(_) => {
// Fallback: try to connect to SNMP port
let addr = format!("{}:161", ip);
match timeout(Duration::from_millis(timeout_ms), TcpStream::connect(&addr)).await {
Ok(Ok(_)) => Ok(true),
_ => Ok(false)
}
}
}
}
// async fn query_snmp_get(target: &str, community: &str, oid: &str, timeout_ms: u64) -> Result<String, String> {
// let session = create_snmp_session(target, community, timeout_ms)?;
async fn query_snmp_get(target: &str, community: &str, oid: &str, timeout_ms: u64) -> Result<String, String> {
let session = create_snmp_session(target, community, timeout_ms)?;
// match session.get(oid.as_bytes()) {
// Ok(response) => {
// if let Some((_, value)) = response.first() {
// let value_str = snmp_value_to_string(value);
// Ok(value_str)
// } else {
// Err("No response received".to_string())
// }
// },
// Err(e) => Err(format!("SNMP get failed: {}", e))
// }
// }
match session.get(oid.as_bytes()) {
Ok(response) => {
if let Some((_, value)) = response.first() {
let value_str = snmp_value_to_string(value);
Ok(value_str)
} else {
Err("No response received".to_string())
}
},
Err(e) => Err(format!("SNMP get failed: {}", e))
}
}
// pub async fn query_snmp_system_info(ip: &str, community: &str, timeout_ms: u64) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
// let mut info = HashMap::new();
pub async fn query_snmp_system_info(ip: &str, community: &str, timeout_ms: u64) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
let mut info = HashMap::new();
// // Query system name (hostname)
// if let Ok(hostname) = query_snmp_get(ip, community, SYSTEM_NAME, timeout_ms).await {
// if !hostname.is_empty() && hostname != "Unknown" {
// info.insert("hostname".to_string(), hostname);
// }
// }
// Query system name (hostname)
if let Ok(hostname) = query_snmp_get(ip, community, SYSTEM_NAME, timeout_ms).await {
if !hostname.is_empty() && hostname != "Unknown" {
info.insert("hostname".to_string(), hostname);
}
}
// // Query system description (often contains vendor info)
// if let Ok(description) = query_snmp_get(ip, community, SYSTEM_DESCR, timeout_ms).await {
// if !description.is_empty() && description != "Unknown" {
// info.insert("description".to_string(), description);
// Query system description (often contains vendor info)
if let Ok(description) = query_snmp_get(ip, community, SYSTEM_DESCR, timeout_ms).await {
if !description.is_empty() && description != "Unknown" {
info.insert("description".to_string(), description);
// // Try to extract vendor from description
// let vendor = extract_vendor_from_description(&description);
// if !vendor.is_empty() {
// info.insert("vendor".to_string(), vendor);
// }
// }
// }
// Try to extract vendor from description
let vendor = extract_vendor_from_description(&description);
if !vendor.is_empty() {
info.insert("vendor".to_string(), vendor);
}
}
}
// // Query system contact
// if let Ok(contact) = query_snmp_get(ip, community, SYSTEM_CONTACT, timeout_ms).await {
// if !contact.is_empty() && contact != "Unknown" {
// info.insert("contact".to_string(), contact);
// }
// }
// Query system contact
if let Ok(contact) = query_snmp_get(ip, community, SYSTEM_CONTACT, timeout_ms).await {
if !contact.is_empty() && contact != "Unknown" {
info.insert("contact".to_string(), contact);
}
}
// // Query system location
// if let Ok(location) = query_snmp_get(ip, community, SYSTEM_LOCATION, timeout_ms).await {
// if !location.is_empty() && location != "Unknown" {
// info.insert("location".to_string(), location);
// }
// }
// Query system location
if let Ok(location) = query_snmp_get(ip, community, SYSTEM_LOCATION, timeout_ms).await {
if !location.is_empty() && location != "Unknown" {
info.insert("location".to_string(), location);
}
}
// Ok(info)
// }
Ok(info)
}
// pub async fn snmp_discovery(
// hosts: &[String],
// config: &DiscoveryConfig,
// stop_signal: Arc<tokio::sync::RwLock<bool>>
// ) -> HashMap<String, HashMap<String, String>> {
// let mut results = HashMap::new();
pub async fn snmp_discovery(
hosts: &[String],
config: &DiscoveryConfig,
stop_signal: Arc<tokio::sync::RwLock<bool>>
) -> HashMap<String, HashMap<String, String>> {
let mut results = HashMap::new();
// for host in hosts {
// if *stop_signal.read().await {
// break;
// }
for host in hosts {
if *stop_signal.read().await {
break;
}
// for community in &config.snmp_communities {
// // Test SNMP connectivity
// if let Ok(true) = test_snmp_connectivity(host, community, config.timeout_ms).await {
// // Query system information
// if let Ok(sys_info) = query_snmp_system_info(host, community, config.timeout_ms).await {
// if !sys_info.is_empty() {
// results.insert(host.clone(), sys_info);
// break; // Found working community, no need to try others
// }
// }
// }
// }
// }
for community in &config.snmp_communities {
// Test SNMP connectivity
if let Ok(true) = test_snmp_connectivity(host, community, config.timeout_ms).await {
// Query system information
if let Ok(sys_info) = query_snmp_system_info(host, community, config.timeout_ms).await {
if !sys_info.is_empty() {
results.insert(host.clone(), sys_info);
break; // Found working community, no need to try others
}
}
}
}
}
// results
// }
results
}
// fn snmp_value_to_string(value: &SnmpValue) -> String {
// match value {
// SnmpValue::Integer(i) => i.to_string(),
// SnmpValue::OctetString(s) => String::from_utf8_lossy(&s).to_string(),
// SnmpValue::ObjectIdentifier(o) => String::from_utf8_lossy(&o).to_string(),
// SnmpValue::IpAddress(ip) => format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]),
// SnmpValue::Counter32(c) => c.to_string(),
// SnmpValue::Gauge32(g) => g.to_string(),
// SnmpValue::TimeTicks(t) => t.to_string(),
// SnmpValue::Counter64(c) => c.to_string(),
// _ => "Unknown".to_string(),
// }
// }
fn snmp_value_to_string(value: &SnmpValue) -> String {
match value {
SnmpValue::Integer(i) => i.to_string(),
SnmpValue::OctetString(s) => String::from_utf8_lossy(&s).to_string(),
SnmpValue::ObjectIdentifier(o) => String::from_utf8_lossy(&o).to_string(),
SnmpValue::IpAddress(ip) => format!("{}.{}.{}.{}", ip[0], ip[1], ip[2], ip[3]),
SnmpValue::Counter32(c) => c.to_string(),
SnmpValue::Gauge32(g) => g.to_string(),
SnmpValue::TimeTicks(t) => t.to_string(),
SnmpValue::Counter64(c) => c.to_string(),
_ => "Unknown".to_string(),
}
}
// fn extract_vendor_from_description(description: &str) -> String {
// let desc_lower = description.to_lowercase();
fn extract_vendor_from_description(description: &str) -> String {
let desc_lower = description.to_lowercase();
// // Common vendor patterns in SNMP system descriptions
// let vendors = vec![
// ("cisco", "Cisco"),
// ("juniper", "Juniper"),
// ("hp ", "HP"),
// ("hewlett", "HP"),
// ("dell", "Dell"),
// ("netgear", "Netgear"),
// ("d-link", "D-Link"),
// ("tp-link", "TP-Link"),
// ("ubiquiti", "Ubiquiti"),
// ("mikrotik", "MikroTik"),
// ("fortinet", "Fortinet"),
// ("palo alto", "Palo Alto"),
// ("aruba", "Aruba"),
// ("huawei", "Huawei"),
// ("zyxel", "ZyXEL"),
// ("linux", "Linux"),
// ("windows", "Microsoft"),
// ("freebsd", "FreeBSD"),
// ("openbsd", "OpenBSD"),
// ];
// Common vendor patterns in SNMP system descriptions
let vendors = vec![
("cisco", "Cisco"),
("juniper", "Juniper"),
("hp ", "HP"),
("hewlett", "HP"),
("dell", "Dell"),
("netgear", "Netgear"),
("d-link", "D-Link"),
("tp-link", "TP-Link"),
("ubiquiti", "Ubiquiti"),
("mikrotik", "MikroTik"),
("fortinet", "Fortinet"),
("palo alto", "Palo Alto"),
("aruba", "Aruba"),
("huawei", "Huawei"),
("zyxel", "ZyXEL"),
("linux", "Linux"),
("windows", "Microsoft"),
("freebsd", "FreeBSD"),
("openbsd", "OpenBSD"),
];
// for (pattern, vendor) in vendors {
// if desc_lower.contains(pattern) {
// return vendor.to_string();
// }
// }
for (pattern, vendor) in vendors {
if desc_lower.contains(pattern) {
return vendor.to_string();
}
}
// "Unknown".to_string()
// }
"Unknown".to_string()
}
+20 -6
View File
@@ -1,6 +1,9 @@
use std::time::{SystemTime, UNIX_EPOCH};
use std::time::{SystemTime, UNIX_EPOCH, Duration};
use crate::components::tests::types::CheckConfig;
use crate::components::diagnostics::types::CheckResult;
use reqwest::Client;
mod basic;
mod vpn;
@@ -12,7 +15,6 @@ mod security;
mod performance;
mod analysis;
mod cdn;
mod utils;
mod vlan;
use basic::*;
@@ -26,10 +28,22 @@ use performance::*;
use analysis::*;
use cdn::*;
pub use utils::{
create_http_client,
get_common_service_name,
};
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
const DEFAULT_USER_AGENT: &str = "Netzoot/1.0";
// Create HTTP client with reasonable defaults
pub fn create_http_client(timeout_ms: Option<u64>) -> Result<Client, String> {
let timeout = timeout_ms
.map(|ms| Duration::from_millis(ms))
.unwrap_or(DEFAULT_TIMEOUT);
Client::builder()
.timeout(timeout)
.user_agent(DEFAULT_USER_AGENT)
.danger_accept_invalid_certs(false)
.build()
.map_err(|e| format!("Failed to create HTTP client: {}", e))
}
#[derive(Debug, thiserror::Error)]
pub enum CheckError {
+2 -1
View File
@@ -1,10 +1,11 @@
use super::{create_http_client, get_common_service_name};
use super::{create_http_client};
use serde_json::{json, Value};
use std::net::ToSocketAddrs;
use std::time::{Duration, Instant};
use tokio::net::TcpStream;
use tokio::time::timeout;
use crate::components::tests::checks::CheckConfig;
use crate::shared::utils::get_common_service_name;
pub async fn ssl_certificate_check(config: &CheckConfig) -> Result<Value, String> {
let target = config.target.as_ref().ok_or("Target hostname is required")?;
-44
View File
@@ -1,44 +0,0 @@
use std::time::Duration;
use reqwest::Client;
const DEFAULT_TIMEOUT: Duration = Duration::from_secs(5);
const DEFAULT_USER_AGENT: &str = "Netzoot/1.0";
// Create HTTP client with reasonable defaults
pub fn create_http_client(timeout_ms: Option<u64>) -> Result<Client, String> {
let timeout = timeout_ms
.map(|ms| Duration::from_millis(ms))
.unwrap_or(DEFAULT_TIMEOUT);
Client::builder()
.timeout(timeout)
.user_agent(DEFAULT_USER_AGENT)
.danger_accept_invalid_certs(false)
.build()
.map_err(|e| format!("Failed to create HTTP client: {}", e))
}
// Helper function for common service names
pub fn get_common_service_name(port: u16) -> &'static str {
match port {
21 => "FTP",
22 => "SSH",
23 => "Telnet",
25 => "SMTP",
53 => "DNS",
80 => "HTTP",
110 => "POP3",
143 => "IMAP",
443 => "HTTPS",
587 => "SMTP-Submission",
993 => "IMAP-SSL",
995 => "POP3-SSL",
3306 => "MySQL",
3389 => "RDP",
5432 => "PostgreSQL",
6379 => "Redis",
27017 => "MongoDB",
_ => "Unknown",
}
}
+14 -7
View File
@@ -10,6 +10,7 @@ use tower_http::{
cors::{Any, CorsLayer},
trace::TraceLayer,
};
use tokio::sync::Mutex;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
mod config;
@@ -19,16 +20,22 @@ mod shared;
use config::ServerConfig;
use shared::handlers::create_router;
use crate::{components::diagnostics::storage::DiagnosticStorage, shared::storage::StorageFactory};
use crate::components::tests::storage::{TestStorage};
use crate::components::nodes::storage::{NodeStorage};
use crate::{
components::{
diagnostics::storage::DiagnosticStorage,
tests::storage::{TestStorage},
nodes::storage::{NodeStorage},
discovery::types::NetworkDiscovery
},
shared::storage::StorageFactory
};
pub struct AppState {
pub config: ServerConfig,
pub node_storage: Box<dyn NodeStorage>,
pub test_storage: Box<dyn TestStorage>,
pub diagnostic_storage: Box<dyn DiagnosticStorage>
// pub discovery: Arc<Mutex<NetworkDiscovery>>,
pub diagnostic_storage: Box<dyn DiagnosticStorage>,
pub discovery: Arc<Mutex<NetworkDiscovery>>,
}
#[derive(Parser)]
@@ -91,7 +98,7 @@ async fn main() -> anyhow::Result<()> {
let sqlite_storage= StorageFactory::new_sqlite(&config.database_url()).await?;
// Initialize discovery
// let discovery = NetworkDiscovery::new()?;
let discovery = NetworkDiscovery::new()?;
// Create app state
let state = Arc::new(AppState {
@@ -99,7 +106,7 @@ async fn main() -> anyhow::Result<()> {
node_storage: sqlite_storage.nodes,
test_storage: sqlite_storage.tests,
diagnostic_storage: sqlite_storage.diagnostics,
// discovery: Arc::new(Mutex::new(discovery))
discovery: Arc::new(Mutex::new(discovery))
});
// Create router
+2 -1
View File
@@ -1,2 +1,3 @@
pub mod handlers;
pub mod storage;
pub mod storage;
pub mod utils;
+26
View File
@@ -0,0 +1,26 @@
// Helper function for common service names
pub fn get_common_service_name(port: u16) -> &'static str {
match port {
21 => "FTP",
22 => "SSH",
23 => "Telnet",
25 => "SMTP",
53 => "DNS",
80 => "HTTP",
110 => "POP3",
143 => "IMAP",
443 => "HTTPS",
587 => "SMTP-Submission",
993 => "IMAP-SSL",
995 => "POP3-SSL",
3306 => "MySQL",
3389 => "RDP",
5432 => "PostgreSQL",
5900 => "VNC",
6379 => "Redis",
8080 => "HTTP-Alt",
8443 => "HTTPS-Alt",
27017 => "MongoDB",
_ => "Unknown",
}
}