From 04b962ab36a8c047559d88d7cb7af9e9d1785163 Mon Sep 17 00:00:00 2001 From: moznion Date: Mon, 23 Nov 2020 16:34:12 +0900 Subject: [PATCH] Add client impl --- src/client.rs | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/packet.rs | 6 ++++- src/server.rs | 1 + 4 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/client.rs diff --git a/src/client.rs b/src/client.rs new file mode 100644 index 0000000..8a2c386 --- /dev/null +++ b/src/client.rs @@ -0,0 +1,69 @@ +use std::net::SocketAddr; + +use thiserror::Error; +use tokio::net::UdpSocket; + +use crate::client::ClientError::{FailedConnection, FailedParsingUDPResponse, FailedRadiusPacketEncoding, FailedReceivingResponse, FailedSendingPacket, FailedUdpSocketBinding}; +use crate::packet::Packet; + +#[derive(Error, Debug)] +pub enum ClientError { + #[error("failed to bind a UDP socket => `{0}`")] + FailedUdpSocketBinding(String), + #[error("failed to connect to `{0}` => `{1}`")] + FailedConnection(String, String), + #[error("failed to encode a RADIUS request => `{0}`")] + FailedRadiusPacketEncoding(String), + #[error("failed to send a UDP datagram to `{0}` => `{1}`")] + FailedSendingPacket(String, String), + #[error("failed to receive the UDP response from `{0}` => `{1}`")] + FailedReceivingResponse(String, String), + #[error("failed to parse a UDP response into a RADIUS packet => `{0}`")] + FailedParsingUDPResponse(String), +} + +pub struct Client {} + +impl Client { + const MAX_DATAGRAM_SIZE: usize = 65507; + + pub async fn send_packet(remote_addr: &SocketAddr, request_packet: &Packet) -> Result { + // TODO retransmission + + let local_addr: SocketAddr = if remote_addr.is_ipv4() { + "0.0.0.0:0" + } else { + "[::]:0" + }.parse().unwrap(); + + let conn = match UdpSocket::bind(local_addr).await { + Ok(conn) => conn, + Err(e) => return Err(FailedUdpSocketBinding(e.to_string())), + }; + match conn.connect(remote_addr).await { + Ok(_) => {} + Err(e) => return Err(FailedConnection(remote_addr.to_string(), e.to_string())) + }; + + let request_data = match request_packet.encode() { + Ok(encoded) => encoded, + Err(e) => return Err(FailedRadiusPacketEncoding(e.to_string())) + }; + + match conn.send(request_data.as_slice()).await { + Ok(_) => {} + Err(e) => return Err(FailedSendingPacket(remote_addr.to_string(), e.to_string())) + }; + + let mut buf = vec![0; Self::MAX_DATAGRAM_SIZE]; + let len = match conn.recv(&mut buf).await { + Ok(len) => len, + Err(e) => return Err(FailedReceivingResponse(remote_addr.to_string(), e.to_string())) + }; + + match Packet::parse(&buf[..len].to_vec(), request_packet.get_secret()) { + Ok(response_packet) => Ok(response_packet), + Err(e) => Err(FailedParsingUDPResponse(e.to_string())) + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 2982981..7877b60 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,3 +9,4 @@ pub mod server; pub mod secret_provider; pub mod request_handler; pub mod request; +pub mod client; diff --git a/src/packet.rs b/src/packet.rs index 49067e2..e4c243a 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -29,10 +29,14 @@ impl Packet { } } - pub(crate) fn get_identifier(&self) -> u8 { + pub fn get_identifier(&self) -> u8 { self.identifier } + pub fn get_secret(&self) -> &Vec { + &self.secret + } + pub fn parse(bs: &Vec, secret: &Vec) -> Result { if bs.len() < 20 { return Err("radius packet doesn't have enough length of bytes; that has to be at least 20 bytes".to_owned()); diff --git a/src/server.rs b/src/server.rs index 05735c3..d6bc133 100644 --- a/src/server.rs +++ b/src/server.rs @@ -118,6 +118,7 @@ impl Server { Err(e) => { error!("failed to parse given request data to pack into the RADIUS packet; {}", e); debug!("failed request data => {:?}", request_data); + // TODO error handler? return; } };