diff --git a/Cargo.toml b/Cargo.toml index 809b1b6..8cd4f86 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,29 +1,69 @@ -[workspace] -resolver = "2" -members = [ - "ntap-db/oui", - "ntap-db/ipv4-asn", - "ntap-db/ipv4-country", - "ntap-db/ipv6-asn", - "ntap-db/ipv6-country", - "ntap-db/tcp-service", - "ntap-db/udp-service", - "ntap-db/as", - "ntap-db/country", - "ntap", -] +[package] +name = "ntap" +version = "0.8.0" +edition = "2021" +authors = ["shellrow "] +description = "Network traffic monitor/analyzer" +repository = "https://github.com/shellrow/ntap" +homepage = "https://github.com/shellrow/ntap" +documentation = "https://github.com/shellrow/ntap" +readme = "README.md" +keywords = ["network","security","cross-platform"] +categories = ["network-programming"] +license = "MIT" -[workspace.package] -version = "0.7.0" -authors = ["shellrow "] - -[workspace.dependencies] +[dependencies] +anyhow = { version = "1" } +bytes = "1" serde = { version = "1", features = ["derive"] } -bincode = "1.3" -rangemap = "1.5" +serde_json = "1" +tracing = { version = "0.1" } +tracing-subscriber = { version = "0.3", features = ["time", "chrono"] } +netdev = { version = "0.36", features = ["serde"] } +nex = { version = "0.22", features = ["serde"] } +tokio = { version = "1.38" } +clap = { version = "4.5", features = ["cargo"] } +crossterm = "0.27" +rand = "0.8" +ratatui = "0.25" +comfy-table = "7.1" +hickory-resolver = { version = "0.24" } +futures = {version = "0.3"} +netsock = { version = "0.3", features = ["serde"] } +#reqwest = { version="0.12", default-features = false, features = ["json", "rustls-tls", "stream"] } +chrono = { version = "0.4", features = ["serde"] } +time = { version = "0.3", features = ["local-offset"] } +ipnet = { version = "2.11" } +home = "0.5" +termtree = "0.5" +indicatif = { version = "0.18" } +inquire = "0.6" +#ndb-oui = { version = "0.2", features = ["bundled"] } +ndb-tcp-service = { version = "0.2", features = ["bundled"] } +ndb-udp-service = { version = "0.2", features = ["bundled"] } +ndb-as = { version = "0.2", features = ["bundled"] } +ndb-ipv4-asn = { version = "0.2", features = ["bundled"] } +ndb-ipv6-asn = { version = "0.2", features = ["bundled"] } +ndb-ipv4-country = { version = "0.2", features = ["bundled"] } +ndb-ipv6-country = { version = "0.2", features = ["bundled"] } + +[target.'cfg(windows)'.dependencies] +winreg = "0.50" + +#[features] +#default = ["bundled"] +#bundled = [ +# "ndb-oui/bundled", +# "ndb-tcp-service/bundled", +# "ndb-udp-service/bundled", +# "ndb-as/bundled", +# "ndb-ipv4-asn/bundled", +# "ndb-ipv6-asn/bundled", +# "ndb-ipv4-country/bundled", +# "ndb-ipv6-country/bundled", +#] # The profile that 'cargo dist' will build with [profile.dist] inherits = "release" lto = "thin" -allow-dirty = true diff --git a/README.md b/README.md index a66d831..9da7a70 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,12 @@ Network traffic monitor/analyzer, for Linux, macOS, and Windows. ## Overview **ntap** provides comprehensive insights into your network's activity, enabling users to monitor traffic, manage connections, and view network configurations with ease. -[Screenshots](#screenshots) - ## Features - **Network Statistics**: Dive into comprehensive statistics about your network traffic, covering bytes/bandwidth usage, top remote hosts, connections, and processes. -- **Live Packet Capture**: Continuously track the flow of network packets in real-time, offering insights into ongoing traffic. - **Real-time Monitoring**: Monitor network utilization with country and Autonomous System (AS) or Internet Service Provider (ISP) information as it unfolds. +- **Live Packet Capture**: Continuously track the flow of network packets in real-time, offering insights into ongoing traffic. - **Connection Management**: Quickly and effectively analyze active network connections to optimize performance and security. - **Interface and Routing Insights**: Obtain detailed views of network interfaces and routing tables to enhance network management and troubleshooting. -- **Your Public IP Address Info**: Effortlessly retrieve and display your current public IP address, along with associated country and AS (or ISP) info. ## Usage See [usage](resources/doc/USAGE.md) diff --git a/ntap-db/as/Cargo.toml b/ntap-db/as/Cargo.toml deleted file mode 100644 index f3fc398..0000000 --- a/ntap-db/as/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "ntap-db-as" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-as" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/as/README.md b/ntap-db/as/README.md deleted file mode 100644 index c094e2c..0000000 --- a/ntap-db/as/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-as diff --git a/ntap-db/as/resources/as.bin b/ntap-db/as/resources/as.bin deleted file mode 100644 index 0f2c422..0000000 Binary files a/ntap-db/as/resources/as.bin and /dev/null differ diff --git a/ntap-db/as/src/lib.rs b/ntap-db/as/src/lib.rs deleted file mode 100644 index 74a9913..0000000 --- a/ntap-db/as/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{collections::HashMap, fs, path::PathBuf}; - -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "bundle")] -pub const AS_BIN: &[u8] = include_bytes!("../resources/as.bin"); - -pub const AS_BIN_NAME: &str = "as.bin"; -pub const AS_R2_URL: &str = "https://r2.ntap.io/as.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct AutonomousSystem { - pub asn: u32, - pub as_name: String, -} - -impl AutonomousSystem { - pub fn file_name() -> String { - AS_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - AS_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> HashMap { - let mut autonomous_map: HashMap = HashMap::new(); - let autonomous_vec: Vec = bincode::deserialize(AS_BIN).unwrap(); - for autonomous in autonomous_vec { - autonomous_map.insert(autonomous.asn, autonomous.as_name); - } - autonomous_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> HashMap { - let mut autonomous_map: HashMap = HashMap::new(); - match fs::read(file_path) { - Ok(f) => { - let autonomous_vec: Vec = bincode::deserialize(&f).unwrap(); - for autonomous in autonomous_vec { - autonomous_map.insert(autonomous.asn, autonomous.as_name); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - autonomous_map -} diff --git a/ntap-db/country/Cargo.toml b/ntap-db/country/Cargo.toml deleted file mode 100644 index a2af271..0000000 --- a/ntap-db/country/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "ntap-db-country" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-country" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/country/README.md b/ntap-db/country/README.md deleted file mode 100644 index e29cb68..0000000 --- a/ntap-db/country/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-country diff --git a/ntap-db/country/resources/country.bin b/ntap-db/country/resources/country.bin deleted file mode 100644 index 2b3b1bf..0000000 Binary files a/ntap-db/country/resources/country.bin and /dev/null differ diff --git a/ntap-db/country/src/lib.rs b/ntap-db/country/src/lib.rs deleted file mode 100644 index e8e3a3f..0000000 --- a/ntap-db/country/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{collections::HashMap, fs, path::PathBuf}; - -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "bundle")] -pub const COUNTRY_BIN: &[u8] = include_bytes!("../resources/country.bin"); - -pub const COUNTRY_BIN_NAME: &str = "country.bin"; -pub const COUNTRY_R2_URL: &str = "https://r2.ntap.io/country.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Country { - pub country_code: String, - pub country_name: String, -} - -impl Country { - pub fn file_name() -> String { - COUNTRY_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - COUNTRY_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> HashMap { - let mut country_map: HashMap = HashMap::new(); - let country_vec: Vec = bincode::deserialize(COUNTRY_BIN).unwrap(); - for country in country_vec { - country_map.insert(country.country_code, country.country_name); - } - country_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> HashMap { - let mut country_map: HashMap = HashMap::new(); - match fs::read(file_path) { - Ok(f) => { - let country_vec: Vec = bincode::deserialize(&f).unwrap(); - for country in country_vec { - country_map.insert(country.country_code, country.country_name); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - country_map -} diff --git a/ntap-db/ipv4-asn/Cargo.toml b/ntap-db/ipv4-asn/Cargo.toml deleted file mode 100644 index ab7c5b1..0000000 --- a/ntap-db/ipv4-asn/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "ntap-db-ipv4-asn" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-ipv4-asn" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } -rangemap = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/ipv4-asn/README.md b/ntap-db/ipv4-asn/README.md deleted file mode 100644 index 9c8fa56..0000000 --- a/ntap-db/ipv4-asn/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-ipv4-asn diff --git a/ntap-db/ipv4-asn/resources/ipv4-asn.bin b/ntap-db/ipv4-asn/resources/ipv4-asn.bin deleted file mode 100644 index 87923cd..0000000 Binary files a/ntap-db/ipv4-asn/resources/ipv4-asn.bin and /dev/null differ diff --git a/ntap-db/ipv4-asn/src/lib.rs b/ntap-db/ipv4-asn/src/lib.rs deleted file mode 100644 index 97d23e9..0000000 --- a/ntap-db/ipv4-asn/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -use rangemap::RangeInclusiveMap; -use serde::{Deserialize, Serialize}; -use std::{fs, path::PathBuf}; - -#[cfg(feature = "bundle")] -pub const IPV4_ASN_BIN: &[u8] = include_bytes!("../resources/ipv4-asn.bin"); - -pub const IPV4_ASN_BIN_NAME: &str = "ipv4-asn.bin"; -pub const IPV4_ASN_R2_URL: &str = "https://r2.ntap.io/ipv4-asn.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct Ipv4Asn { - pub ip_from: u32, - pub ip_to: u32, - pub asn: u32, -} - -impl Ipv4Asn { - pub fn file_name() -> String { - IPV4_ASN_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - IPV4_ASN_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> RangeInclusiveMap { - let mut ipv4_asn_map: RangeInclusiveMap = RangeInclusiveMap::new(); - let ipv4_asn_vec: Vec = bincode::deserialize(IPV4_ASN_BIN).unwrap(); - for ipv4_asn in ipv4_asn_vec { - ipv4_asn_map.insert(ipv4_asn.ip_from..=ipv4_asn.ip_to, ipv4_asn.asn); - } - ipv4_asn_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> RangeInclusiveMap { - let mut ipv4_asn_map: RangeInclusiveMap = RangeInclusiveMap::new(); - match fs::read(file_path) { - Ok(f) => { - let ipv4_asn_vec: Vec = bincode::deserialize(&f).unwrap(); - for ipv4_asn in ipv4_asn_vec { - ipv4_asn_map.insert(ipv4_asn.ip_from..=ipv4_asn.ip_to, ipv4_asn.asn); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - ipv4_asn_map -} diff --git a/ntap-db/ipv4-country/Cargo.toml b/ntap-db/ipv4-country/Cargo.toml deleted file mode 100644 index 1039767..0000000 --- a/ntap-db/ipv4-country/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "ntap-db-ipv4-country" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-ipv4-country" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } -rangemap = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/ipv4-country/README.md b/ntap-db/ipv4-country/README.md deleted file mode 100644 index 6324d74..0000000 --- a/ntap-db/ipv4-country/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-ipv4-country diff --git a/ntap-db/ipv4-country/resources/ipv4-country.bin b/ntap-db/ipv4-country/resources/ipv4-country.bin deleted file mode 100644 index 8b1b6ad..0000000 Binary files a/ntap-db/ipv4-country/resources/ipv4-country.bin and /dev/null differ diff --git a/ntap-db/ipv4-country/src/lib.rs b/ntap-db/ipv4-country/src/lib.rs deleted file mode 100644 index 6ea863d..0000000 --- a/ntap-db/ipv4-country/src/lib.rs +++ /dev/null @@ -1,57 +0,0 @@ -use rangemap::RangeInclusiveMap; -use serde::{Deserialize, Serialize}; -use std::{fs, path::PathBuf}; - -#[cfg(feature = "bundle")] -pub const IPV4_COUNTRY_BIN: &[u8] = include_bytes!("../resources/ipv4-country.bin"); - -pub const IPV4_COUNTRY_BIN_NAME: &str = "ipv4-country.bin"; -pub const IPV4_COUNTRY_R2_URL: &str = "https://r2.ntap.io/ipv4-country.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct Ipv4Country { - pub ip_from: u32, - pub ip_to: u32, - pub country_code: String, -} - -impl Ipv4Country { - pub fn file_name() -> String { - IPV4_COUNTRY_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - IPV4_COUNTRY_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> RangeInclusiveMap { - let mut ipv4_country_map: RangeInclusiveMap = RangeInclusiveMap::new(); - let ipv4_country_vec: Vec = bincode::deserialize(IPV4_COUNTRY_BIN).unwrap(); - for ipv4_country in ipv4_country_vec { - ipv4_country_map.insert( - ipv4_country.ip_from..=ipv4_country.ip_to, - ipv4_country.country_code, - ); - } - ipv4_country_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> RangeInclusiveMap { - let mut ipv4_country_map: RangeInclusiveMap = RangeInclusiveMap::new(); - match fs::read(file_path) { - Ok(f) => { - let ipv4_country_vec: Vec = bincode::deserialize(&f).unwrap(); - for ipv4_country in ipv4_country_vec { - ipv4_country_map.insert( - ipv4_country.ip_from..=ipv4_country.ip_to, - ipv4_country.country_code, - ); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - ipv4_country_map -} diff --git a/ntap-db/ipv6-asn/Cargo.toml b/ntap-db/ipv6-asn/Cargo.toml deleted file mode 100644 index a5315cb..0000000 --- a/ntap-db/ipv6-asn/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "ntap-db-ipv6-asn" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-ipv6-asn" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } -rangemap = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/ipv6-asn/README.md b/ntap-db/ipv6-asn/README.md deleted file mode 100644 index d07a57c..0000000 --- a/ntap-db/ipv6-asn/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-ipv6-asn diff --git a/ntap-db/ipv6-asn/resources/ipv6-asn.bin b/ntap-db/ipv6-asn/resources/ipv6-asn.bin deleted file mode 100644 index 1785ef1..0000000 Binary files a/ntap-db/ipv6-asn/resources/ipv6-asn.bin and /dev/null differ diff --git a/ntap-db/ipv6-asn/src/lib.rs b/ntap-db/ipv6-asn/src/lib.rs deleted file mode 100644 index 2aa2902..0000000 --- a/ntap-db/ipv6-asn/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -use rangemap::RangeInclusiveMap; -use serde::{Deserialize, Serialize}; -use std::{fs, path::PathBuf}; - -#[cfg(feature = "bundle")] -pub const IPV6_ASN_BIN: &[u8] = include_bytes!("../resources/ipv6-asn.bin"); - -pub const IPV6_ASN_BIN_NAME: &str = "ipv6-asn.bin"; -pub const IPV6_ASN_R2_URL: &str = "https://r2.ntap.io/ipv6-asn.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct Ipv6Asn { - pub ip_from: u128, - pub ip_to: u128, - pub asn: u32, -} - -impl Ipv6Asn { - pub fn file_name() -> String { - IPV6_ASN_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - IPV6_ASN_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> RangeInclusiveMap { - let mut ipv6_asn_map: RangeInclusiveMap = RangeInclusiveMap::new(); - let ipv6_asn_vec: Vec = bincode::deserialize(IPV6_ASN_BIN).unwrap(); - for ipv6_asn in ipv6_asn_vec { - ipv6_asn_map.insert(ipv6_asn.ip_from..=ipv6_asn.ip_to, ipv6_asn.asn); - } - ipv6_asn_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> RangeInclusiveMap { - let mut ipv6_asn_map: RangeInclusiveMap = RangeInclusiveMap::new(); - match fs::read(file_path) { - Ok(f) => { - let ipv6_asn_vec: Vec = bincode::deserialize(&f).unwrap(); - for ipv6_asn in ipv6_asn_vec { - ipv6_asn_map.insert(ipv6_asn.ip_from..=ipv6_asn.ip_to, ipv6_asn.asn); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - ipv6_asn_map -} diff --git a/ntap-db/ipv6-country/Cargo.toml b/ntap-db/ipv6-country/Cargo.toml deleted file mode 100644 index e793207..0000000 --- a/ntap-db/ipv6-country/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "ntap-db-ipv6-country" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-ipv6-country" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } -rangemap = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/ipv6-country/README.md b/ntap-db/ipv6-country/README.md deleted file mode 100644 index cb552fb..0000000 --- a/ntap-db/ipv6-country/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-ipv6-country diff --git a/ntap-db/ipv6-country/resources/ipv6-country.bin b/ntap-db/ipv6-country/resources/ipv6-country.bin deleted file mode 100644 index 7e94177..0000000 Binary files a/ntap-db/ipv6-country/resources/ipv6-country.bin and /dev/null differ diff --git a/ntap-db/ipv6-country/src/lib.rs b/ntap-db/ipv6-country/src/lib.rs deleted file mode 100644 index 0d5fb27..0000000 --- a/ntap-db/ipv6-country/src/lib.rs +++ /dev/null @@ -1,57 +0,0 @@ -use rangemap::RangeInclusiveMap; -use serde::{Deserialize, Serialize}; -use std::{fs, path::PathBuf}; - -#[cfg(feature = "bundle")] -pub const IPV6_COUNTRY_BIN: &[u8] = include_bytes!("../resources/ipv6-country.bin"); - -pub const IPV6_COUNTRY_BIN_NAME: &str = "ipv6-country.bin"; -pub const IPV6_COUNTRY_R2_URL: &str = "https://r2.ntap.io/ipv6-country.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct Ipv6Country { - pub ip_from: u128, - pub ip_to: u128, - pub country_code: String, -} - -impl Ipv6Country { - pub fn file_name() -> String { - IPV6_COUNTRY_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - IPV6_COUNTRY_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> RangeInclusiveMap { - let mut ipv6_country_map: RangeInclusiveMap = RangeInclusiveMap::new(); - let ipv6_country_vec: Vec = bincode::deserialize(IPV6_COUNTRY_BIN).unwrap(); - for ipv6_country in ipv6_country_vec { - ipv6_country_map.insert( - ipv6_country.ip_from..=ipv6_country.ip_to, - ipv6_country.country_code, - ); - } - ipv6_country_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> RangeInclusiveMap { - let mut ipv6_country_map: RangeInclusiveMap = RangeInclusiveMap::new(); - match fs::read(file_path) { - Ok(f) => { - let ipv6_country_vec: Vec = bincode::deserialize(&f).unwrap(); - for ipv6_country in ipv6_country_vec { - ipv6_country_map.insert( - ipv6_country.ip_from..=ipv6_country.ip_to, - ipv6_country.country_code, - ); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - ipv6_country_map -} diff --git a/ntap-db/oui/Cargo.toml b/ntap-db/oui/Cargo.toml deleted file mode 100644 index 907409c..0000000 --- a/ntap-db/oui/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "ntap-db-oui" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-oui" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/oui/README.md b/ntap-db/oui/README.md deleted file mode 100644 index c908b9c..0000000 --- a/ntap-db/oui/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-oui diff --git a/ntap-db/oui/resources/oui.bin b/ntap-db/oui/resources/oui.bin deleted file mode 100644 index 7691e07..0000000 Binary files a/ntap-db/oui/resources/oui.bin and /dev/null differ diff --git a/ntap-db/oui/src/lib.rs b/ntap-db/oui/src/lib.rs deleted file mode 100644 index 0c3a53d..0000000 --- a/ntap-db/oui/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{collections::HashMap, fs, path::PathBuf}; - -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "bundle")] -pub const OUI_BIN: &[u8] = include_bytes!("../resources/oui.bin"); - -pub const OUI_BIN_NAME: &str = "oui.bin"; -pub const OUI_R2_URL: &str = "https://r2.ntap.io/oui.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct Oui { - pub mac_prefix: String, - pub vendor_name: String, -} - -impl Oui { - pub fn file_name() -> String { - OUI_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - OUI_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> HashMap { - let mut oui_map: HashMap = HashMap::new(); - let oui_vec: Vec = bincode::deserialize(OUI_BIN).unwrap(); - for oui in oui_vec { - oui_map.insert(oui.mac_prefix, oui.vendor_name); - } - oui_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> HashMap { - let mut oui_map: HashMap = HashMap::new(); - match fs::read(file_path) { - Ok(f) => { - let oui_vec: Vec = bincode::deserialize(&f).unwrap(); - for oui in oui_vec { - oui_map.insert(oui.mac_prefix, oui.vendor_name); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - oui_map -} diff --git a/ntap-db/tcp-service/Cargo.toml b/ntap-db/tcp-service/Cargo.toml deleted file mode 100644 index 83bbb3f..0000000 --- a/ntap-db/tcp-service/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "ntap-db-tcp-service" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-tcp-service" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/tcp-service/README.md b/ntap-db/tcp-service/README.md deleted file mode 100644 index 6a8c826..0000000 --- a/ntap-db/tcp-service/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-tcp-service diff --git a/ntap-db/tcp-service/resources/tcp-service.bin b/ntap-db/tcp-service/resources/tcp-service.bin deleted file mode 100644 index ad73b79..0000000 Binary files a/ntap-db/tcp-service/resources/tcp-service.bin and /dev/null differ diff --git a/ntap-db/tcp-service/src/lib.rs b/ntap-db/tcp-service/src/lib.rs deleted file mode 100644 index 808b63f..0000000 --- a/ntap-db/tcp-service/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{collections::HashMap, fs, path::PathBuf}; - -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "bundle")] -pub const TCP_SERVICE_BIN: &[u8] = include_bytes!("../resources/tcp-service.bin"); - -pub const TCP_SERVICE_BIN_NAME: &str = "tcp-service.bin"; -pub const TCP_SERVICE_R2_URL: &str = "https://r2.ntap.io/tcp-service.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct TcpService { - pub port: u16, - pub service_name: String, -} - -impl TcpService { - pub fn file_name() -> String { - TCP_SERVICE_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - TCP_SERVICE_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> HashMap { - let mut tcp_map: HashMap = HashMap::new(); - let tcp_services: Vec = bincode::deserialize(TCP_SERVICE_BIN).unwrap(); - for tcp_service in tcp_services { - tcp_map.insert(tcp_service.port, tcp_service.service_name); - } - tcp_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> HashMap { - let mut tcp_map: HashMap = HashMap::new(); - match fs::read(file_path) { - Ok(f) => { - let tcp_services: Vec = bincode::deserialize(&f).unwrap(); - for tcp_service in tcp_services { - tcp_map.insert(tcp_service.port, tcp_service.service_name); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - tcp_map -} diff --git a/ntap-db/udp-service/Cargo.toml b/ntap-db/udp-service/Cargo.toml deleted file mode 100644 index f050318..0000000 --- a/ntap-db/udp-service/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "ntap-db-udp-service" -version.workspace = true -edition = "2021" -authors.workspace = true -repository = "https://github.com/shellrow/ntap-db" -documentation = "https://github.com/shellrow/ntap-db" -readme = "README.md" -license = "MIT" -description = "ntap-db-udp-service" - -[dependencies] -serde = { workspace = true } -bincode = { workspace = true } - -[features] -default = ["bundle"] -bundle = [] diff --git a/ntap-db/udp-service/README.md b/ntap-db/udp-service/README.md deleted file mode 100644 index f41f244..0000000 --- a/ntap-db/udp-service/README.md +++ /dev/null @@ -1 +0,0 @@ -# ntap-db-udp-service diff --git a/ntap-db/udp-service/resources/udp-service.bin b/ntap-db/udp-service/resources/udp-service.bin deleted file mode 100644 index 3439d84..0000000 Binary files a/ntap-db/udp-service/resources/udp-service.bin and /dev/null differ diff --git a/ntap-db/udp-service/src/lib.rs b/ntap-db/udp-service/src/lib.rs deleted file mode 100644 index cc31a4b..0000000 --- a/ntap-db/udp-service/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -use std::{collections::HashMap, fs, path::PathBuf}; - -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "bundle")] -pub const UDP_SERVICE_BIN: &[u8] = include_bytes!("../resources/udp-service.bin"); - -pub const UDP_SERVICE_BIN_NAME: &str = "udp-service.bin"; -pub const UDP_SERVICE_R2_URL: &str = "https://r2.ntap.io/udp-service.bin"; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct UdpService { - pub port: u16, - pub service_name: String, -} - -impl UdpService { - pub fn file_name() -> String { - UDP_SERVICE_BIN_NAME.to_owned() - } - pub fn r2_url() -> String { - UDP_SERVICE_R2_URL.to_owned() - } -} - -#[cfg(feature = "bundle")] -pub fn get_map() -> HashMap { - let mut udp_map: HashMap = HashMap::new(); - let udp_services: Vec = bincode::deserialize(UDP_SERVICE_BIN).unwrap(); - for udp_service in udp_services { - udp_map.insert(udp_service.port, udp_service.service_name); - } - udp_map -} - -pub fn get_map_from_file(file_path: PathBuf) -> HashMap { - let mut udp_map: HashMap = HashMap::new(); - match fs::read(file_path) { - Ok(f) => { - let udp_services: Vec = bincode::deserialize(&f).unwrap(); - for udp_service in udp_services { - udp_map.insert(udp_service.port, udp_service.service_name); - } - } - Err(e) => { - eprintln!("Error reading file: {}", e); - } - } - udp_map -} diff --git a/ntap/Cargo.toml b/ntap/Cargo.toml deleted file mode 100644 index 35066fc..0000000 --- a/ntap/Cargo.toml +++ /dev/null @@ -1,71 +0,0 @@ -[package] -name = "ntap" -version.workspace = true -edition = "2021" -authors.workspace = true -description = "Network traffic monitor/analyzer" -repository = "https://github.com/shellrow/ntap" -homepage = "https://github.com/shellrow/ntap" -documentation = "https://github.com/shellrow/ntap" -readme = "../README.md" -keywords = ["network","security","cross-platform"] -categories = ["network-programming"] -license = "MIT" - -[dependencies] -serde = { workspace = true } -serde_json = "1" -bincode = { workspace = true } -rangemap = { workspace = true } -log = "0.4" -simplelog = "0.12" -netdev = { version = "0.34", features = ["serde"] } -nex = { version = "0.19", features = ["serde"] } -tokio = { version = "1.38" } -clap = { version = "4.5", features = ["cargo"] } -crossterm = "0.27" -anyhow = "1.0" -rand = "0.8" -#color-eyre = "0.6" -#palette = "0.7" -ratatui = "0.25" -comfy-table = "7.1" -hickory-resolver = { version = "0.24" } -futures = {version = "0.3"} -netsock = { version = "0.3", features = ["serde"] } -reqwest = { version="0.12", default-features = false, features = ["json", "rustls-tls", "stream"] } -chrono = { version = "0.4", features = ["serde"] } -time = { version = "0.3", features = ["local-offset"] } -ipnet = "2.11" -ipstruct = "0.2" -home = "0.5" -termtree = "0.5" -indicatif = "0.16" -inquire = "0.6" -ntap-db-as = { path = "../ntap-db/as", version = "0.7.0", default-features = false} -ntap-db-country = { path = "../ntap-db/country", version = "0.7.0", default-features = false } -ntap-db-ipv4-asn = { path = "../ntap-db/ipv4-asn", version = "0.7.0", default-features = false } -ntap-db-ipv4-country = { path = "../ntap-db/ipv4-country", version = "0.7.0", default-features = false } -ntap-db-ipv6-asn = { path = "../ntap-db/ipv6-asn", version = "0.7.0", default-features = false } -ntap-db-ipv6-country = { path = "../ntap-db/ipv6-country", version = "0.7.0", default-features = false } -ntap-db-oui = { path = "../ntap-db/oui", version = "0.7.0", default-features = false } -ntap-db-tcp-service = { path = "../ntap-db/tcp-service", version = "0.7.0", default-features = false } -ntap-db-udp-service = { path = "../ntap-db/udp-service", version = "0.7.0", default-features = false } - -[target.'cfg(windows)'.dependencies] -winreg = "0.50" - -[features] -default = [] -bundle = [ - "ntap-db-as/bundle", - "ntap-db-as/bundle", - "ntap-db-country/bundle", - "ntap-db-ipv4-asn/bundle", - "ntap-db-ipv4-country/bundle", - "ntap-db-ipv6-asn/bundle", - "ntap-db-ipv6-country/bundle", - "ntap-db-oui/bundle", - "ntap-db-tcp-service/bundle", - "ntap-db-udp-service/bundle", -] diff --git a/ntap/src/db/ip.rs b/ntap/src/db/ip.rs deleted file mode 100644 index 8ff0aab..0000000 --- a/ntap/src/db/ip.rs +++ /dev/null @@ -1,285 +0,0 @@ -use crate::db; -use rangemap::RangeInclusiveMap; -use serde::{Deserialize, Serialize}; -use std::{ - collections::HashMap, - f32::consts::E, - net::{IpAddr, Ipv4Addr, Ipv6Addr}, -}; - -/// In-memory IP database with range map and hash map -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct IpDatabase { - pub ipv4_country_map: RangeInclusiveMap, - pub ipv6_country_map: RangeInclusiveMap, - pub ipv4_asn_map: RangeInclusiveMap, - pub ipv6_asn_map: RangeInclusiveMap, - pub country_map: HashMap, - pub autonomous_map: HashMap, -} - -impl IpDatabase { - pub fn new() -> IpDatabase { - IpDatabase { - ipv4_country_map: RangeInclusiveMap::new(), - ipv6_country_map: RangeInclusiveMap::new(), - ipv4_asn_map: RangeInclusiveMap::new(), - ipv6_asn_map: RangeInclusiveMap::new(), - country_map: HashMap::new(), - autonomous_map: HashMap::new(), - } - } - #[cfg(feature = "bundle")] - pub fn load() -> Result> { - let mut ip_db = IpDatabase::new(); - ip_db.load_ipv4_country_map()?; - ip_db.load_ipv6_country_map()?; - ip_db.load_ipv4_asn_map()?; - ip_db.load_ipv6_asn_map()?; - ip_db.load_country_map()?; - ip_db.load_autonomous_map()?; - Ok(ip_db) - } - #[cfg(not(feature = "bundle"))] - pub fn load() -> Result> { - let mut ip_db = IpDatabase::new(); - ip_db.load_ipv4_country_file()?; - ip_db.load_ipv6_country_file()?; - ip_db.load_ipv4_asn_file()?; - ip_db.load_ipv6_asn_file()?; - ip_db.load_country_file()?; - ip_db.load_autonomous_file()?; - Ok(ip_db) - } - #[cfg(feature = "bundle")] - pub fn load_ipv4_country_map(&mut self) -> Result<(), Box> { - self.ipv4_country_map = ntap_db_ipv4_country::get_map(); - Ok(()) - } - #[cfg(feature = "bundle")] - pub fn load_ipv6_country_map(&mut self) -> Result<(), Box> { - self.ipv6_country_map = ntap_db_ipv6_country::get_map(); - Ok(()) - } - #[cfg(feature = "bundle")] - pub fn load_ipv4_asn_map(&mut self) -> Result<(), Box> { - self.ipv4_asn_map = ntap_db_ipv4_asn::get_map(); - Ok(()) - } - #[cfg(feature = "bundle")] - pub fn load_ipv6_asn_map(&mut self) -> Result<(), Box> { - self.ipv6_asn_map = ntap_db_ipv6_asn::get_map(); - Ok(()) - } - #[cfg(feature = "bundle")] - pub fn load_country_map(&mut self) -> Result<(), Box> { - self.country_map = ntap_db_country::get_map(); - Ok(()) - } - #[cfg(feature = "bundle")] - pub fn load_autonomous_map(&mut self) -> Result<(), Box> { - self.autonomous_map = ntap_db_as::get_map(); - Ok(()) - } - pub fn load_ipv4_country_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_ipv4_country::IPV4_COUNTRY_BIN_NAME); - self.ipv4_country_map = ntap_db_ipv4_country::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } - pub fn load_ipv6_country_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_ipv6_country::IPV6_COUNTRY_BIN_NAME); - self.ipv6_country_map = ntap_db_ipv6_country::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } - pub fn load_ipv4_asn_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_ipv4_asn::IPV4_ASN_BIN_NAME); - self.ipv4_asn_map = ntap_db_ipv4_asn::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } - pub fn load_ipv6_asn_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_ipv6_asn::IPV6_ASN_BIN_NAME); - self.ipv6_asn_map = ntap_db_ipv6_asn::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } - pub fn load_country_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_country::COUNTRY_BIN_NAME); - self.country_map = ntap_db_country::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } - pub fn load_autonomous_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_as::AS_BIN_NAME); - self.autonomous_map = ntap_db_as::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } - pub fn get_ipv4_info(&self, ipv4_addr: Ipv4Addr) -> Option { - let mut ip_info = IpInfo::new(IpAddr::V4(ipv4_addr)); - let ip_addr_int: u64 = crate::net::ip::ipv4_to_int(ipv4_addr); - ip_info.ip_version = String::from("v4"); - ip_info.ip_addr_dec = ip_addr_int.to_string(); - match self.ipv4_country_map.get(&(ip_addr_int as u32)) { - Some(country_code) => { - ip_info.country_code = country_code.to_string(); - match self.country_map.get(country_code) { - Some(country_name) => { - ip_info.country_name = country_name.to_string(); - } - None => {} - } - } - None => {} - } - match self.ipv4_asn_map.get(&(ip_addr_int as u32)) { - Some(asn) => { - ip_info.asn = *asn; - match self.autonomous_map.get(asn) { - Some(as_name) => { - ip_info.as_name = as_name.to_string(); - } - None => {} - } - } - None => {} - } - if (ip_info.country_code.is_empty() - || ip_info.country_code == "ZZ" - || ip_info.country_code == "-") - && ip_info.asn == 0 - { - return None; - } else { - Some(ip_info) - } - } - pub fn get_ipv6_info(&self, ip_addr: Ipv6Addr) -> Option { - let mut ip_info = IpInfo::new(IpAddr::V6(ip_addr)); - let ip_addr_int: u128 = crate::net::ip::ipv6_to_dec(ip_addr); - ip_info.ip_version = String::from("v6"); - ip_info.ip_addr_dec = ip_addr_int.to_string(); - match self.ipv6_country_map.get(&(ip_addr_int)) { - Some(country_code) => { - ip_info.country_code = country_code.to_string(); - match self.country_map.get(country_code) { - Some(country_name) => { - ip_info.country_name = country_name.to_string(); - } - None => {} - } - } - None => {} - } - match self.ipv6_asn_map.get(&(ip_addr_int)) { - Some(asn) => { - ip_info.asn = *asn; - match self.autonomous_map.get(asn) { - Some(as_name) => { - ip_info.as_name = as_name.to_string(); - } - None => {} - } - } - None => {} - } - if (ip_info.country_code.is_empty() - || ip_info.country_code == "ZZ" - || ip_info.country_code == "-") - && ip_info.asn == 0 - { - return None; - } else { - Some(ip_info) - } - } -} - -#[derive(Serialize, Deserialize)] -pub struct IpInfo { - pub ip_version: String, - pub ip_addr_dec: String, - pub ip_addr: IpAddr, - pub host_name: String, - pub asn: u32, - pub as_name: String, - pub country_code: String, - pub country_name: String, -} - -impl IpInfo { - pub fn new(ip_addr: IpAddr) -> IpInfo { - IpInfo { - ip_version: String::new(), - ip_addr_dec: String::new(), - ip_addr: ip_addr, - host_name: String::new(), - asn: 0, - as_name: String::new(), - country_code: String::new(), - country_name: String::new(), - } - } -} diff --git a/ntap/src/db/mod.rs b/ntap/src/db/mod.rs deleted file mode 100644 index 4a44e70..0000000 --- a/ntap/src/db/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -#![allow(unused)] - -pub mod ip; -pub mod service; diff --git a/ntap/src/db/service.rs b/ntap/src/db/service.rs deleted file mode 100644 index 436b09e..0000000 --- a/ntap/src/db/service.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::db; -use std::collections::HashMap; - -/// In-memory service database with hash map -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct ServiceDatabase { - pub tcp_map: HashMap, - pub udp_map: HashMap, -} - -impl ServiceDatabase { - pub fn new() -> ServiceDatabase { - ServiceDatabase { - tcp_map: HashMap::new(), - udp_map: HashMap::new(), - } - } - #[cfg(feature = "bundle")] - pub fn load() -> Result> { - let mut service_db = ServiceDatabase::new(); - service_db.load_tcp_map()?; - service_db.load_udp_map()?; - Ok(service_db) - } - #[cfg(not(feature = "bundle"))] - pub fn load() -> Result> { - let mut service_db = ServiceDatabase::new(); - service_db.load_tcp_file()?; - service_db.load_udp_file()?; - Ok(service_db) - } - #[cfg(feature = "bundle")] - pub fn load_tcp_map(&mut self) -> Result<(), Box> { - self.tcp_map = ntap_db_tcp_service::get_map(); - Ok(()) - } - #[cfg(feature = "bundle")] - pub fn load_udp_map(&mut self) -> Result<(), Box> { - self.udp_map = ntap_db_udp_service::get_map(); - Ok(()) - } - pub fn load_tcp_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_tcp_service::TCP_SERVICE_BIN_NAME); - self.tcp_map = ntap_db_tcp_service::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } - pub fn load_udp_file(&mut self) -> Result<(), Box> { - match crate::sys::get_database_dir_path() { - Some(mut db_dir) => { - db_dir.push(ntap_db_udp_service::UDP_SERVICE_BIN_NAME); - self.udp_map = ntap_db_udp_service::get_map_from_file(db_dir); - } - None => { - eprintln!("Error: Could not get database directory path"); - return Err(Box::new(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Could not get database directory path", - ))); - } - } - Ok(()) - } -} diff --git a/ntap/src/deps/mod.rs b/ntap/src/deps/mod.rs deleted file mode 100644 index 69f01cb..0000000 --- a/ntap/src/deps/mod.rs +++ /dev/null @@ -1,58 +0,0 @@ -#![allow(unused)] - -#[cfg(not(target_os = "windows"))] -mod unix; -#[cfg(not(target_os = "windows"))] -pub use self::unix::*; - -#[cfg(target_os = "windows")] -mod windows; -#[cfg(target_os = "windows")] -pub use self::windows::*; - -use serde::{Deserialize, Serialize}; -use std::fmt; - -// Custom error type for check dependencies -#[derive(Deserialize, Serialize, Debug, Clone)] -pub enum DepsError { - Missing(String), - Unknown(String), -} - -impl fmt::Display for DepsError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - DepsError::Missing(ref s) => write!(f, "missing: {}", s), - DepsError::Unknown(ref s) => write!(f, "unknown: {}", s), - } - } -} - -// Check Database files -pub fn check_db_files() -> Result<(), DepsError> { - let db_files = vec![ - ntap_db_as::AS_BIN_NAME, - ntap_db_country::COUNTRY_BIN_NAME, - ntap_db_ipv4_asn::IPV4_ASN_BIN_NAME, - ntap_db_ipv4_country::IPV4_COUNTRY_BIN_NAME, - ntap_db_ipv6_asn::IPV6_ASN_BIN_NAME, - ntap_db_ipv6_country::IPV6_COUNTRY_BIN_NAME, - ntap_db_oui::OUI_BIN_NAME, - ntap_db_tcp_service::TCP_SERVICE_BIN_NAME, - ntap_db_udp_service::UDP_SERVICE_BIN_NAME, - ]; - for file in db_files { - match crate::sys::get_db_file_path(file) { - Some(file_path) => { - if !file_path.exists() { - return Err(DepsError::Missing(file.to_string())); - } - } - None => { - return Err(DepsError::Unknown(file.to_string())); - } - } - } - Ok(()) -} diff --git a/ntap/src/deps/unix.rs b/ntap/src/deps/unix.rs deleted file mode 100644 index b331806..0000000 --- a/ntap/src/deps/unix.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::collections::HashMap; - -use super::DepsError; - -// currently only implemented for windows -// basically no-dependency for unix-likes -pub fn check_deps() -> Result<(), DepsError> { - Ok(()) -} - -// currently only implemented for windows -// basically no-dependency for unix-likes -pub fn get_deps_map() -> HashMap { - HashMap::new() -} diff --git a/ntap/src/deps/windows.rs b/ntap/src/deps/windows.rs deleted file mode 100644 index 273c9eb..0000000 --- a/ntap/src/deps/windows.rs +++ /dev/null @@ -1,62 +0,0 @@ -use super::DepsError; -use crate::sys; -use std::collections::HashMap; -use std::error::Error; -use std::fs::File; -use std::path::PathBuf; - -pub const NPCAP_SOFTWARE_NAME: &str = "Npcap"; -pub const NPCAP_INSTALL_DIR_NAME: &str = "npcap"; -pub const NPCAP_SDK_DIR_NAME: &str = "npcap-sdk-1.13"; -pub const NPCAP_INSTALLER_FILENAME: &str = "npcap-1.79.exe"; -pub const NPCAP_SDK_FILENAME: &str = "npcap-sdk-1.13.zip"; -pub const NPCAP_INSTALLER_HASH: &str = - "A95577EBBC67FC45B319E2EF3A55F4E9B211FE82ED4CB9D8BE6B1A9E2425CE53"; -pub const NPCAP_SDK_HASH: &str = "DAD1F2BF1B02B787BE08CA4862F99E39A876C1F274BAC4AC0CEDC9BBC58F94FD"; -pub const NPCAP_DIST_BASE_URL: &str = "https://npcap.com/dist/"; -pub const NPCAP_LIB_NAME: &str = "Packet.lib"; - -pub fn check_deps() -> Result<(), DepsError> { - check_npcap() -} - -// Check dependencies and return a map of dependencies and their status -pub fn get_deps_map() -> HashMap { - let mut deps_map: HashMap = HashMap::new(); - deps_map.insert(NPCAP_SOFTWARE_NAME.to_lowercase(), npcap_installed()); - deps_map -} - -pub fn check_npcap() -> Result<(), DepsError> { - if npcap_installed() { - Ok(()) - } else { - Err(DepsError::Missing(NPCAP_SOFTWARE_NAME.to_owned())) - } -} - -/// Check if npcap is installed. -/// This function only check if npcap is installed, not check version. -pub fn npcap_installed() -> bool { - sys::software_installed(NPCAP_SOFTWARE_NAME.to_owned()) -} - -/// Check if npcap SDK is installed. -/// This function only check if npcap SDK is installed, not check version. -pub fn npcap_sdk_installed() -> bool { - let env_lib_value: String = sys::get_env_lib(); - if env_lib_value.is_empty() { - return false; - } - // Split env_lib_value by ; - let lib_path_list: Vec<&str> = env_lib_value.split(";").collect(); - // Check if npcap sdk is in env_lib_value - // Search for Packet.lib - for lib_path in lib_path_list { - let packet_lib_path: String = format!("{}\\{}", lib_path, NPCAP_LIB_NAME); - if std::path::Path::new(&packet_lib_path).exists() { - return true; - } - } - false -} diff --git a/ntap/src/handler/ip_info.rs b/ntap/src/handler/ip_info.rs deleted file mode 100644 index c2ce5d4..0000000 --- a/ntap/src/handler/ip_info.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::util::tree::node_label; -use std::error::Error; -use termtree::Tree; - -pub fn show_public_ip_info() -> Result<(), Box> { - let runtime = tokio::runtime::Runtime::new()?; - let result: Result<(), Box> = runtime.block_on(async { - let ipv4_info = crate::net::ip::get_self_ipv4_info().await?; - let ipv6_info = crate::net::ip::get_self_ip_info().await?; - let mut tree = Tree::new(node_label("Public IP Info", None, None)); - if ipv4_info.ip_version == "v4" { - let mut ipv4_tree = Tree::new(node_label("IPv4 Info", None, None)); - ipv4_tree.push(node_label("IPv4 Address", Some(&ipv4_info.ip_addr), None)); - ipv4_tree.push(node_label( - "IPv4 Address Decimal", - Some(&ipv4_info.ip_addr_dec), - None, - )); - ipv4_tree.push(node_label( - "Country Code", - Some(&ipv4_info.country_code), - None, - )); - ipv4_tree.push(node_label( - "Country Name", - Some(&ipv4_info.country_name), - None, - )); - ipv4_tree.push(node_label("Network", Some(&ipv4_info.network), None)); - ipv4_tree.push(node_label("ASN", Some(&ipv4_info.asn), None)); - ipv4_tree.push(node_label("AS Name", Some(&ipv4_info.as_name), None)); - tree.push(ipv4_tree); - } else { - tree.push(node_label( - "IPv4 Info", - Some("Failed to get IPv4 info"), - None, - )); - } - if ipv6_info.ip_version == "v6" { - let mut ipv6_tree = Tree::new(node_label("IPv6 Info", None, None)); - ipv6_tree.push(node_label("IPv6 Address", Some(&ipv6_info.ip_addr), None)); - ipv6_tree.push(node_label( - "IPv6 Address Decimal", - Some(&ipv6_info.ip_addr_dec), - None, - )); - ipv6_tree.push(node_label( - "Country Code", - Some(&ipv6_info.country_code), - None, - )); - ipv6_tree.push(node_label( - "Country Name", - Some(&ipv6_info.country_name), - None, - )); - ipv6_tree.push(node_label("Network", Some(&ipv6_info.network), None)); - ipv6_tree.push(node_label("ASN", Some(&ipv6_info.asn), None)); - ipv6_tree.push(node_label("AS Name", Some(&ipv6_info.as_name), None)); - tree.push(ipv6_tree); - } else { - tree.push(node_label( - "IPv6 Info", - Some("Failed to get IPv6 info"), - None, - )); - } - println!("{}", tree); - Ok(()) - }); - result?; - Ok(()) -} diff --git a/ntap/src/handler/update.rs b/ntap/src/handler/update.rs deleted file mode 100644 index 9f0bbb5..0000000 --- a/ntap/src/handler/update.rs +++ /dev/null @@ -1,194 +0,0 @@ -use crate::{config::AppConfig, thread_log}; -use std::{ - fs::File, - path::{Path, PathBuf}, -}; - -fn download_file( - url: &str, - save_dir_path: PathBuf, - file_name: &str, -) -> Result> { - // Check and create download dir - if !save_dir_path.exists() { - std::fs::create_dir_all(&save_dir_path)?; - } - let rt = match tokio::runtime::Runtime::new() { - Ok(rt) => rt, - Err(e) => { - return Err(Box::new(e)); - } - }; - let save_file_path: PathBuf = save_dir_path.join(file_name); - rt.block_on(async { - thread_log!(info, "Downloading {} from {}", file_name, url); - // create a channel for progress - let (progress_tx, mut progress_rx) = tokio::sync::mpsc::channel(100); - let file_url: String = url.to_string(); - let file_path: PathBuf = save_file_path.clone(); - // spawn a task to handle the progress - tokio::spawn(async move { - let _ = crate::net::http::download_file_with_progress(file_url, file_path, progress_tx).await; - }); - // Display progress with indicatif - let bar = indicatif::ProgressBar::new(1000); - bar.set_style(indicatif::ProgressStyle::default_bar().template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})").progress_chars("#>-")); - while let Some(progress) = progress_rx.recv().await { - match progress { - crate::net::http::DownloadProgress::ContentLength(content_length) => { - thread_log!(info, "File URL: {}, Content-Length: {}", url, content_length); - bar.set_length(content_length); - } - crate::net::http::DownloadProgress::Downloaded(downloaded) => { - bar.set_position(downloaded); - } - } - } - bar.finish(); - thread_log!(info, "Downloaded {} to {}", file_name, save_file_path.display()); - }); - Ok(save_file_path) -} - -pub fn download_db_files() -> Result<(), Box> { - // Load AppConfig - let config = AppConfig::load(); - // Init logger - let log_file_path = if let Some(file_path) = &config.logging.file_path { - // Convert to PathBuf - Path::new(&file_path).to_path_buf() - } else { - crate::sys::get_user_file_path(crate::thread_log::DEFAULT_LOG_FILE_PATH).unwrap() - }; - let log_file: File = if log_file_path.exists() { - File::options().write(true).open(&log_file_path)? - } else { - File::create(&log_file_path)? - }; - let mut log_config_builder = simplelog::ConfigBuilder::default(); - log_config_builder.set_time_format_rfc3339(); - if let Some(offset) = crate::time::get_local_offset() { - log_config_builder.set_time_offset(offset); - } - let default_log_config = log_config_builder.build(); - - // Init logger with file and terminal output - // debug build: log to terminal and file - // release build: log to file only - simplelog::CombinedLogger::init(vec![ - simplelog::TermLogger::new( - simplelog::LevelFilter::Info, - default_log_config.clone(), - simplelog::TerminalMode::Mixed, - simplelog::ColorChoice::Auto, - ), - simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - ), - ])?; - - let database_dir = crate::sys::get_database_dir_path().unwrap(); - // AS - match download_file( - ntap_db_as::AS_R2_URL, - database_dir.clone(), - ntap_db_as::AS_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // Country - match download_file( - ntap_db_country::COUNTRY_R2_URL, - database_dir.clone(), - ntap_db_country::COUNTRY_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // IPv4 ASN - match download_file( - ntap_db_ipv4_asn::IPV4_ASN_R2_URL, - database_dir.clone(), - ntap_db_ipv4_asn::IPV4_ASN_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // IPv4 Country - match download_file( - ntap_db_ipv4_country::IPV4_COUNTRY_R2_URL, - database_dir.clone(), - ntap_db_ipv4_country::IPV4_COUNTRY_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // IPv6 ASN - match download_file( - ntap_db_ipv6_asn::IPV6_ASN_R2_URL, - database_dir.clone(), - ntap_db_ipv6_asn::IPV6_ASN_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // IPv6 Country - match download_file( - ntap_db_ipv6_country::IPV6_COUNTRY_R2_URL, - database_dir.clone(), - ntap_db_ipv6_country::IPV6_COUNTRY_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // OUI - match download_file( - ntap_db_oui::OUI_R2_URL, - database_dir.clone(), - ntap_db_oui::OUI_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // TCP Service - match download_file( - ntap_db_tcp_service::TCP_SERVICE_R2_URL, - database_dir.clone(), - ntap_db_tcp_service::TCP_SERVICE_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - // UDP Service - match download_file( - ntap_db_udp_service::UDP_SERVICE_R2_URL, - database_dir.clone(), - ntap_db_udp_service::UDP_SERVICE_BIN_NAME, - ) { - Ok(_) => {} - Err(e) => { - thread_log!(error, "{:?}", e); - } - } - log::info!("Successfully downloaded ntap databases."); - Ok(()) -} diff --git a/ntap/src/sys/unix.rs b/ntap/src/sys/unix.rs deleted file mode 100644 index 411fa83..0000000 --- a/ntap/src/sys/unix.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub fn check_deps() -> Result<(), Box> { - Ok(()) -} diff --git a/ntap/src/sys/windows.rs b/ntap/src/sys/windows.rs deleted file mode 100644 index 9f228d3..0000000 --- a/ntap/src/sys/windows.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::deps::{NPCAP_DIST_BASE_URL, NPCAP_INSTALLER_FILENAME}; -use inquire::Confirm; -use std::collections::HashMap; -use std::path::PathBuf; -use winreg::enums::RegDisposition; -use winreg::enums::HKEY_LOCAL_MACHINE; -use winreg::RegKey; - -/// Application information on system -#[derive(Debug, Clone)] -pub struct AppInfo { - pub display_name: String, - pub display_version: String, - pub uninstall_string: String, -} - -pub fn get_os_bit() -> String { - if cfg!(target_pointer_width = "32") { - return "32-bit".to_owned(); - } else if cfg!(target_pointer_width = "64") { - return "64-bit".to_owned(); - } else { - return "unknown".to_owned(); - } -} - -pub fn get_install_path(install_dir_name: &str) -> String { - match home::home_dir() { - Some(path) => { - let path: String = format!("{}\\{}", path.display(), install_dir_name); - path - } - None => String::new(), - } -} - -// Get software installation status -pub fn software_installed(software_name: String) -> bool { - let hklm: RegKey = RegKey::predef(HKEY_LOCAL_MACHINE); - let os_bit: String = get_os_bit(); - let npcap_key: RegKey = if os_bit == "32-bit" { - match hklm.open_subkey(format!("SOFTWARE\\{}", software_name)) { - Ok(key) => key, - Err(_) => return false, - } - } else { - match hklm.open_subkey(format!("SOFTWARE\\WOW6432Node\\{}", software_name)) { - Ok(key) => key, - Err(_) => return false, - } - }; - let _version: String = npcap_key.get_value("").unwrap_or(String::new()); - true -} - -#[allow(dead_code)] -pub fn get_installed_apps() -> HashMap { - let hklm: RegKey = winreg::RegKey::predef(HKEY_LOCAL_MACHINE); - let uninstall_key: RegKey = hklm - .open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall") - .expect("key is missing"); - - let mut apps: HashMap = HashMap::new(); - for key in uninstall_key.enum_keys() { - let key = match key { - Ok(key) => key, - Err(_) => continue, - }; - //let key = key.unwrap(); - let subkey: RegKey = uninstall_key - .open_subkey(key.clone()) - .expect("key is missing"); - let app: AppInfo = AppInfo { - display_name: subkey.get_value("DisplayName").unwrap_or(String::new()), - display_version: subkey.get_value("DisplayVersion").unwrap_or(String::new()), - uninstall_string: subkey.get_value("UninstallString").unwrap_or(String::new()), - }; - apps.insert(key, app); - } - apps -} - -#[allow(dead_code)] -pub fn app_installed(app_name: String) -> bool { - let hklm: RegKey = winreg::RegKey::predef(HKEY_LOCAL_MACHINE); - let uninstall_key: RegKey = hklm - .open_subkey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall") - .expect("key is missing"); - - for key in uninstall_key.enum_keys() { - let key = match key { - Ok(key) => key, - Err(_) => continue, - }; - //let key = key.unwrap(); - let subkey: RegKey = uninstall_key - .open_subkey(key.clone()) - .expect("key is missing"); - let display_name: String = subkey.get_value("DisplayName").unwrap_or(String::new()); - if display_name == app_name { - return true; - } - } - false -} - -#[allow(dead_code)] -pub fn check_env_path(dir_path: &str) -> bool { - let reg_key: winreg::RegKey = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER) - .open_subkey_with_flags("Environment", winreg::enums::KEY_READ) - .unwrap(); - let reg_value: String = reg_key.get_value("Path").unwrap_or(String::new()); - reg_value.contains(dir_path) -} - -#[allow(dead_code)] -pub fn add_env_path(dir_path: &str) -> std::io::Result<()> { - let hkcu: winreg::RegKey = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER); - let (path, _): (winreg::RegKey, RegDisposition) = hkcu.create_subkey("Environment")?; - let mut path_value: String = path - .get_value::("Path") - .unwrap_or(String::new()); - if !path_value.is_empty() { - path_value.push(';'); - } - path_value.push_str(&std::path::Path::new(dir_path).to_str().unwrap()); - println!("{}", path_value); - path.set_value("Path", &path_value) -} - -pub fn check_env_lib_path(dir_path: &str) -> bool { - let reg_key: winreg::RegKey = match winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER) - .open_subkey_with_flags("Environment", winreg::enums::KEY_READ) - { - Ok(key) => key, - Err(_) => return false, - }; - let reg_value: String = reg_key.get_value("LIB").unwrap_or(String::new()); - reg_value.contains(dir_path) -} - -pub fn add_env_lib_path(dir_path: &str) -> std::io::Result<()> { - let hkcu: winreg::RegKey = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER); - let (path, _): (winreg::RegKey, RegDisposition) = hkcu.create_subkey("Environment")?; - let mut path_value: String = path - .get_value::("LIB") - .unwrap_or(String::new()); - if !path_value.is_empty() { - path_value.push(';'); - } - path_value.push_str(&std::path::Path::new(dir_path).to_str().unwrap()); - println!("{}", path_value); - path.set_value("LIB", &path_value) -} - -pub fn get_env_lib() -> String { - let reg_key: winreg::RegKey = winreg::RegKey::predef(winreg::enums::HKEY_CURRENT_USER) - .open_subkey_with_flags("Environment", winreg::enums::KEY_READ) - .unwrap(); - let reg_value: String = reg_key.get_value("LIB").unwrap_or(String::new()); - if !reg_value.is_empty() { - return reg_value; - } - let reg_key: winreg::RegKey = winreg::RegKey::predef(winreg::enums::HKEY_LOCAL_MACHINE) - .open_subkey_with_flags( - "SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment", - winreg::enums::KEY_READ, - ) - .unwrap(); - let reg_value: String = reg_key.get_value("LIB").unwrap_or(String::new()); - reg_value -} - -pub fn check_deps() -> Result<(), Box> { - match crate::deps::check_deps() { - Ok(_) => { - return Ok(()); - } - Err(e) => { - match e { - crate::deps::DepsError::Missing(s) => { - if s == crate::deps::NPCAP_SOFTWARE_NAME.to_string() { - let ans: bool = Confirm::new( - "Npcap is not installed, would you like to download & install it ?", - ) - .prompt() - .unwrap(); - if ans == false { - return Err("On windows, Npcap is required for ntap to work properly. Please install Npcap and try again.".into()); - } - } - } - crate::deps::DepsError::Unknown(s) => { - eprintln!("Error: Unknown dependency: {}", s); - } - } - } - } - Ok(()) -} diff --git a/ntap/src/thread_log.rs b/ntap/src/thread_log.rs deleted file mode 100644 index ceb6d8e..0000000 --- a/ntap/src/thread_log.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![allow(unused)] - -use serde::{Deserialize, Serialize}; -use std::sync::{Mutex, OnceLock}; - -pub const DEFAULT_LOG_FILE_PATH: &str = "ntap.log"; - -/// Global Mutex lock guard for logging. -pub static LOG_LOCK: OnceLock> = OnceLock::new(); - -/// Thread-safe logging macro. This macro is used to log messages from multiple threads. -#[macro_export] -macro_rules! thread_log { - ($log_macro: ident, $($fmt_args:expr),*) => {{ - let guard = $crate::thread_log::LOG_LOCK.get_or_init(|| std::sync::Mutex::new(())) - .lock() - .unwrap_or_else(|poisoned| poisoned.into_inner()); - log::$log_macro!($($fmt_args,)*); - drop(guard); - }}; -} - -#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] -pub enum LogLevel { - DEBUG, - INFO, - WARN, - ERROR, -} - -impl LogLevel { - pub fn allows(&self, level: &LogLevel) -> bool { - match self { - LogLevel::DEBUG => true, - LogLevel::INFO => level != &LogLevel::DEBUG, - LogLevel::WARN => level == &LogLevel::WARN || level == &LogLevel::ERROR, - LogLevel::ERROR => level == &LogLevel::ERROR, - } - } - pub fn to_string(&self) -> String { - match self { - LogLevel::DEBUG => "DEBUG", - LogLevel::INFO => "INFO", - LogLevel::WARN => "WARN", - LogLevel::ERROR => "ERROR", - } - .to_owned() - } - pub fn to_level_filter(&self) -> simplelog::LevelFilter { - match self { - LogLevel::DEBUG => simplelog::LevelFilter::Debug, - LogLevel::INFO => simplelog::LevelFilter::Info, - LogLevel::WARN => simplelog::LevelFilter::Warn, - LogLevel::ERROR => simplelog::LevelFilter::Error, - } - } -} diff --git a/ntap/src/time.rs b/ntap/src/time.rs deleted file mode 100644 index 0cb8050..0000000 --- a/ntap/src/time.rs +++ /dev/null @@ -1,14 +0,0 @@ -use time::UtcOffset; - -pub fn get_local_offset() -> Option { - let local_now: chrono::DateTime = chrono::Local::now(); - let offset_seconds = local_now.offset().local_minus_utc(); - match time::UtcOffset::from_whole_seconds(offset_seconds) { - Ok(offset) => { - return Some(offset); - } - Err(_) => { - return None; - } - } -} diff --git a/resources/doc/USAGE.md b/resources/doc/USAGE.md index 993d7e3..54da22b 100644 --- a/resources/doc/USAGE.md +++ b/resources/doc/USAGE.md @@ -30,7 +30,7 @@ ntap stat ntap live ``` -### monitor: Enters monitor mode to continuously display live network usage statistics with associated country and AS (or ISP) info. +### monitor: Enters monitor mode to continuously display live network usage statistics. ```bash ntap monitor ``` @@ -55,11 +55,6 @@ ntap interface ntap route ``` -### ipinfo: Displays public IP information. -```bash -ntap ipinfo -``` - ### help: Prints the main help message or help for a specific command. ```bash ntap help diff --git a/ntap/src/config.rs b/src/config.rs similarity index 61% rename from ntap/src/config.rs rename to src/config.rs index 2a6a31a..ff43fa2 100644 --- a/ntap/src/config.rs +++ b/src/config.rs @@ -1,11 +1,10 @@ #![allow(unused)] use crate::sys; -use crate::thread_log; -use crate::thread_log::LogLevel; -use crate::thread_log::DEFAULT_LOG_FILE_PATH; use serde::{Deserialize, Serialize}; + pub const NTAP_CONFIG_FILE_NAME: &str = "ntap-config.json"; +pub const DEFAULT_LOG_FILE_PATH: &str = "ntap.log"; #[derive(Deserialize, Serialize, Debug)] pub struct AppConfig { @@ -15,8 +14,6 @@ pub struct AppConfig { pub network: NetworkConfig, /// Display configuration. pub display: DisplayConfig, - /// Privacy configuration. - pub privacy: PrivacyConfig, } impl AppConfig { @@ -25,7 +22,6 @@ impl AppConfig { logging: LoggingConfig::new(), network: NetworkConfig::new(), display: DisplayConfig::new(), - privacy: PrivacyConfig::new(), } } pub fn load() -> AppConfig { @@ -35,12 +31,12 @@ impl AppConfig { Ok(content) => match serde_json::from_str(&content) { Ok(config) => config, Err(e) => { - thread_log!(error, "{:?}", e); + tracing::error!("{:?}", e); AppConfig::new() } }, Err(e) => { - thread_log!(error, "{:?}", e); + tracing::error!("{:?}", e); // Create default config let config = AppConfig::new(); config.save(); @@ -62,17 +58,53 @@ impl AppConfig { Ok(content) => match std::fs::write(&path, content) { Ok(_) => {} Err(e) => { - thread_log!(error, "{:?}", e); + tracing::error!("{:?}", e); } }, Err(e) => { - thread_log!(error, "{:?}", e); + tracing::error!("{:?}", e); } } } } } +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub enum LogLevel { + DEBUG, + INFO, + WARN, + ERROR, +} + +impl LogLevel { + pub fn allows(&self, level: &LogLevel) -> bool { + match self { + LogLevel::DEBUG => true, + LogLevel::INFO => level != &LogLevel::DEBUG, + LogLevel::WARN => level == &LogLevel::WARN || level == &LogLevel::ERROR, + LogLevel::ERROR => level == &LogLevel::ERROR, + } + } + pub fn to_string(&self) -> String { + match self { + LogLevel::DEBUG => "DEBUG", + LogLevel::INFO => "INFO", + LogLevel::WARN => "WARN", + LogLevel::ERROR => "ERROR", + } + .to_owned() + } + pub fn to_level_filter(&self) -> tracing::Level { + match self { + LogLevel::DEBUG => tracing::Level::DEBUG, + LogLevel::INFO => tracing::Level::INFO, + LogLevel::WARN => tracing::Level::WARN, + LogLevel::ERROR => tracing::Level::ERROR, + } + } +} + #[derive(Deserialize, Serialize, Debug)] pub struct LoggingConfig { /// Log level. @@ -84,7 +116,7 @@ pub struct LoggingConfig { impl LoggingConfig { pub fn new() -> LoggingConfig { LoggingConfig { - level: LogLevel::ERROR, + level: LogLevel::INFO, file_path: if let Some(path) = sys::get_user_file_path(DEFAULT_LOG_FILE_PATH) { Some(path.to_string_lossy().to_string()) } else { @@ -140,60 +172,19 @@ impl DisplayConfig { } } -#[derive(Debug, Deserialize, Serialize)] -pub struct PrivacyConfig { - /// Hide self private IP addresses by default. - pub hide_private_ip_info: bool, - /// Hide self public IP addresses by default. - pub hide_public_ip_info: bool, -} - -impl PrivacyConfig { - pub fn new() -> PrivacyConfig { - PrivacyConfig { - hide_private_ip_info: true, - hide_public_ip_info: true, - } - } -} - #[derive(Deserialize, Serialize, Debug)] pub struct DatabaseConfig { - pub ipv4_asn_db_path: String, - pub ipv6_asn_db_path: String, - pub ipv4_country_db_path: String, - pub ipv6_country_db_path: String, - pub country_db_path: String, - pub asn_db_path: String, + pub oui_db_path: String, pub tcp_service_db_path: String, + pub udp_service_db_path: String, } impl DatabaseConfig { pub fn new() -> DatabaseConfig { - /* DatabaseConfig { - ipv4_asn_db_path: db::ip::Ipv4Asn::bin_file_path().map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_owned()), - ipv6_asn_db_path: db::ip::Ipv6Asn::bin_file_path().map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_owned()), - ipv4_country_db_path: db::ip::Ipv4Country::bin_file_path().map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_owned()), - ipv6_country_db_path: db::ip::Ipv6Country::bin_file_path().map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_owned()), - country_db_path: db::ip::Country::bin_file_path().map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_owned()), - asn_db_path: db::ip::AutonomousSystem::bin_file_path().map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_owned()), - tcp_service_db_path: db::service::TcpService::bin_file_path().map(|p| p.to_string_lossy().to_string()) - .unwrap_or_else(|| "".to_owned()), - } */ DatabaseConfig { - ipv4_asn_db_path: String::new(), - ipv6_asn_db_path: String::new(), - ipv4_country_db_path: String::new(), - ipv6_country_db_path: String::new(), - country_db_path: String::new(), - asn_db_path: String::new(), + oui_db_path: String::new(), tcp_service_db_path: String::new(), + udp_service_db_path: String::new(), } } } diff --git a/src/db/ip.rs b/src/db/ip.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..6a764b1 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,109 @@ +use anyhow::Result; +//use ndb_oui::OuiDb; +use ndb_as::AsDb; +use ndb_ipv4_asn::Ipv4AsnDb; +use ndb_ipv4_country::Ipv4CountryDb; +use ndb_ipv6_asn::Ipv6AsnDb; +use ndb_ipv6_country::Ipv6CountryDb; +use ndb_tcp_service::TcpServiceDb; +use ndb_udp_service::UdpServiceDb; +use std::sync::{OnceLock, RwLock}; + +//pub mod oui; +//pub mod service; + +//pub static OUI_DB: OnceLock> = OnceLock::new(); +pub static TCP_SERVICE_DB: OnceLock> = OnceLock::new(); +pub static UDP_SERVICE_DB: OnceLock> = OnceLock::new(); +pub static AS_DB: OnceLock> = OnceLock::new(); +pub static IPV4_ASN_DB: OnceLock> = OnceLock::new(); +pub static IPV6_ASN_DB: OnceLock> = OnceLock::new(); +pub static IPV4_COUNTRY_DB: OnceLock> = OnceLock::new(); +pub static IPV6_COUNTRY_DB: OnceLock> = OnceLock::new(); + +/// Initialize all databases +pub fn init_databases() -> Result<()> { + tracing::info!("Initializing databases..."); + init_tcp_service_db()?; + init_udp_service_db()?; + //init_oui_db()?; + init_as_db()?; + init_ipv4_asn_db()?; + init_ipv6_asn_db()?; + init_ipv4_country_db()?; + init_ipv6_country_db()?; + tracing::info!("Databases initialized successfully."); + Ok(()) +} + +/* pub fn init_oui_db() -> Result<()> { + // Initialize OUI database + let oui_db = OuiDb::bundled(); + OUI_DB + .set(RwLock::new(oui_db)) + .map_err(|_| anyhow::anyhow!("Failed to set OUI_DB in OnceLock"))?; + Ok(()) +} */ + +pub fn init_tcp_service_db() -> Result<()> { + // Initialize TCP Service database + let tcp_service_db = TcpServiceDb::bundled(); + TCP_SERVICE_DB + .set(RwLock::new(tcp_service_db)) + .map_err(|_| anyhow::anyhow!("Failed to set TCP_SERVICE_DB in OnceLock"))?; + Ok(()) +} + +pub fn init_udp_service_db() -> Result<()> { + // Initialize UDP Service database + let udp_service_db = UdpServiceDb::bundled(); + UDP_SERVICE_DB + .set(RwLock::new(udp_service_db)) + .map_err(|_| anyhow::anyhow!("Failed to set UDP_SERVICE_DB in OnceLock"))?; + Ok(()) +} + +pub fn init_as_db() -> Result<()> { + // Initialize AS database + let as_db = AsDb::bundled(); + AS_DB + .set(RwLock::new(as_db)) + .map_err(|_| anyhow::anyhow!("Failed to set AS_DB in OnceLock"))?; + Ok(()) +} + +pub fn init_ipv4_asn_db() -> Result<()> { + // Initialize IPv4 ASN database + let ipv4_asn_db = Ipv4AsnDb::bundled(); + IPV4_ASN_DB + .set(RwLock::new(ipv4_asn_db)) + .map_err(|_| anyhow::anyhow!("Failed to set IPV4_ASN_DB in OnceLock"))?; + Ok(()) +} + +pub fn init_ipv6_asn_db() -> Result<()> { + // Initialize IPv6 ASN database + let ipv6_asn_db = Ipv6AsnDb::bundled(); + IPV6_ASN_DB + .set(RwLock::new(ipv6_asn_db)) + .map_err(|_| anyhow::anyhow!("Failed to set IPV6_ASN_DB in OnceLock"))?; + Ok(()) +} + +pub fn init_ipv4_country_db() -> Result<()> { + // Initialize IPv4 Country database + let ipv4_country_db = Ipv4CountryDb::bundled(); + IPV4_COUNTRY_DB + .set(RwLock::new(ipv4_country_db)) + .map_err(|_| anyhow::anyhow!("Failed to set IPV4_COUNTRY_DB in OnceLock"))?; + Ok(()) +} + +pub fn init_ipv6_country_db() -> Result<()> { + // Initialize IPv6 Country database + let ipv6_country_db = Ipv6CountryDb::bundled(); + IPV6_COUNTRY_DB + .set(RwLock::new(ipv6_country_db)) + .map_err(|_| anyhow::anyhow!("Failed to set IPV6_COUNTRY_DB in OnceLock"))?; + Ok(()) +} diff --git a/src/db/oui.rs b/src/db/oui.rs new file mode 100644 index 0000000..afa3207 --- /dev/null +++ b/src/db/oui.rs @@ -0,0 +1,36 @@ +use anyhow::Result; +use ndb_oui::OuiDb; + +#[cfg(not(feature = "bundled"))] +use std::path::PathBuf; + +pub const OUI_CSV_NAME: &str = "oui.csv"; +pub const OUI_R2_URL: &str = "https://r2.ntap.io/oui.csv"; + +#[cfg(not(feature = "bundled"))] +pub fn get_oui_db_filepath() -> Option { + match crate::sys::get_database_dir_path() { + Some(mut db_dir) => { + db_dir.push(OUI_CSV_NAME); + Some(db_dir) + } + None => { + eprintln!("Error: Could not get database directory path"); + None + } + } +} + +#[cfg(feature = "bundled")] +pub fn get_oui_db() -> Result { + Ok(OuiDb::bundled()) +} + +#[cfg(not(feature = "bundled"))] +pub fn get_oui_db() -> Result { + let path = get_oui_db_filepath() + .ok_or_else(|| anyhow::anyhow!("Failed to get OUI database file path"))?; + let reader = std::fs::File::open(path) + .map_err(|e| anyhow::anyhow!("Failed to open OUI database file: {}", e))?; + OuiDb::from_csv(reader) +} diff --git a/src/db/service.rs b/src/db/service.rs new file mode 100644 index 0000000..c41298e --- /dev/null +++ b/src/db/service.rs @@ -0,0 +1,104 @@ +use anyhow::Result; +use ndb_tcp_service::TcpServiceDb; +use ndb_udp_service::UdpServiceDb; + +#[cfg(not(feature = "bundled"))] +use std::path::PathBuf; + +pub const TCP_SERVICE_CSV_NAME: &str = "tcp.csv"; +pub const TCP_SERVICE_R2_URL: &str = "https://r2.ntap.io/tcp-services.csv"; + +pub const UDP_SERVICE_CSV_NAME: &str = "udp.csv"; +pub const UDP_SERVICE_R2_URL: &str = "https://r2.ntap.io/udp-services.csv"; + +#[cfg(not(feature = "bundled"))] +pub fn get_tcp_db_filepath() -> Option { + match crate::sys::get_database_dir_path() { + Some(mut db_dir) => { + db_dir.push(TCP_SERVICE_CSV_NAME); + Some(db_dir) + } + None => { + eprintln!("Error: Could not get database directory path"); + None + } + } +} + +#[cfg(not(feature = "bundled"))] +pub fn get_udp_db_filepath() -> Option { + match crate::sys::get_database_dir_path() { + Some(mut db_dir) => { + db_dir.push(UDP_SERVICE_CSV_NAME); + Some(db_dir) + } + None => { + eprintln!("Error: Could not get database directory path"); + None + } + } +} + +#[cfg(feature = "bundled")] +pub fn get_tcp_service_db() -> Result { + Ok(TcpServiceDb::bundled()) +} + +#[cfg(not(feature = "bundled"))] +pub fn get_tcp_service_db() -> Result { + let path = get_tcp_db_filepath() + .ok_or_else(|| anyhow::anyhow!("Failed to get TCP database file path"))?; + let reader = std::fs::File::open(path) + .map_err(|e| anyhow::anyhow!("Failed to open TCP database file: {}", e))?; + TcpServiceDb::from_csv(reader) +} + +#[cfg(feature = "bundled")] +pub fn get_udp_service_db() -> Result { + Ok(UdpServiceDb::bundled()) +} + +#[cfg(not(feature = "bundled"))] +pub fn get_udp_service_db() -> Result { + let path = get_udp_db_filepath() + .ok_or_else(|| anyhow::anyhow!("Failed to get UDP database file path"))?; + let reader = std::fs::File::open(path) + .map_err(|e| anyhow::anyhow!("Failed to open UDP database file: {}", e))?; + UdpServiceDb::from_csv(reader) +} + +/* /// In-memory service database with hash map +pub struct ServiceDatabase { + pub tcp: TcpServiceDb, + pub udp: UdpServiceDb, +} + +impl ServiceDatabase { + #[cfg(feature = "bundled")] + pub fn load() -> Result { + let db = ServiceDatabase { + tcp: TcpServiceDb::bundled(), + udp: UdpServiceDb::bundled(), + }; + Ok(db) + } + #[cfg(not(feature = "bundled"))] + pub fn load() -> Result { + use std::fs::File; + let tcp_path = get_tcp_db_filepath().ok_or_else(|| { + anyhow::anyhow!("Failed to get TCP database file path") + })?; + let udp_path = get_udp_db_filepath().ok_or_else(|| { + anyhow::anyhow!("Failed to get UDP database file path") + })?; + let mut tcp_reader = File::open(tcp_path) + .map_err(|e| anyhow::anyhow!("Failed to open TCP database file: {}", e))?; + let mut udp_reader = File::open(udp_path) + .map_err(|e| anyhow::anyhow!("Failed to open UDP database file: {}", e))?; + let db = ServiceDatabase { + tcp: TcpServiceDb::from_csv(tcp_reader)?, + udp: UdpServiceDb::from_csv(udp_reader)?, + }; + Ok(db) + } +} */ diff --git a/src/deps/mod.rs b/src/deps/mod.rs new file mode 100644 index 0000000..313d586 --- /dev/null +++ b/src/deps/mod.rs @@ -0,0 +1,28 @@ +#[cfg(not(target_os = "windows"))] +mod unix; +#[cfg(not(target_os = "windows"))] +pub use self::unix::*; + +#[cfg(target_os = "windows")] +mod windows; +#[cfg(target_os = "windows")] +pub use self::windows::*; + +use serde::{Deserialize, Serialize}; +use std::fmt; + +// Custom error type for check dependencies +#[derive(Deserialize, Serialize, Debug, Clone)] +pub enum DepsError { + Missing(String), + Unknown(String), +} + +impl fmt::Display for DepsError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DepsError::Missing(ref s) => write!(f, "missing: {}", s), + DepsError::Unknown(ref s) => write!(f, "unknown: {}", s), + } + } +} diff --git a/src/deps/unix.rs b/src/deps/unix.rs new file mode 100644 index 0000000..3a4cfda --- /dev/null +++ b/src/deps/unix.rs @@ -0,0 +1,7 @@ +use super::DepsError; + +// currently only implemented for windows +// basically no-dependency for unix-likes +pub fn check_deps() -> Result<(), DepsError> { + Ok(()) +} diff --git a/src/deps/windows.rs b/src/deps/windows.rs new file mode 100644 index 0000000..ac3da29 --- /dev/null +++ b/src/deps/windows.rs @@ -0,0 +1,22 @@ +use super::DepsError; +use crate::sys; + +pub const NPCAP_SOFTWARE_NAME: &str = "Npcap"; + +pub fn check_deps() -> Result<(), DepsError> { + check_npcap() +} + +pub fn check_npcap() -> Result<(), DepsError> { + if npcap_installed() { + Ok(()) + } else { + Err(DepsError::Missing(NPCAP_SOFTWARE_NAME.to_owned())) + } +} + +/// Check if npcap is installed. +/// This function only check if npcap is installed, not check version. +pub fn npcap_installed() -> bool { + sys::software_installed(NPCAP_SOFTWARE_NAME.to_owned()) +} diff --git a/ntap/src/handler/interface.rs b/src/handler/interface.rs similarity index 93% rename from ntap/src/handler/interface.rs rename to src/handler/interface.rs index 092e877..f15d774 100644 --- a/ntap/src/handler/interface.rs +++ b/src/handler/interface.rs @@ -1,10 +1,10 @@ use crate::util::tree::node_label; +use anyhow::Result; use netdev::mac::MacAddr; use netdev::Interface; -use std::error::Error; use termtree::Tree; -pub fn show_interfaces() -> Result<(), Box> { +pub fn show_interfaces() -> Result<()> { let interfaces: Vec = netdev::get_interfaces(); let mut tree = Tree::new(node_label("Interfaces", None, None)); @@ -65,8 +65,13 @@ pub fn show_interfaces() -> Result<(), Box> { Ok(()) } -pub fn show_default_interface() -> Result<(), Box> { - let iface: Interface = netdev::get_default_interface()?; +pub fn show_default_interface() -> Result<()> { + let iface: Interface = match netdev::get_default_interface() { + Ok(iface) => iface, + Err(e) => { + anyhow::bail!("Failed to get default interface: {}", e); + } + }; let mut tree = Tree::new(node_label("Interface", None, None)); tree.push(node_label("Index", Some(&iface.index.to_string()), None)); tree.push(node_label("Name", Some(&iface.name), None)); diff --git a/ntap/src/handler/live.rs b/src/handler/live.rs similarity index 68% rename from ntap/src/handler/live.rs rename to src/handler/live.rs index d2463c9..3a702ba 100644 --- a/ntap/src/handler/live.rs +++ b/src/handler/live.rs @@ -1,20 +1,17 @@ use crate::config::AppConfig; use crate::net::packet::{PacketFrame, PacketStorage}; -use crate::thread_log; +use anyhow::Result; use std::collections::HashSet; -use std::error::Error; -use std::fs::File; use std::net::IpAddr; -use std::path::Path; use std::sync::mpsc::{channel, Receiver, Sender}; use std::sync::Arc; use std::thread; use clap::ArgMatches; use nex::packet::ethernet::EtherType; -use nex::packet::ip::IpNextLevelProtocol; +use nex::packet::ip::IpNextProtocol; -pub fn live_capture(app: &ArgMatches) -> Result<(), Box> { +pub fn live_capture(app: &ArgMatches) -> Result<()> { let sub_args = match app.subcommand_matches("live") { Some(matches) => matches, None => { @@ -43,6 +40,9 @@ pub fn live_capture(app: &ArgMatches) -> Result<(), Box> { // Load AppConfig let mut config = AppConfig::load(); + // Initialize logger + crate::log::init_logger(&config)?; + if app.contains_id("tickrate") { config.display.tick_rate = *app.get_one("tickrate").unwrap_or(&1000); } @@ -61,7 +61,7 @@ pub fn live_capture(app: &ArgMatches) -> Result<(), Box> { // Protocol filter let mut ethertypes: HashSet = HashSet::new(); - let mut ip_next_protocols: HashSet = HashSet::new(); + let mut ip_next_protocols: HashSet = HashSet::new(); if sub_args.contains_id("protocols") { match sub_args.get_many::("protocols") { Some(protocols_ref) => { @@ -97,8 +97,8 @@ pub fn live_capture(app: &ArgMatches) -> Result<(), Box> { ethertypes.insert(EtherType::Ipv4); ethertypes.insert(EtherType::Ipv6); if ports.len() > 0 { - ip_next_protocols.insert(IpNextLevelProtocol::Tcp); - ip_next_protocols.insert(IpNextLevelProtocol::Udp); + ip_next_protocols.insert(IpNextProtocol::Tcp); + ip_next_protocols.insert(IpNextProtocol::Udp); } } @@ -109,49 +109,6 @@ pub fn live_capture(app: &ArgMatches) -> Result<(), Box> { storage_capacity = u8::MAX; } - // Init logger - let log_file_path = if let Some(file_path) = &config.logging.file_path { - // Convert to PathBuf - Path::new(&file_path).to_path_buf() - } else { - crate::sys::get_user_file_path(crate::thread_log::DEFAULT_LOG_FILE_PATH).unwrap() - }; - let log_file: File = if log_file_path.exists() { - File::options().write(true).open(&log_file_path)? - } else { - File::create(&log_file_path)? - }; - let mut log_config_builder = simplelog::ConfigBuilder::default(); - log_config_builder.set_time_format_rfc3339(); - if let Some(offset) = crate::time::get_local_offset() { - log_config_builder.set_time_offset(offset); - } - let default_log_config = log_config_builder.build(); - - // Init logger with file and terminal output - // debug build: log to terminal and file - // release build: log to file only - if cfg!(debug_assertions) { - simplelog::CombinedLogger::init(vec![ - simplelog::TermLogger::new( - simplelog::LevelFilter::Info, - default_log_config.clone(), - simplelog::TerminalMode::Mixed, - simplelog::ColorChoice::Auto, - ), - simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - ), - ])?; - } else { - simplelog::CombinedLogger::init(vec![simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - )])?; - } // Start threads let mut threads: Vec> = vec![]; let packet_strage: Arc = @@ -183,14 +140,14 @@ pub fn live_capture(app: &ArgMatches) -> Result<(), Box> { let pcap_handler = pcap_thread.spawn(move || { crate::net::pcap::start_live_capture(pcap_option, tx_clone, iface); }); - thread_log!(info, "start thread {}", thread_name); + tracing::info!("start thread {}", thread_name); pcap_thread_index += 1; pcap_handler }) .collect::>(); let receiver_handler = thread::spawn(move || { - thread_log!(info, "start mpsc reveiver thread"); + tracing::info!("start mpsc reveiver thread"); while let Ok(mut frame) = rx.recv() { frame.capture_no = packet_strage.generate_capture_no(); packet_strage.add_packet(frame); @@ -205,21 +162,12 @@ pub fn live_capture(app: &ArgMatches) -> Result<(), Box> { threads.push(handle); } Err(e) => { - thread_log!(error, "Error: {:?}", e); + tracing::error!("Error: {:?}", e); } } } - thread_log!(info, "start TUI, live_packet_capture"); - - // Clear screen before starting TUI - let mut stdout = std::io::stdout(); - crossterm::execute!( - stdout, - crossterm::terminal::Clear(crossterm::terminal::ClearType::All) - )?; - // Move cursor to top left corner - crossterm::execute!(stdout, crossterm::cursor::MoveTo(0, 0))?; + tracing::info!("start TUI, live_packet_capture"); crate::tui::live::terminal::run( config, diff --git a/ntap/src/handler/mod.rs b/src/handler/mod.rs similarity index 82% rename from ntap/src/handler/mod.rs rename to src/handler/mod.rs index beca502..723e81b 100644 --- a/ntap/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1,11 +1,10 @@ pub mod interface; -pub mod ip_info; pub mod live; pub mod monitor; pub mod route; pub mod socket; pub mod stat; -pub mod update; +//pub mod update; pub enum AppCommands { Stat, @@ -15,8 +14,7 @@ pub enum AppCommands { Interface, Route, Socket, - IpInfo, - Update, + //Update, Default, } @@ -30,8 +28,7 @@ impl AppCommands { "interface" => AppCommands::Interface, "route" => AppCommands::Route, "socket" => AppCommands::Socket, - "ipinfo" => AppCommands::IpInfo, - "update" => AppCommands::Update, + //"update" => AppCommands::Update, _ => AppCommands::Default, } } diff --git a/ntap/src/handler/monitor.rs b/src/handler/monitor.rs similarity index 66% rename from ntap/src/handler/monitor.rs rename to src/handler/monitor.rs index 8adbf8c..f20b900 100644 --- a/ntap/src/handler/monitor.rs +++ b/src/handler/monitor.rs @@ -1,22 +1,20 @@ use crate::config::AppConfig; use crate::net::stat::NetStatStrage; -use crate::thread_log; +use anyhow::Result; use std::collections::HashSet; -use std::error::Error; -use std::fs::File; use std::net::IpAddr; -use std::path::Path; use std::sync::Arc; use std::thread; use clap::ArgMatches; -#[cfg(not(feature = "bundle"))] -use inquire::Confirm; +//#[cfg(not(feature = "bundled"))] +//use inquire::Confirm; + use nex::packet::ethernet::EtherType; -use nex::packet::ip::IpNextLevelProtocol; +use nex::packet::ip::IpNextProtocol; -pub fn monitor(app: &ArgMatches) -> Result<(), Box> { +pub fn monitor(app: &ArgMatches) -> Result<()> { let sub_args = match app.subcommand_matches("monitor") { Some(matches) => matches, None => { @@ -29,8 +27,8 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { Some(_config_dir) => {} None => { let err_msg = "Could not get config directory path"; - log::error!("{err_msg}"); - return Err(err_msg.into()); + tracing::error!("{err_msg}"); + anyhow::bail!(err_msg); } } @@ -38,17 +36,17 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { match crate::sys::check_deps() { Ok(_) => {} Err(e) => { - log::error!("Error: {:?}", e); - return Err(e); + tracing::error!("Error: {:?}", e); + anyhow::bail!(e.to_string()); } } - // Check Database files - #[cfg(not(feature = "bundle"))] + /* // Check Database files + #[cfg(not(feature = "bundled"))] match crate::deps::check_db_files() { Ok(_) => {} Err(e) => { - log::error!("{}", e.to_string()); + tracing::error!("{}", e.to_string()); let ans: bool = Confirm::new("ntap databases are missing. Do you want to download them now?") .prompt() @@ -61,11 +59,17 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { return Err(e.to_string().into()); } } - } + } */ // Load AppConfig let mut config = AppConfig::load(); + // Initialize logger + crate::log::init_logger(&config)?; + + // Initialize DB + crate::db::init_databases()?; + if app.contains_id("tickrate") { config.display.tick_rate = *app.get_one("tickrate").unwrap_or(&1000); } @@ -84,7 +88,7 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { // Protocol filter let mut ethertypes: HashSet = HashSet::new(); - let mut ip_next_protocols: HashSet = HashSet::new(); + let mut ip_next_protocols: HashSet = HashSet::new(); if sub_args.contains_id("protocols") { match sub_args.get_many::("protocols") { Some(protocols_ref) => { @@ -120,54 +124,11 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { ethertypes.insert(EtherType::Ipv4); ethertypes.insert(EtherType::Ipv6); if ports.len() > 0 { - ip_next_protocols.insert(IpNextLevelProtocol::Tcp); - ip_next_protocols.insert(IpNextLevelProtocol::Udp); + ip_next_protocols.insert(IpNextProtocol::Tcp); + ip_next_protocols.insert(IpNextProtocol::Udp); } } - // Init logger - let log_file_path = if let Some(file_path) = &config.logging.file_path { - // Convert to PathBuf - Path::new(&file_path).to_path_buf() - } else { - crate::sys::get_user_file_path(crate::thread_log::DEFAULT_LOG_FILE_PATH).unwrap() - }; - let log_file: File = if log_file_path.exists() { - File::options().write(true).open(&log_file_path)? - } else { - File::create(&log_file_path)? - }; - let mut log_config_builder = simplelog::ConfigBuilder::default(); - log_config_builder.set_time_format_rfc3339(); - if let Some(offset) = crate::time::get_local_offset() { - log_config_builder.set_time_offset(offset); - } - let default_log_config = log_config_builder.build(); - - // Init logger with file and terminal output - // debug build: log to terminal and file - // release build: log to file only - if cfg!(debug_assertions) { - simplelog::CombinedLogger::init(vec![ - simplelog::TermLogger::new( - simplelog::LevelFilter::Info, - default_log_config.clone(), - simplelog::TerminalMode::Mixed, - simplelog::ColorChoice::Auto, - ), - simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - ), - ])?; - } else { - simplelog::CombinedLogger::init(vec![simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - )])?; - } // Start threads let mut threads: Vec> = vec![]; @@ -198,23 +159,20 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { let thread_name = format!("pcap-thread-{}", iface.name.clone()); let pcap_thread = thread::Builder::new().name(thread_name.clone()); let pcap_handler = pcap_thread.spawn(move || { - if pcap_thread_index == 0 { - netstat_strage_pcap.load_ipdb(); - } crate::net::pcap::start_background_capture( pcap_option, &mut netstat_strage_pcap, iface, ); }); - thread_log!(info, "start thread {}", thread_name); + tracing::info!("start thread {}", thread_name); pcap_thread_index += 1; pcap_handler }) .collect::>(); let socket_handler = thread::spawn(move || { - thread_log!(info, "start thread socket_info_update"); + tracing::info!("start thread socket_info_update"); crate::net::socket::start_socket_info_update(&mut netstat_strage_socket); }); @@ -224,7 +182,7 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { threads.push(handle); } Err(e) => { - thread_log!(error, "Error: {:?}", e); + tracing::error!("Error: {:?}", e); } } } @@ -233,22 +191,13 @@ pub fn monitor(app: &ArgMatches) -> Result<(), Box> { if config.network.reverse_dns { let mut netstat_strage_dns = Arc::clone(&netstat_strage); let dns_handler = thread::spawn(move || { - thread_log!(info, "start thread dns_map_update"); + tracing::info!("start thread dns_map_update"); crate::net::dns::start_dns_map_update(&mut netstat_strage_dns); }); threads.push(dns_handler); } - thread_log!(info, "start TUI, netstat_data_update"); - - // Clear screen before starting TUI - let mut stdout = std::io::stdout(); - crossterm::execute!( - stdout, - crossterm::terminal::Clear(crossterm::terminal::ClearType::All) - )?; - // Move cursor to top left corner - crossterm::execute!(stdout, crossterm::cursor::MoveTo(0, 0))?; + tracing::info!("start TUI, netstat_data_update"); crate::tui::monitor::terminal::run( config, diff --git a/ntap/src/handler/route.rs b/src/handler/route.rs similarity index 93% rename from ntap/src/handler/route.rs rename to src/handler/route.rs index 8db68ae..fe9fc40 100644 --- a/ntap/src/handler/route.rs +++ b/src/handler/route.rs @@ -1,11 +1,11 @@ -use std::error::Error; use std::net::{Ipv4Addr, Ipv6Addr}; +use anyhow::Result; use comfy_table::presets::NOTHING; use comfy_table::*; use netdev::interface::InterfaceType; -pub fn show_routes() -> Result<(), Box> { +pub fn show_routes() -> Result<()> { let interfaces = netdev::get_interfaces(); // IPv4 routing table @@ -51,7 +51,8 @@ pub fn show_routes() -> Result<(), Box> { } } } else { - if iface.if_type == InterfaceType::Loopback || iface.ipv4[0].addr() == Ipv4Addr::LOCALHOST + if iface.if_type == InterfaceType::Loopback + || iface.ipv4[0].addr() == Ipv4Addr::LOCALHOST { table.add_row(vec![ Cell::new(iface.name), @@ -108,7 +109,8 @@ pub fn show_routes() -> Result<(), Box> { } } } else { - if iface.if_type == InterfaceType::Loopback || iface.ipv6[0].addr() == Ipv6Addr::LOCALHOST + if iface.if_type == InterfaceType::Loopback + || iface.ipv6[0].addr() == Ipv6Addr::LOCALHOST { table.add_row(vec![ Cell::new(iface.name), diff --git a/ntap/src/handler/socket.rs b/src/handler/socket.rs similarity index 93% rename from ntap/src/handler/socket.rs rename to src/handler/socket.rs index 7657c16..63d1746 100644 --- a/ntap/src/handler/socket.rs +++ b/src/handler/socket.rs @@ -1,13 +1,12 @@ -use std::error::Error; - use crate::net::socket::{AddressFamily, SocketInfoOption, TransportProtocol}; +use anyhow::Result; use clap::ArgMatches; use comfy_table::presets::NOTHING; use comfy_table::*; use nex::packet::ethernet::EtherType; -use nex::packet::ip::IpNextLevelProtocol; +use nex::packet::ip::IpNextProtocol; -pub fn show_socket_info(app: &ArgMatches) -> Result<(), Box> { +pub fn show_socket_info(app: &ArgMatches) -> Result<()> { let sub_args = match app.subcommand_matches("socket") { Some(matches) => matches, None => { @@ -40,10 +39,10 @@ pub fn show_socket_info(app: &ArgMatches) -> Result<(), Box> { { // TCP or UDP match ip_next_protocol { - IpNextLevelProtocol::Tcp => { + IpNextProtocol::Tcp => { sock_opt.transport_protocol.push(TransportProtocol::TCP); } - IpNextLevelProtocol::Udp => { + IpNextProtocol::Udp => { sock_opt.transport_protocol.push(TransportProtocol::UDP); } _ => {} diff --git a/ntap/src/handler/stat.rs b/src/handler/stat.rs similarity index 65% rename from ntap/src/handler/stat.rs rename to src/handler/stat.rs index a887c70..c4f4d70 100644 --- a/ntap/src/handler/stat.rs +++ b/src/handler/stat.rs @@ -1,27 +1,23 @@ use crate::config::AppConfig; use crate::net::stat::NetStatStrage; -use crate::thread_log; +use anyhow::Result; +use clap::ArgMatches; use std::collections::HashSet; -use std::error::Error; -use std::fs::File; use std::net::IpAddr; -use std::path::Path; use std::sync::Arc; use std::thread; -use clap::ArgMatches; - use nex::packet::ethernet::EtherType; -use nex::packet::ip::IpNextLevelProtocol; +use nex::packet::ip::IpNextProtocol; -pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { +pub fn show_stat_default(app: &ArgMatches) -> Result<()> { // Check .ntap directory match crate::sys::get_config_dir_path() { Some(_config_dir) => {} None => { let err_msg = "Could not get config directory path"; - log::error!("{err_msg}"); - return Err(err_msg.into()); + tracing::error!("{err_msg}"); + anyhow::bail!(err_msg); } } @@ -29,14 +25,20 @@ pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { match crate::sys::check_deps() { Ok(_) => {} Err(e) => { - log::error!("Error: {:?}", e); - return Err(e); + tracing::error!("Error: {:?}", e); + anyhow::bail!(e.to_string()); } } // Load AppConfig let mut config = AppConfig::load(); + // Initialize logger + crate::log::init_logger(&config)?; + + // Initialize DB + crate::db::init_databases()?; + if app.contains_id("tickrate") { config.display.tick_rate = *app.get_one("tickrate").unwrap_or(&1000); } @@ -55,7 +57,7 @@ pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { // Protocol filter let mut ethertypes: HashSet = HashSet::new(); - let mut ip_next_protocols: HashSet = HashSet::new(); + let mut ip_next_protocols: HashSet = HashSet::new(); if app.contains_id("protocols") { match app.get_many::("protocols") { Some(protocols_ref) => { @@ -91,54 +93,11 @@ pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { ethertypes.insert(EtherType::Ipv4); ethertypes.insert(EtherType::Ipv6); if ports.len() > 0 { - ip_next_protocols.insert(IpNextLevelProtocol::Tcp); - ip_next_protocols.insert(IpNextLevelProtocol::Udp); + ip_next_protocols.insert(IpNextProtocol::Tcp); + ip_next_protocols.insert(IpNextProtocol::Udp); } } - // Init logger - let log_file_path = if let Some(file_path) = &config.logging.file_path { - // Convert to PathBuf - Path::new(&file_path).to_path_buf() - } else { - crate::sys::get_user_file_path(crate::thread_log::DEFAULT_LOG_FILE_PATH).unwrap() - }; - let log_file: File = if log_file_path.exists() { - File::options().write(true).open(&log_file_path)? - } else { - File::create(&log_file_path)? - }; - let mut log_config_builder = simplelog::ConfigBuilder::default(); - log_config_builder.set_time_format_rfc3339(); - if let Some(offset) = crate::time::get_local_offset() { - log_config_builder.set_time_offset(offset); - } - let default_log_config = log_config_builder.build(); - - // Init logger with file and terminal output - // debug build: log to terminal and file - // release build: log to file only - if cfg!(debug_assertions) { - simplelog::CombinedLogger::init(vec![ - simplelog::TermLogger::new( - simplelog::LevelFilter::Info, - default_log_config.clone(), - simplelog::TerminalMode::Mixed, - simplelog::ColorChoice::Auto, - ), - simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - ), - ])?; - } else { - simplelog::CombinedLogger::init(vec![simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - )])?; - } // Start threads let mut threads: Vec> = vec![]; @@ -175,14 +134,14 @@ pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { iface, ); }); - thread_log!(info, "start thread {}", thread_name); + tracing::info!("start thread {}", thread_name); pcap_thread_index += 1; pcap_handler }) .collect::>(); let socket_handler = thread::spawn(move || { - thread_log!(info, "start thread socket_info_update"); + tracing::info!("start thread socket_info_update"); crate::net::socket::start_socket_info_update(&mut netstat_strage_socket); }); @@ -192,7 +151,7 @@ pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { threads.push(handle); } Err(e) => { - thread_log!(error, "Error: {:?}", e); + tracing::error!("Error: {:?}", e); } } } @@ -201,22 +160,13 @@ pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { if config.network.reverse_dns { let mut netstat_strage_dns = Arc::clone(&netstat_strage); let dns_handler = thread::spawn(move || { - thread_log!(info, "start thread dns_map_update"); + tracing::info!("start thread dns_map_update"); crate::net::dns::start_dns_map_update(&mut netstat_strage_dns); }); threads.push(dns_handler); } - thread_log!(info, "start TUI, netstat_data_update"); - - // Clear screen before starting TUI - let mut stdout = std::io::stdout(); - crossterm::execute!( - stdout, - crossterm::terminal::Clear(crossterm::terminal::ClearType::All) - )?; - // Move cursor to top left corner - crossterm::execute!(stdout, crossterm::cursor::MoveTo(0, 0))?; + tracing::info!("start TUI, netstat_data_update"); crate::tui::stat::terminal::run( config, @@ -226,7 +176,7 @@ pub fn show_stat_default(app: &ArgMatches) -> Result<(), Box> { Ok(()) } -pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { +pub fn show_stat(app: &ArgMatches) -> Result<()> { let sub_args = match app.subcommand_matches("stat") { Some(matches) => matches, None => { @@ -240,8 +190,8 @@ pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { Some(_config_dir) => {} None => { let err_msg = "Could not get config directory path"; - log::error!("{err_msg}"); - return Err(err_msg.into()); + tracing::error!("{err_msg}"); + return Err(anyhow::anyhow!(err_msg)); } } @@ -249,14 +199,20 @@ pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { match crate::sys::check_deps() { Ok(_) => {} Err(e) => { - log::error!("Error: {:?}", e); - return Err(e); + tracing::error!("Error: {:?}", e); + anyhow::bail!(e.to_string()); } } // Load AppConfig let mut config = AppConfig::load(); + // Initialize logger + crate::log::init_logger(&config)?; + + // Initialize DB + crate::db::init_databases()?; + if app.contains_id("tickrate") { config.display.tick_rate = *app.get_one("tickrate").unwrap_or(&1000); } @@ -275,7 +231,7 @@ pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { // Protocol filter let mut ethertypes: HashSet = HashSet::new(); - let mut ip_next_protocols: HashSet = HashSet::new(); + let mut ip_next_protocols: HashSet = HashSet::new(); if sub_args.contains_id("protocols") { match sub_args.get_many::("protocols") { Some(protocols_ref) => { @@ -311,54 +267,11 @@ pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { ethertypes.insert(EtherType::Ipv4); ethertypes.insert(EtherType::Ipv6); if ports.len() > 0 { - ip_next_protocols.insert(IpNextLevelProtocol::Tcp); - ip_next_protocols.insert(IpNextLevelProtocol::Udp); + ip_next_protocols.insert(IpNextProtocol::Tcp); + ip_next_protocols.insert(IpNextProtocol::Udp); } } - // Init logger - let log_file_path = if let Some(file_path) = &config.logging.file_path { - // Convert to PathBuf - Path::new(&file_path).to_path_buf() - } else { - crate::sys::get_user_file_path(crate::thread_log::DEFAULT_LOG_FILE_PATH).unwrap() - }; - let log_file: File = if log_file_path.exists() { - File::options().write(true).open(&log_file_path)? - } else { - File::create(&log_file_path)? - }; - let mut log_config_builder = simplelog::ConfigBuilder::default(); - log_config_builder.set_time_format_rfc3339(); - if let Some(offset) = crate::time::get_local_offset() { - log_config_builder.set_time_offset(offset); - } - let default_log_config = log_config_builder.build(); - - // Init logger with file and terminal output - // debug build: log to terminal and file - // release build: log to file only - if cfg!(debug_assertions) { - simplelog::CombinedLogger::init(vec![ - simplelog::TermLogger::new( - simplelog::LevelFilter::Info, - default_log_config.clone(), - simplelog::TerminalMode::Mixed, - simplelog::ColorChoice::Auto, - ), - simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - ), - ])?; - } else { - simplelog::CombinedLogger::init(vec![simplelog::WriteLogger::new( - config.logging.level.to_level_filter(), - default_log_config, - log_file, - )])?; - } // Start threads let mut threads: Vec> = vec![]; @@ -395,14 +308,14 @@ pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { iface, ); }); - thread_log!(info, "start thread {}", thread_name); + tracing::info!("start thread {}", thread_name); pcap_thread_index += 1; pcap_handler }) .collect::>(); let socket_handler = thread::spawn(move || { - thread_log!(info, "start thread socket_info_update"); + tracing::info!("start thread socket_info_update"); crate::net::socket::start_socket_info_update(&mut netstat_strage_socket); }); @@ -412,7 +325,7 @@ pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { threads.push(handle); } Err(e) => { - thread_log!(error, "Error: {:?}", e); + tracing::error!("Error: {:?}", e); } } } @@ -421,22 +334,13 @@ pub fn show_stat(app: &ArgMatches) -> Result<(), Box> { if config.network.reverse_dns { let mut netstat_strage_dns = Arc::clone(&netstat_strage); let dns_handler = thread::spawn(move || { - thread_log!(info, "start thread dns_map_update"); + tracing::info!("start thread dns_map_update"); crate::net::dns::start_dns_map_update(&mut netstat_strage_dns); }); threads.push(dns_handler); } - thread_log!(info, "start TUI, netstat_data_update"); - - // Clear screen before starting TUI - let mut stdout = std::io::stdout(); - crossterm::execute!( - stdout, - crossterm::terminal::Clear(crossterm::terminal::ClearType::All) - )?; - // Move cursor to top left corner - crossterm::execute!(stdout, crossterm::cursor::MoveTo(0, 0))?; + tracing::info!("start TUI, netstat_data_update"); crate::tui::stat::terminal::run( config, diff --git a/src/handler/update.rs b/src/handler/update.rs new file mode 100644 index 0000000..21a704f --- /dev/null +++ b/src/handler/update.rs @@ -0,0 +1,97 @@ +use anyhow::Result; +use tracing::Level; +use tracing_subscriber::{fmt::time::ChronoLocal, FmtSubscriber}; +use std::path::PathBuf; + +fn download_file( + url: &str, + save_dir_path: PathBuf, + file_name: &str, +) -> Result> { + // Check and create download dir + if !save_dir_path.exists() { + std::fs::create_dir_all(&save_dir_path)?; + } + let rt = match tokio::runtime::Runtime::new() { + Ok(rt) => rt, + Err(e) => { + return Err(Box::new(e)); + } + }; + let save_file_path: PathBuf = save_dir_path.join(file_name); + rt.block_on(async { + tracing::info!("Downloading {} from {}", file_name, url); + // create a channel for progress + let (progress_tx, mut progress_rx) = tokio::sync::mpsc::channel(100); + let file_url: String = url.to_string(); + let file_path: PathBuf = save_file_path.clone(); + // spawn a task to handle the progress + tokio::spawn(async move { + let _ = crate::net::http::download_file_with_progress(file_url, file_path, progress_tx).await; + }); + // Display progress with indicatif + let bar = indicatif::ProgressBar::new(1000); + bar.set_style(indicatif::ProgressStyle::default_bar().template("{spinner:.green} [{elapsed_precise}] [{bar:40.cyan/blue}] {bytes}/{total_bytes} ({eta})").unwrap().progress_chars("#>-")); + while let Some(progress) = progress_rx.recv().await { + match progress { + crate::net::http::DownloadProgress::ContentLength(content_length) => { + tracing::info!("File URL: {}, Content-Length: {}", url, content_length); + bar.set_length(content_length); + } + crate::net::http::DownloadProgress::Downloaded(downloaded) => { + bar.set_position(downloaded); + } + } + } + bar.finish(); + tracing::info!("Downloaded {} to {}", file_name, save_file_path.display()); + }); + Ok(save_file_path) +} + +pub fn download_db_files() -> Result<()> { + // Init logger + let subscriber = FmtSubscriber::builder() + .with_max_level(Level::INFO) + .with_target(false) + .with_timer(ChronoLocal::rfc_3339()) + .finish(); + tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); + + let database_dir = crate::sys::get_database_dir_path().unwrap(); + // OUI + match download_file( + crate::db::oui::OUI_R2_URL, + database_dir.clone(), + crate::db::oui::OUI_CSV_NAME, + ) { + Ok(_) => {} + Err(e) => { + tracing::error!("{:?}", e); + } + } + // TCP Service + match download_file( + crate::db::service::TCP_SERVICE_R2_URL, + database_dir.clone(), + crate::db::service::TCP_SERVICE_CSV_NAME, + ) { + Ok(_) => {} + Err(e) => { + tracing::error!("{:?}", e); + } + } + // UDP Service + match download_file( + crate::db::service::UDP_SERVICE_R2_URL, + database_dir, + crate::db::service::UDP_SERVICE_CSV_NAME, + ) { + Ok(_) => {} + Err(e) => { + tracing::error!("{:?}", e); + } + } + tracing::info!("Successfully downloaded ntap databases."); + Ok(()) +} diff --git a/src/log.rs b/src/log.rs new file mode 100644 index 0000000..da58478 --- /dev/null +++ b/src/log.rs @@ -0,0 +1,53 @@ +use anyhow::Result; +use std::fs::File; +use std::path::Path; +use tracing::Level; +use tracing_subscriber::fmt::time::ChronoLocal; +use tracing_subscriber::fmt::writer::BoxMakeWriter; +use tracing_subscriber::fmt::writer::MakeWriterExt; +use tracing_subscriber::FmtSubscriber; + +pub fn init_logger(config: &crate::config::AppConfig) -> Result<()> { + // Init logger + let log_file_path = if let Some(file_path) = &config.logging.file_path { + // Convert to PathBuf + Path::new(&file_path).to_path_buf() + } else { + crate::sys::get_user_file_path(crate::config::DEFAULT_LOG_FILE_PATH).unwrap() + }; + let log_file: File = if log_file_path.exists() { + File::options().write(true).open(&log_file_path)? + } else { + File::create(&log_file_path)? + }; + let error_log = std::sync::Arc::new(log_file); + + if cfg!(debug_assertions) { + let error_writer = error_log.with_max_level(Level::ERROR); + let else_writer = BoxMakeWriter::new(std::io::stdout); + let writer = error_writer.and(else_writer); + let subscriber = FmtSubscriber::builder() + .with_max_level(config.logging.level.to_level_filter()) + .with_ansi(false) + .with_target(false) + .with_timer(ChronoLocal::rfc_3339()) + .with_writer(writer) + .finish(); + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); + } else { + // In release mode, log only to the error log file + let error_writer = error_log.with_max_level(Level::ERROR); + let subscriber = FmtSubscriber::builder() + .with_max_level(Level::ERROR) + .with_ansi(false) + .with_target(false) + .with_timer(ChronoLocal::rfc_3339()) + .with_writer(error_writer) + .finish(); + tracing::subscriber::set_global_default(subscriber) + .expect("setting default subscriber failed"); + } + + Ok(()) +} diff --git a/ntap/src/main.rs b/src/main.rs similarity index 94% rename from ntap/src/main.rs rename to src/main.rs index 6f70035..a326ec7 100644 --- a/ntap/src/main.rs +++ b/src/main.rs @@ -2,26 +2,26 @@ mod config; mod db; mod deps; mod handler; +mod log; mod net; mod notification; mod process; mod sys; -mod thread_log; -mod time; mod tui; mod util; +use anyhow::Result; use clap::{crate_description, crate_name, crate_version, value_parser}; use clap::{Arg, ArgMatches, Command}; use handler::AppCommands; -use std::error::Error; use std::net::IpAddr; -fn main() -> Result<(), Box> { +fn main() -> Result<()> { // Parse command line arguments let args: ArgMatches = parse_args(); let subcommand_name = args.subcommand_name().unwrap_or(""); let app_command = AppCommands::from_str(subcommand_name); + match app_command { AppCommands::Stat => handler::stat::show_stat(&args), AppCommands::Live => handler::live::live_capture(&args), @@ -30,8 +30,7 @@ fn main() -> Result<(), Box> { AppCommands::Interface => handler::interface::show_default_interface(), AppCommands::Route => handler::route::show_routes(), AppCommands::Socket => handler::socket::show_socket_info(&args), - AppCommands::IpInfo => handler::ip_info::show_public_ip_info(), - AppCommands::Update => handler::update::download_db_files(), + //AppCommands::Update => handler::update::download_db_files(), AppCommands::Default => { // If no subcommand is specified, enter stat mode by default handler::stat::show_stat_default(&args) @@ -184,7 +183,7 @@ fn parse_args() -> ArgMatches { ) // Sub-command for monitor mode. .subcommand(Command::new("monitor") - .about("Enter monitor mode. Monitor mode continuously displays live network statistics with Country and AS (or ISP) info.") + .about("Enter monitor mode. Monitor mode continuously displays live network statistics.") .arg( Arg::new("interfaces") .help("Specify the interfaces by name. Example: ntap monitor -i eth0,eth1") @@ -248,14 +247,10 @@ fn parse_args() -> ArgMatches { .subcommand(Command::new("route") .about("Show IP routing table") ) - // Sub-command for show public IP info - .subcommand(Command::new("ipinfo") - .about("Show public IP info") - ) // Sub-command for update ntap database - .subcommand(Command::new("update") - .about("Update ntap database") - ) + //.subcommand(Command::new("update") + // .about("Update ntap database") + //) ; app.get_matches() } diff --git a/ntap/src/net/dns.rs b/src/net/dns.rs similarity index 93% rename from ntap/src/net/dns.rs rename to src/net/dns.rs index 96e8da1..cf23110 100644 --- a/ntap/src/net/dns.rs +++ b/src/net/dns.rs @@ -1,4 +1,3 @@ -use crate::thread_log; use std::net::IpAddr; use std::sync::Arc; use std::time::Duration; @@ -298,7 +297,7 @@ pub fn start_dns_map_update(netstat_strage: &mut Arc) { let remote_hosts_inner = match netstat_strage.remote_hosts.try_lock() { Ok(remote_hosts) => remote_hosts, Err(e) => { - thread_log!(error, "[dns_map_update] lock error: {}", e); + tracing::error!("[dns_map_update] lock error: {}", e); continue; } }; @@ -306,7 +305,7 @@ pub fn start_dns_map_update(netstat_strage: &mut Arc) { let reverse_dns_map_inner = match netstat_strage.reverse_dns_map.try_lock() { Ok(reverse_dns_map) => reverse_dns_map, Err(e) => { - thread_log!(error, "[dns_map_update] lock error: {}", e); + tracing::error!("[dns_map_update] lock error: {}", e); continue; } }; @@ -324,7 +323,7 @@ pub fn start_dns_map_update(netstat_strage: &mut Arc) { let mut remote_hosts_inner = match netstat_strage.remote_hosts.try_lock() { Ok(remote_hosts) => remote_hosts, Err(e) => { - thread_log!(error, "[dns_map_update] lock error: {}", e); + tracing::error!("[dns_map_update] lock error: {}", e); continue; } }; @@ -332,7 +331,7 @@ pub fn start_dns_map_update(netstat_strage: &mut Arc) { let mut reverse_dns_map_inner = match netstat_strage.reverse_dns_map.try_lock() { Ok(reverse_dns_map) => reverse_dns_map, Err(e) => { - thread_log!(error, "[dns_map_update] lock error: {}", e); + tracing::error!("[dns_map_update] lock error: {}", e); continue; } }; @@ -350,18 +349,16 @@ pub fn start_dns_map_update(netstat_strage: &mut Arc) { } pub struct DnsResolver { - //pub dns_map: HashMap, + rt: tokio::runtime::Runtime, } impl DnsResolver { pub fn new() -> Self { - DnsResolver { - //dns_map: HashMap::new(), - } + let rt = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime"); + DnsResolver { rt } } - pub fn lookup_ips(&mut self, ips: Vec) -> HashMap { - let rt: tokio::runtime::Runtime = tokio::runtime::Runtime::new().unwrap(); - let handle = thread::spawn(move || rt.block_on(async { lookup_ips_async(ips).await })); - handle.join().unwrap() + + pub fn lookup_ips(&self, ips: Vec) -> HashMap { + self.rt.block_on(async { lookup_ips_async(ips).await }) } } diff --git a/ntap/src/net/host.rs b/src/net/host.rs similarity index 57% rename from ntap/src/net/host.rs rename to src/net/host.rs index a92a50d..93c6465 100644 --- a/ntap/src/net/host.rs +++ b/src/net/host.rs @@ -7,23 +7,15 @@ pub struct RemoteHostInfo { pub mac_addr: String, pub ip_addr: IpAddr, pub hostname: String, - pub country_code: String, - pub country_name: String, - pub asn: u32, - pub as_name: String, pub traffic_info: TrafficInfo, } impl RemoteHostInfo { - pub fn new(mac_addr: String, ip_addr: IpAddr) -> Self { + pub fn new(mac_addr: String, ip_addr: IpAddr, hostname: String) -> Self { RemoteHostInfo { mac_addr: mac_addr, ip_addr: ip_addr, - hostname: String::new(), - country_code: String::new(), - country_name: String::new(), - asn: 0, - as_name: String::new(), + hostname: hostname, traffic_info: TrafficInfo::new(), } } @@ -36,28 +28,14 @@ impl RemoteHostInfo { if self.hostname.is_empty() { self.hostname = other.hostname.clone(); } - if self.country_code.is_empty() { - self.country_code = other.country_code.clone(); - } - if self.country_name.is_empty() { - self.country_name = other.country_name.clone(); - } - if self.asn == 0 { - self.asn = other.asn; - } - if self.as_name.is_empty() { - self.as_name = other.as_name.clone(); - } } } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct HostDisplayInfo { pub ip_addr: IpAddr, - pub host_name: String, + pub hostname: String, pub country_code: String, - pub country_name: String, - pub asn: u32, pub as_name: String, pub traffic: TrafficDisplayInfo, } diff --git a/ntap/src/net/http.rs b/src/net/http.rs similarity index 100% rename from ntap/src/net/http.rs rename to src/net/http.rs diff --git a/ntap/src/net/interface.rs b/src/net/interface.rs similarity index 100% rename from ntap/src/net/interface.rs rename to src/net/interface.rs diff --git a/ntap/src/net/ip.rs b/src/net/ip.rs similarity index 64% rename from ntap/src/net/ip.rs rename to src/net/ip.rs index f33d24c..f893c65 100644 --- a/ntap/src/net/ip.rs +++ b/src/net/ip.rs @@ -1,19 +1,15 @@ -use ipstruct::{client::Client, ipinfo::IpInfo, setting::ClientSetting}; -use nex::net::ip::{Ipv4Net, Ipv6Net}; +use anyhow::Result; +use ipnet::{Ipv4Net, Ipv6Net}; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -pub fn get_network_address(ip_addr: IpAddr) -> Result { +pub fn get_network_address(ip_addr: IpAddr) -> Result { match ip_addr { IpAddr::V4(ipv4_addr) => { - let net: Ipv4Net = Ipv4Net::new(ipv4_addr, 24).map_err(|e| { - format!("Invalid IPv4 prefix length : {}", e.to_string()) - })?; + let net: Ipv4Net = Ipv4Net::new(ipv4_addr, 24)?; Ok(net.network().to_string()) } IpAddr::V6(ipv6_addr) => { - let net: Ipv6Net = Ipv6Net::new(ipv6_addr, 24).map_err(|e| { - format!("Invalid IPv6 prefix length: {}", e.to_string()) - })?; + let net: Ipv6Net = Ipv6Net::new(ipv6_addr, 24)?; Ok(net.network().to_string()) } } @@ -72,15 +68,3 @@ pub fn ipv6_to_dec(ipv6: Ipv6Addr) -> u128 { } return ip_int; } - -pub async fn get_self_ip_info() -> Result> { - let setting: ClientSetting = ClientSetting::default(); - let client: Client = Client::new(setting).unwrap(); - client.get_self_ip_info().await -} - -pub async fn get_self_ipv4_info() -> Result> { - let setting: ClientSetting = ClientSetting::default(); - let client: Client = Client::new(setting).unwrap(); - client.get_self_ipv4_info().await -} diff --git a/ntap/src/net/mod.rs b/src/net/mod.rs similarity index 92% rename from ntap/src/net/mod.rs rename to src/net/mod.rs index 4557928..3c86ec7 100644 --- a/ntap/src/net/mod.rs +++ b/src/net/mod.rs @@ -2,7 +2,7 @@ pub mod dns; pub mod host; -pub mod http; +//pub mod http; pub mod interface; pub mod ip; pub mod packet; diff --git a/ntap/src/net/packet.rs b/src/net/packet.rs similarity index 95% rename from ntap/src/net/packet.rs rename to src/net/packet.rs index 0df7637..1bcbbe5 100644 --- a/ntap/src/net/packet.rs +++ b/src/net/packet.rs @@ -1,7 +1,8 @@ use crate::sys; +use bytes::Bytes; use nex::packet::ethernet::EtherType; use nex::packet::frame::{DatalinkLayer, IpLayer, TransportLayer}; -use nex::packet::ip::IpNextLevelProtocol; +use nex::packet::ip::IpNextProtocol; use nex::packet::{arp, icmp}; use serde::{Deserialize, Serialize}; use std::collections::VecDeque; @@ -23,7 +24,7 @@ pub struct PacketFrame { /// The transport layer. pub transport: Option, /// Rest of the packet that could not be parsed as a header. (Usually payload) - //pub payload: Vec, + pub payload: Bytes, /// Packet length. pub packet_len: usize, /// Packet arrival time. RFC3339 format. @@ -39,7 +40,7 @@ impl PacketFrame { datalink: None, ip: None, transport: None, - //payload: Vec::new(), + payload: Bytes::new(), packet_len: 0, timestamp: String::new(), } @@ -57,7 +58,7 @@ impl PacketFrame { datalink: frame.datalink, ip: frame.ip, transport: frame.transport, - //payload: frame.payload, + payload: frame.payload, packet_len: frame.packet_len, timestamp: sys::get_sysdate(), } @@ -248,14 +249,14 @@ pub fn get_ethertype_from_str(ethertype_name: &str) -> Option { } } -pub fn get_ip_next_protocol_from_str(protocol_name: &str) -> Option { +pub fn get_ip_next_protocol_from_str(protocol_name: &str) -> Option { let name = protocol_name.to_lowercase(); // Currently, IpNextLevelProtocol not support from_str, so we need to match it manually match name.as_str() { - "icmp" => Some(IpNextLevelProtocol::Icmp), - "icmpv6" => Some(IpNextLevelProtocol::Icmpv6), - "tcp" => Some(IpNextLevelProtocol::Tcp), - "udp" => Some(IpNextLevelProtocol::Udp), + "icmp" => Some(IpNextProtocol::Icmp), + "icmpv6" => Some(IpNextProtocol::Icmpv6), + "tcp" => Some(IpNextProtocol::Tcp), + "udp" => Some(IpNextProtocol::Udp), _ => None, } } diff --git a/ntap/src/net/pcap.rs b/src/net/pcap.rs similarity index 91% rename from ntap/src/net/pcap.rs rename to src/net/pcap.rs index 9c85653..6a4fe1d 100644 --- a/ntap/src/net/pcap.rs +++ b/src/net/pcap.rs @@ -2,11 +2,10 @@ use crate::net::interface; use crate::net::packet::PacketFrame; use crate::net::stat::NetStatStrage; use crate::sys; -use crate::thread_log; use nex::net::interface::Interface; use nex::packet::frame::Frame; use nex::packet::frame::ParseOption; -use nex::packet::{ethernet::EtherType, ip::IpNextLevelProtocol}; +use nex::packet::{ethernet::EtherType, ip::IpNextProtocol}; use serde::{Deserialize, Serialize}; use std::collections::HashSet; use std::net::IpAddr; @@ -56,7 +55,7 @@ pub struct PacketCaptureOptions { /// Ether types to filter. If empty, all ether types will be captured pub ether_types: HashSet, /// IP protocols to filter. If empty, all IP protocols will be captured - pub ip_protocols: HashSet, + pub ip_protocols: HashSet, /// Capture duration limit pub capture_timeout: Duration, /// Read Timeout for read next packet (Linux, BPF only) @@ -193,16 +192,16 @@ impl PacketCaptureOptions { let name = protocol_name.to_lowercase(); match name.as_str() { "icmp" => { - self.ip_protocols.insert(IpNextLevelProtocol::Icmp); + self.ip_protocols.insert(IpNextProtocol::Icmp); } "icmpv6" => { - self.ip_protocols.insert(IpNextLevelProtocol::Icmpv6); + self.ip_protocols.insert(IpNextProtocol::Icmpv6); } "tcp" => { - self.ip_protocols.insert(IpNextLevelProtocol::Tcp); + self.ip_protocols.insert(IpNextProtocol::Tcp); } "udp" => { - self.ip_protocols.insert(IpNextLevelProtocol::Udp); + self.ip_protocols.insert(IpNextProtocol::Udp); } _ => {} } @@ -230,11 +229,11 @@ pub fn start_capture( let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), Ok(_) => { - thread_log!(warn, "Unknown channel type"); + tracing::warn!("Unknown channel type"); return report; } Err(e) => { - thread_log!(error, "Error happened {}", e); + tracing::error!("Error happened {}", e); return report; } }; @@ -259,7 +258,13 @@ pub fn start_capture( } report.bytes = report.bytes.saturating_add(packet.len()); report.packets = report.packets.saturating_add(1); - let frame: Frame = Frame::from_bytes(&packet, parse_option); + let frame: Frame = match Frame::from_buf(&packet, parse_option) { + Some(frame) => frame, + None => { + tracing::error!("Failed to parse packet"); + continue; + } + }; if filter_packet(&frame, &capture_options) { let packet_frame = PacketFrame::from_nex_frame( report.packets, @@ -310,11 +315,11 @@ pub fn start_live_capture( let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), Ok(_) => { - thread_log!(warn, "Unknown channel type"); + tracing::warn!("Unknown channel type"); return; } Err(e) => { - thread_log!(error, "Error happened {}", e); + tracing::error!("Error happened {}", e); return; } }; @@ -336,7 +341,13 @@ pub fn start_live_capture( parse_option.from_ip_packet = true; parse_option.offset = payload_offset; } - let frame: Frame = Frame::from_bytes(&packet, parse_option); + let frame: Frame = match Frame::from_buf(&packet, parse_option) { + Some(frame) => frame, + None => { + tracing::error!("Failed to parse packet"); + continue; + } + }; if filter_packet(&frame, &capture_options) { let packet_frame = PacketFrame::from_nex_frame( 0, @@ -376,11 +387,11 @@ pub fn start_background_capture( let (mut _tx, mut rx) = match nex::datalink::channel(&interface, config) { Ok(nex::datalink::Channel::Ethernet(tx, rx)) => (tx, rx), Ok(_) => { - thread_log!(warn, "Unknown channel type"); + tracing::warn!("Unknown channel type"); return; } Err(e) => { - thread_log!(error, "Error happened {}", e); + tracing::error!("Error happened {}", e); return; } }; @@ -402,7 +413,13 @@ pub fn start_background_capture( parse_option.from_ip_packet = true; parse_option.offset = payload_offset; } - let frame: Frame = Frame::from_bytes(&packet, parse_option); + let frame: Frame = match Frame::from_buf(&packet, parse_option) { + Some(frame) => frame, + None => { + tracing::error!("Failed to parse packet"); + continue; + } + }; if filter_packet(&frame, &capture_options) { let packet_frame = PacketFrame::from_nex_frame( 0, @@ -511,10 +528,7 @@ fn filter_ether_type(ether_type: EtherType, capture_options: &PacketCaptureOptio } } -fn filter_ip_protocol( - protocol: IpNextLevelProtocol, - capture_options: &PacketCaptureOptions, -) -> bool { +fn filter_ip_protocol(protocol: IpNextProtocol, capture_options: &PacketCaptureOptions) -> bool { if capture_options.ip_protocols.len() == 0 || capture_options.ip_protocols.contains(&protocol) { return true; } else { diff --git a/ntap/src/net/protocol.rs b/src/net/protocol.rs similarity index 100% rename from ntap/src/net/protocol.rs rename to src/net/protocol.rs diff --git a/ntap/src/net/service.rs b/src/net/service.rs similarity index 100% rename from ntap/src/net/service.rs rename to src/net/service.rs diff --git a/ntap/src/net/socket.rs b/src/net/socket.rs similarity index 99% rename from ntap/src/net/socket.rs rename to src/net/socket.rs index 92924c9..54a52c4 100644 --- a/ntap/src/net/socket.rs +++ b/src/net/socket.rs @@ -1,7 +1,6 @@ use crate::net::stat::NetStatStrage; use crate::net::traffic::{TrafficDisplayInfo, TrafficInfo}; use crate::process::ProcessInfo; -use crate::thread_log; use netsock::family::AddressFamilyFlags; use netsock::protocol::ProtocolFlags; use netsock::socket::ProtocolSocketInfo; @@ -390,7 +389,7 @@ pub fn start_socket_info_update(netstat_strage: &mut Arc) { let mut local_socket_inner = match netstat_strage.local_socket_map.try_lock() { Ok(connections) => connections, Err(e) => { - thread_log!(error, "[socket_info_update] lock error: {}", e); + tracing::error!("[socket_info_update] lock error: {}", e); continue; } }; diff --git a/ntap/src/net/stat.rs b/src/net/stat.rs similarity index 82% rename from ntap/src/net/stat.rs rename to src/net/stat.rs index 0b21fe9..f747e76 100644 --- a/ntap/src/net/stat.rs +++ b/src/net/stat.rs @@ -5,17 +5,18 @@ use super::{ service::ServiceDisplayInfo, traffic::{Direction, TrafficDisplayInfo, TrafficInfo}, }; -use crate::db::ip::IpDatabase; -use crate::db::service::ServiceDatabase; use crate::net::socket::{ AddressFamily, LocalSocket, ProtocolPort, SocketConnection, SocketDisplayInfo, SocketInfoOption, SocketProcess, TransportProtocol, }; use crate::notification::Notification; use crate::process::{ProcessDisplayInfo, ProcessInfo}; -use crate::thread_log; +use bytes::Bytes; use netdev::{mac::MacAddr, Interface}; +use nex::packet::dns::{DnsPacket, DnsType}; +use nex::packet::packet::Packet; use serde::{Deserialize, Serialize}; +use std::net::Ipv4Addr; use std::{ collections::HashMap, net::IpAddr, @@ -38,8 +39,6 @@ pub struct NetStatStrage { pub reverse_dns_map: Arc>>, /// Local IP Map (IpAddr -> Interface Name) pub local_ip_map: Arc>>, - /// IP Database for IP, ASN, Country, etc. - pub ipdb: Arc>, } impl NetStatStrage { @@ -47,7 +46,7 @@ impl NetStatStrage { let default_interface = match netdev::get_default_interface() { Ok(iface) => iface, Err(e) => { - thread_log!(error, "NetStatStrage get_default_interface error: {:?}", e); + tracing::error!("NetStatStrage get_default_interface error: {:?}", e); Interface::dummy() } }; @@ -60,7 +59,6 @@ impl NetStatStrage { local_socket_map: Arc::new(Mutex::new(HashMap::new())), reverse_dns_map: Arc::new(Mutex::new(HashMap::new())), local_ip_map: Arc::new(Mutex::new(local_ip_map)), - ipdb: Arc::new(Mutex::new(IpDatabase::new())), } } // Set interface @@ -70,7 +68,7 @@ impl NetStatStrage { *iface = new_interface; } Err(e) => { - thread_log!(error, "set_interface error: {:?}", e); + tracing::error!("set_interface error: {:?}", e); } } } @@ -79,7 +77,7 @@ impl NetStatStrage { match self.interface.lock() { Ok(iface) => iface.clone(), Err(e) => { - thread_log!(error, "get_interface error: {:?}", e); + tracing::error!("get_interface error: {:?}", e); Interface::dummy() } } @@ -89,7 +87,7 @@ impl NetStatStrage { match self.interface.lock() { Ok(iface) => iface.index, Err(e) => { - thread_log!(error, "get_if_index error: {:?}", e); + tracing::error!("get_if_index error: {:?}", e); 0 } } @@ -99,7 +97,7 @@ impl NetStatStrage { match self.interface.lock() { Ok(iface) => iface.name.clone(), Err(e) => { - thread_log!(error, "get_if_name error: {:?}", e); + tracing::error!("get_if_name error: {:?}", e); String::new() } } @@ -109,7 +107,7 @@ impl NetStatStrage { match self.traffic.lock() { Ok(traffic) => traffic.clone(), Err(e) => { - thread_log!(error, "get_trrafic error: {:?}", e); + tracing::error!("get_traffic error: {:?}", e); TrafficInfo::new() } } @@ -119,7 +117,7 @@ impl NetStatStrage { match self.remote_hosts.lock() { Ok(remote_hosts) => remote_hosts.clone(), Err(e) => { - thread_log!(error, "get_remote_hosts error: {:?}", e); + tracing::error!("get_remote_hosts error: {:?}", e); HashMap::new() } } @@ -129,7 +127,7 @@ impl NetStatStrage { match self.connection_map.lock() { Ok(connection_map) => connection_map.clone(), Err(e) => { - thread_log!(error, "get_connection_map error: {:?}", e); + tracing::error!("get_connection_map error: {:?}", e); HashMap::new() } } @@ -139,7 +137,7 @@ impl NetStatStrage { match self.local_socket_map.lock() { Ok(local_socket_map) => local_socket_map.clone(), Err(e) => { - thread_log!(error, "get_local_socket_map error: {:?}", e); + tracing::error!("get_local_socket_map error: {:?}", e); HashMap::new() } } @@ -148,18 +146,18 @@ impl NetStatStrage { match self.local_ip_map.try_lock() { Ok(local_ip_map) => local_ip_map.clone(), Err(e) => { - thread_log!(error, "get_local_ip_map error: {:?}", e); + tracing::error!("get_local_ip_map error: {:?}", e); HashMap::new() } } } - fn clear_trraffic(&self) { + fn clear_traffic(&self) { match self.traffic.lock() { Ok(mut traffic) => { *traffic = TrafficInfo::new(); } Err(e) => { - thread_log!(error, "clear_trraffic error: {:?}", e); + tracing::error!("clear_traffic error: {:?}", e); } } } @@ -169,7 +167,7 @@ impl NetStatStrage { remote_hosts.clear(); } Err(e) => { - thread_log!(error, "clear_remote_hosts error: {:?}", e); + tracing::error!("clear_remote_hosts error: {:?}", e); } } } @@ -179,7 +177,7 @@ impl NetStatStrage { connection_map.clear(); } Err(e) => { - thread_log!(error, "clear_connection_map error: {:?}", e); + tracing::error!("clear_connection_map error: {:?}", e); } } } @@ -189,7 +187,7 @@ impl NetStatStrage { local_socket_map.clear(); } Err(e) => { - thread_log!(error, "clear_local_socket_map error: {:?}", e); + tracing::error!("clear_local_socket_map error: {:?}", e); } } } @@ -199,19 +197,19 @@ impl NetStatStrage { reverse_dns_map.clear(); } Err(e) => { - thread_log!(error, "clear_reverse_dns_map error: {:?}", e); + tracing::error!("clear_reverse_dns_map error: {:?}", e); } } } pub fn reset(&self) { - self.clear_trraffic(); + self.clear_traffic(); self.clear_remote_hosts(); self.clear_connection_map(); self.clear_local_socket_map(); self.clear_reverse_dns_map(); } pub fn reset_data(&self) { - self.clear_trraffic(); + self.clear_traffic(); self.clear_remote_hosts(); self.clear_connection_map(); self.clear_local_socket_map(); @@ -252,22 +250,56 @@ impl NetStatStrage { } false } - pub fn load_ipdb(&self) { - match IpDatabase::load() { - Ok(ipdb) => { - let mut ipdb_mutex = self.ipdb.lock().unwrap(); - *ipdb_mutex = ipdb; + pub fn parse_dns_packet(&self, dns_packet: &Bytes) { + if let Some(dns) = DnsPacket::from_buf(dns_packet) { + let mut name = String::new(); + let mut ip_addr = IpAddr::V4(Ipv4Addr::UNSPECIFIED); + for query in &dns.queries { + match query.qtype { + DnsType::A | DnsType::AAAA => match query.get_qname_parsed() { + Ok(qname) => { + name = qname.to_string(); + } + Err(e) => { + tracing::error!("Failed to parse query name: {:?}", e); + } + }, + _ => {} + } } - Err(e) => { - thread_log!(error, "load_ipdb error: {:?}", e); + for response in &dns.responses { + match response.rtype { + DnsType::A | DnsType::AAAA => { + if let Some(ip) = response.get_ip() { + ip_addr = ip; + } else { + tracing::error!( + "Failed to get IP address from response: {:?}", + response + ); + } + } + _ => {} + } } + let mut reverse_dns_map_inner = match self.reverse_dns_map.lock() { + Ok(inner) => inner, + Err(e) => { + tracing::error!("Failed to lock reverse_dns_map: {:?}", e); + return; + } + }; + if !name.is_empty() && ip_addr != IpAddr::V4(Ipv4Addr::UNSPECIFIED) { + reverse_dns_map_inner.insert(ip_addr, name); + } + drop(reverse_dns_map_inner); } } pub fn update(&self, frame: PacketFrame) { let local_ip_map_inner = match self.local_ip_map.lock() { Ok(inner) => inner, Err(e) => { - thread_log!(error, "Failed to lock local_ips: {:?}", e); + tracing::error!("Failed to lock local_ips: {:?}", e); return; } }; @@ -275,7 +307,7 @@ impl NetStatStrage { let mut traffic_inner = match self.traffic.lock() { Ok(inner) => inner, Err(e) => { - thread_log!(error, "Failed to lock traffic: {:?}", e); + tracing::error!("Failed to lock traffic: {:?}", e); return; } }; @@ -283,7 +315,7 @@ impl NetStatStrage { let mut remote_hosts_inner = match self.remote_hosts.lock() { Ok(inner) => inner, Err(e) => { - thread_log!(error, "Failed to lock remote_hosts: {:?}", e); + tracing::error!("Failed to lock remote_hosts: {:?}", e); return; } }; @@ -291,15 +323,7 @@ impl NetStatStrage { let mut connections_inner = match self.connection_map.lock() { Ok(inner) => inner, Err(e) => { - thread_log!(error, "Failed to lock connection_map: {:?}", e); - return; - } - }; - // Lock ipdb field - let ipdb_inner = match self.ipdb.lock() { - Ok(inner) => inner, - Err(e) => { - thread_log!(error, "Failed to lock ipdb: {:?}", e); + tracing::error!("Failed to lock connection_map: {:?}", e); return; } }; @@ -458,7 +482,7 @@ impl NetStatStrage { } } }; - // Update or Insert RemoteHostInfo + /* // Update or Insert RemoteHostInfo let remote_host: &mut RemoteHostInfo = remote_hosts_inner .entry(remote_ip_addr) .or_insert(RemoteHostInfo::new(mac_addr, remote_ip_addr)); @@ -471,25 +495,7 @@ impl NetStatStrage { remote_host.traffic_info.packet_received += 1; remote_host.traffic_info.bytes_received += frame.packet_len; } - } - match remote_host.ip_addr { - IpAddr::V4(ipv4) => { - if let Some(ipv4_info) = ipdb_inner.get_ipv4_info(ipv4) { - remote_host.country_code = ipv4_info.country_code; - remote_host.country_name = ipv4_info.country_name; - remote_host.asn = ipv4_info.asn; - remote_host.as_name = ipv4_info.as_name; - } - } - IpAddr::V6(ipv6) => { - if let Some(ipv6_info) = ipdb_inner.get_ipv6_info(ipv6) { - remote_host.country_code = ipv6_info.country_code; - remote_host.country_name = ipv6_info.country_name; - remote_host.asn = ipv6_info.asn; - remote_host.as_name = ipv6_info.as_name; - } - } - } + } */ // Update SocketConnection if the packet is TCP or UDP. if let Some(transport) = frame.transport { if let Some(_tcp) = transport.tcp { @@ -537,13 +543,49 @@ impl NetStatStrage { socket_traffic.bytes_received += frame.packet_len; } } + // Try parse DNS packet + self.parse_dns_packet(&frame.payload); } } + let mut reverse_dns_map_inner = match self.reverse_dns_map.lock() { + Ok(inner) => inner, + Err(e) => { + tracing::error!("Failed to lock reverse_dns_map: {:?}", e); + return; + } + }; + // Check Reverse DNS Map + let mut hostname = String::new(); + if let Some(name) = reverse_dns_map_inner.get(&remote_ip_addr) { + hostname = name.to_string(); + } + // Update or Insert RemoteHostInfo + let remote_host: &mut RemoteHostInfo = + remote_hosts_inner + .entry(remote_ip_addr) + .or_insert(RemoteHostInfo::new( + mac_addr, + remote_ip_addr, + hostname.clone(), + )); + match direction { + Direction::Egress => { + remote_host.traffic_info.packet_sent += 1; + remote_host.traffic_info.bytes_sent += frame.packet_len; + } + Direction::Ingress => { + remote_host.traffic_info.packet_received += 1; + remote_host.traffic_info.bytes_received += frame.packet_len; + } + } + if remote_host.hostname.is_empty() && !hostname.is_empty() { + remote_host.hostname = hostname.clone(); + } + // Drop the locks drop(traffic_inner); drop(remote_hosts_inner); drop(connections_inner); - drop(ipdb_inner); } } @@ -583,7 +625,7 @@ impl NetStatData { let default_interface = match netdev::get_default_interface() { Ok(iface) => iface, Err(e) => { - thread_log!(error, "NetStatData get_default_interface error: {:?}", e); + tracing::error!("NetStatData get_default_interface error: {:?}", e); Interface::dummy() } }; @@ -668,7 +710,7 @@ impl NetStatData { true } Err(e) => { - thread_log!(error, "remove_old_entries error: {:?}", e); + tracing::error!("remove_old_entries error: {:?}", e); false } }, @@ -694,7 +736,7 @@ impl NetStatData { true } Err(e) => { - thread_log!(error, "remove_old_entries error: {:?}", e); + tracing::error!("remove_old_entries error: {:?}", e); remove_local_socket.push(LocalSocket { interface_name: conn.interface_name.clone(), port: conn.local_port, @@ -714,6 +756,13 @@ impl NetStatData { } pub fn get_remote_hosts(&self, limit: Option) -> Vec { + let ipv4_asn_db = crate::db::IPV4_ASN_DB.get().unwrap().read().unwrap(); + let ipv4_country_db = crate::db::IPV4_COUNTRY_DB.get().unwrap().read().unwrap(); + let ipv6_asn_db = crate::db::IPV6_ASN_DB.get().unwrap().read().unwrap(); + let ipv6_country_db = crate::db::IPV6_COUNTRY_DB.get().unwrap().read().unwrap(); + let as_db = crate::db::AS_DB.get().unwrap().read().unwrap(); + + // Create a map to store the traffic info for each remote host. let mut host_traffic_map: HashMap = HashMap::new(); self.remote_hosts.iter().for_each(|(_ip, host)| { match host_traffic_map.get(&host.ip_addr) { @@ -742,11 +791,39 @@ impl NetStatData { if let Some(host) = self.remote_hosts.get(ip) { let host = HostDisplayInfo { ip_addr: host.ip_addr, - host_name: host.hostname.clone(), - country_code: host.country_code.clone(), - country_name: host.country_name.clone(), - asn: host.asn.clone(), - as_name: host.as_name.clone(), + hostname: host.hostname.clone(), + country_code: { + if nex::net::ip::is_global_ip(&host.ip_addr) { + match host.ip_addr { + IpAddr::V4(ipv4) => match ipv4_country_db.lookup(ipv4) { + Some(country) => country.to_string(), + None => "N/A".to_string(), + }, + IpAddr::V6(ipv6) => match ipv6_country_db.lookup(ipv6) { + Some(country) => country.to_string(), + None => "N/A".to_string(), + }, + } + } else { + String::from("N/A") + } + }, + as_name: { + if nex::net::ip::is_global_ip(&host.ip_addr) { + match host.ip_addr { + IpAddr::V4(ipv4) => ipv4_asn_db + .lookup(ipv4) + .and_then(|asn| as_db.get_name(*asn)) + .map_or_else(|| String::from("N/A"), |asn| asn.to_string()), + IpAddr::V6(ipv6) => ipv6_asn_db + .lookup(ipv6) + .and_then(|asn| as_db.get_name(*asn)) + .map_or_else(|| String::from("N/A"), |asn| asn.to_string()), + } + } else { + String::from("N/A") + } + }, traffic: host.traffic_info.to_display_info(), }; remote_hosts.push(host); @@ -914,13 +991,8 @@ impl NetStatData { } pub fn get_app_protocols(&self, limit: Option) -> Vec { - let service_db: ServiceDatabase = match crate::db::service::ServiceDatabase::load() { - Ok(db) => db, - Err(e) => { - thread_log!(error, "get_app_protocols load service db error: {:?}", e); - ServiceDatabase::new() - } - }; + let tcp_db = crate::db::TCP_SERVICE_DB.get().unwrap().read().unwrap(); + let udp_db = crate::db::UDP_SERVICE_DB.get().unwrap().read().unwrap(); let mut protocol_port_map: HashMap = HashMap::new(); self.connection_map.iter().for_each(|(conn, traffic_info)| { let protocol_port: ProtocolPort = ProtocolPort { @@ -956,16 +1028,14 @@ impl NetStatData { port: protocol_port.port, protocol: protocol_port.protocol.as_str().to_string(), name: match protocol_port.protocol { - TransportProtocol::TCP => service_db - .tcp_map - .get(&protocol_port.port) - .unwrap_or(&String::from("unknown")) - .clone(), - TransportProtocol::UDP => service_db - .udp_map - .get(&protocol_port.port) - .unwrap_or(&String::from("unknown")) - .clone(), + TransportProtocol::TCP => tcp_db + .get_name(protocol_port.port) + .unwrap_or("Unknown TCP Service") + .to_string(), + TransportProtocol::UDP => udp_db + .get_name(protocol_port.port) + .unwrap_or("Unknown UDP Service") + .to_string(), }, traffic: traffic.to_display_info(), }; @@ -999,7 +1069,7 @@ pub fn update_netstat_data( data.merge(netstat_strage.clone_data_and_reset(), interval); } Err(e) => { - thread_log!(error, "Error: {:?}", e); + tracing::error!("Error: {:?}", e); continue; } } diff --git a/ntap/src/net/traffic.rs b/src/net/traffic.rs similarity index 100% rename from ntap/src/net/traffic.rs rename to src/net/traffic.rs diff --git a/ntap/src/notification.rs b/src/notification.rs similarity index 100% rename from ntap/src/notification.rs rename to src/notification.rs diff --git a/ntap/src/process.rs b/src/process.rs similarity index 100% rename from ntap/src/process.rs rename to src/process.rs diff --git a/ntap/src/sys/mod.rs b/src/sys/mod.rs similarity index 62% rename from ntap/src/sys/mod.rs rename to src/sys/mod.rs index 289f26d..6a14a21 100644 --- a/ntap/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -1,6 +1,3 @@ -#![allow(unused)] - -use crate::thread_log; use clap::{crate_name, crate_version}; use std::path::PathBuf; @@ -19,22 +16,6 @@ pub fn get_app_title() -> String { } pub const USER_CONFIG_DIR_NAME: &str = ".ntap"; -pub const DOWNLOAD_DIR_NAME: &str = "Downloads"; - -#[cfg(target_os = "windows")] -pub fn get_os_type() -> String { - "windows".to_owned() -} - -#[cfg(target_os = "linux")] -pub fn get_os_type() -> String { - "linux".to_owned() -} - -#[cfg(target_os = "macos")] -pub fn get_os_type() -> String { - "macos".to_owned() -} pub fn get_sysdate() -> String { let now = chrono::Local::now(); @@ -49,7 +30,7 @@ pub fn get_config_dir_path() -> Option { match std::fs::create_dir_all(&path) { Ok(_) => {} Err(e) => { - thread_log!(error, "{:?}", e); + tracing::error!("Failed to create config dir: {:?}", e); return None; } } @@ -70,17 +51,7 @@ pub fn get_user_file_path(file_name: &str) -> Option { } } -pub fn get_download_dir_path() -> Option { - match home::home_dir() { - Some(mut path) => { - path.push(DOWNLOAD_DIR_NAME); - Some(path) - } - None => None, - } -} - -pub fn get_database_dir_path() -> Option { +/* pub fn get_database_dir_path() -> Option { match get_config_dir_path() { Some(mut path) => { path.push("db"); @@ -88,7 +59,7 @@ pub fn get_database_dir_path() -> Option { match std::fs::create_dir_all(&path) { Ok(_) => {} Err(e) => { - thread_log!(error, "{:?}", e); + tracing::error!("Failed to create database dir: {:?}", e); return None; } } @@ -97,14 +68,4 @@ pub fn get_database_dir_path() -> Option { } None => None, } -} - -pub fn get_db_file_path(file_name: &str) -> Option { - match get_database_dir_path() { - Some(mut path) => { - path.push(file_name); - Some(path) - } - None => None, - } -} +} */ diff --git a/src/sys/unix.rs b/src/sys/unix.rs new file mode 100644 index 0000000..d2b0df4 --- /dev/null +++ b/src/sys/unix.rs @@ -0,0 +1,5 @@ +use crate::deps::DepsError; + +pub fn check_deps() -> Result<(), DepsError> { + crate::deps::check_deps() +} diff --git a/src/sys/windows.rs b/src/sys/windows.rs new file mode 100644 index 0000000..b382f81 --- /dev/null +++ b/src/sys/windows.rs @@ -0,0 +1,60 @@ +use inquire::Confirm; +use winreg::enums::HKEY_LOCAL_MACHINE; +use winreg::RegKey; + +use crate::deps::DepsError; + +pub fn get_os_bit() -> String { + if cfg!(target_pointer_width = "32") { + return "32-bit".to_owned(); + } else if cfg!(target_pointer_width = "64") { + return "64-bit".to_owned(); + } else { + return "unknown".to_owned(); + } +} + +// Get software installation status +pub fn software_installed(software_name: String) -> bool { + let hklm: RegKey = RegKey::predef(HKEY_LOCAL_MACHINE); + let os_bit: String = get_os_bit(); + let npcap_key: RegKey = if os_bit == "32-bit" { + match hklm.open_subkey(format!("SOFTWARE\\{}", software_name)) { + Ok(key) => key, + Err(_) => return false, + } + } else { + match hklm.open_subkey(format!("SOFTWARE\\WOW6432Node\\{}", software_name)) { + Ok(key) => key, + Err(_) => return false, + } + }; + let _version: String = npcap_key.get_value("").unwrap_or(String::new()); + true +} + +pub fn check_deps() -> Result<(), DepsError> { + match crate::deps::check_deps() { + Ok(_) => { + return Ok(()); + } + Err(e) => match e { + crate::deps::DepsError::Missing(s) => { + if s == crate::deps::NPCAP_SOFTWARE_NAME.to_string() { + let ans: bool = Confirm::new( + "Npcap is not installed, would you like to download & install it ?", + ) + .prompt() + .unwrap(); + if ans == false { + return Err(DepsError::Missing("On windows, Npcap is required for ntap to work properly. Please install Npcap and try again.".to_string())); + } + } + } + crate::deps::DepsError::Unknown(s) => { + eprintln!("Error: Unknown dependency: {}", s); + } + }, + } + Ok(()) +} diff --git a/ntap/src/tui/live/app.rs b/src/tui/live/app.rs similarity index 100% rename from ntap/src/tui/live/app.rs rename to src/tui/live/app.rs diff --git a/ntap/src/tui/live/mod.rs b/src/tui/live/mod.rs similarity index 100% rename from ntap/src/tui/live/mod.rs rename to src/tui/live/mod.rs diff --git a/ntap/src/tui/live/terminal.rs b/src/tui/live/terminal.rs similarity index 97% rename from ntap/src/tui/live/terminal.rs rename to src/tui/live/terminal.rs index a68f863..6d1f029 100644 --- a/ntap/src/tui/live/terminal.rs +++ b/src/tui/live/terminal.rs @@ -1,5 +1,5 @@ +use anyhow::Result; use std::{ - error::Error, io, time::{Duration, Instant}, }; @@ -21,14 +21,14 @@ pub fn run( app_config: AppConfig, enhanced_graphics: bool, packet_strage: &Arc, -) -> Result<(), Box> { +) -> Result<()> { // setup terminal enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; - + terminal.clear()?; // create app and run it let title = sys::get_app_title(); let app = App::new(&title, enhanced_graphics, app_config); diff --git a/ntap/src/tui/live/ui.rs b/src/tui/live/ui.rs similarity index 100% rename from ntap/src/tui/live/ui.rs rename to src/tui/live/ui.rs diff --git a/ntap/src/tui/mod.rs b/src/tui/mod.rs similarity index 100% rename from ntap/src/tui/mod.rs rename to src/tui/mod.rs diff --git a/ntap/src/tui/monitor/app.rs b/src/tui/monitor/app.rs similarity index 100% rename from ntap/src/tui/monitor/app.rs rename to src/tui/monitor/app.rs diff --git a/ntap/src/tui/monitor/mod.rs b/src/tui/monitor/mod.rs similarity index 100% rename from ntap/src/tui/monitor/mod.rs rename to src/tui/monitor/mod.rs diff --git a/ntap/src/tui/monitor/terminal.rs b/src/tui/monitor/terminal.rs similarity index 97% rename from ntap/src/tui/monitor/terminal.rs rename to src/tui/monitor/terminal.rs index 692687d..c7ea70b 100644 --- a/ntap/src/tui/monitor/terminal.rs +++ b/src/tui/monitor/terminal.rs @@ -1,5 +1,5 @@ +use anyhow::Result; use std::{ - error::Error, io, time::{Duration, Instant}, }; @@ -18,14 +18,14 @@ pub fn run( app_config: AppConfig, enhanced_graphics: bool, netstat_strage: &mut Arc, -) -> Result<(), Box> { +) -> Result<()> { // setup terminal enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; - + terminal.clear()?; // create app and run it let title = sys::get_app_title(); let app = App::new(&title, enhanced_graphics, app_config); diff --git a/ntap/src/tui/monitor/ui.rs b/src/tui/monitor/ui.rs similarity index 98% rename from ntap/src/tui/monitor/ui.rs rename to src/tui/monitor/ui.rs index 9daecc2..3eb92e1 100644 --- a/ntap/src/tui/monitor/ui.rs +++ b/src/tui/monitor/ui.rs @@ -133,8 +133,8 @@ fn draw_top_data(f: &mut Frame, app: &mut App, area: Rect) { ingress_traffic, egress_traffic, host.country_code.clone(), - host.asn.to_string(), host.as_name.clone(), + host.hostname.clone(), ]) }) .collect::>(); @@ -143,8 +143,8 @@ fn draw_top_data(f: &mut Frame, app: &mut App, area: Rect) { Constraint::Length(11), Constraint::Length(11), Constraint::Length(8), - Constraint::Length(8), - Constraint::Length(24), + Constraint::Length(30), + Constraint::Length(40), ]; //let mut table_state = TableState::default(); @@ -156,8 +156,8 @@ fn draw_top_data(f: &mut Frame, app: &mut App, area: Rect) { "↓ Bytes", "↑ Bytes", "Country", - "ASN", "AS Name", + "Hostname", ]) .style(Style::new().bold()), //.bottom_margin(1), ) @@ -273,8 +273,8 @@ fn draw_remotehosts_table(f: &mut Frame, app: &mut App, area: Rect) { ingress_traffic, egress_traffic, host.country_code.clone(), - host.asn.to_string(), host.as_name.clone(), + host.hostname.clone(), ]) }) .collect::>(); @@ -283,8 +283,8 @@ fn draw_remotehosts_table(f: &mut Frame, app: &mut App, area: Rect) { Constraint::Length(11), Constraint::Length(11), Constraint::Length(8), - Constraint::Length(8), - Constraint::Length(24), + Constraint::Length(30), + Constraint::Length(40), ]; //let mut table_state = TableState::default(); @@ -297,8 +297,8 @@ fn draw_remotehosts_table(f: &mut Frame, app: &mut App, area: Rect) { "↓ Bytes", "↑ Bytes", "Country", - "ASN", "AS Name", + "Hostname", ]) .style(Style::new().bold()), //.bottom_margin(1), ) diff --git a/ntap/src/tui/stat/app.rs b/src/tui/stat/app.rs similarity index 100% rename from ntap/src/tui/stat/app.rs rename to src/tui/stat/app.rs diff --git a/ntap/src/tui/stat/mod.rs b/src/tui/stat/mod.rs similarity index 100% rename from ntap/src/tui/stat/mod.rs rename to src/tui/stat/mod.rs diff --git a/ntap/src/tui/stat/terminal.rs b/src/tui/stat/terminal.rs similarity index 97% rename from ntap/src/tui/stat/terminal.rs rename to src/tui/stat/terminal.rs index 9eb66e5..4abafda 100644 --- a/ntap/src/tui/stat/terminal.rs +++ b/src/tui/stat/terminal.rs @@ -1,11 +1,11 @@ use std::{ - error::Error, io, time::{Duration, Instant}, }; use crate::{config::AppConfig, net::stat::NetStatStrage}; use crate::{sys, tui::stat::app::App, tui::stat::ui}; +use anyhow::Result; use crossterm::{ event::{self, DisableMouseCapture, EnableMouseCapture, Event, KeyCode, KeyEventKind}, execute, @@ -18,14 +18,14 @@ pub fn run( app_config: AppConfig, enhanced_graphics: bool, netstat_strage: &mut Arc, -) -> Result<(), Box> { +) -> Result<()> { // setup terminal enable_raw_mode()?; let mut stdout = io::stdout(); execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?; let backend = CrosstermBackend::new(stdout); let mut terminal = Terminal::new(backend)?; - + terminal.clear()?; // create app and run it let title = sys::get_app_title(); let app = App::new(&title, enhanced_graphics, app_config); diff --git a/ntap/src/tui/stat/ui.rs b/src/tui/stat/ui.rs similarity index 100% rename from ntap/src/tui/stat/ui.rs rename to src/tui/stat/ui.rs diff --git a/ntap/src/util/mod.rs b/src/util/mod.rs similarity index 100% rename from ntap/src/util/mod.rs rename to src/util/mod.rs diff --git a/ntap/src/util/tree.rs b/src/util/tree.rs similarity index 100% rename from ntap/src/util/tree.rs rename to src/util/tree.rs