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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright 2023 Mullvad VPN AB.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Constants for OpenVPN. Taken from include/openvpn-plugin.h in the OpenVPN repository:
//! https://github.com/OpenVPN/openvpn/blob/master/include/openvpn-plugin.h.in

use std::os::raw::c_int;

use derive_try_from_primitive::TryFromPrimitive;


/// All the events that an OpenVPN plugin can register for and get notified about.
/// This is a Rust representation of the constants named `OPENVPN_PLUGIN_*` in `openvpn-plugin.h`.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[non_exhaustive]
#[repr(i32)]
pub enum EventType {
    Up = 0,
    Down = 1,
    RouteUp = 2,
    IpChange = 3,
    TlsVerify = 4,
    AuthUserPassVerify = 5,
    ClientConnect = 6,
    ClientDisconnect = 7,
    LearnAddress = 8,
    ClientConnectV2 = 9,
    TlsFinal = 10,
    EnablePf = 11, // NOTE: feature has been removed as of OpenVPN 2.6
    RoutePredown = 12,
    ClientConnectDefer = 13,
    ClientConnectDeferV2 = 14,
    ClientCrresponse = 15,
    #[cfg(feature = "auth-failed-event")]
    AuthFailed = 16,
}

/// Translates a collection of `EventType` instances into a bitmask in the format OpenVPN
/// expects it in `type_mask`.
pub fn events_to_bitmask(events: &[EventType]) -> c_int {
    let mut bitmask: c_int = 0;
    for event in events {
        bitmask |= 1 << (*event as i32);
    }
    bitmask
}


/// Enum representing the results an OpenVPN plugin can return from an event callback.
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum EventResult {
    /// Will return `OPENVPN_PLUGIN_FUNC_SUCCESS` to OpenVPN.
    /// Indicates that the plugin marks the event as a success. This means an auth is approved
    /// or similar, depending on which type of event.
    Success,

    /// Will return `OPENVPN_PLUGIN_FUNC_DEFERRED` to OpenVPN.
    /// WARNING: Can only be returned from the `EventType::AuthUserPassVerify`
    /// (`OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY`) event. No other events may return this variant.
    /// Returning this tells OpenVPN to continue its normal work and that the decision on if the
    /// authentication is accepted or not will be delivered later, via writing to the path under
    /// the `auth_control_file` environment variable.
    Deferred,

    /// Will return `OPENVPN_PLUGIN_FUNC_ERROR` to OpenVPN.
    /// Both returning `Ok(EventResult::Failure)` and `Err(e)` from a callback will result in
    /// `OPENVPN_PLUGIN_FUNC_ERROR` being returned to OpenVPN. The difference being that an
    /// `Err(e)` will also log the error `e`. This variant is intended for when the plugin did
    /// not encounter an error, but the event is a failure or is to be declined. Intended to be
    /// used to decline an authentication request and similar.
    Failure,
}


#[cfg(test)]
mod tests {
    use super::*;
    use std::convert::TryFrom;

    #[test]
    fn event_enum_to_str() {
        let result = format!("{:?}", EventType::Up);
        assert_eq!("Up", result);
    }

    #[test]
    fn events_to_bitmask_no_events() {
        let result = events_to_bitmask(&[]);
        assert_eq!(0, result);
    }

    #[test]
    fn events_to_bitmask_one_event() {
        let result = events_to_bitmask(&[EventType::Up]);
        assert_eq!(0b1, result);
    }

    #[test]
    fn events_to_bitmask_another_event() {
        let result = events_to_bitmask(&[EventType::RouteUp]);
        assert_eq!(0b100, result);
    }

    #[test]
    fn events_to_bitmask_many_events() {
        let result = events_to_bitmask(&[EventType::RouteUp, EventType::RoutePredown]);
        assert_eq!((1 << 12) | (1 << 2), result);
    }

    #[test]
    fn events_max_value() {
        let auth_failed = EventType::try_from(15);
        #[cfg(feature = "auth-failed-event")]
        assert_eq!(auth_failed.unwrap(), EventType::AuthFailed);
        #[cfg(not(feature = "auth-failed-event"))]
        assert_eq!(auth_failed, Err(15));

        assert_eq!(EventType::try_from(16), Err(16));
    }
}