netget/client/radsec/
mod.rs1pub 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}