Select Git revision
new_test_refs.output
parselog.rs 7.61 KiB
use std::net::TcpListener;
use std::process;
use std::io::{self, BufRead};
use std::env;
use regex::Regex;
use syslog::{Facility, Formatter3164};
use std::sync::OnceLock;
use gethostname::gethostname;
mod hashmap;
static HOSTNAME: OnceLock<Option<String>> = OnceLock::new();
static REGEXP_CLOSE: OnceLock<Regex> = OnceLock::new();
static REGEXP_CMD: OnceLock<Regex> = OnceLock::new();
static REGEXP_CONNECT: OnceLock<Regex> = OnceLock::new();
static REGEXP_LAUNCH: OnceLock<Regex> = OnceLock::new();
static REGEXP_OTHERLOG: OnceLock<Regex> = OnceLock::new();
static REGEXP_RESULT: OnceLock<Regex> = OnceLock::new();
fn help() {
println!("Usage: parselog-ldap --listen=NUMBER");
println!(" NUMBER: Le port sur lequel on reçoit les logs.");
}
fn getport()->Result<String,String> {
let regexp_arg = Regex::new(r"--listen=(?<port>[0-9]{1,5})").unwrap();
for argument in env::args() {
let Some(caps) = regexp_arg.captures(&argument) else {
continue;
};
return Ok(caps["port"].to_string());
}
println!("Port invalide");
help();
process::exit(1);
}
fn com_accept(line: String){
let Some(caps) = REGEXP_CONNECT.get().expect("unitialized Regexp").captures(&line) else {
return;
};
let val = caps["ip"].to_string();
let key = caps["con"].to_string();
hashmap::set_val("ip",key.clone(),val);
}
fn com_result(line: String){
let Some(caps) = REGEXP_RESULT.get().expect("unitialized Regexp").captures(&line) else {
return;
};
let nentries = caps.name("nentries").map_or("0", |m| m.as_str());
let ip = hashmap::get_val("ip",caps["con"].to_string());
if ip == "0" { return; }
let con = &caps["con"];
let bind = hashmap::get_val("bind",caps["con"].to_string());
let key = format!("{}-{}",&caps["con"],&caps["op"]);
let cmd = hashmap::get_val("cmd",key.clone());
let val1 = hashmap::get_val("val1",key.clone());
let val2 = hashmap::get_val("val2",key.clone());
let attr = hashmap::get_val("attr",key.clone());
let linelog : String;
let err : String;
let user : String;
match &caps["err"]{
"err=0" => err = "OK".to_string(),
"err=" => err = "OK".to_string(),
_ => err = "refused".to_string(),
}
match bind.as_str(){
"\"\"" => user = "Anonymous".to_string(),
_ => user = bind,
}
match cmd.as_str() {
"BIND" => {
if &caps["err"] == "err=0" {
hashmap::set_val("bind",caps["con"].to_string(),val1.clone());
}
if val1 == "\"\"" { hashmap::removeop(key.clone()); return; }
linelog = format!("ID={} SRC={} BIND={} {} -> {}",con,ip,val1,cmd,err);
},
"UNBIND" => {
hashmap::set_val("bind",caps["con"].to_string(),"Anonymous".to_string());
return;
},
"SRCH" => {
linelog = format!("ID={} SRC={} BIND={} {} attr {} in {} with filter {} -> {} ({} answer)",con,ip,user,cmd,attr,val1,val2,err,nentries);
},
"MOD" => {
linelog = format!("ID={} SRC={} BIND={} {} dn={} attr={} -> {}",con,ip,user,cmd,val1,attr,err);
},
"CMP" => {
linelog = format!("ID={} SRC={} BIND={} {} dn={} attr={} -> {}",con,ip,user,cmd,val1,val2,err);
},
_ => { // ADD DEL
linelog = format!("ID={} SRC={} BIND={} {} dn={} -> {}",con,ip,user,cmd,val1,err);
}
}
hashmap::removeop(key.clone());
sendlog(linelog,false);
}
fn create_log(line: String){
let Some(caps) = REGEXP_CMD.get().expect("unitialized Regexp").captures(&line) else {
return;
};
let val2 = caps.name("val2").map_or("-", |m| m.as_str());
let key = format!("{}-{}",&caps["con"],&caps["op"]);
hashmap::set_val("cmd",key.clone(),caps["cmd"].to_string());
match &caps["v"]{
"base"=>{
hashmap::set_val("val1",key.clone(),caps["val1"].to_string());
hashmap::set_val("val2",key.clone(),val2.to_string());
},
"dn"=>{
hashmap::set_val("val1",key.clone(),caps["val1"].to_string());
hashmap::set_val("val2",key.clone(),val2.to_string());
},
"attr"=>{
hashmap::set_val("attr",key,caps["val1"].to_string());
},
"id"=>{
hashmap::set_val("val1",key,caps["val1"].to_string());
},
_=>com_default(line.to_string()),
}
}
fn com_closed(line: String){
let Some(caps) = REGEXP_CLOSE.get().expect("unitialized Regexp").captures(&line) else {
return;
};
hashmap::removecon(caps["con"].to_string());
}
fn com_default(line: String){
let _ = line;
//println!("DEF {}", &line);
}
fn sendlog(line: String,otherlog: bool){
let formatter = Formatter3164 {
facility: Facility::LOG_LOCAL5,
hostname: None,
process: "slapd".into(),
pid: 6666,
};
match syslog::unix(formatter) {
Err(e) => println!("impossible to connect to syslog: {:?}", e),
Ok(mut writer) => {
if otherlog {
let Some(caps) = REGEXP_OTHERLOG.get().expect("unitialized Regexp").captures(&line) else { return; };
let log = &caps["log"].to_string();
writer.debug(log).expect("could not write message");
} else {
writer.info(line).expect("could not write message");
}
}
}
}
fn launch<R: BufRead>(reader: R) {
for l in reader.lines() {
let line = l.unwrap();
//println!("LINE: {}",line);
let Some(caps) = REGEXP_LAUNCH.get().expect("unitialized Regexp").captures(&line) else {
sendlog(line.to_string(),true);
return;
};
match &caps["command"]{
"ABANDON" =>com_closed(line.to_string()),
"do_abandon:" =>{},
"ACCEPT" =>com_accept(line.to_string()),
"closed" =>com_closed(line.to_string()),
"RESULT" =>com_result(line.to_string()),
"SEARCH" =>com_result(line.to_string()),
"ADD" =>create_log(line.to_string()),
"BIND" =>create_log(line.to_string()),
"CMP" =>create_log(line.to_string()),
"DEL" =>create_log(line.to_string()),
"MOD" =>create_log(line.to_string()),
"PASSMOD" =>create_log(line.to_string()),
"SRCH" =>create_log(line.to_string()),
"TLS" =>{},
"UNBIND" =>create_log(line.to_string()),
_ =>sendlog(line.to_string(),true),
}
}
}
fn main() {
let port = getport().unwrap();
let hostname = Some(gethostname().into_string().unwrap());
let _ = HOSTNAME.set(hostname);
let _ = REGEXP_LAUNCH.set(Regex::new(r"^\S+ \S+ \S+ \S+ \S+ (?<con>\S+) (?<op>\S+) (?<command>\S+)").unwrap());
let _ = REGEXP_OTHERLOG.set(Regex::new(r"^\S+ \S+ \S+ \S+ \S+ (?<log>.+)").unwrap());
let _ = REGEXP_RESULT.set(Regex::new(r"^\S+ \S+ \S+ \S+ \S+ conn=(?<con>[0-9]+) op=(?<op>[0-9]+)( SEARCH)? RESULT \S+ (?<err>\S+)( nentries=(?<nentries>[0-9]+))?").unwrap());
let _ = REGEXP_CMD.set(Regex::new(r"^\S+ \S+ \S+ \S+ \S+ conn=(?<con>[0-9]+) op=(?<op>[0-9]+) (?<cmd>(ADD|DEL|BIND|CMP|MOD|PASSMOD|SRCH)) (?<v>[a-z]+)=(?<val1>\S+)( \S+ \S+)?( (attr|filter)=(?<val2>\S+))?").unwrap());
let _ = REGEXP_CONNECT.set(Regex::new(r"^\S+ \S+ \S+ \S+ \S+ conn=(?<con>[0-9]+) \S+ ACCEPT from IP=(?<ip>[0-9.:]+):[0-9]+").unwrap());
let _ = REGEXP_CLOSE.set(Regex::new(r"^\S+ \S+ \S+ \S+ \S+ conn=(?<con>[0-9]+) \S+ (closed|ABANDON)").unwrap());
let s = format!("127.0.0.1:{}",port);
let listener = TcpListener::bind(s).unwrap();
for stream in listener.incoming() {
let reader = io::BufReader::new(stream.unwrap());
launch(reader);
}
}