commit 8e4d062a47b7003064cb9ff22232e4e9b804571b from: Murilo Ijanc date: Thu Apr 9 17:45:48 2026 UTC organize imports and move libtls bindings to mod ffi commit - 9919a59522a2e5d6fff449a0db34a65ea3739fa0 commit + 8e4d062a47b7003064cb9ff22232e4e9b804571b blob - df99c69198f5813df5fc3eaa007a2af0e60a7bbd blob + ffaf97ed9dd4c2c3e5388126b167e66e9e946c6a --- .rustfmt.toml +++ .rustfmt.toml @@ -1 +1,2 @@ max_width = 80 +imports_granularity = "Crate" blob - b1c6e1b68dee3acc61a1caa530b96c67464d8796 blob + ef5baa7e0c6d3d1cbb5673ba4f4a8e308cb42714 --- asp.rs +++ asp.rs @@ -15,20 +15,19 @@ // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // -use std::env; -use std::ffi::{CStr, CString}; -use std::fs; -use std::io::{self, Read, Write}; -use std::net::{SocketAddr, TcpStream, ToSocketAddrs}; -use std::os::raw::{c_char, c_int}; -use std::os::unix::io::AsRawFd; -use std::process::Command; -use std::thread; -use std::time::Duration; +use std::{ + env, + ffi::{CStr, CString}, + fs, + io::{self, Read, Write}, + net::{SocketAddr, TcpStream, ToSocketAddrs}, + os::unix::io::AsRawFd, + process::Command, + thread, + time::Duration, +}; const VERSION: &str = env!("ASP_VERSION"); -const TLS_WANT_POLLIN: isize = -2; -const TLS_WANT_POLLOUT: isize = -3; const HEAD: &str = r#" @@ -62,38 +61,45 @@ li { list-style: none; margin-bottom: 2px; padding: 5p
"#; -// libtls(3) FFI +use ffi::*; -enum Tls {} -enum TlsCfg {} +mod ffi { + use std::os::raw::{c_char, c_int}; -unsafe extern "C" { - fn tls_init() -> c_int; - fn tls_config_new() -> *mut TlsCfg; - fn tls_config_free(cfg: *mut TlsCfg); - fn tls_client() -> *mut Tls; - fn tls_configure( - ctx: *mut Tls, - cfg: *mut TlsCfg, - ) -> c_int; - fn tls_connect_socket( - ctx: *mut Tls, - fd: c_int, - name: *const c_char, - ) -> c_int; - fn tls_read( - ctx: *mut Tls, - buf: *mut u8, - len: usize, - ) -> isize; - fn tls_write( - ctx: *mut Tls, - buf: *const u8, - len: usize, - ) -> isize; - fn tls_close(ctx: *mut Tls) -> c_int; - fn tls_free(ctx: *mut Tls); - fn tls_error(ctx: *mut Tls) -> *const c_char; + pub enum Tls {} + pub enum TlsCfg {} + + pub const TLS_WANT_POLLIN: isize = -2; + pub const TLS_WANT_POLLOUT: isize = -3; + + unsafe extern "C" { + pub fn tls_init() -> c_int; + pub fn tls_config_new() -> *mut TlsCfg; + pub fn tls_config_free(cfg: *mut TlsCfg); + pub fn tls_client() -> *mut Tls; + pub fn tls_configure( + ctx: *mut Tls, + cfg: *mut TlsCfg, + ) -> c_int; + pub fn tls_connect_socket( + ctx: *mut Tls, + fd: c_int, + name: *const c_char, + ) -> c_int; + pub fn tls_read( + ctx: *mut Tls, + buf: *mut u8, + len: usize, + ) -> isize; + pub fn tls_write( + ctx: *mut Tls, + buf: *const u8, + len: usize, + ) -> isize; + pub fn tls_close(ctx: *mut Tls) -> c_int; + pub fn tls_free(ctx: *mut Tls); + pub fn tls_error(ctx: *mut Tls) -> *const c_char; + } } struct Check { @@ -162,8 +168,7 @@ fn parse_checks(path: &str) -> Vec { !t.is_empty() && !t.starts_with('#') }) .filter_map(|line| { - let c: Vec<&str> = - line.splitn(4, ',').map(|s| s.trim()).collect(); + let c: Vec<&str> = line.splitn(4, ',').map(|s| s.trim()).collect(); if c.len() < 4 { return None; } @@ -187,23 +192,15 @@ fn ipv(kind: &str) -> &str { } } -fn resolve( - host: &str, - port: u16, - v: &str, -) -> io::Result { - let addrs: Vec = - (host, port).to_socket_addrs()?.collect(); +fn resolve(host: &str, port: u16, v: &str) -> io::Result { + let addrs: Vec = (host, port).to_socket_addrs()?.collect(); let addr = match v { "4" => addrs.iter().find(|a| a.is_ipv4()), "6" => addrs.iter().find(|a| a.is_ipv6()), _ => addrs.first(), }; addr.copied().ok_or_else(|| { - io::Error::new( - io::ErrorKind::AddrNotAvailable, - "no address", - ) + io::Error::new(io::ErrorKind::AddrNotAvailable, "no address") }) } @@ -216,22 +213,19 @@ fn parse_url(url: &str) -> Option<(&str, &str, u16, &s let (host, port) = if hp.starts_with('[') { let end = hp.find(']')?; let h = &hp[1..end]; - let p = - if hp.len() > end + 1 && hp.as_bytes()[end + 1] == b':' - { - hp[end + 2..].parse().ok()? - } else if scheme == "https" { - 443 - } else { - 80 - }; + let p = if hp.len() > end + 1 && hp.as_bytes()[end + 1] == b':' { + hp[end + 2..].parse().ok()? + } else if scheme == "https" { + 443 + } else { + 80 + }; (h, p) } else { match hp.rsplit_once(':') { Some((h, p)) => (h, p.parse().ok()?), None => { - let p = - if scheme == "https" { 443 } else { 80 }; + let p = if scheme == "https" { 443 } else { 80 }; (hp, p) } } @@ -251,9 +245,7 @@ unsafe fn tls_err(ctx: *mut Tls) -> String { if p.is_null() { "TLS error".into() } else { - unsafe { - CStr::from_ptr(p).to_string_lossy().into_owned() - } + unsafe { CStr::from_ptr(p).to_string_lossy().into_owned() } } } @@ -261,11 +253,7 @@ fn now() -> String { Command::new("date") .arg("+%FT%T%z") .output() - .map(|o| { - String::from_utf8_lossy(&o.stdout) - .trim() - .to_string() - }) + .map(|o| String::from_utf8_lossy(&o.stdout).trim().to_string()) .unwrap_or_default() } @@ -284,8 +272,7 @@ fn parse_headers(raw: &str) -> (i32, Option) { if line.len() > 9 { let lower = line[..9].to_ascii_lowercase(); if lower == "location:" { - location = - Some(line[9..].trim().to_string()); + location = Some(line[9..].trim().to_string()); } } } @@ -293,14 +280,11 @@ fn parse_headers(raw: &str) -> (i32, Option) { } fn resolve_redirect(base: &str, loc: &str) -> String { - if loc.starts_with("http://") - || loc.starts_with("https://") - { + if loc.starts_with("http://") || loc.starts_with("https://") { return loc.to_string(); } if loc.starts_with("//") { - let scheme = - base.split("://").next().unwrap_or("https"); + let scheme = base.split("://").next().unwrap_or("https"); return format!("{}:{}", scheme, loc); } if let Some((scheme, host, port, _)) = parse_url(base) { @@ -321,10 +305,8 @@ fn http_get( v: &str, timeout: Duration, ) -> Result<(i32, Option), String> { - let (scheme, host, port, path) = - parse_url(url).ok_or("invalid URL")?; - let addr = - resolve(host, port, v).map_err(|e| e.to_string())?; + let (scheme, host, port, path) = parse_url(url).ok_or("invalid URL")?; + let addr = resolve(host, port, v).map_err(|e| e.to_string())?; let stream = TcpStream::connect_timeout(&addr, timeout) .map_err(|e| e.to_string())?; let _ = stream.set_read_timeout(Some(timeout)); @@ -353,8 +335,7 @@ fn check_http( ) -> (bool, String) { let mut url = url.to_string(); for _ in 0..5 { - let (code, loc) = match http_get(&url, v, timeout) - { + let (code, loc) = match http_get(&url, v, timeout) { Ok(r) => r, Err(e) => return (false, e), }; @@ -363,10 +344,7 @@ fn check_http( url = resolve_redirect(&url, &l); } _ => { - let info = format!( - "status {}, expected {}", - code, expected - ); + let info = format!("status {}, expected {}", code, expected); return (code == expected, info); } } @@ -374,28 +352,18 @@ fn check_http( (false, "too many redirects".into()) } -fn http_req( - stream: &TcpStream, - req: &str, -) -> Result { +fn http_req(stream: &TcpStream, req: &str) -> Result { let mut s = stream; - s.write_all(req.as_bytes()) - .map_err(|e| e.to_string())?; + s.write_all(req.as_bytes()).map_err(|e| e.to_string())?; let mut buf = [0u8; 4096]; let mut total = 0; loop { - let n = s - .read(&mut buf[total..]) - .map_err(|e| e.to_string())?; + let n = s.read(&mut buf[total..]).map_err(|e| e.to_string())?; if n == 0 { break; } total += n; - if total >= 4 - && buf[..total] - .windows(4) - .any(|w| w == b"\r\n\r\n") - { + if total >= 4 && buf[..total].windows(4).any(|w| w == b"\r\n\r\n") { break; } if total >= buf.len() { @@ -431,14 +399,8 @@ unsafe fn https_req( } tls_config_free(cfg); - let name = CString::new(host) - .map_err(|_| "invalid host")?; - if tls_connect_socket( - ctx, - stream.as_raw_fd(), - name.as_ptr(), - ) != 0 - { + let name = CString::new(host).map_err(|_| "invalid host")?; + if tls_connect_socket(ctx, stream.as_raw_fd(), name.as_ptr()) != 0 { let e = tls_err(ctx); tls_close(ctx); tls_free(ctx); @@ -448,11 +410,7 @@ unsafe fn https_req( let bytes = req.as_bytes(); let mut off = 0; while off < bytes.len() { - let n = tls_write( - ctx, - bytes[off..].as_ptr(), - bytes.len() - off, - ); + let n = tls_write(ctx, bytes[off..].as_ptr(), bytes.len() - off); if n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT { continue; } @@ -468,11 +426,7 @@ unsafe fn https_req( let mut buf = [0u8; 4096]; let mut total = 0; loop { - let n = tls_read( - ctx, - buf[total..].as_mut_ptr(), - buf.len() - total, - ); + let n = tls_read(ctx, buf[total..].as_mut_ptr(), buf.len() - total); if n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT { continue; } @@ -480,11 +434,7 @@ unsafe fn https_req( break; } total += n as usize; - if total >= 4 - && buf[..total] - .windows(4) - .any(|w| w == b"\r\n\r\n") - { + if total >= 4 && buf[..total].windows(4).any(|w| w == b"\r\n\r\n") { break; } if total >= buf.len() { @@ -498,8 +448,7 @@ unsafe fn https_req( if total == 0 { return Err("empty response".into()); } - Ok(String::from_utf8_lossy(&buf[..total]) - .into_owned()) + Ok(String::from_utf8_lossy(&buf[..total]).into_owned()) } } @@ -556,10 +505,7 @@ fn check_port( } } -fn run_checks( - checks: &[Check], - timeout: u64, -) -> Vec { +fn run_checks(checks: &[Check], timeout: u64) -> Vec { let dur = Duration::from_secs(timeout); thread::scope(|s| { let handles: Vec<_> = checks @@ -575,29 +521,15 @@ fn run_checks( }; } let v = ipv(&c.kind); - let (ok, info) = - if c.kind.starts_with("http") { - check_http( - &c.host, c.expected, v, dur, - ) - } else if c.kind.starts_with("ping") { - check_ping( - &c.host, c.expected, v, - timeout, - ) - } else if c.kind.starts_with("port") { - check_port( - &c.host, c.expected, v, dur, - ) - } else { - ( - false, - format!( - "unknown: {}", - c.kind - ), - ) - }; + let (ok, info) = if c.kind.starts_with("http") { + check_http(&c.host, c.expected, v, dur) + } else if c.kind.starts_with("ping") { + check_ping(&c.host, c.expected, v, timeout) + } else if c.kind.starts_with("port") { + check_port(&c.host, c.expected, v, dur) + } else { + (false, format!("unknown: {}", c.kind)) + }; CheckResult { name: c.name.clone(), ok, @@ -607,18 +539,12 @@ fn run_checks( }) }) .collect(); - handles - .into_iter() - .map(|h| h.join().unwrap()) - .collect() + handles.into_iter().map(|h| h.join().unwrap()).collect() }) } fn render_html(results: &[CheckResult], incidents: &str) { - let outages = results - .iter() - .filter(|r| !r.group && !r.ok) - .count(); + let outages = results.iter().filter(|r| !r.group && !r.ok).count(); print!("{}", HEAD); println!("

