mirror of
https://github.com/cubixle/radius-rs.git
synced 2026-04-29 12:38:41 +01:00
Add server
This commit is contained in:
+1
-1
@@ -14,7 +14,7 @@ pub struct AVP {
|
||||
pub struct Attributes(pub(crate) Vec<AVP>);
|
||||
|
||||
impl Attributes {
|
||||
pub fn parse_attributes(bs: &Vec<u8>) -> Result<Attributes, String> {
|
||||
pub(crate) fn parse_attributes(bs: &Vec<u8>) -> Result<Attributes, String> {
|
||||
let mut i = 0;
|
||||
let mut attrs = Vec::new();
|
||||
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ use std::convert::TryFrom;
|
||||
|
||||
use num_enum::TryFromPrimitive;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, TryFromPrimitive)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, TryFromPrimitive)]
|
||||
#[repr(u8)]
|
||||
pub enum Code {
|
||||
AccessRequest = 1,
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
|
||||
pub mod code;
|
||||
pub mod attribute;
|
||||
pub mod attributes;
|
||||
pub mod packet;
|
||||
pub mod server;
|
||||
pub mod secret_provider;
|
||||
pub mod request_handler;
|
||||
pub mod request;
|
||||
|
||||
+5
-2
@@ -1,5 +1,4 @@
|
||||
use std::convert::TryInto;
|
||||
use std::io::Write;
|
||||
|
||||
use rand::Rng;
|
||||
|
||||
@@ -30,6 +29,10 @@ impl Packet {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_identifier(&self) -> u8 {
|
||||
self.identifier
|
||||
}
|
||||
|
||||
pub fn parse(bs: &Vec<u8>, secret: &Vec<u8>) -> Result<Self, String> {
|
||||
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());
|
||||
@@ -129,7 +132,7 @@ impl Packet {
|
||||
].concat()).to_vec().eq(&response[4..20].to_vec())
|
||||
}
|
||||
|
||||
pub fn is_authentic_request(request: Vec<u8>, secret: Vec<u8>) -> bool {
|
||||
pub fn is_authentic_request(request: &Vec<u8>, secret: &Vec<u8>) -> bool {
|
||||
if request.len() < 20 || secret.len() == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use crate::packet::Packet;
|
||||
|
||||
pub struct Request {
|
||||
local_addr: SocketAddr,
|
||||
remote_addr: SocketAddr,
|
||||
packet: Packet,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub(crate) fn new(local_addr: SocketAddr, remote_addr: SocketAddr, packet: Packet) -> Self {
|
||||
Self {
|
||||
local_addr,
|
||||
remote_addr,
|
||||
packet,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_local_addr(&self) -> SocketAddr {
|
||||
self.local_addr
|
||||
}
|
||||
|
||||
pub fn get_remote_addr(&self) -> SocketAddr {
|
||||
self.remote_addr
|
||||
}
|
||||
|
||||
pub fn get_packet(&self) -> &Packet {
|
||||
&self.packet
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::request::Request;
|
||||
|
||||
pub trait RequestHandler: Sync {
|
||||
fn handle_radius_request(&self, conn: &UdpSocket, request: &Request);
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SecretProviderError {
|
||||
#[error("failed to fetch a secret value => `{0}`")]
|
||||
FailedFetching(String)
|
||||
}
|
||||
|
||||
pub trait SecretProvider: Sync {
|
||||
fn fetch_secret(&self, remote_addr: SocketAddr) -> Result<Vec<u8>, SecretProviderError>;
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
use std::borrow::Borrow;
|
||||
use std::collections::HashSet;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use crate::packet::Packet;
|
||||
use crate::request::Request;
|
||||
use crate::request_handler::RequestHandler;
|
||||
use crate::secret_provider::SecretProvider;
|
||||
|
||||
pub struct Server<T: 'static + RequestHandler, U: 'static + SecretProvider> {
|
||||
address: String,
|
||||
buf_size: u8,
|
||||
skip_authenticity_validation: bool,
|
||||
request_handler: &'static T,
|
||||
secret_provider: &'static U,
|
||||
}
|
||||
|
||||
impl<T: RequestHandler, U: SecretProvider> Server<T, U> {
|
||||
pub fn new(host: &str, port: u16, buf_size: u8, skip_authenticity_validation: bool, request_handler: &'static T, secret_provider: &'static U) -> Self {
|
||||
Self {
|
||||
address: format!("{}:{}", host, port),
|
||||
buf_size,
|
||||
skip_authenticity_validation,
|
||||
request_handler,
|
||||
secret_provider,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run(&'static self) -> Result<(), io::Error> {
|
||||
let mut buf = vec![0, self.buf_size];
|
||||
|
||||
let conn_arc = Arc::new(UdpSocket::bind(&self.address).await?);
|
||||
let undergoing_requests_lock_arc = Arc::new(RwLock::new(HashSet::new()));
|
||||
|
||||
loop {
|
||||
let conn = conn_arc.clone();
|
||||
|
||||
let (size, remote_addr) = conn.recv_from(&mut buf).await?;
|
||||
let request_data = buf[..size].to_vec();
|
||||
|
||||
let local_addr = match conn.local_addr() {
|
||||
Ok(addr) => addr,
|
||||
Err(e) => {
|
||||
error!("failed to get a local address from from a connection; {}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let undergoing_requests_lock = undergoing_requests_lock_arc.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
Self::process_request(
|
||||
conn,
|
||||
&request_data,
|
||||
local_addr,
|
||||
remote_addr,
|
||||
undergoing_requests_lock,
|
||||
self.request_handler,
|
||||
self.secret_provider,
|
||||
self.skip_authenticity_validation,
|
||||
).await;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_request(
|
||||
conn: Arc<UdpSocket>,
|
||||
request_data: &Vec<u8>,
|
||||
local_addr: SocketAddr,
|
||||
remote_addr: SocketAddr,
|
||||
undergoing_requests_lock: Arc<RwLock<HashSet<RequestKey>>>,
|
||||
request_handler: &T,
|
||||
secret_provider: &U,
|
||||
skip_authenticity_validation: bool,
|
||||
) {
|
||||
let secret: Vec<u8> = match secret_provider.fetch_secret(remote_addr) {
|
||||
Ok(secret) => secret,
|
||||
Err(e) => {
|
||||
error!("failed to fetch secret binary vector from the secret provider; {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
if secret.len() <= 0 {
|
||||
error!("empty secret returned from secret source; empty secret is prohibited");
|
||||
return;
|
||||
}
|
||||
|
||||
if !skip_authenticity_validation && !Packet::is_authentic_request(request_data, &secret) {
|
||||
info!("packet validation failed; bad secret");
|
||||
return;
|
||||
}
|
||||
|
||||
let packet = match Packet::parse(request_data, &secret) {
|
||||
Ok(packet) => packet,
|
||||
Err(e) => {
|
||||
error!("failed to parse given request data to pack into the RADIUS packet; {}", e);
|
||||
debug!("failed request data => {:?}", request_data);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let key = RequestKey {
|
||||
ip: remote_addr.to_string(),
|
||||
identifier: packet.get_identifier(),
|
||||
};
|
||||
let key_for_remove = key.clone();
|
||||
|
||||
{
|
||||
let mut undergoing_requests = undergoing_requests_lock.write().unwrap();
|
||||
if undergoing_requests.contains(&key) {
|
||||
return;
|
||||
}
|
||||
undergoing_requests.insert(key);
|
||||
}
|
||||
|
||||
request_handler.handle_radius_request(conn.borrow(), &Request::new(local_addr, remote_addr, packet));
|
||||
|
||||
let mut undergoing_requests = undergoing_requests_lock.write().unwrap();
|
||||
undergoing_requests.remove(&key_for_remove);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Clone)]
|
||||
struct RequestKey {
|
||||
ip: String,
|
||||
identifier: u8,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user