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}