mirror of
https://github.com/cubixle/radius-rs.git
synced 2026-04-24 19:54:45 +01:00
Improve error enums
This commit is contained in:
@@ -556,7 +556,7 @@ fn generate_fixed_length_octets_attribute_code(
|
||||
let code = format!(
|
||||
"pub fn add_{method_identifier}(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {{
|
||||
if value.len() != {fixed_octets_length} {{
|
||||
return Err(AVPError::InvalidAttributeLengthError({fixed_octets_length}));
|
||||
return Err(AVPError::InvalidAttributeLengthError(\"{fixed_octets_length} bytes\".to_owned(), value.len()));
|
||||
}}
|
||||
packet.add(AVP::from_bytes({type_identifier}, value));
|
||||
Ok(())
|
||||
|
||||
@@ -7,27 +7,39 @@ use tokio::time::timeout;
|
||||
|
||||
use radius::packet::Packet;
|
||||
|
||||
use crate::client::ClientError::{
|
||||
FailedConnection, FailedParsingUDPResponse, FailedRadiusPacketEncoding,
|
||||
FailedReceivingResponse, FailedSendingPacket, FailedUdpSocketBinding,
|
||||
};
|
||||
|
||||
#[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),
|
||||
/// This error is occurred when UDP socket binding has been failed.
|
||||
#[error("failed to bind a UDP socket; {0}")]
|
||||
FailedUdpSocketBindingError(String),
|
||||
|
||||
/// This error is raised when it failed to establish the connection.
|
||||
#[error("failed to establish a UDP connection to {0}; {1}")]
|
||||
FailedEstablishingUdpConnectionError(String, String),
|
||||
|
||||
/// This error is raised when encoding RADIUS packet has been failed.
|
||||
#[error("failed to encode a RADIUS request; {0}")]
|
||||
FailedRadiusPacketEncodingError(String),
|
||||
|
||||
/// This error is raised when it fails to send a RADIUS packet.
|
||||
#[error("failed to send a UDP datagram to {0}; {1}")]
|
||||
FailedSendingRadiusPacketError(String, String),
|
||||
|
||||
/// This error is raised when it fails to receive a RADIUS response.
|
||||
#[error("failed to receive the UDP response from {0}; {1}")]
|
||||
FailedReceivingResponseError(String, String),
|
||||
|
||||
/// This error is raised when it fails to decode a RADIUS response packet.
|
||||
#[error("failed to decode a RADIUS response packet; {0}")]
|
||||
FailedDecodingRadiusResponseError(String),
|
||||
|
||||
/// This error is raised when it exceeds the connection timeout duration.
|
||||
/// Connection timeout means it fails to establish a connection in time.
|
||||
#[error("connection timeout")]
|
||||
ConnectionTimeoutError(),
|
||||
|
||||
/// This error is raised when it exceeds the socket timeout duration.
|
||||
/// Socket timeout means it fails to receive a response from the request target in time.
|
||||
#[error("socket timeout")]
|
||||
SocketTimeoutError(),
|
||||
}
|
||||
@@ -74,7 +86,7 @@ impl Client {
|
||||
|
||||
let conn = match UdpSocket::bind(local_addr).await {
|
||||
Ok(conn) => conn,
|
||||
Err(e) => return Err(FailedUdpSocketBinding(e.to_string())),
|
||||
Err(e) => return Err(ClientError::FailedUdpSocketBindingError(e.to_string())),
|
||||
};
|
||||
|
||||
match self.connection_timeout {
|
||||
@@ -89,7 +101,12 @@ impl Client {
|
||||
|
||||
let request_data = match request_packet.encode() {
|
||||
Ok(encoded) => encoded,
|
||||
Err(e) => return Err(FailedRadiusPacketEncoding(format!("{:?}", e))),
|
||||
Err(e) => {
|
||||
return Err(ClientError::FailedRadiusPacketEncodingError(format!(
|
||||
"{:?}",
|
||||
e
|
||||
)))
|
||||
}
|
||||
};
|
||||
|
||||
let response = match self.socket_timeout {
|
||||
@@ -109,14 +126,20 @@ impl Client {
|
||||
|
||||
match Packet::decode(&response.to_vec(), request_packet.get_secret()) {
|
||||
Ok(response_packet) => Ok(response_packet),
|
||||
Err(e) => Err(FailedParsingUDPResponse(format!("{:?}", e))),
|
||||
Err(e) => Err(ClientError::FailedDecodingRadiusResponseError(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())),
|
||||
Err(e) => Err(ClientError::FailedEstablishingUdpConnectionError(
|
||||
remote_addr.to_string(),
|
||||
e.to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,13 +151,18 @@ impl Client {
|
||||
) -> Result<Vec<u8>, ClientError> {
|
||||
match conn.send(request_data).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => return Err(FailedSendingPacket(remote_addr.to_string(), e.to_string())),
|
||||
Err(e) => {
|
||||
return Err(ClientError::FailedSendingRadiusPacketError(
|
||||
remote_addr.to_string(),
|
||||
e.to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let mut buf = vec![0; Self::MAX_DATAGRAM_SIZE];
|
||||
match conn.recv(&mut buf).await {
|
||||
Ok(len) => Ok(buf[..len].to_vec()),
|
||||
Err(e) => Err(FailedReceivingResponse(
|
||||
Err(e) => Err(ClientError::FailedReceivingResponseError(
|
||||
remote_addr.to_string(),
|
||||
e.to_string(),
|
||||
)),
|
||||
|
||||
@@ -4,8 +4,12 @@ use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum SecretProviderError {
|
||||
#[error("failed to fetch a secret value => `{0}`")]
|
||||
FailedFetching(String),
|
||||
/// An error that represents a failure to fetch a secret value from the provider.
|
||||
#[error("failed to fetch a secret value: {0}")]
|
||||
FailedFetchingError(String),
|
||||
/// An error that represents a generic (i.e. unclassified) error that occurs on the secret value provider.
|
||||
#[error("unexpected error: {0}")]
|
||||
GenericError(String),
|
||||
}
|
||||
|
||||
/// SecretProvider is a provider for secret value.
|
||||
|
||||
@@ -9,23 +9,39 @@ use crate::tag::{Tag, UNUSED_TAG_VALUE};
|
||||
|
||||
#[derive(Error, PartialEq, 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")]
|
||||
/// This error is raised on the length of given plain text for user-password exceeds the maximum limit.
|
||||
#[error("the maximum length of the plain text for user-password is 128, but the given value has {0} bytes")]
|
||||
UserPasswordPlainTextMaximumLengthExceededError(usize),
|
||||
|
||||
/// This error is raised when the given secret value for a password is empty.
|
||||
#[error("secret for password mustn't be empty, but the given value is empty")]
|
||||
PasswordSecretMissingError(),
|
||||
|
||||
/// This error is raised when the given request-authenticator for the password doesn't have 16 bytes length exactly.
|
||||
#[error("request authenticator for password has to have 16-bytes payload, but the given value doesn't")]
|
||||
InvalidRequestAuthenticatorLength(),
|
||||
#[error("invalid attribute length: {0}")]
|
||||
InvalidAttributeLengthError(usize),
|
||||
// ^ TODO: more meaningful error message
|
||||
#[error("unexpected decoding error: {0}")]
|
||||
UnexpectedDecodingError(String),
|
||||
|
||||
/// This error is raised when attribute length is conflicted with the expected.
|
||||
#[error("invalid attribute length: expected={0}, actual={1} bytes")]
|
||||
InvalidAttributeLengthError(String, usize),
|
||||
|
||||
/// This error is raised when the tagged-value doesn't have a tag byte.
|
||||
#[error("tag value is missing")]
|
||||
TagMissingError(),
|
||||
|
||||
/// This error represents AVP decoding error.
|
||||
#[error("decoding error: {0}")]
|
||||
DecodingError(String),
|
||||
|
||||
/// This error is raised when the MSB of salt is invalid.
|
||||
#[error("invalid salt. the MSB has to be 1, but given value isn't: {0}")]
|
||||
InvalidSaltMSBError(u8),
|
||||
|
||||
/// This error is raised when a tag is invalid for the tagged-staring value.
|
||||
#[error("invalid tag for string value. this must not be zero")]
|
||||
InvalidTagForStringValueError(),
|
||||
|
||||
/// This error is raised when a tag is invalid for the tagged-integer value.
|
||||
#[error("invalid tag for integer value. this must be less than or equal 0x1f")]
|
||||
InvalidTagForIntegerValueError(),
|
||||
}
|
||||
@@ -115,7 +131,10 @@ impl AVP {
|
||||
pub fn from_ipv4_prefix(typ: AVPType, prefix: &[u8]) -> Result<Self, AVPError> {
|
||||
let prefix_len = prefix.len();
|
||||
if prefix_len != 4 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(prefix_len));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"4 bytes".to_owned(),
|
||||
prefix_len,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(AVP {
|
||||
@@ -136,7 +155,10 @@ impl AVP {
|
||||
pub fn from_ipv6_prefix(typ: AVPType, prefix: &[u8]) -> Result<Self, AVPError> {
|
||||
let prefix_len = prefix.len();
|
||||
if prefix_len > 16 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(prefix_len));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"16 bytes".to_owned(),
|
||||
prefix_len,
|
||||
));
|
||||
}
|
||||
|
||||
Ok(AVP {
|
||||
@@ -169,11 +191,13 @@ impl AVP {
|
||||
// ref: https://tools.ietf.org/html/rfc2865#section-5.2
|
||||
|
||||
if plain_text.len() > 128 {
|
||||
return Err(AVPError::PlainTextMaximumLengthExceededError());
|
||||
return Err(AVPError::UserPasswordPlainTextMaximumLengthExceededError(
|
||||
plain_text.len(),
|
||||
));
|
||||
}
|
||||
|
||||
if secret.is_empty() {
|
||||
return Err(AVPError::SecretMissingError());
|
||||
return Err(AVPError::PasswordSecretMissingError());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
@@ -251,6 +275,7 @@ impl AVP {
|
||||
|
||||
if request_authenticator.len() > 240 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"240 bytes".to_owned(),
|
||||
request_authenticator.len(),
|
||||
));
|
||||
}
|
||||
@@ -259,7 +284,7 @@ impl AVP {
|
||||
let salt: [u8; 2] = [rng.gen::<u8>() | 0x80, rng.gen::<u8>()];
|
||||
|
||||
if secret.is_empty() {
|
||||
return Err(AVPError::SecretMissingError());
|
||||
return Err(AVPError::PasswordSecretMissingError());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
@@ -313,13 +338,16 @@ impl AVP {
|
||||
pub fn encode_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()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
format!("{} bytes", U32_SIZE),
|
||||
self.value.len(),
|
||||
));
|
||||
}
|
||||
|
||||
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(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,20 +355,23 @@ impl AVP {
|
||||
pub fn encode_u16(&self) -> Result<u16, AVPError> {
|
||||
const U16_SIZE: usize = std::mem::size_of::<u16>();
|
||||
if self.value.len() != U16_SIZE {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
format!("{} bytes", U16_SIZE),
|
||||
self.value.len(),
|
||||
));
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.value.split_at(U16_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok(boxed_array) => Ok(u16::from_be_bytes(boxed_array)),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
/// (This method is for dictionary developers) encode an AVP into a tag and u32 value.
|
||||
pub fn encode_tagged_u32(&self) -> Result<(u32, Tag), AVPError> {
|
||||
if self.value.is_empty() {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
return Err(AVPError::TagMissingError());
|
||||
}
|
||||
|
||||
let tag = Tag {
|
||||
@@ -356,12 +387,15 @@ impl AVP {
|
||||
|
||||
const U32_SIZE: usize = std::mem::size_of::<u32>();
|
||||
if self.value[1..].len() != U32_SIZE {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
format!("{} bytes", U32_SIZE + 1),
|
||||
self.value.len(),
|
||||
));
|
||||
}
|
||||
let (int_bytes, _) = self.value[1..].split_at(U32_SIZE);
|
||||
match int_bytes.try_into() {
|
||||
Ok(boxed_array) => Ok((u32::from_be_bytes(boxed_array), tag)),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -369,7 +403,7 @@ impl AVP {
|
||||
pub fn encode_string(&self) -> Result<String, AVPError> {
|
||||
match String::from_utf8(self.value.to_vec()) {
|
||||
Ok(str) => Ok(str),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -377,7 +411,7 @@ impl AVP {
|
||||
pub fn encode_tagged_string(&self) -> Result<(String, Option<Tag>), AVPError> {
|
||||
let string_vec = self.value.to_vec();
|
||||
if string_vec.is_empty() {
|
||||
return Err(AVPError::InvalidAttributeLengthError(string_vec.len()));
|
||||
return Err(AVPError::TagMissingError());
|
||||
}
|
||||
|
||||
let tag = Tag {
|
||||
@@ -392,7 +426,7 @@ impl AVP {
|
||||
if tag.is_valid_value() {
|
||||
return match String::from_utf8(string_vec[1..].to_vec()) {
|
||||
Ok(str) => Ok((str, Some(tag))),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -405,7 +439,7 @@ impl AVP {
|
||||
// interpreted as the first byte of the following String field.
|
||||
match String::from_utf8(self.value.to_vec()) {
|
||||
Ok(str) => Ok((str, None)),
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -418,13 +452,16 @@ impl AVP {
|
||||
pub fn encode_ipv4(&self) -> Result<Ipv4Addr, AVPError> {
|
||||
const IPV4_SIZE: usize = std::mem::size_of::<Ipv4Addr>();
|
||||
if self.value.len() != IPV4_SIZE {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
format!("{} bytes", IPV4_SIZE),
|
||||
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(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,7 +469,10 @@ impl AVP {
|
||||
pub fn encode_ipv4_prefix(&self) -> Result<Vec<u8>, AVPError> {
|
||||
match self.value.len() == 6 {
|
||||
true => Ok(self.value[2..].to_owned()),
|
||||
false => Err(AVPError::InvalidAttributeLengthError(self.value.len())),
|
||||
false => Err(AVPError::InvalidAttributeLengthError(
|
||||
"6 bytes".to_owned(),
|
||||
self.value.len(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,13 +480,16 @@ impl AVP {
|
||||
pub fn encode_ipv6(&self) -> Result<Ipv6Addr, AVPError> {
|
||||
const IPV6_SIZE: usize = std::mem::size_of::<Ipv6Addr>();
|
||||
if self.value.len() != IPV6_SIZE {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
format!("{} bytes", IPV6_SIZE),
|
||||
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(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -454,7 +497,10 @@ impl AVP {
|
||||
pub fn encode_ipv6_prefix(&self) -> Result<Vec<u8>, AVPError> {
|
||||
match self.value.len() >= 2 {
|
||||
true => Ok(self.value[2..].to_owned()),
|
||||
false => Err(AVPError::InvalidAttributeLengthError(self.value.len())),
|
||||
false => Err(AVPError::InvalidAttributeLengthError(
|
||||
"2+ bytes".to_owned(),
|
||||
self.value.len(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,11 +511,14 @@ impl AVP {
|
||||
request_authenticator: &[u8],
|
||||
) -> Result<Vec<u8>, AVPError> {
|
||||
if self.value.len() < 16 || self.value.len() > 128 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"16 >= bytes && 128 <= bytes".to_owned(),
|
||||
self.value.len(),
|
||||
));
|
||||
}
|
||||
|
||||
if secret.is_empty() {
|
||||
return Err(AVPError::SecretMissingError());
|
||||
return Err(AVPError::PasswordSecretMissingError());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
@@ -506,7 +555,10 @@ impl AVP {
|
||||
pub fn encode_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()));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
format!("{}", U32_SIZE),
|
||||
self.value.len(),
|
||||
));
|
||||
}
|
||||
|
||||
let (int_bytes, _) = self.value.split_at(U32_SIZE);
|
||||
@@ -515,7 +567,7 @@ impl AVP {
|
||||
let timestamp = u32::from_be_bytes(boxed_array);
|
||||
Ok(Utc.timestamp(timestamp as i64, 0))
|
||||
}
|
||||
Err(e) => Err(AVPError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => Err(AVPError::DecodingError(e.to_string())),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,11 +577,11 @@ impl AVP {
|
||||
secret: &[u8],
|
||||
request_authenticator: &[u8],
|
||||
) -> Result<(Vec<u8>, Tag), AVPError> {
|
||||
if self.value.len() - 3 < 16
|
||||
|| self.value.len() - 3 > 240
|
||||
|| (self.value.len() - 3) % 16 != 0
|
||||
{
|
||||
return Err(AVPError::InvalidAttributeLengthError(self.value.len()));
|
||||
if self.value.len() < 19 || self.value.len() > 243 || (self.value.len() - 3) % 16 != 0 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"19 <= bytes && bytes <= 242 && (bytes - 3) % 16 == 0".to_owned(),
|
||||
self.value.len(),
|
||||
));
|
||||
}
|
||||
|
||||
if self.value[1] & 0x80 != 0x80 {
|
||||
@@ -538,7 +590,7 @@ impl AVP {
|
||||
}
|
||||
|
||||
if secret.is_empty() {
|
||||
return Err(AVPError::SecretMissingError());
|
||||
return Err(AVPError::PasswordSecretMissingError());
|
||||
}
|
||||
|
||||
if request_authenticator.len() != 16 {
|
||||
@@ -804,10 +856,16 @@ mod tests {
|
||||
#[test]
|
||||
fn should_convert_ipv4_prefix_fail_because_of_invalid_prefix_length() {
|
||||
let avp = AVP::from_ipv4_prefix(1, &[0x01, 0x02, 0x03]);
|
||||
assert_eq!(avp.unwrap_err(), AVPError::InvalidAttributeLengthError(3));
|
||||
assert_eq!(
|
||||
avp.unwrap_err(),
|
||||
AVPError::InvalidAttributeLengthError("4 bytes".to_owned(), 3)
|
||||
);
|
||||
|
||||
let avp = AVP::from_ipv4_prefix(1, &[0x01, 0x02, 0x03, 0x04, 0x05]);
|
||||
assert_eq!(avp.unwrap_err(), AVPError::InvalidAttributeLengthError(5));
|
||||
assert_eq!(
|
||||
avp.unwrap_err(),
|
||||
AVPError::InvalidAttributeLengthError("4 bytes".to_owned(), 5)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
AVP {
|
||||
@@ -816,7 +874,7 @@ mod tests {
|
||||
}
|
||||
.encode_ipv4_prefix()
|
||||
.unwrap_err(),
|
||||
AVPError::InvalidAttributeLengthError(0)
|
||||
AVPError::InvalidAttributeLengthError("6 bytes".to_owned(), 0)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -849,6 +907,9 @@ mod tests {
|
||||
0x0e, 0x0f, 0x10,
|
||||
],
|
||||
);
|
||||
assert_eq!(avp.unwrap_err(), AVPError::InvalidAttributeLengthError(17));
|
||||
assert_eq!(
|
||||
avp.unwrap_err(),
|
||||
AVPError::InvalidAttributeLengthError("16 bytes".to_owned(), 17)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,17 +12,28 @@ const RADIUS_PACKET_HEADER_LENGTH: usize = 20; // i.e. minimum packet length
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
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),
|
||||
/// An error indicates the entire length of the given packet has insufficient length.
|
||||
#[error("RADIUS packet doesn't have enough length of bytes; it has to be at least {0} bytes, but actual length was {1}")]
|
||||
InsufficientPacketPayloadLengthError(usize, usize),
|
||||
|
||||
/// An error indicates the length that is instructed by a header is insufficient.
|
||||
#[error("RADIUS packet header indicates the length as {0} bytes, but this is insufficient; this must have {1} bytes at least")]
|
||||
InsufficientHeaderDefinedPacketLengthError(usize, usize),
|
||||
|
||||
/// An error indicates the length that is instructed by a header exceeds the maximum length of the RADIUS packet.
|
||||
#[error("RADIUS packet header indicates the length as {0} bytes, but this exceeds the maximum length {1} bytes")]
|
||||
HeaderDefinedPacketLengthExceedsMaximumLimitError(usize, usize),
|
||||
|
||||
/// An error that is raised when an error has been occurred on decoding bytes for a packet.
|
||||
#[error("failed to decode the packet: {0}")]
|
||||
DecodingError(String),
|
||||
|
||||
/// An error that is raised when an error has been occurred on encoding a packet into bytes.
|
||||
#[error("failed to encode the packet: {0}")]
|
||||
EncodingError(String),
|
||||
#[error("Unknown radius packet code: {0}")]
|
||||
|
||||
/// An error that is raised when it received unknown packet type code of RADIUS.
|
||||
#[error("Unknown RADIUS packet type code: {0}")]
|
||||
UnknownCodeError(String),
|
||||
}
|
||||
|
||||
@@ -69,17 +80,35 @@ impl Packet {
|
||||
/// This decodes bytes into a Packet.
|
||||
pub fn decode(bs: &[u8], secret: &[u8]) -> Result<Self, PacketError> {
|
||||
if bs.len() < RADIUS_PACKET_HEADER_LENGTH {
|
||||
return Err(PacketError::InsufficientPacketLengthError(
|
||||
return Err(PacketError::InsufficientPacketPayloadLengthError(
|
||||
RADIUS_PACKET_HEADER_LENGTH,
|
||||
bs.len(),
|
||||
));
|
||||
}
|
||||
|
||||
let len = match bs[2..4].try_into() {
|
||||
Ok(v) => u16::from_be_bytes(v),
|
||||
Err(e) => return Err(PacketError::UnexpectedDecodingError(e.to_string())),
|
||||
Err(e) => return Err(PacketError::DecodingError(e.to_string())),
|
||||
} as usize;
|
||||
if len < RADIUS_PACKET_HEADER_LENGTH || len > MAX_PACKET_LENGTH || bs.len() < len {
|
||||
return Err(PacketError::InvalidPacketLengthError(len));
|
||||
if len < RADIUS_PACKET_HEADER_LENGTH {
|
||||
return Err(PacketError::InsufficientHeaderDefinedPacketLengthError(
|
||||
len,
|
||||
RADIUS_PACKET_HEADER_LENGTH,
|
||||
));
|
||||
}
|
||||
if len > MAX_PACKET_LENGTH {
|
||||
return Err(
|
||||
PacketError::HeaderDefinedPacketLengthExceedsMaximumLimitError(
|
||||
len,
|
||||
MAX_PACKET_LENGTH,
|
||||
),
|
||||
);
|
||||
}
|
||||
if bs.len() < len {
|
||||
return Err(PacketError::InsufficientPacketPayloadLengthError(
|
||||
len,
|
||||
bs.len(),
|
||||
));
|
||||
}
|
||||
|
||||
let attributes = match Attributes::decode(&bs[RADIUS_PACKET_HEADER_LENGTH..len].to_vec()) {
|
||||
@@ -266,7 +295,7 @@ mod tests {
|
||||
|
||||
use crate::avp::AVP;
|
||||
use crate::code::Code;
|
||||
use crate::packet::{Packet, PacketError};
|
||||
use crate::packet::{Packet, PacketError, MAX_PACKET_LENGTH, RADIUS_PACKET_HEADER_LENGTH};
|
||||
use crate::rfc2865;
|
||||
|
||||
#[test]
|
||||
@@ -468,19 +497,19 @@ mod tests {
|
||||
let test_cases = &[
|
||||
TestCase {
|
||||
plain_text: "\x01",
|
||||
expected_error: PacketError::InsufficientPacketLengthError(20),
|
||||
expected_error: PacketError::InsufficientPacketPayloadLengthError(RADIUS_PACKET_HEADER_LENGTH, 1),
|
||||
},
|
||||
TestCase {
|
||||
plain_text: "\x01\x7f\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
|
||||
expected_error: PacketError::InvalidPacketLengthError(0),
|
||||
expected_error: PacketError::InsufficientHeaderDefinedPacketLengthError(0, RADIUS_PACKET_HEADER_LENGTH),
|
||||
},
|
||||
TestCase {
|
||||
plain_text: "\x01\x7f\x7f\x7f\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
|
||||
expected_error: PacketError::InvalidPacketLengthError(32639),
|
||||
expected_error: PacketError::HeaderDefinedPacketLengthExceedsMaximumLimitError(32639, MAX_PACKET_LENGTH),
|
||||
},
|
||||
TestCase {
|
||||
plain_text: "\x00\x7f\x00\x16\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00",
|
||||
expected_error: PacketError::InvalidPacketLengthError(22),
|
||||
expected_error: PacketError::InsufficientPacketPayloadLengthError(22, 21),
|
||||
},
|
||||
TestCase {
|
||||
plain_text: "\x01\x01\x00\x16\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00",
|
||||
|
||||
@@ -69,7 +69,10 @@ pub fn delete_arap_password(packet: &mut Packet) {
|
||||
}
|
||||
pub fn add_arap_password(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
if value.len() != 16 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(16));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"16 bytes".to_owned(),
|
||||
value.len(),
|
||||
));
|
||||
}
|
||||
packet.add(AVP::from_bytes(ARAP_PASSWORD_TYPE, value));
|
||||
Ok(())
|
||||
@@ -91,7 +94,10 @@ pub fn delete_arap_features(packet: &mut Packet) {
|
||||
}
|
||||
pub fn add_arap_features(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
if value.len() != 14 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(14));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"14 bytes".to_owned(),
|
||||
value.len(),
|
||||
));
|
||||
}
|
||||
packet.add(AVP::from_bytes(ARAP_FEATURES_TYPE, value));
|
||||
Ok(())
|
||||
@@ -290,7 +296,10 @@ pub fn delete_arap_challenge_response(packet: &mut Packet) {
|
||||
}
|
||||
pub fn add_arap_challenge_response(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
if value.len() != 8 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(8));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"8 bytes".to_owned(),
|
||||
value.len(),
|
||||
));
|
||||
}
|
||||
packet.add(AVP::from_bytes(ARAP_CHALLENGE_RESPONSE_TYPE, value));
|
||||
Ok(())
|
||||
|
||||
@@ -31,7 +31,10 @@ pub fn delete_framed_interface_id(packet: &mut Packet) {
|
||||
}
|
||||
pub fn add_framed_interface_id(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
if value.len() != 8 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(8));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"8 bytes".to_owned(),
|
||||
value.len(),
|
||||
));
|
||||
}
|
||||
packet.add(AVP::from_bytes(FRAMED_INTERFACE_ID_TYPE, value));
|
||||
Ok(())
|
||||
|
||||
@@ -181,7 +181,10 @@ pub fn delete_pmip6_home_interface_id(packet: &mut Packet) {
|
||||
}
|
||||
pub fn add_pmip6_home_interface_id(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
if value.len() != 8 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(8));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"8 bytes".to_owned(),
|
||||
value.len(),
|
||||
));
|
||||
}
|
||||
packet.add(AVP::from_bytes(PMIP6_HOME_INTERFACE_ID_TYPE, value));
|
||||
Ok(())
|
||||
@@ -205,7 +208,10 @@ pub fn delete_pmip6_visited_interface_id(packet: &mut Packet) {
|
||||
}
|
||||
pub fn add_pmip6_visited_interface_id(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
if value.len() != 8 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(8));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"8 bytes".to_owned(),
|
||||
value.len(),
|
||||
));
|
||||
}
|
||||
packet.add(AVP::from_bytes(PMIP6_VISITED_INTERFACE_ID_TYPE, value));
|
||||
Ok(())
|
||||
|
||||
@@ -9,7 +9,10 @@ pub fn delete_originating_line_info(packet: &mut Packet) {
|
||||
}
|
||||
pub fn add_originating_line_info(packet: &mut Packet, value: &[u8]) -> Result<(), AVPError> {
|
||||
if value.len() != 2 {
|
||||
return Err(AVPError::InvalidAttributeLengthError(2));
|
||||
return Err(AVPError::InvalidAttributeLengthError(
|
||||
"2 bytes".to_owned(),
|
||||
value.len(),
|
||||
));
|
||||
}
|
||||
packet.add(AVP::from_bytes(ORIGINATING_LINE_INFO_TYPE, value));
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user