Skip to main content

Crate openvpn_mgmt_codec

Crate openvpn_mgmt_codec 

Source
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 Raw escape hatch for anything new.
  • High-level clientManagementClient separates command responses from async notifications and returns parsed results directly.
  • Stream classification – the ClassifyExt trait splits a raw message stream into Response and Notification variants.
  • Status & state parsing – typed parsers for status, state, version, and hold responses.

§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:

APIWhen to use
ManagementClientMost 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).

DirectionTypeDescription
EncodeOvpnCommandOne of 50 command variants – serialised to the wire format with proper escaping and multi-line framing.
DecodeOvpnMessageSuccess, 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 OvpnCodec encoder/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 for SUCCESS: 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 Debug and Display output 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 status command responses (client table, routing, stats). Typed parsers for status command 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 version command.