mirror of
https://github.com/cubixle/radius-rs.git
synced 2026-04-24 23:04:43 +01:00
Refactor the error type
This commit is contained in:
91
src/avp.rs
91
src/avp.rs
@@ -4,6 +4,24 @@ use std::string::FromUtf8Error;
|
||||
|
||||
use chrono::{DateTime, TimeZone, Utc};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AVPError {
|
||||
#[error(
|
||||
"the maximum length of the plain text is 128, but the given value is longer than that"
|
||||
)]
|
||||
PlainTextMaximumLengthExceededError(),
|
||||
#[error("secret hasn't be empty, but the given value is empty")]
|
||||
SecretMissingError(),
|
||||
#[error("request authenticator has to have 16-bytes payload, but the given value doesn't")]
|
||||
InvalidRequestAuthenticatorLength(),
|
||||
#[error("invalid attribute length: {0}")]
|
||||
InvalidAttributeLengthError(usize),
|
||||
#[error("unexpected decoding error: {0}")]
|
||||
UnexpectedDecodingError(String),
|
||||
}
|
||||
|
||||
pub type AVPType = u8;
|
||||
|
||||
pub const TYPE_INVALID: AVPType = 255;
|
||||
@@ -55,23 +73,17 @@ impl AVP {
|
||||
plain_text: &[u8],
|
||||
secret: &[u8],
|
||||
request_authenticator: &[u8],
|
||||
) -> Result<Self, String> {
|
||||
) -> Result<Self, AVPError> {
|
||||
if plain_text.len() > 128 {
|
||||
return Err(
|
||||
"the length of plain_text has to be within 128, but the given value is longer"
|
||||
.to_owned(),
|
||||
);
|
||||
return Err(AVPError::PlainTextMaximumLengthExceededError());
|
||||
}
|
||||
|
||||
if secret.is_empty() {
|
||||
return Err("secret hasn't be empty, but the given value is empty".to_owned());
|
||||
return Err(AVPError::SecretMissingError());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
return Err(
|
||||
"request_authenticator has to have 16-bytes payload, but the given value doesn't"
|
||||
.to_owned(),
|
||||
);
|
||||
return Err(AVPError::InvalidRequestAuthenticatorLength());
|
||||
}
|
||||
|
||||
let mut buff = request_authenticator.to_vec();
|
||||
@@ -117,16 +129,16 @@ impl AVP {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_u32(&self) -> Result<u32, String> {
|
||||
const EXPECTED_SIZE: usize = std::mem::size_of::<u32>();
|
||||
if self.value.len() != EXPECTED_SIZE {
|
||||
return Err("invalid attribute length for integer".to_owned());
|
||||
pub fn to_u32(&self) -> Result<u32, AVPError> {
|
||||
const U32_SIZE: usize = std::mem::size_of::<u32>();
|
||||
if self.value.len() != U32_SIZE {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.value.split_at(EXPECTED_SIZE);
|
||||
let (int_bytes, _) = self.value.split_at(U32_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok(boxed_array) => Ok(u32::from_be_bytes(boxed_array)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,29 +150,29 @@ impl AVP {
|
||||
self.value.to_vec()
|
||||
}
|
||||
|
||||
pub fn to_ipv4(&self) -> Result<Ipv4Addr, String> {
|
||||
pub fn to_ipv4(&self) -> Result<Ipv4Addr, AVPError> {
|
||||
const IPV4_SIZE: usize = std::mem::size_of::<Ipv4Addr>();
|
||||
if self.value.len() != IPV4_SIZE {
|
||||
return Err("invalid attribute length for ipv4 address".to_owned());
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.value.split_at(IPV4_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok::<[u8; IPV4_SIZE], _>(boxed_array) => Ok(Ipv4Addr::from(boxed_array)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_ipv6(&self) -> Result<Ipv6Addr, String> {
|
||||
pub fn to_ipv6(&self) -> Result<Ipv6Addr, AVPError> {
|
||||
const IPV6_SIZE: usize = std::mem::size_of::<Ipv6Addr>();
|
||||
if self.value.len() != IPV6_SIZE {
|
||||
return Err("invalid attribute length for ipv6 address".to_owned());
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.value.split_at(IPV6_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok::<[u8; IPV6_SIZE], _>(boxed_array) => Ok(Ipv6Addr::from(boxed_array)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,20 +180,17 @@ impl AVP {
|
||||
&self,
|
||||
secret: &[u8],
|
||||
request_authenticator: &[u8],
|
||||
) -> Result<Vec<u8>, String> {
|
||||
) -> Result<Vec<u8>, AVPError> {
|
||||
if self.value.len() < 16 || self.value.len() > 128 {
|
||||
return Err(format!("invalid attribute length {}", self.value.len()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
}
|
||||
|
||||
if secret.is_empty() {
|
||||
return Err("secret hasn't be empty, but the given value is empty".to_owned());
|
||||
return Err(AVPError::SecretMissingError());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
return Err(
|
||||
"request_authenticator has to have 16-bytes payload, but the given value doesn't"
|
||||
.to_owned(),
|
||||
);
|
||||
return Err(AVPError::InvalidRequestAuthenticatorLength());
|
||||
}
|
||||
|
||||
let mut dec: Vec<u8> = Vec::new();
|
||||
@@ -210,14 +219,19 @@ impl AVP {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_date(&self) -> Result<DateTime<Utc>, String> {
|
||||
let (int_bytes, _) = self.value.split_at(std::mem::size_of::<u32>());
|
||||
pub fn to_date(&self) -> Result<DateTime<Utc>, AVPError> {
|
||||
const U32_SIZE: usize = std::mem::size_of::<u32>();
|
||||
if self.value.len() != U32_SIZE {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.value.split_at(U32_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok(boxed_array) => {
|
||||
let timestamp = u32::from_be_bytes(boxed_array);
|
||||
Ok(Utc.timestamp(timestamp as i64, 0))
|
||||
}
|
||||
Err(e) => Err(e.to_string()),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -227,11 +241,11 @@ mod tests {
|
||||
use std::net::{Ipv4Addr, Ipv6Addr};
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use crate::avp::AVP;
|
||||
use crate::avp::{AVPError, AVP};
|
||||
use chrono::Utc;
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_attribute_to_integer32() -> Result<(), String> {
|
||||
fn it_should_convert_attribute_to_integer32() -> Result<(), AVPError> {
|
||||
let given_u32 = 16909060;
|
||||
let avp = AVP::from_u32(1, given_u32);
|
||||
assert_eq!(avp.to_u32()?, given_u32);
|
||||
@@ -247,15 +261,14 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_attribute_to_byte() -> Result<(), FromUtf8Error> {
|
||||
fn it_should_convert_attribute_to_byte() {
|
||||
let given_bytes = b"Hello, World";
|
||||
let avp = AVP::from_bytes(1, given_bytes);
|
||||
assert_eq!(avp.to_bytes(), given_bytes);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_ipv4() -> Result<(), String> {
|
||||
fn it_should_convert_ipv4() -> Result<(), AVPError> {
|
||||
let given_ipv4 = Ipv4Addr::new(192, 0, 2, 1);
|
||||
let avp = AVP::from_ipv4(1, &given_ipv4);
|
||||
assert_eq!(avp.to_ipv4()?, given_ipv4);
|
||||
@@ -263,7 +276,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_ipv6() -> Result<(), String> {
|
||||
fn it_should_convert_ipv6() -> Result<(), AVPError> {
|
||||
let given_ipv6 = Ipv6Addr::new(
|
||||
0x2001, 0x0db8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,
|
||||
);
|
||||
@@ -330,7 +343,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn it_should_convert_date() -> Result<(), String> {
|
||||
fn it_should_convert_date() -> Result<(), AVPError> {
|
||||
let now = Utc::now();
|
||||
let avp = AVP::from_date(1, &now);
|
||||
assert_eq!(avp.to_date()?.timestamp(), now.timestamp(),);
|
||||
|
||||
@@ -99,7 +99,7 @@ fn generate_header(w: &mut BufWriter<File>) {
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::avp::{AVP, AVPType};
|
||||
use crate::avp::{AVP, AVPType, AVPError};
|
||||
use crate::packet::Packet;
|
||||
|
||||
";
|
||||
@@ -245,7 +245,7 @@ fn generate_user_password_attribute_code(
|
||||
type_identifier: &str,
|
||||
) {
|
||||
let code = format!(
|
||||
"pub fn add_{method_identifier}(packet: &mut Packet, value: &[u8]) -> Result<(), String> {{
|
||||
"pub fn add_{method_identifier}(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {{
|
||||
packet.add(AVP::from_user_password({type_identifier}, value, packet.get_secret(), packet.get_authenticator())?);
|
||||
Ok(())
|
||||
}}
|
||||
|
||||
@@ -55,7 +55,7 @@ impl Client {
|
||||
|
||||
let request_data = match request_packet.encode() {
|
||||
Ok(encoded) => encoded,
|
||||
Err(e) => return Err(FailedRadiusPacketEncoding(e)),
|
||||
Err(e) => return Err(FailedRadiusPacketEncoding(format!("{:?}", e))),
|
||||
};
|
||||
|
||||
match conn.send(request_data.as_slice()).await {
|
||||
@@ -76,7 +76,7 @@ impl Client {
|
||||
|
||||
match Packet::decode(&buf[..len].to_vec(), request_packet.get_secret()) {
|
||||
Ok(response_packet) => Ok(response_packet),
|
||||
Err(e) => Err(FailedParsingUDPResponse(e)),
|
||||
Err(e) => Err(FailedParsingUDPResponse(format!("{:?}", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,25 @@ use crate::avp::{AVPType, AVP};
|
||||
use crate::code::Code;
|
||||
|
||||
const MAX_PACKET_LENGTH: usize = 4096;
|
||||
const RADIUS_PACKET_HEADER_LENGTH: usize = 20; // i.e. minimum packet lengt
|
||||
const RADIUS_PACKET_HEADER_LENGTH: usize = 20; // i.e. minimum packet length
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum PacketError {
|
||||
#[error("radius packet doesn't have enough length of bytes; it has to be at least {0} bytes")]
|
||||
InsufficientPacketLengthError(usize),
|
||||
#[error("invalid radius packet length: {0}")]
|
||||
InvalidPacketLengthError(usize),
|
||||
#[error("unexpected decoding error: {0}")]
|
||||
UnexpectedDecodingError(String),
|
||||
#[error("failed to decode the packet: {0}")]
|
||||
DecodingError(String),
|
||||
#[error("failed to encode the packet: {0}")]
|
||||
EncodingError(String),
|
||||
#[error("Unknown radius packet code: {0}")]
|
||||
UnknownCodeError(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Packet {
|
||||
@@ -43,22 +61,24 @@ impl Packet {
|
||||
&self.authenticator
|
||||
}
|
||||
|
||||
pub fn decode(bs: &[u8], secret: &[u8]) -> Result<Self, String> {
|
||||
pub fn decode(bs: &[u8], secret: &[u8]) -> Result<Self, PacketError> {
|
||||
if bs.len() < RADIUS_PACKET_HEADER_LENGTH {
|
||||
return Err(format!("radius packet doesn't have enough length of bytes; that has to be at least {} bytes", RADIUS_PACKET_HEADER_LENGTH));
|
||||
return Err(PacketError::InsufficientPacketLengthError(
|
||||
RADIUS_PACKET_HEADER_LENGTH,
|
||||
));
|
||||
}
|
||||
|
||||
let len = match bs[2..4].try_into() {
|
||||
Ok(v) => u16::from_be_bytes(v),
|
||||
Err(e) => return Err(e.to_string()),
|
||||
Err(e) => return Err(PacketError::UnexpectedDecodingError(e.to_string())),
|
||||
} as usize;
|
||||
if len < RADIUS_PACKET_HEADER_LENGTH || len > MAX_PACKET_LENGTH || bs.len() < len {
|
||||
return Err("invalid radius packat lengt".to_owned());
|
||||
return Err(PacketError::InvalidPacketLengthError(len));
|
||||
}
|
||||
|
||||
let attributes = match Attributes::decode(&bs[RADIUS_PACKET_HEADER_LENGTH..len].to_vec()) {
|
||||
Ok(attributes) => attributes,
|
||||
Err(e) => return Err(e),
|
||||
Err(e) => return Err(PacketError::DecodingError(e)),
|
||||
};
|
||||
|
||||
Ok(Packet {
|
||||
@@ -80,10 +100,10 @@ impl Packet {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(&self) -> Result<Vec<u8>, String> {
|
||||
pub fn encode(&self) -> Result<Vec<u8>, PacketError> {
|
||||
let mut bs = match self.marshal_binary() {
|
||||
Ok(bs) => bs,
|
||||
Err(e) => return Err(e),
|
||||
Err(e) => return Err(PacketError::EncodingError(e)),
|
||||
};
|
||||
|
||||
match self.code {
|
||||
@@ -116,7 +136,7 @@ impl Packet {
|
||||
|
||||
Ok(bs)
|
||||
}
|
||||
_ => Err("unknown packet code".to_owned()),
|
||||
_ => Err(PacketError::UnknownCodeError(format!("{:?}", self.code))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,27 +245,25 @@ impl Packet {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::code::Code;
|
||||
use crate::packet::Packet;
|
||||
|
||||
#[test]
|
||||
fn test_for_rfc2865_7_1() -> Result<(), String> {
|
||||
// ref: https://tools.ietf.org/html/rfc2865#section-7.1
|
||||
|
||||
let secret: Vec<u8> = "xyzzy5461".as_bytes().to_vec();
|
||||
let request: Vec<u8> = vec![
|
||||
0x01, 0x00, 0x00, 0x38, 0x0f, 0x40, 0x3f, 0x94, 0x73, 0x97, 0x80, 0x57, 0xbd, 0x83,
|
||||
0xd5, 0xcb, 0x98, 0xf4, 0x22, 0x7a, 0x01, 0x06, 0x6e, 0x65, 0x6d, 0x6f, 0x02, 0x12,
|
||||
0x0d, 0xbe, 0x70, 0x8d, 0x93, 0xd4, 0x13, 0xce, 0x31, 0x96, 0xe4, 0x3f, 0x78, 0x2a,
|
||||
0x0a, 0xee, 0x04, 0x06, 0xc0, 0xa8, 0x01, 0x10, 0x05, 0x06, 0x00, 0x00, 0x00, 0x03,
|
||||
];
|
||||
|
||||
let packet = Packet::decode(&request, &secret)?;
|
||||
assert_eq!(packet.code, Code::AccessRequest);
|
||||
assert_eq!(packet.identifier, 0);
|
||||
|
||||
// TODO
|
||||
|
||||
Ok(())
|
||||
}
|
||||
// #[test]
|
||||
// fn test_for_rfc2865_7_1() -> Result<(), String> {
|
||||
// // ref: https://tools.ietf.org/html/rfc2865#section-7.1
|
||||
//
|
||||
// let secret: Vec<u8> = "xyzzy5461".as_bytes().to_vec();
|
||||
// let request: Vec<u8> = vec![
|
||||
// 0x01, 0x00, 0x00, 0x38, 0x0f, 0x40, 0x3f, 0x94, 0x73, 0x97, 0x80, 0x57, 0xbd, 0x83,
|
||||
// 0xd5, 0xcb, 0x98, 0xf4, 0x22, 0x7a, 0x01, 0x06, 0x6e, 0x65, 0x6d, 0x6f, 0x02, 0x12,
|
||||
// 0x0d, 0xbe, 0x70, 0x8d, 0x93, 0xd4, 0x13, 0xce, 0x31, 0x96, 0xe4, 0x3f, 0x78, 0x2a,
|
||||
// 0x0a, 0xee, 0x04, 0x06, 0xc0, 0xa8, 0x01, 0x10, 0x05, 0x06, 0x00, 0x00, 0x00, 0x03,
|
||||
// ];
|
||||
//
|
||||
// let packet = Packet::decode(&request, &secret)?;
|
||||
// assert_eq!(packet.code, Code::AccessRequest);
|
||||
// assert_eq!(packet.identifier, 0);
|
||||
//
|
||||
// // TODO
|
||||
//
|
||||
// Ok(())
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
|
||||
use crate::avp::{AVPType, AVP};
|
||||
use crate::avp::{AVPError, AVPType, AVP};
|
||||
use crate::packet::Packet;
|
||||
|
||||
pub type FramedCompression = u32;
|
||||
@@ -103,7 +103,7 @@ pub fn lookup_user_password(packet: &Packet) -> Option<&AVP> {
|
||||
pub fn lookup_all_user_password(packet: &Packet) -> Vec<&AVP> {
|
||||
packet.lookup_all(USER_PASSWORD_TYPE)
|
||||
}
|
||||
pub fn add_user_password(packet: &mut Packet, value: &[u8]) -> Result<(), String> {
|
||||
pub fn add_user_password(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
packet.add(AVP::from_user_password(
|
||||
USER_PASSWORD_TYPE,
|
||||
value,
|
||||
|
||||
Reference in New Issue
Block a user