mod client;
mod error;
pub use crate::client::Client;
pub use crate::error::{OpenvpnError, OpenvpnResult as Result};
use chrono::prelude::{DateTime, TimeZone, Utc};
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) -> &[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_all(b"status\n")?;
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 })
}
}
pub struct CommandManagerBuilder {
management_url: String,
}
impl CommandManagerBuilder {
pub fn new() -> Self {
Default::default()
}
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(),
}
}
}
impl Default for CommandManagerBuilder {
fn default() -> Self {
CommandManagerBuilder {
management_url: DEFAULT_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: &str) -> Result<Client> {
let vec: Vec<_> = raw_client.split('\t').collect();
if vec.len() < 9 {
return Err(OpenvpnError::MalformedResponse(raw_client.to_string()));
}
let name = vec[1];
let address = vec[2]
.split(':')
.next()
.ok_or_else(|| OpenvpnError::MalformedResponse(raw_client.to_string()))?;
let timestamp: i64 = vec[8].parse()?;
let bytes_received: f64 = vec[5].parse()?;
let bytes_sent: f64 = vec[6].parse()?;
Ok(Client::new(
String::from(name),
String::from(address),
get_utc_start_time(timestamp),
bytes_received,
bytes_sent,
))
}
fn get_utc_start_time(timestamp: i64) -> DateTime<Utc> {
Utc.timestamp(timestamp, 0)
}