Skip to main content

openvpn_mgmt_codec/
message.rs

1use crate::auth::AuthType;
2use crate::client_event::ClientEvent;
3use crate::log_level::LogLevel;
4use crate::openvpn_state::OpenVpnState;
5use crate::transport_protocol::TransportProtocol;
6
7/// Sub-types of `>PASSWORD:` notifications. The password notification
8/// has several distinct forms with completely different structures.
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub enum PasswordNotification {
11    /// `>PASSWORD:Need 'Auth' username/password`
12    NeedAuth {
13        /// The credential set being requested.
14        auth_type: AuthType,
15    },
16
17    /// `>PASSWORD:Need 'Private Key' password`
18    NeedPassword {
19        /// The credential set being requested.
20        auth_type: AuthType,
21    },
22
23    /// `>PASSWORD:Verification Failed: 'Auth'`
24    VerificationFailed {
25        /// The credential set that failed verification.
26        auth_type: AuthType,
27    },
28
29    /// Static challenge: `>PASSWORD:Need 'Auth' username/password SC:{echo},{challenge}`
30    StaticChallenge {
31        /// Whether to echo the user's response (from the `echo` flag: `0` or `1`).
32        echo: bool,
33        /// The challenge text presented to the user.
34        challenge: String,
35    },
36
37    /// Dynamic challenge (CRV1):
38    /// `>PASSWORD:Need 'Auth' username/password CRV1:{flags}:{state_id}:{username_b64}:{challenge}`
39    DynamicChallenge {
40        /// Comma-separated CRV1 flags.
41        flags: String,
42        /// Opaque state identifier for the auth backend.
43        state_id: String,
44        /// Base64-encoded username.
45        username_b64: String,
46        /// The challenge text presented to the user.
47        challenge: String,
48    },
49}
50
51/// A parsed real-time notification from OpenVPN.
52#[derive(Debug, Clone, PartialEq, Eq)]
53pub enum Notification {
54    /// A multi-line `>CLIENT:` notification (CONNECT, REAUTH, ESTABLISHED,
55    /// DISCONNECT). The header and all ENV key=value pairs are accumulated
56    /// into a single struct before this is emitted.
57    Client {
58        /// The client event sub-type.
59        event: ClientEvent,
60        /// Client ID (sequential, assigned by OpenVPN).
61        cid: u64,
62        /// Key ID (present for CONNECT/REAUTH, absent for ESTABLISHED/DISCONNECT).
63        kid: Option<u64>,
64        /// Accumulated ENV pairs, in order. Each `>CLIENT:ENV,key=val` line
65        /// becomes one `(key, val)` entry. The terminating `>CLIENT:ENV,END`
66        /// is consumed but not included.
67        env: Vec<(String, String)>,
68    },
69
70    /// A single-line `>CLIENT:ADDRESS` notification.
71    ClientAddress {
72        /// Client ID.
73        cid: u64,
74        /// Assigned virtual address.
75        addr: String,
76        /// Whether this is the primary address for the client.
77        primary: bool,
78    },
79
80    /// `>STATE:timestamp,name,description,local_ip,remote_ip[,local_port,remote_port]`
81    State {
82        /// Unix timestamp of the state change.
83        timestamp: u64,
84        /// State name (e.g. `Connected`, `Reconnecting`).
85        name: OpenVpnState,
86        /// Verbose description of the state.
87        description: String,
88        /// Local tunnel IP address (may be empty).
89        local_ip: String,
90        /// Remote server IP address (may be empty).
91        remote_ip: String,
92        /// Local port (may be empty, OpenVPN 2.1+).
93        local_port: String,
94        /// Remote port (may be empty, OpenVPN 2.1+).
95        remote_port: String,
96    },
97
98    /// `>BYTECOUNT:bytes_in,bytes_out` (client mode)
99    ByteCount {
100        /// Bytes received since last reset.
101        bytes_in: u64,
102        /// Bytes sent since last reset.
103        bytes_out: u64,
104    },
105
106    /// `>BYTECOUNT_CLI:cid,bytes_in,bytes_out` (server mode, per-client)
107    ByteCountCli {
108        /// Client ID.
109        cid: u64,
110        /// Bytes received from this client.
111        bytes_in: u64,
112        /// Bytes sent to this client.
113        bytes_out: u64,
114    },
115
116    /// `>LOG:timestamp,level,message`
117    Log {
118        /// Unix timestamp of the log entry.
119        timestamp: u64,
120        /// Log severity level.
121        level: LogLevel,
122        /// The log message text.
123        message: String,
124    },
125
126    /// `>ECHO:timestamp,param_string`
127    Echo {
128        /// Unix timestamp.
129        timestamp: u64,
130        /// The echoed parameter string.
131        param: String,
132    },
133
134    /// `>HOLD:Waiting for hold release[:N]`
135    Hold {
136        /// The hold message text.
137        text: String,
138    },
139
140    /// `>FATAL:message`
141    Fatal {
142        /// The fatal error message.
143        message: String,
144    },
145
146    /// `>PKCS11ID-COUNT:count`
147    Pkcs11IdCount {
148        /// Number of available PKCS#11 identities.
149        count: u32,
150    },
151
152    /// `>NEED-OK:Need 'name' confirmation MSG:message`
153    NeedOk {
154        /// The prompt name.
155        name: String,
156        /// The prompt message to display.
157        message: String,
158    },
159
160    /// `>NEED-STR:Need 'name' input MSG:message`
161    NeedStr {
162        /// The prompt name.
163        name: String,
164        /// The prompt message to display.
165        message: String,
166    },
167
168    /// `>RSA_SIGN:base64_data`
169    RsaSign {
170        /// Base64-encoded data to be signed.
171        data: String,
172    },
173
174    /// `>REMOTE:host,port,protocol`
175    Remote {
176        /// Remote server hostname or IP.
177        host: String,
178        /// Remote server port.
179        port: u16,
180        /// Transport protocol.
181        protocol: TransportProtocol,
182    },
183
184    /// `>PROXY:proto_num,proto_type,host[,port]`
185    Proxy {
186        /// Numeric protocol identifier.
187        proto_num: u32,
188        /// Transport protocol type.
189        proto_type: TransportProtocol,
190        /// Server hostname or IP.
191        host: String,
192        /// Server port (`0` if not provided by the server).
193        port: u16,
194    },
195
196    /// `>PASSWORD:...` — see [`PasswordNotification`] for the sub-types.
197    Password(PasswordNotification),
198
199    /// Fallback for any notification type not explicitly modeled above.
200    /// Kept for forward compatibility with future OpenVPN versions.
201    Simple {
202        /// The notification type keyword (e.g. `"BYTECOUNT"`).
203        kind: String,
204        /// Everything after the first colon.
205        payload: String,
206    },
207}
208
209/// A fully decoded message from the OpenVPN management interface.
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub enum OvpnMessage {
212    /// A success response: `SUCCESS: [text]`.
213    Success(String),
214
215    /// An error response: `ERROR: [text]`.
216    Error(String),
217
218    /// A multi-line response block (from `status`, `version`, `help`, etc.).
219    /// The terminating `END` line is consumed but not included.
220    MultiLine(Vec<String>),
221
222    /// A single non-SUCCESS/ERROR value line (from bare `hold`, bare `state`,
223    /// etc.).
224    SingleValue(String),
225
226    /// Parsed response from `pkcs11-id-get`. The wire format is:
227    /// `PKCS11ID-ENTRY:'index', ID:'id', BLOB:'base64_cert'`
228    Pkcs11IdEntry {
229        /// Certificate index.
230        index: String,
231        /// PKCS#11 identifier.
232        id: String,
233        /// Base64-encoded certificate blob.
234        blob: String,
235    },
236
237    /// A real-time notification, either single-line or accumulated multi-line.
238    Notification(Notification),
239
240    /// The `>INFO:` banner sent when the management socket first connects.
241    /// Technically a notification, but surfaced separately since it's always
242    /// the first thing you see and is useful for version detection.
243    Info(String),
244
245    /// Management interface password prompt. Sent when `--management` is
246    /// configured with a password file. The client must respond with the
247    /// password (via [`crate::OvpnCommand::ManagementPassword`]) before any
248    /// commands are accepted.
249    PasswordPrompt,
250
251    /// A line that could not be classified into any known message type.
252    /// Contains the raw line and a description of what went wrong.
253    Unrecognized {
254        /// The raw line that could not be parsed.
255        line: String,
256        /// Why the line was not recognized.
257        kind: crate::unrecognized::UnrecognizedKind,
258    },
259}