openvpn_plugin/
types.rs

1// Copyright 2023 Mullvad VPN AB.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Constants for OpenVPN. Taken from include/openvpn-plugin.h in the OpenVPN repository:
10//! https://github.com/OpenVPN/openvpn/blob/master/include/openvpn-plugin.h.in
11
12use std::os::raw::c_int;
13
14use derive_try_from_primitive::TryFromPrimitive;
15
16
17/// All the events that an OpenVPN plugin can register for and get notified about.
18/// This is a Rust representation of the constants named `OPENVPN_PLUGIN_*` in `openvpn-plugin.h`.
19#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, TryFromPrimitive)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21#[non_exhaustive]
22#[repr(i32)]
23pub enum EventType {
24    Up = 0,
25    Down = 1,
26    RouteUp = 2,
27    IpChange = 3,
28    TlsVerify = 4,
29    AuthUserPassVerify = 5,
30    ClientConnect = 6,
31    ClientDisconnect = 7,
32    LearnAddress = 8,
33    ClientConnectV2 = 9,
34    TlsFinal = 10,
35    EnablePf = 11, // NOTE: feature has been removed as of OpenVPN 2.6
36    RoutePredown = 12,
37    ClientConnectDefer = 13,
38    ClientConnectDeferV2 = 14,
39    ClientCrresponse = 15,
40    #[cfg(feature = "auth-failed-event")]
41    AuthFailed = 16,
42}
43
44/// Translates a collection of `EventType` instances into a bitmask in the format OpenVPN
45/// expects it in `type_mask`.
46pub fn events_to_bitmask(events: &[EventType]) -> c_int {
47    let mut bitmask: c_int = 0;
48    for event in events {
49        bitmask |= 1 << (*event as i32);
50    }
51    bitmask
52}
53
54
55/// Enum representing the results an OpenVPN plugin can return from an event callback.
56#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
57#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
58pub enum EventResult {
59    /// Will return `OPENVPN_PLUGIN_FUNC_SUCCESS` to OpenVPN.
60    /// Indicates that the plugin marks the event as a success. This means an auth is approved
61    /// or similar, depending on which type of event.
62    Success,
63
64    /// Will return `OPENVPN_PLUGIN_FUNC_DEFERRED` to OpenVPN.
65    /// WARNING: Can only be returned from the `EventType::AuthUserPassVerify`
66    /// (`OPENVPN_PLUGIN_AUTH_USER_PASS_VERIFY`) event. No other events may return this variant.
67    /// Returning this tells OpenVPN to continue its normal work and that the decision on if the
68    /// authentication is accepted or not will be delivered later, via writing to the path under
69    /// the `auth_control_file` environment variable.
70    Deferred,
71
72    /// Will return `OPENVPN_PLUGIN_FUNC_ERROR` to OpenVPN.
73    /// Both returning `Ok(EventResult::Failure)` and `Err(e)` from a callback will result in
74    /// `OPENVPN_PLUGIN_FUNC_ERROR` being returned to OpenVPN. The difference being that an
75    /// `Err(e)` will also log the error `e`. This variant is intended for when the plugin did
76    /// not encounter an error, but the event is a failure or is to be declined. Intended to be
77    /// used to decline an authentication request and similar.
78    Failure,
79}
80
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use std::convert::TryFrom;
86
87    #[test]
88    fn event_enum_to_str() {
89        let result = format!("{:?}", EventType::Up);
90        assert_eq!("Up", result);
91    }
92
93    #[test]
94    fn events_to_bitmask_no_events() {
95        let result = events_to_bitmask(&[]);
96        assert_eq!(0, result);
97    }
98
99    #[test]
100    fn events_to_bitmask_one_event() {
101        let result = events_to_bitmask(&[EventType::Up]);
102        assert_eq!(0b1, result);
103    }
104
105    #[test]
106    fn events_to_bitmask_another_event() {
107        let result = events_to_bitmask(&[EventType::RouteUp]);
108        assert_eq!(0b100, result);
109    }
110
111    #[test]
112    fn events_to_bitmask_many_events() {
113        let result = events_to_bitmask(&[EventType::RouteUp, EventType::RoutePredown]);
114        assert_eq!((1 << 12) | (1 << 2), result);
115    }
116
117    #[test]
118    fn events_max_value() {
119        let auth_failed = EventType::try_from(15);
120        #[cfg(feature = "auth-failed-event")]
121        assert_eq!(auth_failed.unwrap(), EventType::AuthFailed);
122        #[cfg(not(feature = "auth-failed-event"))]
123        assert_eq!(auth_failed, Err(15));
124
125        assert_eq!(EventType::try_from(16), Err(16));
126    }
127}