bubble up parsing errors

This commit is contained in:
Pierre Dubouilh
2021-10-17 18:59:56 +02:00
parent 70cbeae0ed
commit 6741f48285
6 changed files with 121 additions and 30 deletions
Generated
+21
View File
@@ -51,6 +51,7 @@ dependencies = [
"lazy_static",
"linemux",
"regex",
"thiserror",
"tokio",
]
@@ -398,6 +399,26 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "thiserror"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio"
version = "1.12.0"
+1
View File
@@ -12,3 +12,4 @@ tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
lazy_static = "1.4.0"
regex = "1.5.4"
clap = "2.33.3"
thiserror = "1.0.26"
+37 -16
View File
@@ -1,28 +1,30 @@
use std::net::IpAddr;
use crate::utils::Error;
use lazy_static::lazy_static;
use std::net::IpAddr;
// TODO: allow user-provided list
// TODO: match different error-levels (10 404, but only 5 401, etc...)
lazy_static! {
static ref BAD_STATUSES: [&'static str; 2] = ["401", "429"];
static ref BAD_STATUSES: [u32; 2] = [401, 429];
}
pub fn parse(line: &str) -> Option<IpAddr> {
pub fn parse(line: &str) -> Result<Option<IpAddr>, Error> {
// TODO: Use a proper parser ?
let elts: Vec<&str> = line.split_whitespace().collect();
let ip = elts[0].parse::<IpAddr>().ok();
let http_code = elts[elts.len() - 2] as &str;
BAD_STATUSES.iter().find_map(
|bad_status| {
if http_code == *bad_status {
ip
} else {
None
let ip_str = elts[0];
let ip = ip_str.parse::<IpAddr>().or(Err(Error::CantParse))?;
let http_code_str = elts[elts.len() - 2] as &str;
let http_code = http_code_str.parse::<u32>().or(Err(Error::CantParse))?;
for status in BAD_STATUSES.iter() {
if *status == http_code {
return Ok(Some(ip));
}
},
)
}
Ok(None)
}
#[cfg(test)]
@@ -36,7 +38,10 @@ mod tests {
"8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 429 923",
];
vectors.iter().for_each(|e| assert!(parse(*e).is_some()))
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_some());
})
}
#[test]
@@ -46,6 +51,22 @@ mod tests {
"8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 404 923",
];
vectors.iter().for_each(|e| assert!(parse(*e).is_none()))
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_none());
})
}
#[test]
fn malformed() {
let vectors = [
"8.8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\" 200 923",
"8.8.8.8 - p [25/Sep/2021:13:49:56 +0200] \"POST /some/rpc HTTP/2.0\"",
];
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.is_err());
})
}
}
+11 -4
View File
@@ -6,6 +6,7 @@ mod utils;
mod jail;
use crate::jail::Jail;
use crate::utils::Error;
async fn run() -> Option<()> {
let args = utils::cli().get_matches();
@@ -36,12 +37,18 @@ async fn run() -> Option<()> {
let payload = line.line();
let path = line.source().display().to_string();
if do_sshd && path.ends_with(path_sshd) {
sshd::parse(payload).and_then(|ip| jail.probe(ip));
let res = if do_sshd && path.ends_with(path_sshd) {
sshd::parse(payload)
} else if do_clf && path.ends_with(path_clf) {
clf::parse(payload).and_then(|ip| jail.probe(ip));
clf::parse(payload)
} else {
eprintln!("! unknown logline: {}", path);
Err(Error::UnknownError)
};
if let Ok(Some(ip)) = res {
jail.probe(ip);
} else {
eprintln!("! error processing logline: {}", path);
}
}
+40 -8
View File
@@ -1,7 +1,9 @@
use crate::utils::Error;
use std::net::IpAddr;
use lazy_static::lazy_static;
use regex::Regex;
use std::str::FromStr;
struct Rule {
matcher: String,
@@ -15,7 +17,7 @@ lazy_static! {
extractor: Regex::new(r"(from.)(.*)(.port)").unwrap(),
},
Rule {
matcher: "Invalid user".to_string(),
matcher: "Invalid user ".to_string(),
extractor: Regex::new(r"(from.)(.*)").unwrap(),
},
Rule {
@@ -25,16 +27,27 @@ lazy_static! {
];
}
pub fn parse(line: &str) -> Option<IpAddr> {
let ret = SSHD_BAD.iter().find_map(|rule| {
pub fn parse(line: &str) -> Result<Option<IpAddr>, Error> {
let hits = SSHD_BAD.iter().find_map(|rule| {
if line.contains(&rule.matcher) {
rule.extractor.captures(line)?.get(2)
rule.extractor.captures(line)
} else {
None
}
})?;
});
ret.as_str().parse::<IpAddr>().ok()
if hits.is_none() {
return Ok(None);
}
let ip = hits
.and_then(|c| c.get(2))
.and_then(|m| IpAddr::from_str(m.as_str()).ok());
match ip {
Some(ip) => Ok(Some(ip)),
None => Err(Error::CantParse),
}
}
#[cfg(test)]
@@ -49,7 +62,10 @@ mod tests {
"Sep 26 06:25:32 livecompute sshd[23254]: Invalid user neal from 35.184.211.144"
];
vectors.iter().for_each(|e| assert!(parse(*e).is_some()))
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_some());
})
}
#[test]
@@ -60,6 +76,22 @@ mod tests {
"Sep 26 06:25:32 livecompute sshd[23254]: very good user neal from 35.184.211.144"
];
vectors.iter().for_each(|e| assert!(parse(*e).is_none()))
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.unwrap().is_none());
})
}
#[test]
fn malformed() {
let vectors = [
"Sep 26 06:25:19 livecompute sshd[23246]: Failed password for root from 179.124.36.195.232 port 41883 ssh2",
"Sep 26 06:26:14 livecompute sshd[23292]: pam_unix(sshd:auth): authentication failure; logname= u =0 tty=ssh ruser= rhost=",
];
vectors.iter().for_each(|e| {
let ret = parse(*e);
assert!(ret.is_err());
})
}
}
+9
View File
@@ -1,5 +1,14 @@
use clap::{App, Arg};
#[derive(Debug, thiserror::Error)]
#[allow(clippy::large_enum_variant)]
pub enum Error {
#[error("cant parse")]
CantParse,
#[error("general error")]
UnknownError,
}
pub fn cli() -> App<'static, 'static> {
App::new("ban internets scanner fast 🍶")
.version("v0.0.1")