use std::net::SocketAddr;
use clap::Parser;
use futures::{SinkExt, StreamExt};
use openvpn_mgmt_codec::{
OvpnCodec, OvpnCommand,
command::server_connection_sequence,
stream::{ClassifyExt, ManagementEvent},
};
use tokio::net::{TcpListener, TcpStream};
use tokio_util::codec::Framed;
use tracing::{debug, error, info};
#[derive(Parser)]
struct Args {
#[arg(default_value = "127.0.0.1:7505")]
address: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| "server_mode=debug,openvpn_mgmt_codec=info".parse().unwrap()),
)
.init();
let addr = Args::parse().address;
let listener = TcpListener::bind(&addr).await?;
info!(%addr, "listening, waiting for OpenVPN to connect");
loop {
let (stream, peer) = listener.accept().await?;
info!(%peer, "accepted connection");
tokio::spawn(async move {
if let Err(error) = handle_connection(stream, peer).await {
error!(%peer, %error, "session error");
}
info!(%peer, "connection closed");
});
}
}
async fn handle_connection(stream: TcpStream, peer: SocketAddr) -> anyhow::Result<()> {
let framed = Framed::new(stream, OvpnCodec::new());
let (mut sink, raw_stream) = framed.split();
let mut mgmt = raw_stream.classify();
for cmd in server_connection_sequence(5, 0) {
sink.send(cmd).await?;
}
while let Some(event) = mgmt.next().await {
match event? {
ManagementEvent::Notification(notification) => {
info!(%peer, ?notification, "notification");
if let openvpn_mgmt_codec::Notification::Client {
event: openvpn_mgmt_codec::ClientEvent::Connect,
cid,
kid: Some(kid),
..
} = ¬ification
{
info!(%peer, %cid, "auto-approving client");
sink.send(OvpnCommand::ClientAuthNt {
cid: *cid,
kid: *kid,
})
.await?;
}
}
ManagementEvent::Response(response) => {
debug!(%peer, ?response, "response");
}
}
}
Ok(())
}