Status

"); @@ -639,10 +565,7 @@ fn render_html(results: &[CheckResult], incidents: &st println!("

Services

\n
    "); for r in results { if r.group { - println!( - "
  • {}
  • ", - esc(&r.name) - ); + println!("
  • {}
  • ", esc(&r.name)); } else if r.ok { println!( "
  • {} \ @@ -671,11 +594,8 @@ fn render_html(results: &[CheckResult], incidents: &st } } + println!("

    Last check: {}

    ", now()); println!( - "

    Last check: {}

    ", - now() - ); - println!( "

    Powered by \ asp

    " ); @@ -683,14 +603,13 @@ fn render_html(results: &[CheckResult], incidents: &st } fn main() { - let (checks_path, incidents_path, timeout) = - match parse_args() { - Some(a) => a, - None => { - usage(); - std::process::exit(1); - } - }; + let (checks_path, incidents_path, timeout) = match parse_args() { + Some(a) => a, + None => { + usage(); + std::process::exit(1); + } + }; unsafe { if tls_init() != 0 { @@ -701,8 +620,7 @@ fn main() { let checks = parse_checks(&checks_path); let results = run_checks(&checks, timeout); - let incidents = - fs::read_to_string(&incidents_path).unwrap_or_default(); + let incidents = fs::read_to_string(&incidents_path).unwrap_or_default(); render_html(&results, &incidents); }