From c8f5561de4784abf56b632ec435e6dac6ba755a3 Mon Sep 17 00:00:00 2001 From: shellrow Date: Tue, 29 Apr 2025 10:45:35 +0900 Subject: [PATCH 1/4] Update dependencies --- Cargo.toml | 26 ++--- src/dns/mod.rs | 1 - src/dns/scanner.rs | 224 +++------------------------------------ src/dns/setting.rs | 6 -- src/handler/dns.rs | 2 +- src/handler/interface.rs | 8 +- src/handler/mod.rs | 2 +- src/handler/port.rs | 2 +- src/interface/mod.rs | 34 +++--- src/main.rs | 1 + src/output.rs | 2 +- src/scan/packet.rs | 32 +++--- src/scan/service.rs | 183 +++++++++++++++++--------------- src/tls/cert.rs | 90 ++++++++++++++++ src/tls/key.rs | 16 +++ src/tls/mod.rs | 2 + 16 files changed, 275 insertions(+), 356 deletions(-) delete mode 100644 src/dns/setting.rs create mode 100644 src/tls/cert.rs create mode 100644 src/tls/key.rs create mode 100644 src/tls/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 5756fad..eb912fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,31 +13,33 @@ categories = ["network-programming"] license = "MIT" [dependencies] +anyhow = { version = "1" } serde = { version = "1", features = ["derive"] } serde_json = "1" -netdev = { version = "0.30", features = ["serde"] } -nex = { version = "0.18", features = ["serde"] } -nex-socket = { version = "0.18", features = ["tls"] } -privilege = "0.3" -async-io = "2.3" -futures-lite = "2.3" +netdev = { version = "0.34", features = ["serde"] } +nex = { version = "0.19.1", features = ["serde"] } futures = {version = "0.3", features = ["executor", "thread-pool"]} +rustls = { version = "0.23", default-features = false, features = ["ring", "std"] } +rustls-native-certs = "0.7" +rustls-pemfile = "2.1" +rustls-pki-types = "1.8" tokio = { version = "1" } +tokio-rustls = { version = "0.26", default-features = false, features = ["ring"]} hickory-resolver = { version = "0.24" } chrono = { version = "0.4", features = ["serde"] } -uuid = { version = "1.3", features = ["v4","v5","fast-rng","macro-diagnostics"] } +uuid = { version = "1.16", features = ["v4","v5","fast-rng","macro-diagnostics"] } bincode = "1.3" phf = { version = "0.11", features = ["macros"] } rand = "0.8" -clap = { version = "4.4", features = ["cargo"] } -indicatif = "0.16" -inquire = "0.6" -ipnet = "2.7" +clap = { version = "4.5", features = ["cargo"] } +indicatif = "0.17" +inquire = "0.7" +ipnet = "2.11" num_cpus = "1.16" termtree = "0.5" [target.'cfg(windows)'.dependencies] -winreg = "0.52" +winreg = "0.55" # The profile that 'cargo dist' will build with [profile.dist] diff --git a/src/dns/mod.rs b/src/dns/mod.rs index b1a3f2c..0b3da16 100644 --- a/src/dns/mod.rs +++ b/src/dns/mod.rs @@ -1,7 +1,6 @@ pub mod domain; pub mod result; pub mod scanner; -pub mod setting; use std::net::IpAddr; use std::time::Duration; diff --git a/src/dns/scanner.rs b/src/dns/scanner.rs index 5d32a74..b7d5e1e 100644 --- a/src/dns/scanner.rs +++ b/src/dns/scanner.rs @@ -11,14 +11,7 @@ use tokio::time::timeout; use hickory_resolver::config::{ResolverConfig, ResolverOpts}; use hickory_resolver::AsyncResolver; -use super::setting::DEFAULT_USER_AGENT_FIREFOX; -#[cfg(feature = "passive")] -use crate::config::URL_CRT; -#[cfg(feature = "passive")] -use crate::model::CertEntry; use crate::scan::result::ScanStatus; -#[cfg(feature = "passive")] -use reqwest::Url; /// Structure for domain scan /// @@ -41,10 +34,6 @@ pub struct DomainScanner { tx: Arc>>, /// Receiver for progress messaging rx: Arc>>, - /// Run passive scan - pub passive: bool, - /// User-Agent for passive scan - user_agent: String, } impl DomainScanner { @@ -60,8 +49,6 @@ impl DomainScanner { scan_result: DomainScanResult::new(), tx: Arc::new(Mutex::new(tx)), rx: Arc::new(Mutex::new(rx)), - passive: false, - user_agent: DEFAULT_USER_AGENT_FIREFOX.to_string(), }; Ok(domain_scanner) } @@ -84,57 +71,24 @@ impl DomainScanner { pub fn set_timeout(&mut self, timeout: Duration) { self.timeout = timeout; } - /// Set active/passive scan (default is active) - pub fn set_passive(&mut self, passive: bool) { - self.passive = passive; - } - /// Set user-agent for passive scan - pub fn set_user_agent(&mut self, user_agent: String) { - self.user_agent = user_agent; - } async fn scan_domain(&self) -> Result, ()> { - if self.passive { - #[cfg(feature = "passive")] - match timeout( - self.timeout, - scan_subdomain_passive( - self.base_domain.clone(), - &self.tx, - self.resolve_timeout, - self.concurrent_limit, - self.user_agent.clone(), - ), - ) - .await - { - Ok(domains) => { - return Ok(domains); - } - Err(_) => { - return Err(()); - } + match timeout( + self.timeout, + scan_subdomain( + self.base_domain.clone(), + self.word_list.clone(), + &self.tx, + self.resolve_timeout, + self.concurrent_limit, + ), + ) + .await + { + Ok(domains) => { + return Ok(domains); } - #[cfg(not(feature = "passive"))] - return Err(()); - } else { - match timeout( - self.timeout, - scan_subdomain( - self.base_domain.clone(), - self.word_list.clone(), - &self.tx, - self.resolve_timeout, - self.concurrent_limit, - ), - ) - .await - { - Ok(domains) => { - return Ok(domains); - } - Err(_) => { - return Err(()); - } + Err(_) => { + return Err(()); } } } @@ -142,11 +96,6 @@ impl DomainScanner { /// /// Results are stored in DomainScanner::scan_result pub async fn run_scan(&mut self) { - if self.passive && cfg!(not(feature = "passive")) { - self.scan_result.scan_status = - ScanStatus::Error(String::from("Passive scan not supported")); - return; - } let start_time = Instant::now(); let res = self.scan_domain().await; match res { @@ -190,7 +139,6 @@ async fn resolve_domain(host_name: String) -> Vec { ips } -#[cfg(feature = "async")] #[cfg(not(any(unix, target_os = "windows")))] async fn resolve_domain(host_name: String) -> Vec { let mut ips: Vec = vec![]; @@ -207,25 +155,6 @@ async fn resolve_domain(host_name: String) -> Vec { ips } -#[cfg(feature = "passive")] -fn extract_domain(target: String) -> String { - let mut domain_name: String = target; - match domain_name.strip_prefix("*.") { - Some(d) => { - domain_name = d.to_string(); - } - None => {} - } - domain_name -} - -#[cfg(feature = "passive")] -fn is_subdomain(domain: String, apex_domain: String) -> bool { - domain.contains(&apex_domain) - && domain.ends_with(&apex_domain) - && domain.len() > apex_domain.len() -} - async fn scan_subdomain( base_domain: String, word_list: Vec, @@ -279,124 +208,3 @@ async fn scan_subdomain( } result } - -#[cfg(feature = "passive")] -async fn scan_subdomain_passive( - base_domain: String, - ptx: &Arc>>, - resolve_timeout: Duration, - concurrent_limit: usize, - user_agent: String, -) -> Vec { - let mut result: Vec = vec![]; - let scan_results: Arc>> = Arc::new(Mutex::new(vec![])); - let mut certs: Vec = vec![]; - //"https://crt.sh/?dNSName=example.com&output=json" - let url = match Url::parse_with_params( - URL_CRT, - &[ - ("dNSName", base_domain.clone().as_str()), - ("output", "json"), - ], - ) { - Ok(url) => url, - Err(e) => { - println!("{}", e); - return result; - } - }; - let client = reqwest::Client::builder() - .timeout(Duration::from_secs(60)) - .build() - .expect("failed to build HTTP reqest client"); - let res = client - .get(url) - .header(reqwest::header::USER_AGENT, user_agent) - .send() - .await; - match res { - Ok(r) => { - if r.status().is_success() { - match r.text().await { - Ok(res_text) => { - let certs_json: serde_json::Value = serde_json::from_str(res_text.as_str()) - .unwrap_or(serde_json::json!({})); - if certs_json.is_array() { - let cert_array = certs_json.as_array().unwrap(); - for cert in cert_array { - match serde_json::to_string(cert) { - Ok(cert) => { - let cert: CertEntry = - match serde_json::from_str(cert.as_str()) { - Ok(cert) => cert, - Err(_) => continue, - }; - certs.push(cert); - } - Err(_) => {} - } - } - } - } - Err(_) => {} - }; - } - } - Err(_) => {} - } - let mut target_domains: Vec = vec![]; - for cert in certs { - let domain_name: String = extract_domain(cert.common_name); - if is_subdomain(domain_name.clone(), base_domain.clone()) - && !target_domains.contains(&domain_name) - { - target_domains.push(domain_name); - } - let name_values: Vec<&str> = cert.name_value.trim().split("\n").collect(); - for value in name_values { - let name: String = extract_domain(value.to_string()); - if is_subdomain(name.clone(), base_domain.clone()) && !target_domains.contains(&name) { - target_domains.push(name); - } - } - } - let results = stream::iter(target_domains) - .map(|domain| async move { - let mut d: Domain = Domain { - domain_name: domain.clone(), - ips: vec![], - }; - match timeout(resolve_timeout, resolve_domain(domain.clone())).await { - Ok(ips) => { - d.ips = ips; - match ptx.lock() { - Ok(lr) => match lr.send(domain) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - } - } - Err(_) => match ptx.lock() { - Ok(lr) => match lr.send(domain) { - Ok(_) => {} - Err(_) => {} - }, - Err(_) => {} - }, - } - d - }) - .buffer_unordered(concurrent_limit); - results - .for_each(|domain| async { - if domain.ips.len() > 0 { - scan_results.lock().unwrap().push(domain); - } - }) - .await; - for domain in scan_results.lock().unwrap().iter() { - result.push(domain.to_owned()); - } - result -} diff --git a/src/dns/setting.rs b/src/dns/setting.rs deleted file mode 100644 index 49fb031..0000000 --- a/src/dns/setting.rs +++ /dev/null @@ -1,6 +0,0 @@ -#[cfg(feature = "passive")] -pub(crate) const URL_CRT: &str = "https://crt.sh/"; -pub const DEFAULT_USER_AGENT_FIREFOX: &str = - "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/121.0"; -pub const DEFAULT_USER_AGENT_CHROME: &str = - "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"; diff --git a/src/handler/dns.rs b/src/handler/dns.rs index b37726a..a8b2529 100644 --- a/src/handler/dns.rs +++ b/src/handler/dns.rs @@ -73,7 +73,7 @@ pub fn handle_subdomain_scan(args: &ArgMatches) { if crate::app::is_quiet_mode() { bar.set_draw_target(ProgressDrawTarget::hidden()); } - bar.enable_steady_tick(120); + bar.enable_steady_tick(Duration::from_millis(120)); bar.set_style(output::get_progress_style()); bar.set_position(0); bar.set_message("SubdomainScan"); diff --git a/src/handler/interface.rs b/src/handler/interface.rs index 44b3efd..005226a 100644 --- a/src/handler/interface.rs +++ b/src/handler/interface.rs @@ -86,13 +86,13 @@ pub fn show_interface_tree(iface: &Interface) { )); let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); for ipv4 in &iface.ipv4 { - ipv4_tree.push(node_label(&ipv4.addr.to_string(), None, None)); + ipv4_tree.push(node_label(&ipv4.addr().to_string(), None, None)); } tree.push(ipv4_tree); let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); for ipv6 in &iface.ipv6 { - ipv6_tree.push(node_label(&ipv6.addr.to_string(), None, None)); + ipv6_tree.push(node_label(&ipv6.addr().to_string(), None, None)); } tree.push(ipv6_tree); @@ -142,13 +142,13 @@ pub fn show_interfaces_tree(interfaces: &Vec) { )); let mut ipv4_tree = Tree::new(node_label("IPv4 Addresses", None, None)); for ipv4 in &iface.ipv4 { - ipv4_tree.push(node_label(&ipv4.addr.to_string(), None, None)); + ipv4_tree.push(node_label(&ipv4.addr().to_string(), None, None)); } iface_tree.push(ipv4_tree); let mut ipv6_tree = Tree::new(node_label("IPv6 Addresses", None, None)); for ipv6 in &iface.ipv6 { - ipv6_tree.push(node_label(&ipv6.addr.to_string(), None, None)); + ipv6_tree.push(node_label(&ipv6.addr().to_string(), None, None)); } iface_tree.push(ipv6_tree); diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 5cafaf8..2bb7ec5 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -140,7 +140,7 @@ pub fn default_probe(target_host: &str, args: &ArgMatches) { if crate::app::is_quiet_mode() { bar.set_draw_target(ProgressDrawTarget::hidden()); } - bar.enable_steady_tick(120); + bar.enable_steady_tick(Duration::from_millis(120)); bar.set_style(output::get_progress_style()); bar.set_position(0); bar.set_message("ServiceDetection"); diff --git a/src/handler/port.rs b/src/handler/port.rs index c7be4c2..472f263 100644 --- a/src/handler/port.rs +++ b/src/handler/port.rs @@ -182,7 +182,7 @@ pub fn handle_portscan(args: &ArgMatches) { if crate::app::is_quiet_mode() { bar.set_draw_target(ProgressDrawTarget::hidden()); } - bar.enable_steady_tick(120); + bar.enable_steady_tick(Duration::from_millis(120)); bar.set_style(output::get_progress_style()); bar.set_position(0); bar.set_message("ServiceDetection"); diff --git a/src/interface/mod.rs b/src/interface/mod.rs index 433c979..49e16db 100644 --- a/src/interface/mod.rs +++ b/src/interface/mod.rs @@ -8,12 +8,12 @@ use std::{ pub fn get_interface_by_ip(ip_addr: IpAddr) -> Option { for iface in netdev::interface::get_interfaces() { for ip in iface.ipv4.clone() { - if ip.addr == ip_addr { + if ip.addr() == ip_addr { return Some(iface); } } for ip in iface.ipv6.clone() { - if ip.addr == ip_addr { + if ip.addr() == ip_addr { return Some(iface); } } @@ -41,15 +41,15 @@ pub fn get_interface_by_name(name: String) -> Option { pub fn get_interface_ipv4(iface: &Interface) -> Option { for ip in iface.ipv4.clone() { - return Some(IpAddr::V4(ip.addr)); + return Some(IpAddr::V4(ip.addr())); } return None; } pub fn get_interface_global_ipv6(iface: &Interface) -> Option { for ip in iface.ipv6.clone() { - if nex::net::ip::is_global_ipv6(&ip.addr) { - return Some(IpAddr::V6(ip.addr)); + if nex::net::ip::is_global_ipv6(&ip.addr()) { + return Some(IpAddr::V6(ip.addr())); } } return None; @@ -57,8 +57,8 @@ pub fn get_interface_global_ipv6(iface: &Interface) -> Option { pub fn get_interface_local_ipv6(iface: &Interface) -> Option { for ip in iface.ipv6.clone() { - if !nex::net::ip::is_global_ipv6(&ip.addr) { - return Some(IpAddr::V6(ip.addr)); + if !nex::net::ip::is_global_ipv6(&ip.addr()) { + return Some(IpAddr::V6(ip.addr())); } } return None; @@ -67,10 +67,10 @@ pub fn get_interface_local_ipv6(iface: &Interface) -> Option { pub fn get_interface_ips(iface: &Interface) -> Vec { let mut ips: Vec = Vec::new(); for ip in iface.ipv4.clone() { - ips.push(ip.addr.to_string()); + ips.push(ip.addr().to_string()); } for ip in iface.ipv6.clone() { - ips.push(ip.addr.to_string()); + ips.push(ip.addr().to_string()); } ips } @@ -79,10 +79,10 @@ pub fn get_local_ips(if_index: u32) -> HashSet { let interface = get_interface_by_index(if_index).unwrap(); let mut ips: HashSet = HashSet::new(); for ip in interface.ipv4.clone() { - ips.insert(IpAddr::V4(ip.addr)); + ips.insert(IpAddr::V4(ip.addr())); } for ip in interface.ipv6.clone() { - ips.insert(IpAddr::V6(ip.addr)); + ips.insert(IpAddr::V6(ip.addr())); } // localhost IP addresses ips.insert(IpAddr::V4(Ipv4Addr::LOCALHOST)); @@ -95,10 +95,10 @@ pub fn get_default_local_ips() -> HashSet { let default_interface = netdev::get_default_interface().unwrap(); let mut ips: HashSet = HashSet::new(); for ip in default_interface.ipv4.clone() { - ips.insert(IpAddr::V4(ip.addr)); + ips.insert(IpAddr::V4(ip.addr())); } for ip in default_interface.ipv6.clone() { - ips.insert(IpAddr::V6(ip.addr)); + ips.insert(IpAddr::V6(ip.addr())); } // localhost IP addresses ips.insert(IpAddr::V4(Ipv4Addr::LOCALHOST)); @@ -109,10 +109,10 @@ pub fn get_default_local_ips() -> HashSet { pub fn get_interface_local_ips(iface: &Interface) -> HashSet { let mut ips: HashSet = HashSet::new(); for ip in iface.ipv4.clone() { - ips.insert(IpAddr::V4(ip.addr)); + ips.insert(IpAddr::V4(ip.addr())); } for ip in iface.ipv6.clone() { - ips.insert(IpAddr::V6(ip.addr)); + ips.insert(IpAddr::V6(ip.addr())); } // localhost IP addresses ips.insert(IpAddr::V4(Ipv4Addr::LOCALHOST)); @@ -124,10 +124,10 @@ pub fn get_local_ip_map() -> HashMap { let mut ip_map: HashMap = HashMap::new(); for iface in netdev::interface::get_interfaces() { for ip in iface.ipv4.clone() { - ip_map.insert(IpAddr::V4(ip.addr), iface.name.clone()); + ip_map.insert(IpAddr::V4(ip.addr()), iface.name.clone()); } for ip in iface.ipv6.clone() { - ip_map.insert(IpAddr::V6(ip.addr), iface.name.clone()); + ip_map.insert(IpAddr::V6(ip.addr()), iface.name.clone()); } } ip_map diff --git a/src/main.rs b/src/main.rs index 4e8e1fe..d21f5dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ pub mod scan; pub mod sys; pub mod trace; pub mod util; +pub mod tls; // CLI pub mod app; pub mod handler; diff --git a/src/output.rs b/src/output.rs index 99ad8e6..d21d2be 100644 --- a/src/output.rs +++ b/src/output.rs @@ -29,7 +29,7 @@ pub fn get_progress_style() -> ProgressStyle { ProgressStyle::default_bar() .template( "{spinner:.green} {msg} [{elapsed_precise}] [{bar:40.cyan/blue}] {pos}/{len} ({eta})", - ) + ).unwrap() .tick_strings(&["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏", "✓"]) .progress_chars("#>-") } diff --git a/src/scan/packet.rs b/src/scan/packet.rs index 7e30dcc..4a54a47 100644 --- a/src/scan/packet.rs +++ b/src/scan/packet.rs @@ -22,20 +22,20 @@ pub(crate) fn build_hostscan_packet( match target_host.ip_addr { IpAddr::V4(ipv4_addr) => { interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr); + build_setting.src_ip = IpAddr::V4(ipv4.addr()); }); build_setting.dst_ip = IpAddr::V4(ipv4_addr); } IpAddr::V6(ipv6_addr) => { if is_global_ipv6(&ipv6_addr) { interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr) { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + if is_global_ipv6(&ipv6.addr()) { + build_setting.src_ip = IpAddr::V6(ipv6.addr()); } }); } else { interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + build_setting.src_ip = IpAddr::V6(ipv6.addr()); }); } build_setting.dst_ip = IpAddr::V6(ipv6_addr); @@ -76,20 +76,20 @@ pub(crate) fn build_hostscan_ip_next_packet( match target_host.ip_addr { IpAddr::V4(ipv4_addr) => { interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr); + build_setting.src_ip = IpAddr::V4(ipv4.addr()); }); build_setting.dst_ip = IpAddr::V4(ipv4_addr); } IpAddr::V6(ipv6_addr) => { if is_global_ipv6(&ipv6_addr) { interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr) { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + if is_global_ipv6(&ipv6.addr()) { + build_setting.src_ip = IpAddr::V6(ipv6.addr()); } }); } else { interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + build_setting.src_ip = IpAddr::V6(ipv6.addr()); }); } build_setting.dst_ip = IpAddr::V6(ipv6_addr); @@ -131,20 +131,20 @@ pub(crate) fn build_portscan_packet( match target_ip_addr { IpAddr::V4(ipv4_addr) => { interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr); + build_setting.src_ip = IpAddr::V4(ipv4.addr()); }); build_setting.dst_ip = IpAddr::V4(ipv4_addr); } IpAddr::V6(ipv6_addr) => { if is_global_ipv6(&ipv6_addr) { interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr) { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + if is_global_ipv6(&ipv6.addr()) { + build_setting.src_ip = IpAddr::V6(ipv6.addr()); } }); } else { interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + build_setting.src_ip = IpAddr::V6(ipv6.addr()); }); } build_setting.dst_ip = IpAddr::V6(ipv6_addr); @@ -174,20 +174,20 @@ pub(crate) fn build_portscan_ip_next_packet( match target_ip_addr { IpAddr::V4(ipv4_addr) => { interface.ipv4.iter().for_each(|ipv4| { - build_setting.src_ip = IpAddr::V4(ipv4.addr); + build_setting.src_ip = IpAddr::V4(ipv4.addr()); }); build_setting.dst_ip = IpAddr::V4(ipv4_addr); } IpAddr::V6(ipv6_addr) => { if is_global_ipv6(&ipv6_addr) { interface.ipv6.iter().for_each(|ipv6| { - if is_global_ipv6(&ipv6.addr) { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + if is_global_ipv6(&ipv6.addr()) { + build_setting.src_ip = IpAddr::V6(ipv6.addr()); } }); } else { interface.ipv6.iter().for_each(|ipv6| { - build_setting.src_ip = IpAddr::V6(ipv6.addr); + build_setting.src_ip = IpAddr::V6(ipv6.addr()); }); } build_setting.dst_ip = IpAddr::V6(ipv6_addr); diff --git a/src/scan/service.rs b/src/scan/service.rs index 5afe641..1fc2cdb 100644 --- a/src/scan/service.rs +++ b/src/scan/service.rs @@ -2,18 +2,15 @@ use super::payload::{PayloadInfo, PayloadType}; use super::result::{ServiceProbeError, ServiceProbeResult}; use super::setting::ServiceProbeSetting; use crate::db::tcp_service::PORT_SERVICE_MAP; -use async_io::{Async, Timer}; use futures::stream::{self, StreamExt}; -use futures_lite::future::FutureExt; -use futures_lite::{AsyncReadExt, AsyncWriteExt}; -use nex::socket::tls::rustls; -use nex::socket::tls::TlsClient; use std::collections::HashMap; -use std::io::{Read, Write}; -use std::net::{IpAddr, SocketAddr, TcpStream}; +use std::net::{IpAddr, SocketAddr}; use std::sync::mpsc::Sender; use std::sync::{Arc, Mutex}; use std::time::Duration; +use tokio::io::{AsyncReadExt, AsyncWriteExt}; +use tokio::net::TcpStream; +use tokio_rustls::TlsConnector; /// Parse HTTP header and return server name /// @@ -41,43 +38,42 @@ fn parse_http_header(res_bytes: &Vec) -> Option { /// This ignore io::Error on read_to_end because it is expected when reading response. /// If no response is received, and io::Error is occurred, return Err. async fn read_response_timeout( - tcp_stream: &mut Async, - timeout: Duration, + tcp_stream: &mut TcpStream, + timeout_duration: Duration, ) -> std::io::Result> { - let mut io_error: std::io::Error = - std::io::Error::new(std::io::ErrorKind::Other, "No response"); - let mut response: Vec = Vec::new(); - match tcp_stream - .read_to_end(&mut response) - .or(async { - Timer::after(timeout).await; - Err(std::io::ErrorKind::TimedOut.into()) - }) - .await - { - Ok(_) => {} - Err(e) => { - io_error = e; + let mut response = Vec::new(); + let mut buf = [0u8; 1024]; + + loop { + match tokio::time::timeout(timeout_duration, tcp_stream.read(&mut buf)).await { + Ok(Ok(0)) => break, + Ok(Ok(n)) => { + response.extend_from_slice(&buf[..n]); + break; + } + Ok(Err(e)) => return Err(e), + Err(_) => break, } } - if response.len() == 0 { - return Err(io_error); + + if response.is_empty() { + Err(std::io::Error::new( + std::io::ErrorKind::TimedOut, + "No response", + )) } else { Ok(response) } } -pub async fn async_tcp_connect_timeout( - addr: &SocketAddr, - timeout: Duration, -) -> std::io::Result> { - let stream = Async::::connect(*addr) - .or(async { - Timer::after(timeout).await; - Err(std::io::ErrorKind::TimedOut.into()) - }) - .await?; - Ok(stream) +fn set_read_timeout(tcp_stream: TcpStream, timeout: Duration) -> std::io::Result { + // Convert to std::net::TcpStream + let std_tcp_stream = tcp_stream.into_std()?; + // Set read timeout + std_tcp_stream.set_read_timeout(Some(timeout))?; + // Convert back to tokio TcpStream + let tokio_tcp_stream = TcpStream::from_std(std_tcp_stream)?; + Ok(tokio_tcp_stream) } async fn probe_port( @@ -92,21 +88,28 @@ async fn probe_port( None => String::new(), }; let socket_addr: SocketAddr = SocketAddr::new(ip_addr, port); - let mut tcp_stream = match async_tcp_connect_timeout(&socket_addr, timeout).await { - Ok(tcp_stream) => tcp_stream, - Err(e) => { + let tcp_stream = match tokio::time::timeout(timeout, TcpStream::connect(socket_addr)).await { + Ok(connect_result) => match connect_result { + Ok(tcp_stream) => tcp_stream, + Err(e) => { + return ServiceProbeResult::with_error( + port, + service_name, + ServiceProbeError::ConnectionError(e.to_string()), + ) + } + }, + Err(elapsed) => { return ServiceProbeResult::with_error( port, service_name, - ServiceProbeError::ConnectionError(e.to_string()), + ServiceProbeError::ConnectionError(elapsed.to_string()), ) } }; - match tcp_stream - .write_with(|inner| inner.set_read_timeout(Some(timeout))) - .await - { - Ok(_) => {} + // Set read timeout + let mut tcp_stream = match set_read_timeout(tcp_stream, timeout) { + Ok(tcp_stream) => tcp_stream, Err(e) => { return ServiceProbeResult::with_error( port, @@ -114,7 +117,7 @@ async fn probe_port( ServiceProbeError::ConnectionError(e.to_string()), ) } - } + }; if let Some(payload) = payload_info { match payload.payload_type { PayloadType::Http => match tcp_stream.write_all(&payload.payload).await { @@ -154,12 +157,13 @@ async fn probe_port( } }, PayloadType::Https => { - let native_certs = nex::socket::tls::certs::get_native_certs().unwrap(); + let native_certs = crate::tls::cert::get_native_certs().unwrap(); let config = rustls::ClientConfig::builder() .with_root_certificates(native_certs) .with_no_client_auth(); - let tcp_stream_inner = match tcp_stream.into_inner() { - Ok(tcp_stream_inner) => tcp_stream_inner, + let tls_connector = TlsConnector::from(Arc::new(config)); + let name = match rustls_pki_types::ServerName::try_from(hostname) { + Ok(name) => name, Err(e) => { return ServiceProbeResult::with_error( port, @@ -168,30 +172,31 @@ async fn probe_port( ) } }; - match tcp_stream_inner.set_nonblocking(false) { - Ok(_) => {} - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - } - let mut tls_client = - match TlsClient::new(hostname.to_string(), tcp_stream_inner, config) { - Ok(tls_client) => tls_client, - Err(e) => { + let mut tls_stream = + match tokio::time::timeout(timeout, tls_connector.connect(name, tcp_stream)) + .await + { + Ok(connect_result) => match connect_result { + Ok(tls_stream) => tls_stream, + Err(e) => { + return ServiceProbeResult::with_error( + port, + service_name, + ServiceProbeError::ConnectionError(e.to_string()), + ) + } + }, + Err(elapsed) => { return ServiceProbeResult::with_error( port, service_name, - ServiceProbeError::ConnectionError(e.to_string()), + ServiceProbeError::ConnectionError(elapsed.to_string()), ) } }; - match tls_client.write_all(&payload.payload) { + match tls_stream.write_all(&payload.payload).await { Ok(_) => { - match tls_client.flush() { + match tls_stream.flush().await { Ok(_) => {} Err(e) => { return ServiceProbeResult::with_error( @@ -202,7 +207,7 @@ async fn probe_port( } } let mut buf: Vec = Vec::new(); - match tls_client.read_to_end(&mut buf) { + match tls_stream.read_to_end(&mut buf).await { Ok(_) => { let mut result = ServiceProbeResult::new(port, service_name, buf.clone()); @@ -268,12 +273,13 @@ async fn probe_port( } }, PayloadType::CommonTls => { - let native_certs = nex::socket::tls::certs::get_native_certs().unwrap(); + let native_certs = crate::tls::cert::get_native_certs().unwrap(); let config = rustls::ClientConfig::builder() .with_root_certificates(native_certs) .with_no_client_auth(); - let tcp_stream_inner = match tcp_stream.into_inner() { - Ok(tcp_stream_inner) => tcp_stream_inner, + let tls_connector = TlsConnector::from(Arc::new(config)); + let name = match rustls_pki_types::ServerName::try_from(hostname) { + Ok(name) => name, Err(e) => { return ServiceProbeResult::with_error( port, @@ -282,30 +288,31 @@ async fn probe_port( ) } }; - match tcp_stream_inner.set_nonblocking(false) { - Ok(_) => {} - Err(e) => { - return ServiceProbeResult::with_error( - port, - service_name, - ServiceProbeError::ConnectionError(e.to_string()), - ) - } - } - let mut tls_client = - match TlsClient::new(hostname.to_string(), tcp_stream_inner, config) { - Ok(tls_client) => tls_client, - Err(e) => { + let mut tls_stream = + match tokio::time::timeout(timeout, tls_connector.connect(name, tcp_stream)) + .await + { + Ok(connect_result) => match connect_result { + Ok(tls_stream) => tls_stream, + Err(e) => { + return ServiceProbeResult::with_error( + port, + service_name, + ServiceProbeError::ConnectionError(e.to_string()), + ) + } + }, + Err(elapsed) => { return ServiceProbeResult::with_error( port, service_name, - ServiceProbeError::ConnectionError(e.to_string()), + ServiceProbeError::ConnectionError(elapsed.to_string()), ) } }; - match tls_client.write_all(&payload.payload) { + match tls_stream.write_all(&payload.payload).await { Ok(_) => { - match tls_client.flush() { + match tls_stream.flush().await { Ok(_) => {} Err(e) => { return ServiceProbeResult::with_error( @@ -316,7 +323,7 @@ async fn probe_port( } } let mut buf: Vec = Vec::new(); - match tls_client.read_to_end(&mut buf) { + match tls_stream.read_to_end(&mut buf).await { Ok(_) => { let mut result = ServiceProbeResult::new(port, service_name, buf.clone()); diff --git a/src/tls/cert.rs b/src/tls/cert.rs new file mode 100644 index 0000000..1a50fd9 --- /dev/null +++ b/src/tls/cert.rs @@ -0,0 +1,90 @@ +use anyhow::Result; +use rustls::client::danger::ServerCertVerifier; +use rustls::pki_types::{CertificateDer, ServerName, UnixTime}; +use std::sync::Arc; +use std::{fs, io, path::Path}; + +/// Get the native certificates from the system. return rustls::RootCertStore +pub(crate) fn get_native_certs() -> io::Result { + let mut root_store = rustls::RootCertStore::empty(); + match rustls_native_certs::load_native_certs() { + Ok(certs) => { + for cert in certs { + match root_store.add(cert) { + Ok(_) => {} + Err(_) => {} + } + } + Ok(root_store) + } + Err(e) => return Err(e), + } +} + +/// Load certificate chain from a file +#[allow(dead_code)] +pub(crate) fn load_certs(cert_path: &Path) -> Result>> { + let cert_chain = fs::read(cert_path)?; + let cert_chain = if cert_path.extension().map_or(false, |x| x == "der") { + vec![CertificateDer::from(cert_chain)] + } else { + rustls_pemfile::certs(&mut &*cert_chain).collect::, _>>()? + }; + Ok(cert_chain) +} + +/// Dummy certificate verifier that treats any certificate as valid. +/// NOTE, such verification is vulnerable to MITM attacks, but convenient for testing. +#[derive(Debug)] +pub struct SkipServerVerification(Arc); + +impl SkipServerVerification { + pub fn new() -> Arc { + Arc::new(Self(Arc::new(rustls::crypto::ring::default_provider()))) + } +} + +impl ServerCertVerifier for SkipServerVerification { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &ServerName<'_>, + _ocsp: &[u8], + _now: UnixTime, + ) -> Result { + Ok(rustls::client::danger::ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls12_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &rustls::DigitallySignedStruct, + ) -> Result { + rustls::crypto::verify_tls13_signature( + message, + cert, + dss, + &self.0.signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec { + self.0.signature_verification_algorithms.supported_schemes() + } +} diff --git a/src/tls/key.rs b/src/tls/key.rs new file mode 100644 index 0000000..32f65f1 --- /dev/null +++ b/src/tls/key.rs @@ -0,0 +1,16 @@ +use anyhow::anyhow; +use anyhow::Result; +use rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer}; +use std::{fs, path::Path}; + +/// Load private key from a file +#[allow(dead_code)] +pub(crate) fn load_key(key_path: &Path) -> Result> { + let key = fs::read(key_path)?; + let key = if key_path.extension().map_or(false, |x| x == "der") { + PrivateKeyDer::Pkcs8(PrivatePkcs8KeyDer::from(key)) + } else { + rustls_pemfile::private_key(&mut &*key)?.ok_or_else(|| anyhow!("no keys found"))? + }; + Ok(key) +} diff --git a/src/tls/mod.rs b/src/tls/mod.rs new file mode 100644 index 0000000..cb3a5d7 --- /dev/null +++ b/src/tls/mod.rs @@ -0,0 +1,2 @@ +pub mod cert; +pub mod key; From 4c74531235eed28e2a2fc42750a9580efa4601be Mon Sep 17 00:00:00 2001 From: shellrow Date: Tue, 29 Apr 2025 10:47:56 +0900 Subject: [PATCH 2/4] Update cargo-dist --- .github/workflows/release.yml | 117 +++++++++++++--------------------- Cargo.toml | 21 ------ dist-workspace.toml | 21 ++++++ 3 files changed, 67 insertions(+), 92 deletions(-) create mode 100644 dist-workspace.toml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4567b55..99e9fb1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,10 +1,13 @@ +# This file was autogenerated by dist: https://github.com/astral-sh/cargo-dist +# # Copyright 2022-2024, axodotdev +# Copyright 2025 Astral Software Inc. # SPDX-License-Identifier: MIT or Apache-2.0 # # CI that: # # * checks for a Git Tag that looks like a release -# * builds artifacts with cargo-dist (archives, installers, hashes) +# * builds artifacts with dist (archives, installers, hashes) # * uploads those artifacts to temporary workflow zip # * on success, uploads the artifacts to a GitHub Release # @@ -22,10 +25,10 @@ permissions: # must be a Cargo-style SemVer Version (must have at least major.minor.patch). # # If PACKAGE_NAME is specified, then the announcement will be for that -# package (erroring out if it doesn't have the given version or isn't cargo-dist-able). +# package (erroring out if it doesn't have the given version or isn't dist-able). # # If PACKAGE_NAME isn't specified, then the announcement will be for all -# (cargo-dist-able) packages in the workspace with that version (this mode is +# (dist-able) packages in the workspace with that version (this mode is # intended for workspaces with only one dist-able package, or with all dist-able # packages versioned/released in lockstep). # @@ -43,9 +46,9 @@ on: - '**[0-9]+.[0-9]+.[0-9]+*' jobs: - # Run 'cargo dist plan' (or host) to determine what tasks we need to do + # Run 'dist plan' (or host) to determine what tasks we need to do plan: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" outputs: val: ${{ steps.plan.outputs.manifest }} tag: ${{ !github.event.pull_request && github.ref_name || '' }} @@ -56,17 +59,18 @@ jobs: steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - - name: Install cargo-dist + - name: Install dist # we specify bash to get pipefail; it guards against the `curl` command # failing. otherwise `sh` won't catch that `curl` returned non-0 shell: bash - run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.18.0/cargo-dist-installer.sh | sh" - - name: Cache cargo-dist + run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/astral-sh/cargo-dist/releases/download/v0.28.4/cargo-dist-installer.sh | sh" + - name: Cache dist uses: actions/upload-artifact@v4 with: name: cargo-dist-cache - path: ~/.cargo/bin/cargo-dist + path: ~/.cargo/bin/dist # sure would be cool if github gave us proper conditionals... # so here's a doubly-nested ternary-via-truthiness to try to provide the best possible # functionality based on whether this is a pull_request, and whether it's from a fork. @@ -74,8 +78,8 @@ jobs: # but also really annoying to build CI around when it needs secrets to work right.) - id: plan run: | - cargo dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json - echo "cargo dist ran successfully" + dist ${{ (!github.event.pull_request && format('host --steps=create --tag={0}', github.ref_name)) || 'plan' }} --output-format=json > plan-dist-manifest.json + echo "dist ran successfully" cat plan-dist-manifest.json echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT" - name: "Upload dist-manifest.json" @@ -93,18 +97,19 @@ jobs: if: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix.include != null && (needs.plan.outputs.publishing == 'true' || fromJson(needs.plan.outputs.val).ci.github.pr_run_mode == 'upload') }} strategy: fail-fast: false - # Target platforms/runners are computed by cargo-dist in create-release. + # Target platforms/runners are computed by dist in create-release. # Each member of the matrix has the following arguments: # # - runner: the github runner - # - dist-args: cli flags to pass to cargo dist - # - install-dist: expression to run to install cargo-dist on the runner + # - dist-args: cli flags to pass to dist + # - install-dist: expression to run to install dist on the runner # # Typically there will be: # - 1 "global" task that builds universal installers # - N "local" tasks that build each platform's binaries and platform-specific installers matrix: ${{ fromJson(needs.plan.outputs.val).ci.github.artifacts_matrix }} runs-on: ${{ matrix.runner }} + container: ${{ matrix.container && matrix.container.image || null }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/${{ join(matrix.targets, '-') }}-dist-manifest.json @@ -114,9 +119,17 @@ jobs: git config --global core.longpaths true - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - - name: Install cargo-dist - run: ${{ matrix.install_dist }} + - name: Install Rust non-interactively if not already installed + if: ${{ matrix.container }} + run: | + if ! command -v cargo > /dev/null 2>&1; then + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + echo "$HOME/.cargo/bin" >> $GITHUB_PATH + fi + - name: Install dist + run: ${{ matrix.install_dist.run }} # Get the dist-manifest - name: Fetch local artifacts uses: actions/download-artifact@v4 @@ -130,8 +143,8 @@ jobs: - name: Build artifacts run: | # Actually do builds and make zips and whatnot - cargo dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json - echo "cargo dist ran successfully" + dist build ${{ needs.plan.outputs.tag-flag }} --print=linkage --output-format=json ${{ matrix.dist_args }} > dist-manifest.json + echo "dist ran successfully" - id: cargo-dist name: Post-build # We force bash here just because github makes it really hard to get values up @@ -141,7 +154,7 @@ jobs: run: | # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" - jq --raw-output ".upload_files[]" dist-manifest.json >> "$GITHUB_OUTPUT" + dist print-upload-files-from-manifest --manifest dist-manifest.json >> "$GITHUB_OUTPUT" echo "EOF" >> "$GITHUB_OUTPUT" cp dist-manifest.json "$BUILD_MANIFEST_NAME" @@ -158,20 +171,21 @@ jobs: needs: - plan - build-local-artifacts - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} BUILD_MANIFEST_NAME: target/distrib/global-dist-manifest.json steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - - name: Install cached cargo-dist + - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - - run: chmod +x ~/.cargo/bin/cargo-dist + - run: chmod +x ~/.cargo/bin/dist # Get all the local artifacts for the global tasks to use (for e.g. checksums) - name: Fetch local artifacts uses: actions/download-artifact@v4 @@ -182,8 +196,8 @@ jobs: - id: cargo-dist shell: bash run: | - cargo dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json - echo "cargo dist ran successfully" + dist build ${{ needs.plan.outputs.tag-flag }} --output-format=json "--artifacts=global" > dist-manifest.json + echo "dist ran successfully" # Parse out what we just built and upload it to scratch storage echo "paths<> "$GITHUB_OUTPUT" @@ -208,19 +222,20 @@ jobs: if: ${{ always() && needs.plan.outputs.publishing == 'true' && (needs.build-global-artifacts.result == 'skipped' || needs.build-global-artifacts.result == 'success') && (needs.build-local-artifacts.result == 'skipped' || needs.build-local-artifacts.result == 'success') }} env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" outputs: val: ${{ steps.host.outputs.manifest }} steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive - - name: Install cached cargo-dist + - name: Install cached dist uses: actions/download-artifact@v4 with: name: cargo-dist-cache path: ~/.cargo/bin/ - - run: chmod +x ~/.cargo/bin/cargo-dist + - run: chmod +x ~/.cargo/bin/dist # Fetch artifacts from scratch-storage - name: Fetch artifacts uses: actions/download-artifact@v4 @@ -231,7 +246,7 @@ jobs: - id: host shell: bash run: | - cargo dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json + dist host ${{ needs.plan.outputs.tag-flag }} --steps=upload --steps=release --output-format=json > dist-manifest.json echo "artifacts uploaded and released successfully" cat dist-manifest.json echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT" @@ -264,59 +279,19 @@ jobs: gh release create "${{ needs.plan.outputs.tag }}" --target "$RELEASE_COMMIT" $PRERELEASE_FLAG --title "$ANNOUNCEMENT_TITLE" --notes-file "$RUNNER_TEMP/notes.txt" artifacts/* - publish-homebrew-formula: - needs: - - plan - - host - runs-on: "ubuntu-20.04" - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - PLAN: ${{ needs.plan.outputs.val }} - GITHUB_USER: "axo bot" - GITHUB_EMAIL: "admin+bot@axo.dev" - if: ${{ !fromJson(needs.plan.outputs.val).announcement_is_prerelease || fromJson(needs.plan.outputs.val).publish_prereleases }} - steps: - - uses: actions/checkout@v4 - with: - repository: "shellrow/homebrew-tap-nrev" - token: ${{ secrets.HOMEBREW_TAP_TOKEN }} - # So we have access to the formula - - name: Fetch homebrew formulae - uses: actions/download-artifact@v4 - with: - pattern: artifacts-* - path: Formula/ - merge-multiple: true - # This is extra complex because you can make your Formula name not match your app name - # so we need to find releases with a *.rb file, and publish with that filename. - - name: Commit formula files - run: | - git config --global user.name "${GITHUB_USER}" - git config --global user.email "${GITHUB_EMAIL}" - - for release in $(echo "$PLAN" | jq --compact-output '.releases[] | select([.artifacts[] | endswith(".rb")] | any)'); do - filename=$(echo "$release" | jq '.artifacts[] | select(endswith(".rb"))' --raw-output) - name=$(echo "$filename" | sed "s/\.rb$//") - version=$(echo "$release" | jq .app_version --raw-output) - - git add "Formula/${filename}" - git commit -m "${name} ${version}" - done - git push - announce: needs: - plan - host - - publish-homebrew-formula # use "always() && ..." to allow us to wait for all publish jobs while # still allowing individual publish jobs to skip themselves (for prereleases). # "host" however must run to completion, no skipping allowed! - if: ${{ always() && needs.host.result == 'success' && (needs.publish-homebrew-formula.result == 'skipped' || needs.publish-homebrew-formula.result == 'success') }} - runs-on: "ubuntu-20.04" + if: ${{ always() && needs.host.result == 'success' }} + runs-on: "ubuntu-22.04" env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: + persist-credentials: false submodules: recursive diff --git a/Cargo.toml b/Cargo.toml index eb912fc..809ca10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,24 +45,3 @@ winreg = "0.55" [profile.dist] inherits = "release" lto = "thin" - -# Config for 'cargo dist' -[workspace.metadata.dist] -# The preferred cargo-dist version to use in CI (Cargo.toml SemVer syntax) -cargo-dist-version = "0.18.0" -# CI backends to support -ci = "github" -# The installers to generate for each app -#installers = ["shell", "powershell", "homebrew"] -installers = ["shell", "homebrew"] -# A GitHub repo to push Homebrew formulas to -tap = "shellrow/homebrew-tap-nrev" -# Target platforms to build apps for (Rust target-triple syntax) -#targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] -targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"] -# Publish jobs to run in CI -publish-jobs = ["homebrew"] -# Publish jobs to run in CI -pr-run-mode = "plan" -# Whether to install an updater program -install-updater = false diff --git a/dist-workspace.toml b/dist-workspace.toml new file mode 100644 index 0000000..7164eed --- /dev/null +++ b/dist-workspace.toml @@ -0,0 +1,21 @@ +[workspace] +members = ["cargo:."] + +# Config for 'dist' +[dist] +# The preferred dist version to use in CI (Cargo.toml SemVer syntax) +cargo-dist-version = "0.28.4" +# CI backends to support +ci = "github" +# The installers to generate for each app +installers = ["shell"] +#installers = ["shell", "powershell"] +# Target platforms to build apps for (Rust target-triple syntax) +targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu"] +#targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] +# Which actions to run on pull requests +pr-run-mode = "plan" +# Whether to install an updater program +install-updater = false +# Path that installers should place binaries in +install-path = "CARGO_HOME" From e834edb7654a08e35f061f6fedf8d9fcf7ad3c9e Mon Sep 17 00:00:00 2001 From: shellrow Date: Tue, 29 Apr 2025 10:48:55 +0900 Subject: [PATCH 3/4] Update README.md --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 9e59bec..a7b918b 100644 --- a/README.md +++ b/README.md @@ -32,12 +32,6 @@ curl --proto '=https' --tlsv1.2 -LsSf https://github.com/shellrow/nrev/releases/ irm https://github.com/shellrow/nrev/releases/latest/download/nrev-installer.ps1 | iex ``` -### Install prebuilt binaries via Homebrew - -```sh -brew install shellrow/tap-nrev/nrev -``` - ### From Releases You can download archives of precompiled binaries from the [releases](https://github.com/shellrow/nrev/releases) . From 0665e12b04856934cada21ea1cbffbe37729b311 Mon Sep 17 00:00:00 2001 From: shellrow Date: Tue, 29 Apr 2025 10:52:40 +0900 Subject: [PATCH 4/4] Bump version to 0.4.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 809ca10..94d698c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nrev" -version = "0.3.0" +version = "0.4.0" edition = "2021" authors = ["shellrow "] description = "Simple and Fast Network Revealer/Mapper."