mod client;
mod error;
pub use crate::client::Client;
pub use crate::error::{OpenvpnError, OpenvpnResult as Result};
use chrono::prelude::{DateTime, Local, TimeZone};
use std::io::{BufRead, BufReader, Write};
use std::net::TcpStream;
const DEFAULT_MANAGEMENT_URL: &str = "localhost:5555";
const ENDING: &str = "END";
const START_LINE: &str = "CLIENT_LIST";
const HEADER_START_LINE: &str = "HEADER\tCLIENT_LIST";
const UNDEF: &str = "UNDEF";
#[derive(Clone, Debug)]
pub struct Status {
clients: Vec<Client>,
}
impl Status {
pub fn clients(&self) -> &Vec<Client> {
&self.clients
}
}
pub struct CommandManager {
management_url: String,
}
pub trait EventManager {
fn get_status(&mut self) -> Result<Status>;
}
impl EventManager for CommandManager {
fn get_status(&mut self) -> Result<Status> {
let mut stream = TcpStream::connect(self.management_url.to_owned())?;
stream.write("status\n".as_bytes())?;
let mut reader = BufReader::new(&stream);
let mut output = String::new();
while !output.trim().ends_with(ENDING) {
reader.read_line(&mut output)?;
}
let clients = parse_status_output(output)?;
Ok(Status { clients: clients })
}
}
pub struct CommandManagerBuilder {
management_url: String,
}
impl CommandManagerBuilder {
pub fn new() -> Self {
CommandManagerBuilder {
management_url: DEFAULT_MANAGEMENT_URL.to_owned(),
}
}
pub fn management_url(&mut self, url: &str) -> &mut CommandManagerBuilder {
self.management_url = url.to_owned();
self
}
pub fn build(&mut self) -> CommandManager {
CommandManager {
management_url: self.management_url.to_owned(),
}
}
}
fn parse_status_output(output: String) -> Result<Vec<Client>> {
let split = output.split("\n");
let mut clients = Vec::new();
let mut has_client_list = false;
for s in split {
let line = String::from(s);
if line.starts_with(HEADER_START_LINE) {
has_client_list = true;
}
if line.starts_with(START_LINE) {
let client = parse_client(line)?;
if client.name() != UNDEF {
clients.push(client);
}
}
}
if has_client_list {
return Ok(clients);
}
Err(OpenvpnError::MalformedResponse(output))
}
fn parse_client(raw_client: String) -> Result<Client> {
let split = raw_client.split("\t");
let vec = split.collect::<Vec<&str>>();
if vec.len() < 9 {
return Err(OpenvpnError::MalformedResponse(raw_client.clone()));
}
let name = vec[1];
let address = match vec[2].split(":").next() {
Some(a) => a,
None => return Err(OpenvpnError::MalformedResponse(raw_client.clone())),
};
let timestamp = vec[8].parse::<i64>()?;
let bytes_received = vec[5].parse::<f64>()?;
let bytes_sent = vec[6].parse::<f64>()?;
Ok(Client::new(
String::from(name),
String::from(address),
get_local_start_time(timestamp),
bytes_received,
bytes_sent,
))
}
fn get_local_start_time(timestamp: i64) -> DateTime<Local> {
let datetime: DateTime<Local> = Local.timestamp(timestamp, 0);
datetime
}