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}