Expand description
§openvpn-mgmt-codec
A Rust tokio_util::codec for the
OpenVPN management interface
protocol. It gives you fully typed, escape-aware command encoding and
stateful response decoding so you can talk to an OpenVPN daemon over TCP
or a Unix socket without hand-rolling string parsing.
§Features
- Type-safe commands – every management-interface command is a variant
of
OvpnCommand; the compiler prevents malformed protocol strings. - Stateful decoder – tracks which command was sent so it can disambiguate single-line replies, multi-line blocks, and real-time notifications (even when they arrive interleaved).
- Command pipelining – send multiple commands without waiting for each response; the codec queues expected response types internally.
- Automatic escaping – backslashes and double-quotes are escaped following the OpenVPN config-file lexer rules.
- Full protocol coverage – 50 commands including auth, signals,
client management, PKCS#11, external keys, proxy/remote overrides,
and a
Rawescape hatch for anything new. - High-level client –
ManagementClientseparates command responses from async notifications and returns parsed results directly. - Stream classification – the
ClassifyExttrait splits a raw message stream intoResponseandNotificationvariants. - Status & state parsing – typed parsers for
status,state,version, andholdresponses.
§Quick start
Add the crate to your project:
[dependencies]
openvpn-mgmt-codec = "0.7"
tokio = { version = "1", features = ["full"] }
tokio-util = { version = "0.7", features = ["codec"] }Then wrap a TCP stream with the codec:
use tokio::net::TcpStream;
use tokio_util::codec::Framed;
use futures::{SinkExt, StreamExt};
use openvpn_mgmt_codec::{OvpnCodec, OvpnCommand, OvpnMessage, StatusFormat};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let stream = TcpStream::connect("127.0.0.1:7505").await?;
let mut framed = Framed::new(stream, OvpnCodec::new());
// ask for status
framed.send(OvpnCommand::Status(StatusFormat::V3)).await?;
// read responses
while let Some(msg) = framed.next().await {
match msg? {
OvpnMessage::Success(text) => println!("OK: {text}"),
OvpnMessage::Error(text) => eprintln!("ERR: {text}"),
OvpnMessage::MultiLine(lines) => {
for line in &lines {
println!(" {line}");
}
}
OvpnMessage::Notification(n) => println!("event: {n:?}"),
other => println!("{other:?}"),
}
}
Ok(())
}§Choosing an API level
The crate offers two ways to talk to OpenVPN:
| API | When to use |
|---|---|
ManagementClient | Most applications. Sends commands and returns typed responses; dispatches notifications to a broadcast channel. See the client module. |
Framed<T, OvpnCodec> | When you need full control over the stream (custom backpressure, multiplexing, or integration with an existing tower/axum stack). |
Both layers share the same OvpnCommand / OvpnMessage types.
§High-level client
ManagementClient handles command/response pairing and forwards
notifications to a broadcast channel:
use tokio::net::TcpStream;
use tokio::sync::broadcast;
use tokio_util::codec::Framed;
use openvpn_mgmt_codec::{
ManagementClient, Notification, OvpnCodec, StatusFormat,
};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let stream = TcpStream::connect("127.0.0.1:7505").await?;
let framed = Framed::new(stream, OvpnCodec::new());
let (notification_tx, mut notification_rx) = broadcast::channel::<Notification>(256);
let mut client = ManagementClient::new(framed, notification_tx);
let version = client.version().await?;
println!("version: {:?}", version.openvpn_version_line());
let status = client.status(StatusFormat::V3).await?;
for c in &status.clients {
println!("{}: {}B in", c.common_name, c.bytes_in);
}
client.hold_release().await?;
Ok(())
}§Startup helpers
connection_sequence and server_connection_sequence return the
commands that a management client typically sends right after connecting
(enable log/state streaming, request PID, start byte-count
notifications, release the hold). Use them to avoid hand-rolling the
same boilerplate:
use openvpn_mgmt_codec::command::{connection_sequence, server_connection_sequence};
// Client mode — bytecount every 5 s
let cmds = connection_sequence(5);
// Server mode — bytecount every 5 s, env-filter level 0 (all vars)
let cmds = server_connection_sequence(5, 0);§How it works
OvpnCodec implements Encoder<OvpnCommand> and Decoder (Item =
OvpnMessage).
| Direction | Type | Description |
|---|---|---|
| Encode | OvpnCommand | One of 50 command variants – serialised to the wire format with proper escaping and multi-line framing. |
| Decode | OvpnMessage | Success, Error, MultiLine, Pkcs11IdEntry, Notification, Info, PasswordPrompt, or Unrecognized. |
Real-time notifications (>STATE:, >BYTECOUNT:, >CLIENT:, etc.) are
emitted as OvpnMessage::Notification and can arrive at any time,
including in the middle of a multi-line response block. The codec handles
this transparently.
§License
Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Re-exports§
pub use auth::AuthRetryMode;pub use auth::AuthType;pub use auth::ParseAuthRetryModeError;pub use auth::ParseAuthTypeError;pub use client_deny::ClientDeny;pub use client_event::ClientEvent;pub use client_event::ParseClientEventError;pub use codec::AccumulationLimit;pub use codec::EncodeError;pub use codec::EncoderMode;pub use codec::OvpnCodec;pub use command::CommandParseError;pub use command::OvpnCommand;pub use command::RemoteEntryRange;pub use crv1_challenge::Crv1Challenge;pub use kill_target::KillTarget;pub use log_level::LogLevel;pub use log_level::ParseLogLevelError;pub use message::Notification;pub use message::OvpnMessage;pub use message::PasswordNotification;pub use need_ok::NeedOkResponse;pub use openvpn_state::OpenVpnState;pub use openvpn_state::ParseOpenVpnStateError;pub use proxy_action::ProxyAction;pub use redacted::Redacted;pub use remote_action::RemoteAction;pub use signal::ParseSignalError;pub use signal::Signal;pub use status_format::ParseStatusFormatError;pub use status_format::StatusFormat;pub use stream_mode::ParseStreamModeError;pub use stream_mode::StreamMode;pub use transport_protocol::ParseTransportProtocolError;pub use transport_protocol::TransportProtocol;pub use unrecognized::UnrecognizedKind;pub use version_info::VersionInfo;pub use client::ClientError;pub use client::ManagementClient;pub use command::connection_sequence;pub use command::server_connection_sequence;pub use parsed_response::LoadStats;pub use parsed_response::ParseResponseError;pub use parsed_response::StateEntry;pub use status::ClientStatistics;pub use status::ConnectedClient;pub use status::ParseStatusError;pub use status::RoutingEntry;pub use status::StatusResponse;pub use status::parse_client_statistics;pub use status::parse_status;pub use stream::ClassifyExt;pub use stream::ManagementEvent;
Modules§
- auth
- Authentication credential types and retry strategies.
- client
- High-level management client with notification dispatch. High-level management client with notification dispatch.
- client_
deny - Typed client-deny command with builder support.
- client_
event - Client notification event types (CONNECT, REAUTH, etc.).
- codec
- The
OvpnCodecencoder/decoder implementation. - command
- Typed management-interface commands (
OvpnCommand). - crv1_
challenge - Builder for outgoing CRV1 dynamic-challenge strings. Builder for outgoing CRV1 dynamic-challenge strings.
- kill_
target - Client kill-target addressing.
- log_
level - Log severity levels (Info, Debug, Warning, etc.).
- message
- Decoded messages and real-time notifications.
- need_ok
- Responses to
>NEED-OK:prompts. - openvpn_
state - OpenVPN connection states (CONNECTING, CONNECTED, etc.).
- parsed_
response - Typed parsers for
SUCCESS:payloads and multi-line responses. Typed parsers forSUCCESS:payloads and multi-line responses. - proxy_
action - Proxy configuration for
>PROXY:responses. - redacted
- A wrapper type that masks sensitive values in debug/display output.
A wrapper type that masks sensitive values in
DebugandDisplayoutput to prevent accidental exposure in logs. - remote_
action - Remote-override actions for
>REMOTE:responses. - signal
- Daemon signals (HUP, TERM, USR1, USR2).
- status
- Typed parsers for
statuscommand responses (client table, routing, stats). Typed parsers forstatuscommand responses. - status_
format - Status output format versions (V1/V2/V3).
- stream
- Stream adapter categorizing messages as responses or notifications.
Helpers for categorizing
OvpnMessages into responses and notifications. - stream_
mode - Stream mode selectors (on/off/all/recent).
- timestamp
- Lightweight UTC timestamp formatting. Lightweight UTC timestamp formatting without external dependencies.
- transport_
protocol - Transport protocol (UDP, TCP) for remote/proxy notifications.
- unrecognized
- Error classification for unrecognized protocol lines.
- version_
info - Parsed version information from the
versioncommand.