mirror of
https://github.com/cubixle/radius-rs.git
synced 2026-04-30 14:28:44 +01:00
Make the client configurable connection-timeout and socket-timeout
This commit is contained in:
@@ -30,7 +30,6 @@ Simple example implementations are here:
|
|||||||
|
|
||||||
## Roadmap
|
## Roadmap
|
||||||
|
|
||||||
- timeout feature on the client
|
|
||||||
- retransmission feature on the client
|
- retransmission feature on the client
|
||||||
- Support the following RFC dictionaries:
|
- Support the following RFC dictionaries:
|
||||||
- rfc2869
|
- rfc2869
|
||||||
|
|||||||
+4
-2
@@ -5,6 +5,7 @@ use radius::packet::Packet;
|
|||||||
use radius::rfc2865;
|
use radius::rfc2865;
|
||||||
use radius_client::client::Client;
|
use radius_client::client::Client;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use tokio::time::Duration;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
@@ -14,8 +15,9 @@ async fn main() {
|
|||||||
|
|
||||||
let mut req_packet = Packet::new(Code::AccessRequest, &b"secret".to_vec());
|
let mut req_packet = Packet::new(Code::AccessRequest, &b"secret".to_vec());
|
||||||
rfc2865::add_user_name(&mut req_packet, "admin");
|
rfc2865::add_user_name(&mut req_packet, "admin");
|
||||||
rfc2865::add_user_password(&mut req_packet, b"p@ssw0rd").unwrap(); // TODO
|
rfc2865::add_user_password(&mut req_packet, b"p@ssw0rd").unwrap();
|
||||||
|
|
||||||
let res = Client::send_packet(&remote_addr, &req_packet).await;
|
let client = Client::new(Some(Duration::from_secs(3)), Some(Duration::from_secs(5)));
|
||||||
|
let res = client.send_packet(&remote_addr, &req_packet).await;
|
||||||
info!("response: {:?}", res);
|
info!("response: {:?}", res);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,5 @@ keywords = ["radius", "client"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
radius = { path = "../radius" }
|
radius = { path = "../radius" }
|
||||||
tokio = { version = "0.3.4", features = ["net"] }
|
tokio = { version = "0.3.4", features = ["net", "time"] }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
|||||||
+71
-20
@@ -1,13 +1,16 @@
|
|||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tokio::net::UdpSocket;
|
use tokio::net::UdpSocket;
|
||||||
|
use tokio::time::timeout;
|
||||||
|
|
||||||
|
use radius::packet::Packet;
|
||||||
|
|
||||||
use crate::client::ClientError::{
|
use crate::client::ClientError::{
|
||||||
FailedConnection, FailedParsingUDPResponse, FailedRadiusPacketEncoding,
|
FailedConnection, FailedParsingUDPResponse, FailedRadiusPacketEncoding,
|
||||||
FailedReceivingResponse, FailedSendingPacket, FailedUdpSocketBinding,
|
FailedReceivingResponse, FailedSendingPacket, FailedUdpSocketBinding,
|
||||||
};
|
};
|
||||||
use radius::packet::Packet;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum ClientError {
|
pub enum ClientError {
|
||||||
@@ -23,14 +26,29 @@ pub enum ClientError {
|
|||||||
FailedReceivingResponse(String, String),
|
FailedReceivingResponse(String, String),
|
||||||
#[error("failed to parse a UDP response into a RADIUS packet => `{0}`")]
|
#[error("failed to parse a UDP response into a RADIUS packet => `{0}`")]
|
||||||
FailedParsingUDPResponse(String),
|
FailedParsingUDPResponse(String),
|
||||||
|
#[error("connection timeout")]
|
||||||
|
ConnectionTimeoutError(),
|
||||||
|
#[error("socket timeout")]
|
||||||
|
SocketTimeoutError(),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Client {}
|
pub struct Client {
|
||||||
|
connection_timeout: Option<Duration>,
|
||||||
|
socket_timeout: Option<Duration>,
|
||||||
|
}
|
||||||
|
|
||||||
impl Client {
|
impl Client {
|
||||||
const MAX_DATAGRAM_SIZE: usize = 65507;
|
const MAX_DATAGRAM_SIZE: usize = 65507;
|
||||||
|
|
||||||
|
pub fn new(connection_timeout: Option<Duration>, socket_timeout: Option<Duration>) -> Self {
|
||||||
|
Client {
|
||||||
|
connection_timeout,
|
||||||
|
socket_timeout,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send_packet(
|
pub async fn send_packet(
|
||||||
|
&self,
|
||||||
remote_addr: &SocketAddr,
|
remote_addr: &SocketAddr,
|
||||||
request_packet: &Packet,
|
request_packet: &Packet,
|
||||||
) -> Result<Packet, ClientError> {
|
) -> Result<Packet, ClientError> {
|
||||||
@@ -48,35 +66,68 @@ impl Client {
|
|||||||
Ok(conn) => conn,
|
Ok(conn) => conn,
|
||||||
Err(e) => return Err(FailedUdpSocketBinding(e.to_string())),
|
Err(e) => return Err(FailedUdpSocketBinding(e.to_string())),
|
||||||
};
|
};
|
||||||
match conn.connect(remote_addr).await {
|
|
||||||
Ok(_) => {}
|
match self.connection_timeout {
|
||||||
Err(e) => return Err(FailedConnection(remote_addr.to_string(), e.to_string())),
|
Some(connection_timeout) => {
|
||||||
};
|
match timeout(connection_timeout, self.connect(&conn, remote_addr)).await {
|
||||||
|
Ok(conn_establish_res) => conn_establish_res,
|
||||||
|
Err(_) => Err(ClientError::ConnectionTimeoutError()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => self.connect(&conn, remote_addr).await,
|
||||||
|
}?;
|
||||||
|
|
||||||
let request_data = match request_packet.encode() {
|
let request_data = match request_packet.encode() {
|
||||||
Ok(encoded) => encoded,
|
Ok(encoded) => encoded,
|
||||||
Err(e) => return Err(FailedRadiusPacketEncoding(format!("{:?}", e))),
|
Err(e) => return Err(FailedRadiusPacketEncoding(format!("{:?}", e))),
|
||||||
};
|
};
|
||||||
|
|
||||||
match conn.send(request_data.as_slice()).await {
|
let response = match self.socket_timeout {
|
||||||
|
Some(socket_timeout) => {
|
||||||
|
match timeout(
|
||||||
|
socket_timeout,
|
||||||
|
self.request(&conn, &request_data, remote_addr),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(response) => response,
|
||||||
|
Err(_) => Err(ClientError::SocketTimeoutError()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => self.request(&conn, &request_data, remote_addr).await,
|
||||||
|
}?;
|
||||||
|
|
||||||
|
match Packet::decode(&response.to_vec(), request_packet.get_secret()) {
|
||||||
|
Ok(response_packet) => Ok(response_packet),
|
||||||
|
Err(e) => Err(FailedParsingUDPResponse(format!("{:?}", e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn connect(&self, conn: &UdpSocket, remote_addr: &SocketAddr) -> Result<(), ClientError> {
|
||||||
|
match conn.connect(remote_addr).await {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => Err(FailedConnection(remote_addr.to_string(), e.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request(
|
||||||
|
&self,
|
||||||
|
conn: &UdpSocket,
|
||||||
|
request_data: &[u8],
|
||||||
|
remote_addr: &SocketAddr,
|
||||||
|
) -> Result<Vec<u8>, ClientError> {
|
||||||
|
match conn.send(request_data).await {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => return Err(FailedSendingPacket(remote_addr.to_string(), e.to_string())),
|
Err(e) => return Err(FailedSendingPacket(remote_addr.to_string(), e.to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut buf = vec![0; Self::MAX_DATAGRAM_SIZE];
|
let mut buf = vec![0; Self::MAX_DATAGRAM_SIZE];
|
||||||
let len = match conn.recv(&mut buf).await {
|
match conn.recv(&mut buf).await {
|
||||||
Ok(len) => len,
|
Ok(len) => Ok(buf[..len].to_vec()),
|
||||||
Err(e) => {
|
Err(e) => Err(FailedReceivingResponse(
|
||||||
return Err(FailedReceivingResponse(
|
remote_addr.to_string(),
|
||||||
remote_addr.to_string(),
|
e.to_string(),
|
||||||
e.to_string(),
|
)),
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
match Packet::decode(&buf[..len].to_vec(), request_packet.get_secret()) {
|
|
||||||
Ok(response_packet) => Ok(response_packet),
|
|
||||||
Err(e) => Err(FailedParsingUDPResponse(format!("{:?}", e))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user