1use super::dictionary::{ Dictionary, SupportedAttributeTypes };
5use super::error::RadiusError;
6use crate::tools::{
7 bytes_to_integer,
8 bytes_to_integer64,
9 bytes_to_interfaceid_string,
10 bytes_to_ipv4_string,
11 bytes_to_ipv6_string,
12 bytes_to_timestamp,
13 u16_from_be_bytes
14};
15
16use hmac::{ Hmac, Mac };
17use md5::Md5;
18
19use rand::{ thread_rng, Rng };
20use rand::distributions::{ Distribution, Uniform };
21
22
23use std::convert::TryInto;
24use std::fmt;
25
26
27type HmacMd5 = Hmac<Md5>;
28
29
30#[derive(PartialEq, Eq, Hash)]
31pub enum RadiusMsgType {
36 AUTH,
38 ACCT,
40 COA
42}
43
44impl fmt::Display for RadiusMsgType {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match *self {
47 RadiusMsgType::AUTH => f.write_str("Auth"),
48 RadiusMsgType::ACCT => f.write_str("Acct"),
49 RadiusMsgType::COA => f.write_str("CoA"),
50 }
51 }
52}
53
54
55#[derive(Debug, Clone, PartialEq)]
56pub enum TypeCode {
59 AccessRequest,
61 AccessAccept,
63 AccessReject,
65 AccountingRequest,
67 AccountingResponse,
69 AccessChallenge,
71 StatusServer,
73 StatusClient,
75 DisconnectRequest,
77 DisconnectACK,
79 DisconnectNAK,
81 CoARequest,
83 CoAACK,
85 CoANAK
87}
88
89impl TypeCode {
90 pub fn from_u8(code: u8) -> Result<TypeCode, RadiusError> {
92 match code {
93 1u8 => Ok(TypeCode::AccessRequest),
94 2u8 => Ok(TypeCode::AccessAccept),
95 3u8 => Ok(TypeCode::AccessReject),
96 4u8 => Ok(TypeCode::AccountingRequest),
97 5u8 => Ok(TypeCode::AccountingResponse),
98 11u8 => Ok(TypeCode::AccessChallenge),
99 12u8 => Ok(TypeCode::StatusServer),
100 13u8 => Ok(TypeCode::StatusClient),
101 40u8 => Ok(TypeCode::DisconnectRequest),
102 41u8 => Ok(TypeCode::DisconnectACK),
103 42u8 => Ok(TypeCode::DisconnectNAK),
104 43u8 => Ok(TypeCode::CoARequest ),
105 44u8 => Ok(TypeCode::CoAACK),
106 45u8 => Ok(TypeCode::CoANAK),
107 _ => Err( RadiusError::UnsupportedTypeCodeError { error: format!("Unknown RADIUS code: {}", code) }),
108 }
109 }
110
111 pub fn to_u8(&self) -> u8 {
113 match self {
114 TypeCode::AccessRequest => 1u8,
115 TypeCode::AccessAccept => 2u8,
116 TypeCode::AccessReject => 3u8,
117 TypeCode::AccountingRequest => 4u8,
118 TypeCode::AccountingResponse => 5u8,
119 TypeCode::AccessChallenge => 11u8,
120 TypeCode::StatusServer => 12u8,
121 TypeCode::StatusClient => 13u8,
122 TypeCode::DisconnectRequest => 40u8,
123 TypeCode::DisconnectACK => 41u8,
124 TypeCode::DisconnectNAK => 42u8,
125 TypeCode::CoARequest => 43u8,
126 TypeCode::CoAACK => 44u8,
127 TypeCode::CoANAK => 45u8
128 }
129 }
130}
131
132
133#[derive(Debug, PartialEq)]
134pub struct RadiusAttribute {
136 id: u8,
137 name: String,
138 value: Vec<u8>
139}
140
141impl RadiusAttribute {
142 pub fn create_by_name(dictionary: &Dictionary, attribute_name: &str, value: Vec<u8>) -> Option<RadiusAttribute> {
146 dictionary.attributes().iter().find(|&attr| attr.name() == attribute_name).map(|attr| RadiusAttribute {
147 id: attr.code(),
148 name: attr.name().to_string(),
149 value
150 })
151 }
152
153 pub fn create_by_id(dictionary: &Dictionary, attribute_code: u8, value: Vec<u8>) -> Option<RadiusAttribute> {
157 dictionary.attributes().iter().find(|&attr| attr.code() == attribute_code).map(|attr| RadiusAttribute {
158 id: attribute_code,
159 name: attr.name().to_string(),
160 value
161 })
162 }
163
164 pub fn override_value(&mut self, new_value: Vec<u8>) {
168 self.value = new_value
169 }
170
171 pub fn id(&self) -> u8 {
173 self.id
174 }
175
176 pub fn value(&self) -> &[u8] {
178 &self.value
179 }
180
181 pub fn name(&self) -> &str {
183 &self.name
184 }
185
186 pub fn verify_original_value(&self, allowed_type: &Option<SupportedAttributeTypes>) -> Result<(), RadiusError> {
188 match allowed_type {
189 Some(SupportedAttributeTypes::AsciiString) => {
190 match String::from_utf8(self.value().to_vec()) {
191 Ok(_) => Ok(()),
192 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid ASCII(Text) bytes")} )
193 }
194 },
195 Some(SupportedAttributeTypes::ByteString) => {
196 Ok(())
198 },
199 Some(SupportedAttributeTypes::Concat) => {
200 Ok(())
202 },
203 Some(SupportedAttributeTypes::Integer) => {
204 match self.value().try_into() {
205 Ok(value) => {
206 bytes_to_integer(value);
207 Ok(())
208 },
209 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid Integer bytes")} )
210 }
211 },
212 Some(SupportedAttributeTypes::Integer64) => {
213 match self.value().try_into() {
214 Ok(value) => {
215 bytes_to_integer64(value);
216 Ok(())
217 },
218 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid Integer64 bytes")} )
219 }
220 },
221 Some(SupportedAttributeTypes::Date) => {
222 match self.value().try_into() {
223 Ok(value) => {
224 bytes_to_timestamp(value);
225 Ok(())
226 },
227 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid Date bytes")} )
228 }
229 },
230 Some(SupportedAttributeTypes::IPv4Addr) => {
231 match bytes_to_ipv4_string(self.value()) {
232 Ok(_) => Ok(()),
233 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv4 bytes")} )
234 }
235 },
236 Some(SupportedAttributeTypes::IPv4Prefix) => {
237 match bytes_to_ipv4_string(self.value()) {
238 Ok(_) => Ok(()),
239 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv4Prefix bytes")} )
240 }
241 },
242 Some(SupportedAttributeTypes::IPv6Addr) => {
243 match bytes_to_ipv6_string(self.value()) {
244 Ok(_) => Ok(()),
245 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv6 bytes")} )
246 }
247 },
248 Some(SupportedAttributeTypes::IPv6Prefix) => {
249 match bytes_to_ipv6_string(self.value()) {
250 Ok(_) => Ok(()),
251 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv6Prefix bytes")} )
252 }
253 },
254 Some(SupportedAttributeTypes::InterfaceId) => {
255 match bytes_to_interfaceid_string(self.value()) {
256 Ok(_) => Ok(()),
257 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid InterfaceId bytes")} )
258 }
259 },
260 _ => Err( RadiusError::MalformedAttributeError {error: String::from("unsupported attribute code type")} )
261 }
262 }
263
264 pub fn original_string_value(&self, allowed_type: &Option<SupportedAttributeTypes>) -> Result<String, RadiusError> {
267 match allowed_type {
268 Some(SupportedAttributeTypes::AsciiString) => {
269 match String::from_utf8(self.value().to_vec()) {
270 Ok(value) => Ok(value),
271 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid ASCII bytes")} )
272 }
273 },
274 Some(SupportedAttributeTypes::IPv4Addr) => {
275 match bytes_to_ipv4_string(self.value()) {
276 Ok(value) => Ok(value),
277 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv4 bytes")} )
278 }
279 },
280 Some(SupportedAttributeTypes::IPv4Prefix) => {
281 match bytes_to_ipv4_string(self.value()) {
282 Ok(value) => Ok(value),
283 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv4Prefix bytes")} )
284 }
285 },
286 Some(SupportedAttributeTypes::IPv6Addr) => {
287 match bytes_to_ipv6_string(self.value()) {
288 Ok(value) => Ok(value),
289 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv6 bytes")} )
290 }
291 },
292 Some(SupportedAttributeTypes::IPv6Prefix) => {
293 match bytes_to_ipv6_string(self.value()) {
294 Ok(value) => Ok(value),
295 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid IPv6 bytes")} )
296 }
297 },
298 Some(SupportedAttributeTypes::InterfaceId) => {
299 match bytes_to_interfaceid_string(self.value()) {
300 Ok(value) => Ok(value),
301 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid InterfaceId bytes")} )
302 }
303 },
304 _ => Err( RadiusError::MalformedAttributeError {error: String::from("not a String data type")} )
305 }
306 }
307
308 pub fn original_integer_value(&self, allowed_type: &Option<SupportedAttributeTypes>) -> Result<u32, RadiusError> {
311 match allowed_type {
312 Some(SupportedAttributeTypes::Integer) => {
313 match self.value().try_into() {
314 Ok(value) => Ok(bytes_to_integer(value)),
315 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid Integer bytes")} )
316 }
317 } ,
318 Some(SupportedAttributeTypes::Date) => {
319 match self.value().try_into() {
320 Ok(value) => Ok(bytes_to_timestamp(value)),
321 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid Date bytes")} )
322 }
323 },
324 _ => Err( RadiusError::MalformedAttributeError {error: String::from("not an Integer data type")} )
325 }
326 }
327
328 pub fn original_integer64_value(&self, allowed_type: &Option<SupportedAttributeTypes>) -> Result<u64, RadiusError> {
331 match allowed_type {
332 Some(SupportedAttributeTypes::Integer64) => {
333 match self.value().try_into() {
334 Ok(value) => Ok(bytes_to_integer64(value)),
335 _ => Err( RadiusError::MalformedAttributeError {error: String::from("invalid Integer64 bytes")} )
336 }
337 },
338 _ => Err( RadiusError::MalformedAttributeError {error: String::from("not an Integer data type")} )
339 }
340 }
341
342 fn to_bytes(&self) -> Vec<u8> {
343 [ &[self.id], &[(2 + self.value.len()) as u8], self.value.as_slice() ].concat()
353 }
354}
355
356
357#[derive(Debug, PartialEq)]
358pub struct RadiusPacket {
360 id: u8,
361 code: TypeCode,
362 authenticator: Vec<u8>,
363 attributes: Vec<RadiusAttribute>
364}
365
366impl RadiusPacket {
367 pub fn initialise_packet(code: TypeCode) -> RadiusPacket {
369 RadiusPacket {
370 id: RadiusPacket::create_id(),
371 code,
372 authenticator: RadiusPacket::create_authenticator(),
373 attributes: Vec::new()
374 }
375 }
376
377 pub fn initialise_packet_from_bytes(dictionary: &Dictionary, bytes: &[u8]) -> Result<RadiusPacket, RadiusError> {
379 if bytes.len() < 20 || bytes.len() > 4096 {
380 return Err( RadiusError::MalformedPacketError {error: String::from("packet length should be of size between 20 and 4096 octets")} )
381 }
382
383 let code = TypeCode::from_u8(bytes[0])?;
384 let id = bytes[1];
385 let packet_len = u16_from_be_bytes(&bytes[2..4]) as usize;
386 let authenticator = bytes[4..20].to_vec();
387 let mut attributes = Vec::new();
388
389 if packet_len > bytes.len() {
390 return Err( RadiusError::MalformedPacketError {error:format!("defined packet length: [{}] is greater than actual packet length received: [{}]", packet_len, bytes.len())} )
391 }
392
393 let mut last_index = 20;
394
395 while last_index != packet_len {
396 let attr_id = bytes[last_index];
397 let attr_length = bytes[last_index + 1] as usize;
398
399 if attr_length == 0 {
400 return Err( RadiusError::MalformedPacketError {error:format!("attribute with ID: {} has invalid length 0", attr_id)} )
401 }
402
403 let attr_value = &bytes[(last_index + 2)..=(last_index + attr_length - 1)];
404
405 match RadiusAttribute::create_by_id(dictionary, attr_id, attr_value.to_vec()) {
406 Some(attr) => {
407 attributes.push(attr);
408 last_index += attr_length;
409 },
410 _ => return Err( RadiusError::MalformedPacketError {error:format!("attribute with ID: {} is not found in dictionary", attr_id)} )
411 }
412 }
413
414 let mut packet = RadiusPacket{
415 id,
416 code,
417 authenticator,
418 attributes: Vec::new()
419 };
420 packet.set_attributes(attributes);
421
422 Ok(packet)
423 }
424
425 pub fn set_attributes(&mut self, attributes: Vec<RadiusAttribute>) {
427 self.attributes = attributes;
428 }
429
430 pub fn override_id(&mut self, new_id: u8) {
432 self.id = new_id
433 }
434
435 pub fn override_authenticator(&mut self, new_authenticator: Vec<u8>) {
437 self.authenticator = new_authenticator
438 }
439
440 pub fn override_message_authenticator(&mut self, new_message_authenticator: Vec<u8>) -> Result<(), RadiusError> {
444 match self.attributes.iter_mut().find(|attr| attr.name() == "Message-Authenticator") {
445 Some(attr) => {
446 attr.override_value(new_message_authenticator);
447 Ok(())
448 },
449 _ => Err( RadiusError::MalformedPacketError {error:String::from("Message-Authenticator attribute not found in packet")} )
450 }
451 }
452
453 pub fn generate_message_authenticator(&mut self, secret: &str) -> Result<(), RadiusError> {
458 let zeroed_authenticator = [0; 16];
460 self.override_message_authenticator(zeroed_authenticator.to_vec())?;
461
462 let mut hash = HmacMd5::new_from_slice(secret.as_bytes()).map_err(|error| RadiusError::MalformedPacketError { error: error.to_string() })?;
464 hash.update(&self.to_bytes());
465
466 self.override_message_authenticator(hash.finalize().into_bytes().to_vec())?;
468
469 Ok(())
470 }
471
472 pub fn message_authenticator(&self) -> Result<&[u8], RadiusError> {
474 match self.attributes.iter().find(|attr| attr.name() == "Message-Authenticator") {
475 Some(attr) => {
476 Ok(attr.value())
477 },
478 _ => Err( RadiusError::MalformedPacketError {error: String::from("Message-Authenticator attribute not found in packet")} )
479 }
480 }
481
482 pub fn id(&self) -> u8 {
484 self.id
485 }
486
487 pub fn authenticator(&self) -> &[u8] {
489 &self.authenticator
490 }
491
492 pub fn code(&self) -> &TypeCode {
494 &self.code
495 }
496
497 pub fn attributes(&self) -> &[RadiusAttribute] {
499 &self.attributes
500 }
501
502 pub fn attribute_by_name(&self, name: &str) -> Option<&RadiusAttribute> {
504 self.attributes.iter().find(|&attr| attr.name() == name)
505 }
506
507 pub fn attribute_by_id(&self, id: u8) -> Option<&RadiusAttribute> {
509 self.attributes.iter().find(|&attr| attr.id() == id)
510 }
511
512 pub fn to_bytes(&mut self) -> Vec<u8> {
514 let mut packet_bytes = Vec::new();
533 let mut packet_attr = Vec::new();
534
535 if self.authenticator.is_empty() {
536 self.authenticator = Self::create_authenticator();
537 }
538
539 for attr in self.attributes.iter() {
540 packet_attr.extend(&attr.to_bytes());
541 }
542
543 packet_bytes.push(self.code.to_u8());
544 packet_bytes.push(self.id);
545 packet_bytes.append(&mut Self::packet_length_to_bytes(((20 + packet_attr.len()) as u16).to_be()).to_vec());
546 packet_bytes.append(&mut self.authenticator.as_slice().to_vec());
547 packet_bytes.append(&mut packet_attr);
548
549 packet_bytes
550 }
551
552 fn create_id() -> u8 {
553 thread_rng().gen_range(0u8..=255u8)
554 }
555
556 fn create_authenticator() -> Vec<u8> {
557 let allowed_values = Uniform::from(0u8..=255u8);
558 let mut rng = thread_rng();
559 let mut authenticator: Vec<u8> = Vec::with_capacity(16);
560
561 for _ in 0..16 {
562 authenticator.push(allowed_values.sample(&mut rng))
563 }
564
565 authenticator
566 }
567
568 fn packet_length_to_bytes(length: u16) -> [u8; 2] {
569 RadiusPacket::u16_to_u8(length)
570 }
571
572 fn u16_to_u8(u16_data: u16) -> [u8;2] {
573 [u16_data as u8, (u16_data >> 8) as u8]
574 }
575}
576
577#[cfg(test)]
578mod tests {
579 use crate::tools::{ integer_to_bytes, ipv4_string_to_bytes};
580 use super::*;
581
582 #[test]
583 fn test_radius_attribute_create_by_name() {
584 let dictionary_path = "./dict_examples/test_dictionary_dict";
585 let dict = Dictionary::from_file(dictionary_path).unwrap();
586
587 let expected = RadiusAttribute {
588 id: 1,
589 name: String::from("User-Name"),
590 value: vec![1,2,3]
591 };
592
593 assert_eq!(Some(expected), RadiusAttribute::create_by_name(&dict, "User-Name", vec![1,2,3]));
594 }
595 #[test]
596 fn test_radius_attribute_create_by_name_non_existing() {
597 let dictionary_path = "./dict_examples/test_dictionary_dict";
598 let dict = Dictionary::from_file(dictionary_path).unwrap();
599
600 assert_eq!(None, RadiusAttribute::create_by_name(&dict, "Non-Existing", vec![1,2,3]));
601 }
602
603 #[test]
604 fn test_radius_attribute_create_by_id() {
605 let dictionary_path = "./dict_examples/test_dictionary_dict";
606 let dict = Dictionary::from_file(dictionary_path).unwrap();
607
608 let expected = RadiusAttribute {
609 id: 5,
610 name: String::from("NAS-Port-Id"),
611 value: vec![1,2,3]
612 };
613
614 assert_eq!(Some(expected), RadiusAttribute::create_by_id(&dict, 5, vec![1,2,3]));
615 }
616 #[test]
617 fn test_radius_attribute_create_by_id_non_existing() {
618 let dictionary_path = "./dict_examples/test_dictionary_dict";
619 let dict = Dictionary::from_file(dictionary_path).unwrap();
620
621 assert_eq!(None, RadiusAttribute::create_by_id(&dict, 205, vec![1,2,3]));
622 }
623
624 #[test]
625 fn test_initialise_packet_from_bytes() {
626 let dictionary_path = "./dict_examples/integration_dict";
627 let dict = Dictionary::from_file(dictionary_path).unwrap();
628
629 let nas_ip_addr_bytes = ipv4_string_to_bytes("192.168.1.10").unwrap();
630 let framed_ip_addr_bytes = ipv4_string_to_bytes("10.0.0.100").unwrap();
631 let attributes = vec![
632 RadiusAttribute::create_by_name(&dict, "NAS-IP-Address", nas_ip_addr_bytes).unwrap(),
633 RadiusAttribute::create_by_name(&dict, "NAS-Port-Id", integer_to_bytes(0)).unwrap(),
634 RadiusAttribute::create_by_name(&dict, "NAS-Identifier", String::from("trillian").into_bytes()).unwrap(),
635 RadiusAttribute::create_by_name(&dict, "Called-Station-Id", String::from("00-04-5F-00-0F-D1").into_bytes()).unwrap(),
636 RadiusAttribute::create_by_name(&dict, "Calling-Station-Id", String::from("00-01-24-80-B3-9C").into_bytes()).unwrap(),
637 RadiusAttribute::create_by_name(&dict, "Framed-IP-Address", framed_ip_addr_bytes).unwrap()
638 ];
639 let authenticator = vec![215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73];
640 let mut expected_packet = RadiusPacket::initialise_packet(TypeCode::AccountingRequest);
641 expected_packet.set_attributes(attributes);
642 expected_packet.override_id(43);
643 expected_packet.override_authenticator(authenticator);
644
645 let bytes = [4, 43, 0, 86, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73, 4, 6, 192, 168, 1, 10, 5, 6, 0, 0, 0, 0, 32, 10, 116, 114, 105, 108, 108, 105, 97, 110, 30, 19, 48, 48, 45, 48, 52, 45, 53, 70, 45, 48, 48, 45, 48, 70, 45, 68, 49, 31, 19, 48, 48, 45, 48, 49, 45, 50, 52, 45, 56, 48, 45, 66, 51, 45, 57, 67, 8, 6, 10, 0, 0, 100];
646 let packet_from_bytes = RadiusPacket::initialise_packet_from_bytes(&dict, &bytes).unwrap();
647
648 assert_eq!(expected_packet, packet_from_bytes);
649 }
650
651 #[test]
652 fn test_initialise_packet_from_bytes_invalid_length() {
653 let dictionary_path = "./dict_examples/integration_dict";
654 let dict = Dictionary::from_file(dictionary_path).unwrap();
655
656 let bytes = [4, 43, 0, 26, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73];
657
658
659 match RadiusPacket::initialise_packet_from_bytes(&dict, &bytes) {
660 Err(err) => assert_eq!(String::from("Radius packet is malformed: defined packet length: [26] is greater than actual packet length received: [20]"), err.to_string()),
661 _ => assert!(false)
662 }
663 }
664
665 #[test]
666 fn test_initialise_packet_from_bytes_invalid_length_less_than_20() {
667 let dictionary_path = "./dict_examples/integration_dict";
668 let dict = Dictionary::from_file(dictionary_path).unwrap();
669
670 let bytes = [4, 43, 0, 19, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227];
671
672 match RadiusPacket::initialise_packet_from_bytes(&dict, &bytes) {
673 Err(err) => assert_eq!(String::from("Radius packet is malformed: packet length should be of size between 20 and 4096 octets"), err.to_string()),
674 _ => assert!(false)
675 }
676 }
677
678 #[test]
679 fn test_initialise_packet_from_bytes_invalid_length_greater_than_4096() {
680 let dictionary_path = "./dict_examples/integration_dict";
681 let dict = Dictionary::from_file(dictionary_path).unwrap();
682
683 let bytes = [0; 4097];
684
685 match RadiusPacket::initialise_packet_from_bytes(&dict, &bytes) {
686 Err(err) => assert_eq!(String::from("Radius packet is malformed: packet length should be of size between 20 and 4096 octets"), err.to_string()),
687 _ => assert!(false)
688 }
689 }
690
691 #[test]
692 fn test_initialise_packet_from_bytes_invalid_attr_length() {
693 let dictionary_path = "./dict_examples/integration_dict";
694 let dict = Dictionary::from_file(dictionary_path).unwrap();
695
696 let bytes = [4, 43, 0, 26, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73, 4, 0, 192, 168, 1, 10];
697
698 match RadiusPacket::initialise_packet_from_bytes(&dict, &bytes) {
699 Err(err) => assert_eq!(String::from("Radius packet is malformed: attribute with ID: 4 has invalid length 0"), err.to_string()),
700 _ => assert!(false)
701 }
702 }
703
704 #[test]
705 fn test_initialise_packet_from_bytes_missing_attr() {
706 let dictionary_path = "./dict_examples/integration_dict";
707 let dict = Dictionary::from_file(dictionary_path).unwrap();
708
709 let bytes = [4, 43, 0, 26, 215, 189, 213, 172, 57, 94, 141, 70, 134, 121, 101, 57, 187, 220, 227, 73, 234, 6, 192, 168, 1, 10];
710
711 match RadiusPacket::initialise_packet_from_bytes(&dict, &bytes) {
712 Err(err) => assert_eq!(String::from("Radius packet is malformed: attribute with ID: 234 is not found in dictionary"), err.to_string()),
713 _ => assert!(false)
714 }
715 }
716
717 #[test]
718 fn test_radius_packet_override_id() {
719 let attributes: Vec<RadiusAttribute> = Vec::with_capacity(1);
720 let new_id: u8 = 50;
721
722 let mut packet = RadiusPacket::initialise_packet(TypeCode::AccessRequest);
723 packet.set_attributes(attributes);
724 packet.override_id(new_id);
725
726 assert_eq!(new_id, packet.id());
727 }
728 #[test]
729 fn test_radius_packet_override_authenticator() {
730 let attributes: Vec<RadiusAttribute> = Vec::with_capacity(1);
731 let new_authenticator: Vec<u8> = vec![0, 25, 100, 56, 13];
732
733 let mut packet = RadiusPacket::initialise_packet(TypeCode::AccessRequest);
734 packet.set_attributes(attributes);
735 packet.override_authenticator(new_authenticator.to_vec());
736
737 assert_eq!(new_authenticator, packet.authenticator());
738 }
739 #[test]
740 fn test_radius_packet_to_bytes() {
741 let attributes: Vec<RadiusAttribute> = Vec::with_capacity(1);
742 let new_id: u8 = 50;
743 let new_authenticator: Vec<u8> = vec![0, 25, 100, 56, 13, 0, 67, 34, 39, 12, 88, 153, 0, 1, 2, 3];
744
745 let exepcted_bytes = vec![1, 50, 0, 20, 0, 25, 100, 56, 13, 0, 67, 34, 39, 12, 88, 153, 0, 1, 2, 3];
746 let mut packet = RadiusPacket::initialise_packet(TypeCode::AccessRequest);
747 packet.set_attributes(attributes);
748 packet.override_id(new_id);
749 packet.override_authenticator(new_authenticator);
750
751 assert_eq!(exepcted_bytes, packet.to_bytes());
752 }
753
754 #[test]
755 fn test_override_message_authenticator_fail() {
756 let dictionary_path = "./dict_examples/integration_dict";
757 let dict = Dictionary::from_file(dictionary_path).unwrap();
758
759 let nas_ip_addr_bytes = ipv4_string_to_bytes("192.168.1.10").unwrap();
760 let framed_ip_addr_bytes = ipv4_string_to_bytes("10.0.0.100").unwrap();
761 let attributes = vec![
762 RadiusAttribute::create_by_name(&dict, "NAS-IP-Address", nas_ip_addr_bytes).unwrap(),
763 RadiusAttribute::create_by_name(&dict, "NAS-Port-Id", integer_to_bytes(0)).unwrap(),
764 RadiusAttribute::create_by_name(&dict, "NAS-Identifier", String::from("trillian").into_bytes()).unwrap(),
765 RadiusAttribute::create_by_name(&dict, "Called-Station-Id", String::from("00-04-5F-00-0F-D1").into_bytes()).unwrap(),
766 RadiusAttribute::create_by_name(&dict, "Calling-Station-Id", String::from("00-01-24-80-B3-9C").into_bytes()).unwrap(),
767 RadiusAttribute::create_by_name(&dict, "Framed-IP-Address", framed_ip_addr_bytes).unwrap()
768 ];
769
770 let new_message_authenticator = vec![1, 50, 0, 20, 0, 25, 100, 56, 13, 0, 67, 34, 39, 12, 88, 153];
771 let mut packet = RadiusPacket::initialise_packet(TypeCode::AccountingRequest);
772 packet.set_attributes(attributes);
773
774 match packet.override_message_authenticator(new_message_authenticator) {
775 Err(err) => assert_eq!(String::from("Radius packet is malformed: Message-Authenticator attribute not found in packet"), err.to_string()),
776 _ => assert!(false)
777 }
778 }
779
780 #[test]
781 fn test_override_message_authenticator_success() {
782 let dictionary_path = "./dict_examples/integration_dict";
783 let dict = Dictionary::from_file(dictionary_path).unwrap();
784
785 let attributes = vec![
786 RadiusAttribute::create_by_name(&dict, "Calling-Station-Id", String::from("00-01-24-80-B3-9C").into_bytes()).unwrap(),
787 RadiusAttribute::create_by_name(&dict, "Message-Authenticator", vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).unwrap()
788 ];
789
790 let mut packet = RadiusPacket::initialise_packet(TypeCode::AccessRequest);
791 let new_message_authenticator = vec![1, 50, 0, 20, 0, 25, 100, 56, 13, 0, 67, 34, 39, 12, 88, 153];
792 let new_id: u8 = 50;
793 let new_authenticator: Vec<u8> = vec![0, 25, 100, 56, 13, 0, 67, 34, 39, 12, 88, 153, 0, 1, 2, 3];
794
795 packet.set_attributes(attributes);
796 packet.override_id(new_id);
797 packet.override_authenticator(new_authenticator);
798
799 let expected_packet_bytes: Vec<u8> = vec![1, 50, 0, 57, 0, 25, 100, 56, 13, 0, 67, 34, 39, 12, 88, 153, 0, 1, 2, 3, 31, 19, 48, 48, 45, 48, 49, 45, 50, 52, 45, 56, 48, 45, 66, 51, 45, 57, 67, 80, 18, 1, 50, 0, 20, 0, 25, 100, 56, 13, 0, 67, 34, 39, 12, 88, 153];
800
801 match packet.override_message_authenticator(new_message_authenticator) {
802 Err(_) => assert!(false),
803 _ => assert_eq!(expected_packet_bytes, packet.to_bytes())
804 }
805 }
806
807 #[test]
808 fn test_generate_message_authenticator_success() {
809 let expected_message_authenticator = vec![85, 134, 2, 170, 83, 101, 202, 79, 109, 163, 59, 12, 66, 170, 183, 220];
810
811 let dictionary_path = "./dict_examples/integration_dict";
812 let dict = Dictionary::from_file(dictionary_path).unwrap();
813 let secret = "secret";
814 let mut packet = RadiusPacket::initialise_packet(TypeCode::AccessRequest);
815
816 let attributes = vec![
817 RadiusAttribute::create_by_name(&dict, "User-Name", String::from("testing").into_bytes()).unwrap(),
818 RadiusAttribute::create_by_name(&dict, "Message-Authenticator", [0;16].to_vec()).unwrap()
819 ];
820 let new_authenticator = vec![152, 137, 115, 14, 56, 250, 103, 56, 57, 57, 104, 246, 226, 80, 71, 167];
821 let new_id: u8 = 220;
822
823 packet.set_attributes(attributes);
824 packet.override_id(new_id);
825 packet.override_authenticator(new_authenticator);
826
827 packet.generate_message_authenticator(&secret).unwrap();
828
829 assert_eq!(expected_message_authenticator, packet.message_authenticator().unwrap());
830 }
831}