summaryrefslogtreecommitdiffhomepage
path: root/talpid-openvpn-plugin/src/lib.rs
blob: ab31000a951616ce71487fe7dc97d2496cb212e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use openvpn_plugin::{EventResult, EventType, openvpn_plugin};
use std::{collections::HashMap, ffi::CString, io, sync::Mutex};
use talpid_types::ErrorExt;

mod processing;
use crate::processing::EventProcessor;

#[derive(thiserror::Error, Debug)]
pub enum Error {
    #[error("No core server id given as first argument")]
    MissingCoreServerId,

    // TODO: Remove box when upgrading tonic to a version with
    // https://github.com/hyperium/tonic/pull/2282
    #[error("Failed to send an event to daemon over the IPC channel")]
    SendEvent(#[source] Box<tonic::Status>),

    #[error("Unable to start Tokio runtime")]
    CreateRuntime(#[source] io::Error),

    #[error("Unable to create IPC transport")]
    CreateTransport(#[source] tonic::transport::Error),

    #[error("Unable to parse environment variables from OpenVPN")]
    ParseEnvFailed(#[source] std::str::Utf8Error),

    #[error("Unable to parse arguments from OpenVPN")]
    ParseArgsFailed(#[source] std::str::Utf8Error),

    #[error("Unhandled event type: {0:?}")]
    UnhandledEvent(openvpn_plugin::EventType),
}

/// All the OpenVPN events this plugin will register for listening to. Edit this variable to change
/// events.
pub static INTERESTING_EVENTS: &[EventType] = &[
    EventType::AuthFailed,
    EventType::Up,
    EventType::RouteUp,
    EventType::RoutePredown,
];

openvpn_plugin!(
    crate::openvpn_open,
    crate::openvpn_close,
    crate::openvpn_event,
    crate::Mutex<Option<EventProcessor>>
);

pub struct Arguments {
    ipc_socket_path: String,
}

fn openvpn_open(
    args: Vec<CString>,
    _env: HashMap<CString, CString>,
) -> Result<(Vec<EventType>, Mutex<Option<EventProcessor>>), Error> {
    env_logger::init();
    log::debug!("Initializing plugin");

    let arguments = parse_args(&args)?;
    log::info!(
        "Connecting back to talpid core at {}",
        arguments.ipc_socket_path
    );
    let processor = EventProcessor::new(arguments)?;

    Ok((INTERESTING_EVENTS.to_vec(), Mutex::new(Some(processor))))
}

fn parse_args(args: &[CString]) -> Result<Arguments, Error> {
    let mut args_iter = openvpn_plugin::ffi::parse::string_array_utf8(args)
        .map_err(Error::ParseArgsFailed)?
        .into_iter();

    let _plugin_path = args_iter.next();
    let ipc_socket_path: String = args_iter.next().ok_or(Error::MissingCoreServerId)?;

    Ok(Arguments { ipc_socket_path })
}

fn openvpn_close(_handle: Mutex<Option<EventProcessor>>) {
    log::info!("Unloading plugin");
}

fn openvpn_event(
    event: EventType,
    _args: Vec<CString>,
    env: HashMap<CString, CString>,
    handle: &mut Mutex<Option<EventProcessor>>,
) -> Result<EventResult, Error> {
    log::debug!("Received event: {:?}", event);

    let parsed_env = openvpn_plugin::ffi::parse::env_utf8(&env).map_err(Error::ParseEnvFailed)?;

    let ctx = handle
        .get_mut()
        .expect("failed to obtain mutex for EventProcessor");
    if let Some(processor) = ctx.as_mut() {
        match processor.process_event(event, parsed_env) {
            Ok(()) => Ok(EventResult::Success),
            Err(e) => {
                log::error!("{}", e.display_chain());
                *ctx = None;
                Ok(EventResult::Failure)
            }
        }
    } else {
        log::error!("Client has been closed");
        Ok(EventResult::Failure)
    }
}