Skip to main content

netget/client/radsec/
mod.rs

1//! RadSec client (RFC 6614): the RADIUS client's transport and turns over one TLS connection.
2pub mod actions;
3
4use crate::client::radius::{Link, RadiusClient, Settings};
5use crate::protocol::ConnectContext;
6use crate::server::radius::packet::read_frames;
7use crate::server::radsec::tls;
8pub use actions::RadsecClientProtocol;
9use anyhow::{ensure, Context, Result};
10use std::net::SocketAddr;
11use std::time::Duration;
12use tokio::sync::mpsc;
13
14pub async fn connect(ctx: ConnectContext) -> Result<SocketAddr> {
15    let params = ctx.startup_params.as_ref();
16    let get = |k: &str| -> Result<Option<String>> {
17        Ok(params
18            .map(|p| p.get_optional_string(k))
19            .transpose()?
20            .flatten())
21    };
22    let ca_file = get("ca_file")?.context(
23        "the RadSec client needs ca_file, the CA the server's certificate must chain to; it never connects unverified",
24    )?;
25    let config = tls::client_config(
26        &ca_file,
27        get("certificate_file")?.as_deref(),
28        get("private_key_file")?.as_deref(),
29    )?;
30    let secret = get("shared_secret")?.unwrap_or_else(|| tls::DEFAULT_SECRET.to_string());
31    ensure!(!secret.is_empty(), "shared_secret must not be empty");
32    let timeout_ms = params
33        .map(|p| p.get_optional_u64("timeout_ms"))
34        .transpose()?
35        .flatten()
36        .unwrap_or(actions::DEFAULT_TIMEOUT_MS);
37    ensure!(
38        (100..=60_000).contains(&timeout_ms),
39        "timeout_ms {timeout_ms} is outside 100-60000"
40    );
41    let peer = tokio::net::lookup_host(&ctx.remote_addr)
42        .await
43        .with_context(|| format!("cannot resolve RadSec server {}", ctx.remote_addr))?
44        .next()
45        .with_context(|| format!("{} resolved to no address", ctx.remote_addr))?;
46    let name = match get("server_name")? {
47        Some(n) => n,
48        None => ctx
49            .remote_addr
50            .rsplit_once(':')
51            .map_or(ctx.remote_addr.as_str(), |(h, _)| h)
52            .trim_matches(['[', ']'])
53            .to_string(),
54    };
55    let stream = tokio::time::timeout(tls::HANDSHAKE_TIMEOUT, async {
56        let tcp = tokio::net::TcpStream::connect(peer).await?;
57        let local = tcp.local_addr()?;
58        let tls = tokio_rustls::TlsConnector::from(config)
59            .connect(tls::server_name(&name)?, tcp)
60            .await
61            .context("RadSec TLS handshake failed")?;
62        Ok::<_, anyhow::Error>((tls, local))
63    })
64    .await
65    .context("RadSec connect and handshake timed out")??;
66    let (tls, local) = stream;
67    let (reader, writer) = tokio::io::split(tls);
68    let (frames_tx, frames) = mpsc::channel(8);
69    ctx.state
70        .spawn_client_task(ctx.client_id, read_frames(reader, frames_tx))
71        .await;
72    RadiusClient::run(
73        Link::Stream {
74            writer: Box::new(writer),
75            frames,
76            peer,
77        },
78        peer,
79        peer,
80        Settings {
81            secret: secret.into_bytes(),
82            accounting_port: None,
83            timeout: Duration::from_millis(timeout_ms),
84            retries: 0,
85        },
86        ctx.llm_client,
87        ctx.state,
88        ctx.status_tx,
89        ctx.client_id,
90    )
91    .await;
92    Ok(local)
93}