mirror of
https://github.com/pldubouilh/blockfast.git
synced 2026-08-28 04:46:58 -04:00
bubble up parsing errors
This commit is contained in:
Generated
+21
@@ -51,6 +51,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"linemux",
|
"linemux",
|
||||||
"regex",
|
"regex",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -398,6 +399,26 @@ dependencies = [
|
|||||||
"unicode-width",
|
"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]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
|
|||||||
+2
-1
@@ -11,4 +11,5 @@ linemux = "0.2"
|
|||||||
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
|
||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
regex = "1.5.4"
|
regex = "1.5.4"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
|
thiserror = "1.0.26"
|
||||||
|
|||||||
+38
-17
@@ -1,28 +1,30 @@
|
|||||||
use std::net::IpAddr;
|
use crate::utils::Error;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
|
||||||
// TODO: allow user-provided list
|
// TODO: allow user-provided list
|
||||||
// TODO: match different error-levels (10 404, but only 5 401, etc...)
|
// TODO: match different error-levels (10 404, but only 5 401, etc...)
|
||||||
lazy_static! {
|
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 ?
|
// TODO: Use a proper parser ?
|
||||||
let elts: Vec<&str> = line.split_whitespace().collect();
|
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(
|
let ip_str = elts[0];
|
||||||
|bad_status| {
|
let ip = ip_str.parse::<IpAddr>().or(Err(Error::CantParse))?;
|
||||||
if http_code == *bad_status {
|
|
||||||
ip
|
let http_code_str = elts[elts.len() - 2] as &str;
|
||||||
} else {
|
let http_code = http_code_str.parse::<u32>().or(Err(Error::CantParse))?;
|
||||||
None
|
|
||||||
}
|
for status in BAD_STATUSES.iter() {
|
||||||
},
|
if *status == http_code {
|
||||||
)
|
return Ok(Some(ip));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[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",
|
"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]
|
#[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",
|
"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
@@ -6,6 +6,7 @@ mod utils;
|
|||||||
|
|
||||||
mod jail;
|
mod jail;
|
||||||
use crate::jail::Jail;
|
use crate::jail::Jail;
|
||||||
|
use crate::utils::Error;
|
||||||
|
|
||||||
async fn run() -> Option<()> {
|
async fn run() -> Option<()> {
|
||||||
let args = utils::cli().get_matches();
|
let args = utils::cli().get_matches();
|
||||||
@@ -36,12 +37,18 @@ async fn run() -> Option<()> {
|
|||||||
let payload = line.line();
|
let payload = line.line();
|
||||||
let path = line.source().display().to_string();
|
let path = line.source().display().to_string();
|
||||||
|
|
||||||
if do_sshd && path.ends_with(path_sshd) {
|
let res = if do_sshd && path.ends_with(path_sshd) {
|
||||||
sshd::parse(payload).and_then(|ip| jail.probe(ip));
|
sshd::parse(payload)
|
||||||
} else if do_clf && path.ends_with(path_clf) {
|
} else if do_clf && path.ends_with(path_clf) {
|
||||||
clf::parse(payload).and_then(|ip| jail.probe(ip));
|
clf::parse(payload)
|
||||||
} else {
|
} 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
@@ -1,7 +1,9 @@
|
|||||||
|
use crate::utils::Error;
|
||||||
use std::net::IpAddr;
|
use std::net::IpAddr;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
struct Rule {
|
struct Rule {
|
||||||
matcher: String,
|
matcher: String,
|
||||||
@@ -15,7 +17,7 @@ lazy_static! {
|
|||||||
extractor: Regex::new(r"(from.)(.*)(.port)").unwrap(),
|
extractor: Regex::new(r"(from.)(.*)(.port)").unwrap(),
|
||||||
},
|
},
|
||||||
Rule {
|
Rule {
|
||||||
matcher: "Invalid user".to_string(),
|
matcher: "Invalid user ".to_string(),
|
||||||
extractor: Regex::new(r"(from.)(.*)").unwrap(),
|
extractor: Regex::new(r"(from.)(.*)").unwrap(),
|
||||||
},
|
},
|
||||||
Rule {
|
Rule {
|
||||||
@@ -25,16 +27,27 @@ lazy_static! {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse(line: &str) -> Option<IpAddr> {
|
pub fn parse(line: &str) -> Result<Option<IpAddr>, Error> {
|
||||||
let ret = SSHD_BAD.iter().find_map(|rule| {
|
let hits = SSHD_BAD.iter().find_map(|rule| {
|
||||||
if line.contains(&rule.matcher) {
|
if line.contains(&rule.matcher) {
|
||||||
rule.extractor.captures(line)?.get(2)
|
rule.extractor.captures(line)
|
||||||
} else {
|
} else {
|
||||||
None
|
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)]
|
#[cfg(test)]
|
||||||
@@ -49,7 +62,10 @@ mod tests {
|
|||||||
"Sep 26 06:25:32 livecompute sshd[23254]: Invalid user neal from 35.184.211.144"
|
"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]
|
#[test]
|
||||||
@@ -60,6 +76,22 @@ mod tests {
|
|||||||
"Sep 26 06:25:32 livecompute sshd[23254]: very good user neal from 35.184.211.144"
|
"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());
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
use clap::{App, Arg};
|
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> {
|
pub fn cli() -> App<'static, 'static> {
|
||||||
App::new("ban internets scanner fast 🍶")
|
App::new("ban internets scanner fast 🍶")
|
||||||
.version("v0.0.1")
|
.version("v0.0.1")
|
||||||
|
|||||||
Reference in New Issue
Block a user