From f27b0d85fbfbf6dd1a730b37348dfee864b1cca9 Mon Sep 17 00:00:00 2001 From: moznion Date: Wed, 2 Dec 2020 02:26:55 +0900 Subject: [PATCH] Support RFC2868 --- README.md | 2 +- code-generator/src/main.rs | 131 +++++++++++++++- dicts/dictionary.rfc2868 | 57 +++++++ radius/src/avp.rs | 121 +++++++++++++-- radius/src/lib.rs | 1 + radius/src/rfc2868.rs | 308 +++++++++++++++++++++++++++++++++++++ radius/src/tag.rs | 8 +- 7 files changed, 607 insertions(+), 21 deletions(-) create mode 100644 dicts/dictionary.rfc2868 diff --git a/README.md b/README.md index d638440..8ab03b8 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ This supports the following RFC dictionaries at the moment: - [RFC2865](https://tools.ietf.org/html/rfc2865) - [RFC2866](https://tools.ietf.org/html/rfc2866) - [RFC2867](https://tools.ietf.org/html/rfc2867) +- [RFC2868](https://tools.ietf.org/html/rfc2868) ## Usage @@ -26,7 +27,6 @@ Simple example implementations are here: - timeout feature on the client - retransmission feature on the client - Support the following RFC dictionaries: - - rfc2868 - rfc2869 - rfc3162 - rfc3576 diff --git a/code-generator/src/main.rs b/code-generator/src/main.rs index f3998af..4969ce0 100644 --- a/code-generator/src/main.rs +++ b/code-generator/src/main.rs @@ -117,6 +117,7 @@ use std::net::Ipv4Addr; use crate::avp::{AVP, AVPType, AVPError}; use crate::packet::Packet; +use crate::tag::Tag; "; @@ -182,7 +183,7 @@ fn generate_attribute_code( generate_common_attribute_code(w, &attr_name, &type_identifier, type_value); match attr.value_type { RadiusAttributeValueType::String => match attr.has_tag { - true => unimplemented!(), + true => generate_tagged_string_attribute_code(w, &method_identifier, &type_identifier), false => generate_string_attribute_code(w, &method_identifier, &type_identifier), }, RadiusAttributeValueType::UserPassword => match attr.has_tag { @@ -190,7 +191,9 @@ fn generate_attribute_code( false => generate_user_password_attribute_code(w, &method_identifier, &type_identifier), }, RadiusAttributeValueType::TunnelPassword => match attr.has_tag { - true => unimplemented!(), + true => { + generate_tunnel_password_attribute_code(w, &method_identifier, &type_identifier) + } false => unimplemented!(), }, RadiusAttributeValueType::Octets => match attr.has_tag { @@ -204,7 +207,12 @@ fn generate_attribute_code( RadiusAttributeValueType::Integer => { match value_defined_attributes_set.contains(&attr_name) { true => match attr.has_tag { - true => unimplemented!(), + true => generate_value_tagged_defined_integer_attribute_code( + w, + &method_identifier, + &type_identifier, + &attr_name.to_pascal_case(), + ), false => generate_value_defined_integer_attribute_code( w, &method_identifier, @@ -213,7 +221,11 @@ fn generate_attribute_code( ), }, false => match attr.has_tag { - true => unimplemented!(), + true => generate_tagged_integer_attribute_code( + w, + &method_identifier, + &type_identifier, + ), false => { generate_integer_attribute_code(w, &method_identifier, &type_identifier) } @@ -270,6 +282,32 @@ pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result, AV w.write_all(code.as_bytes()).unwrap(); } +fn generate_tagged_string_attribute_code( + w: &mut BufWriter, + method_identifier: &str, + type_identifier: &str, +) { + let code = format!( + "pub fn add_{method_identifier}(packet: &mut Packet, tag: Option<&Tag>, value: &str) {{ + packet.add(AVP::encode_tagged_string({type_identifier}, tag, value)); +}} +pub fn lookup_{method_identifier}(packet: &Packet) -> Option), AVPError>> {{ + packet.lookup({type_identifier}).map(|v| v.decode_tagged_string()) +}} +pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result)>, AVPError> {{ + let mut vec = Vec::new(); + for avp in packet.lookup_all({type_identifier}) {{ + vec.push(avp.decode_tagged_string()?) + }} + Ok(vec) +}} +", + method_identifier = method_identifier, + type_identifier = type_identifier, + ); + w.write_all(code.as_bytes()).unwrap(); +} + fn generate_user_password_attribute_code( w: &mut BufWriter, method_identifier: &str, @@ -297,6 +335,33 @@ pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result>, A w.write_all(code.as_bytes()).unwrap(); } +fn generate_tunnel_password_attribute_code( + w: &mut BufWriter, + method_identifier: &str, + type_identifier: &str, +) { + let code = format!( + "pub fn add_{method_identifier}(packet: &mut Packet, tag: Option<&Tag>, value: &[u8]) -> Result<(), AVPError> {{ + packet.add(AVP::encode_tunnel_password({type_identifier}, tag, value, packet.get_secret(), packet.get_authenticator())?); + Ok(()) +}} +pub fn lookup_{method_identifier}(packet: &Packet) -> Option, Tag), AVPError>> {{ + packet.lookup({type_identifier}).map(|v| v.decode_tunnel_password(packet.get_secret(), packet.get_authenticator())) +}} +pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result, Tag)>, AVPError> {{ + let mut vec = Vec::new(); + for avp in packet.lookup_all({type_identifier}) {{ + vec.push(avp.decode_tunnel_password(packet.get_secret(), packet.get_authenticator())?) + }} + Ok(vec) +}} +", + method_identifier = method_identifier, + type_identifier = type_identifier, + ); + w.write_all(code.as_bytes()).unwrap(); +} + fn generate_octets_attribute_code( w: &mut BufWriter, method_identifier: &str, @@ -375,6 +440,32 @@ pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result, AVPEr w.write_all(code.as_bytes()).unwrap(); } +fn generate_tagged_integer_attribute_code( + w: &mut BufWriter, + method_identifier: &str, + type_identifier: &str, +) { + let code = format!( + "pub fn add_{method_identifier}(packet: &mut Packet, tag: Option<&Tag>, value: u32) {{ + packet.add(AVP::encode_tagged_u32({type_identifier}, tag, value)); +}} +pub fn lookup_{method_identifier}(packet: &Packet) -> Option> {{ + packet.lookup({type_identifier}).map(|v| v.decode_tagged_u32()) +}} +pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result, AVPError> {{ + let mut vec = Vec::new(); + for avp in packet.lookup_all({type_identifier}) {{ + vec.push(avp.decode_tagged_u32()?) + }} + Ok(vec) +}} +", + method_identifier = method_identifier, + type_identifier = type_identifier, + ); + w.write_all(code.as_bytes()).unwrap(); +} + fn generate_value_defined_integer_attribute_code( w: &mut BufWriter, method_identifier: &str, @@ -403,6 +494,38 @@ pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result, + method_identifier: &str, + type_identifier: &str, + value_type: &str, +) { + let code = format!( + "pub fn add_{method_identifier}(packet: &mut Packet, tag: Option<&Tag>, value: {value_type}) {{ + packet.add(AVP::encode_tagged_u32({type_identifier}, tag, value as u32)); +}} +pub fn lookup_{method_identifier}(packet: &Packet) -> Option> {{ + packet.lookup({type_identifier}).map(|v| {{ + let (v, t) = v.decode_tagged_u32()?; + Ok((v as {value_type}, t)) + }}) +}} +pub fn lookup_all_{method_identifier}(packet: &Packet) -> Result, AVPError> {{ + let mut vec = Vec::new(); + for avp in packet.lookup_all({type_identifier}) {{ + let (v, t) = avp.decode_tagged_u32()?; + vec.push((v as {value_type}, t)) + }} + Ok(vec) +}} +", + method_identifier = method_identifier, + type_identifier = type_identifier, + value_type = value_type, + ); + w.write_all(code.as_bytes()).unwrap(); +} + fn generate_vsa_attribute_code() { // NOP } diff --git a/dicts/dictionary.rfc2868 b/dicts/dictionary.rfc2868 new file mode 100644 index 0000000..ae280d3 --- /dev/null +++ b/dicts/dictionary.rfc2868 @@ -0,0 +1,57 @@ +# -*- text -*- +# Copyright (C) 2020 The FreeRADIUS Server project and contributors +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# Version $Id$ +# +# Attributes and values defined in RFC 2868. +# http://www.ietf.org/rfc/rfc2868.txt +# +# $Id$ +# +ATTRIBUTE Tunnel-Type 64 integer has_tag +ATTRIBUTE Tunnel-Medium-Type 65 integer has_tag +ATTRIBUTE Tunnel-Client-Endpoint 66 string has_tag +ATTRIBUTE Tunnel-Server-Endpoint 67 string has_tag + +ATTRIBUTE Tunnel-Password 69 string has_tag,encrypt=2 + +ATTRIBUTE Tunnel-Private-Group-Id 81 string has_tag +ATTRIBUTE Tunnel-Assignment-Id 82 string has_tag +ATTRIBUTE Tunnel-Preference 83 integer has_tag + +ATTRIBUTE Tunnel-Client-Auth-Id 90 string has_tag +ATTRIBUTE Tunnel-Server-Auth-Id 91 string has_tag + +# Tunnel Type + +VALUE Tunnel-Type PPTP 1 +VALUE Tunnel-Type L2F 2 +VALUE Tunnel-Type L2TP 3 +VALUE Tunnel-Type ATMP 4 +VALUE Tunnel-Type VTP 5 +VALUE Tunnel-Type AH 6 +VALUE Tunnel-Type IP 7 +VALUE Tunnel-Type MIN-IP 8 +VALUE Tunnel-Type ESP 9 +VALUE Tunnel-Type GRE 10 +VALUE Tunnel-Type DVS 11 +VALUE Tunnel-Type IP-in-IP 12 + +# Tunnel Medium Type + +VALUE Tunnel-Medium-Type IP 1 +VALUE Tunnel-Medium-Type IPv4 1 +VALUE Tunnel-Medium-Type IPv6 2 +VALUE Tunnel-Medium-Type NSAP 3 +VALUE Tunnel-Medium-Type HDLC 4 +VALUE Tunnel-Medium-Type BBN-1822 5 +VALUE Tunnel-Medium-Type IEEE-802 6 +VALUE Tunnel-Medium-Type E.163 7 +VALUE Tunnel-Medium-Type E.164 8 +VALUE Tunnel-Medium-Type F.69 9 +VALUE Tunnel-Medium-Type X.121 10 +VALUE Tunnel-Medium-Type IPX 11 +VALUE Tunnel-Medium-Type Appletalk 12 +VALUE Tunnel-Medium-Type DecNet-IV 13 +VALUE Tunnel-Medium-Type Banyan-Vines 14 +VALUE Tunnel-Medium-Type E.164-NSAP 15 diff --git a/radius/src/avp.rs b/radius/src/avp.rs index 29711a3..3943507 100644 --- a/radius/src/avp.rs +++ b/radius/src/avp.rs @@ -1,10 +1,12 @@ +use rand::Rng; use std::convert::TryInto; use std::net::{Ipv4Addr, Ipv6Addr}; -use crate::tag::Tag; use chrono::{DateTime, TimeZone, Utc}; use thiserror::Error; +use crate::tag::{Tag, UNUSED_TAG_VALUE}; + #[derive(Error, Debug)] pub enum AVPError { #[error( @@ -22,6 +24,10 @@ pub enum AVPError { UnexpectedDecodingError(String), #[error("invalid salt. the MSB has to be 1, but given value isn't: {0}")] InvalidSaltMSBError(u8), + #[error("invalid tag for string value. this must not be zero")] + InvalidTagForStringValueError(), + #[error("invalid tag for integer value. this must be less than or equal 0x1f")] + InvalidTagForIntegerValueError(), } pub type AVPType = u8; @@ -42,6 +48,20 @@ impl AVP { } } + pub fn encode_tagged_u32(typ: AVPType, tag: Option<&Tag>, value: u32) -> Self { + let tag = match tag { + None => &Tag { + value: UNUSED_TAG_VALUE, + }, + Some(tag) => tag, + }; + + AVP { + typ, + value: [vec![tag.value], u32::to_be_bytes(value).to_vec()].concat(), + } + } + pub fn encode_string(typ: AVPType, value: &str) -> Self { AVP { typ, @@ -49,6 +69,19 @@ impl AVP { } } + pub fn encode_tagged_string(typ: AVPType, tag: Option<&Tag>, value: &str) -> Self { + match tag { + None => AVP { + typ, + value: value.as_bytes().to_vec(), + }, + Some(tag) => AVP { + typ, + value: [vec![tag.value], value.as_bytes().to_vec()].concat(), + }, + } + } + pub fn encode_bytes(typ: AVPType, value: &[u8]) -> Self { AVP { typ, @@ -142,9 +175,8 @@ impl AVP { pub fn encode_tunnel_password( typ: AVPType, + tag: Option<&Tag>, plain_text: &[u8], - tag: u8, - salt: &[u8], secret: &[u8], request_authenticator: &[u8], ) -> Result { @@ -176,13 +208,8 @@ impl AVP { )); } - if salt.len() != 2 { - return Err(AVPError::InvalidAttributeLengthError(2)); - } - - if salt[0] & 0x80 != 0x80 { - return Err(AVPError::InvalidSaltMSBError(salt[0])); - } + let mut rng = rand::thread_rng(); + let salt: [u8; 2] = [rng.gen::() | 0x80, rng.gen::()]; if secret.is_empty() { return Err(AVPError::SecretMissingError()); @@ -194,9 +221,13 @@ impl AVP { // NOTE: prepend one byte as a tag and two bytes as a salt // TODO: should it separate them to private struct fields? - let mut enc: Vec = [vec![tag], salt.to_vec()].concat(); + let mut enc: Vec = [ + vec![tag.map_or(UNUSED_TAG_VALUE, |v| v.value)], + salt.to_vec(), + ] + .concat(); - let mut buff = [request_authenticator, salt].concat(); + let mut buff = [request_authenticator, &salt].concat(); if plain_text.is_empty() { return Ok(AVP { typ, @@ -244,6 +275,33 @@ impl AVP { } } + pub fn decode_tagged_u32(&self) -> Result<(u32, Tag), AVPError> { + if self.value.is_empty() { + return Err(AVPError::InvalidAttributeLengthError(self.value.len())); + } + + let tag = Tag { + value: self.value[0], + }; + + // ref RFC2868: + // Valid values for this field are 0x01 through 0x1F, + // inclusive. If the Tag field is unused, it MUST be zero (0x00) + if !tag.is_valid_value() && !tag.is_zero() { + return Err(AVPError::InvalidTagForIntegerValueError()); + } + + const U32_SIZE: usize = std::mem::size_of::(); + if self.value[1..].len() != U32_SIZE { + return Err(AVPError::InvalidAttributeLengthError(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())), + } + } + pub fn decode_string(&self) -> Result { match String::from_utf8(self.value.to_vec()) { Ok(str) => Ok(str), @@ -251,6 +309,41 @@ impl AVP { } } + pub fn decode_tagged_string(&self) -> Result<(String, Option), AVPError> { + let string_vec = self.value.to_vec(); + if string_vec.is_empty() { + return Err(AVPError::InvalidAttributeLengthError(string_vec.len())); + } + + let tag = Tag { + value: string_vec[0], + }; + + // ref RFC2868: + // If the value of the Tag field is greater than 0x00 + // and less than or equal to 0x1F, it SHOULD be interpreted as + // indicating which tunnel (of several alternatives) this attribute + // pertains. + 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())), + }; + } + + if tag.is_zero() { + return Err(AVPError::InvalidTagForStringValueError()); + } + + // ref RFC2868: + // If the Tag field is greater than 0x1F, it SHOULD be + // 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())), + } + } + pub fn decode_bytes(&self) -> Vec { self.value.to_vec() } @@ -510,7 +603,6 @@ mod tests { #[test] fn it_should_convert_tunnel_password() -> Result<(), AVPError> { - let salt: Vec = vec![0x80, 0xef]; let tag = Tag { value: 0x1e }; let secret = b"12345".to_vec(); let request_authenticator = b"0123456789abcdef".to_vec(); @@ -550,9 +642,8 @@ mod tests { for test_case in test_cases { let user_password_avp_result = AVP::encode_tunnel_password( 1, + Some(&tag), test_case.plain_text.as_bytes(), - tag.value, - &salt, &secret, &request_authenticator, ); diff --git a/radius/src/lib.rs b/radius/src/lib.rs index 23319fa..1027757 100644 --- a/radius/src/lib.rs +++ b/radius/src/lib.rs @@ -6,4 +6,5 @@ pub mod request; pub mod rfc2865; pub mod rfc2866; pub mod rfc2867; +pub mod rfc2868; pub mod tag; diff --git a/radius/src/rfc2868.rs b/radius/src/rfc2868.rs index e69de29..105217b 100644 --- a/radius/src/rfc2868.rs +++ b/radius/src/rfc2868.rs @@ -0,0 +1,308 @@ +// Code generated by machine generator; DO NOT EDIT. + +use crate::avp::{AVPError, AVPType, AVP}; +use crate::packet::Packet; +use crate::tag::Tag; + +pub type TunnelMediumType = u32; +pub const TUNNEL_MEDIUM_TYPE_IP: TunnelMediumType = 1; +pub const TUNNEL_MEDIUM_TYPE_I_PV_4: TunnelMediumType = 1; +pub const TUNNEL_MEDIUM_TYPE_I_PV_6: TunnelMediumType = 2; +pub const TUNNEL_MEDIUM_TYPE_NSAP: TunnelMediumType = 3; +pub const TUNNEL_MEDIUM_TYPE_HDLC: TunnelMediumType = 4; +pub const TUNNEL_MEDIUM_TYPE_BBN_1822: TunnelMediumType = 5; +pub const TUNNEL_MEDIUM_TYPE_IEEE_802: TunnelMediumType = 6; +pub const TUNNEL_MEDIUM_TYPE_E_163: TunnelMediumType = 7; +pub const TUNNEL_MEDIUM_TYPE_E_164: TunnelMediumType = 8; +pub const TUNNEL_MEDIUM_TYPE_F_69: TunnelMediumType = 9; +pub const TUNNEL_MEDIUM_TYPE_X_121: TunnelMediumType = 10; +pub const TUNNEL_MEDIUM_TYPE_IPX: TunnelMediumType = 11; +pub const TUNNEL_MEDIUM_TYPE_APPLETALK: TunnelMediumType = 12; +pub const TUNNEL_MEDIUM_TYPE_DEC_NET_IV: TunnelMediumType = 13; +pub const TUNNEL_MEDIUM_TYPE_BANYAN_VINES: TunnelMediumType = 14; +pub const TUNNEL_MEDIUM_TYPE_E_164_NSAP: TunnelMediumType = 15; + +pub type TunnelType = u32; +pub const TUNNEL_TYPE_PPTP: TunnelType = 1; +pub const TUNNEL_TYPE_L2F: TunnelType = 2; +pub const TUNNEL_TYPE_L2TP: TunnelType = 3; +pub const TUNNEL_TYPE_ATMP: TunnelType = 4; +pub const TUNNEL_TYPE_VTP: TunnelType = 5; +pub const TUNNEL_TYPE_AH: TunnelType = 6; +pub const TUNNEL_TYPE_IP: TunnelType = 7; +pub const TUNNEL_TYPE_MIN_IP: TunnelType = 8; +pub const TUNNEL_TYPE_ESP: TunnelType = 9; +pub const TUNNEL_TYPE_GRE: TunnelType = 10; +pub const TUNNEL_TYPE_DVS: TunnelType = 11; +pub const TUNNEL_TYPE_IP_IN_IP: TunnelType = 12; + +pub const TUNNEL_TYPE_TYPE: AVPType = 64; +pub fn delete_tunnel_type(packet: &mut Packet) { + packet.delete(TUNNEL_TYPE_TYPE); +} +pub fn add_tunnel_type(packet: &mut Packet, tag: Option<&Tag>, value: TunnelType) { + packet.add(AVP::encode_tagged_u32(TUNNEL_TYPE_TYPE, tag, value as u32)); +} +pub fn lookup_tunnel_type(packet: &Packet) -> Option> { + packet.lookup(TUNNEL_TYPE_TYPE).map(|v| { + let (v, t) = v.decode_tagged_u32()?; + Ok((v as TunnelType, t)) + }) +} +pub fn lookup_all_tunnel_type(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_TYPE_TYPE) { + let (v, t) = avp.decode_tagged_u32()?; + vec.push((v as TunnelType, t)) + } + Ok(vec) +} + +pub const TUNNEL_MEDIUM_TYPE_TYPE: AVPType = 65; +pub fn delete_tunnel_medium_type(packet: &mut Packet) { + packet.delete(TUNNEL_MEDIUM_TYPE_TYPE); +} +pub fn add_tunnel_medium_type(packet: &mut Packet, tag: Option<&Tag>, value: TunnelMediumType) { + packet.add(AVP::encode_tagged_u32( + TUNNEL_MEDIUM_TYPE_TYPE, + tag, + value as u32, + )); +} +pub fn lookup_tunnel_medium_type( + packet: &Packet, +) -> Option> { + packet.lookup(TUNNEL_MEDIUM_TYPE_TYPE).map(|v| { + let (v, t) = v.decode_tagged_u32()?; + Ok((v as TunnelMediumType, t)) + }) +} +pub fn lookup_all_tunnel_medium_type( + packet: &Packet, +) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_MEDIUM_TYPE_TYPE) { + let (v, t) = avp.decode_tagged_u32()?; + vec.push((v as TunnelMediumType, t)) + } + Ok(vec) +} + +pub const TUNNEL_CLIENT_ENDPOINT_TYPE: AVPType = 66; +pub fn delete_tunnel_client_endpoint(packet: &mut Packet) { + packet.delete(TUNNEL_CLIENT_ENDPOINT_TYPE); +} +pub fn add_tunnel_client_endpoint(packet: &mut Packet, tag: Option<&Tag>, value: &str) { + packet.add(AVP::encode_tagged_string( + TUNNEL_CLIENT_ENDPOINT_TYPE, + tag, + value, + )); +} +pub fn lookup_tunnel_client_endpoint( + packet: &Packet, +) -> Option), AVPError>> { + packet + .lookup(TUNNEL_CLIENT_ENDPOINT_TYPE) + .map(|v| v.decode_tagged_string()) +} +pub fn lookup_all_tunnel_client_endpoint( + packet: &Packet, +) -> Result)>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_CLIENT_ENDPOINT_TYPE) { + vec.push(avp.decode_tagged_string()?) + } + Ok(vec) +} + +pub const TUNNEL_SERVER_ENDPOINT_TYPE: AVPType = 67; +pub fn delete_tunnel_server_endpoint(packet: &mut Packet) { + packet.delete(TUNNEL_SERVER_ENDPOINT_TYPE); +} +pub fn add_tunnel_server_endpoint(packet: &mut Packet, tag: Option<&Tag>, value: &str) { + packet.add(AVP::encode_tagged_string( + TUNNEL_SERVER_ENDPOINT_TYPE, + tag, + value, + )); +} +pub fn lookup_tunnel_server_endpoint( + packet: &Packet, +) -> Option), AVPError>> { + packet + .lookup(TUNNEL_SERVER_ENDPOINT_TYPE) + .map(|v| v.decode_tagged_string()) +} +pub fn lookup_all_tunnel_server_endpoint( + packet: &Packet, +) -> Result)>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_SERVER_ENDPOINT_TYPE) { + vec.push(avp.decode_tagged_string()?) + } + Ok(vec) +} + +pub const TUNNEL_PASSWORD_TYPE: AVPType = 69; +pub fn delete_tunnel_password(packet: &mut Packet) { + packet.delete(TUNNEL_PASSWORD_TYPE); +} +pub fn add_tunnel_password( + packet: &mut Packet, + tag: Option<&Tag>, + value: &[u8], +) -> Result<(), AVPError> { + packet.add(AVP::encode_tunnel_password( + TUNNEL_PASSWORD_TYPE, + tag, + value, + packet.get_secret(), + packet.get_authenticator(), + )?); + Ok(()) +} +pub fn lookup_tunnel_password(packet: &Packet) -> Option, Tag), AVPError>> { + packet + .lookup(TUNNEL_PASSWORD_TYPE) + .map(|v| v.decode_tunnel_password(packet.get_secret(), packet.get_authenticator())) +} +pub fn lookup_all_tunnel_password(packet: &Packet) -> Result, Tag)>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_PASSWORD_TYPE) { + vec.push(avp.decode_tunnel_password(packet.get_secret(), packet.get_authenticator())?) + } + Ok(vec) +} + +pub const TUNNEL_PRIVATE_GROUP_ID_TYPE: AVPType = 81; +pub fn delete_tunnel_private_group_id(packet: &mut Packet) { + packet.delete(TUNNEL_PRIVATE_GROUP_ID_TYPE); +} +pub fn add_tunnel_private_group_id(packet: &mut Packet, tag: Option<&Tag>, value: &str) { + packet.add(AVP::encode_tagged_string( + TUNNEL_PRIVATE_GROUP_ID_TYPE, + tag, + value, + )); +} +pub fn lookup_tunnel_private_group_id( + packet: &Packet, +) -> Option), AVPError>> { + packet + .lookup(TUNNEL_PRIVATE_GROUP_ID_TYPE) + .map(|v| v.decode_tagged_string()) +} +pub fn lookup_all_tunnel_private_group_id( + packet: &Packet, +) -> Result)>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_PRIVATE_GROUP_ID_TYPE) { + vec.push(avp.decode_tagged_string()?) + } + Ok(vec) +} + +pub const TUNNEL_ASSIGNMENT_ID_TYPE: AVPType = 82; +pub fn delete_tunnel_assignment_id(packet: &mut Packet) { + packet.delete(TUNNEL_ASSIGNMENT_ID_TYPE); +} +pub fn add_tunnel_assignment_id(packet: &mut Packet, tag: Option<&Tag>, value: &str) { + packet.add(AVP::encode_tagged_string( + TUNNEL_ASSIGNMENT_ID_TYPE, + tag, + value, + )); +} +pub fn lookup_tunnel_assignment_id( + packet: &Packet, +) -> Option), AVPError>> { + packet + .lookup(TUNNEL_ASSIGNMENT_ID_TYPE) + .map(|v| v.decode_tagged_string()) +} +pub fn lookup_all_tunnel_assignment_id( + packet: &Packet, +) -> Result)>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_ASSIGNMENT_ID_TYPE) { + vec.push(avp.decode_tagged_string()?) + } + Ok(vec) +} + +pub const TUNNEL_PREFERENCE_TYPE: AVPType = 83; +pub fn delete_tunnel_preference(packet: &mut Packet) { + packet.delete(TUNNEL_PREFERENCE_TYPE); +} +pub fn add_tunnel_preference(packet: &mut Packet, tag: Option<&Tag>, value: u32) { + packet.add(AVP::encode_tagged_u32(TUNNEL_PREFERENCE_TYPE, tag, value)); +} +pub fn lookup_tunnel_preference(packet: &Packet) -> Option> { + packet + .lookup(TUNNEL_PREFERENCE_TYPE) + .map(|v| v.decode_tagged_u32()) +} +pub fn lookup_all_tunnel_preference(packet: &Packet) -> Result, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_PREFERENCE_TYPE) { + vec.push(avp.decode_tagged_u32()?) + } + Ok(vec) +} + +pub const TUNNEL_CLIENT_AUTH_ID_TYPE: AVPType = 90; +pub fn delete_tunnel_client_auth_id(packet: &mut Packet) { + packet.delete(TUNNEL_CLIENT_AUTH_ID_TYPE); +} +pub fn add_tunnel_client_auth_id(packet: &mut Packet, tag: Option<&Tag>, value: &str) { + packet.add(AVP::encode_tagged_string( + TUNNEL_CLIENT_AUTH_ID_TYPE, + tag, + value, + )); +} +pub fn lookup_tunnel_client_auth_id( + packet: &Packet, +) -> Option), AVPError>> { + packet + .lookup(TUNNEL_CLIENT_AUTH_ID_TYPE) + .map(|v| v.decode_tagged_string()) +} +pub fn lookup_all_tunnel_client_auth_id( + packet: &Packet, +) -> Result)>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_CLIENT_AUTH_ID_TYPE) { + vec.push(avp.decode_tagged_string()?) + } + Ok(vec) +} + +pub const TUNNEL_SERVER_AUTH_ID_TYPE: AVPType = 91; +pub fn delete_tunnel_server_auth_id(packet: &mut Packet) { + packet.delete(TUNNEL_SERVER_AUTH_ID_TYPE); +} +pub fn add_tunnel_server_auth_id(packet: &mut Packet, tag: Option<&Tag>, value: &str) { + packet.add(AVP::encode_tagged_string( + TUNNEL_SERVER_AUTH_ID_TYPE, + tag, + value, + )); +} +pub fn lookup_tunnel_server_auth_id( + packet: &Packet, +) -> Option), AVPError>> { + packet + .lookup(TUNNEL_SERVER_AUTH_ID_TYPE) + .map(|v| v.decode_tagged_string()) +} +pub fn lookup_all_tunnel_server_auth_id( + packet: &Packet, +) -> Result)>, AVPError> { + let mut vec = Vec::new(); + for avp in packet.lookup_all(TUNNEL_SERVER_AUTH_ID_TYPE) { + vec.push(avp.decode_tagged_string()?) + } + Ok(vec) +} diff --git a/radius/src/tag.rs b/radius/src/tag.rs index b599510..019ece0 100644 --- a/radius/src/tag.rs +++ b/radius/src/tag.rs @@ -1,15 +1,21 @@ +pub(crate) const UNUSED_TAG_VALUE: u8 = 0x00; + #[derive(Debug, PartialEq)] pub struct Tag { pub(crate) value: u8, } impl Tag { + pub fn new(value: u8) -> Self { + Tag { value } + } + pub fn get_value(&self) -> u8 { self.value } pub fn is_zero(&self) -> bool { - self.value == 0 + self.value == UNUSED_TAG_VALUE } pub fn is_valid_value(&self) -> bool {