radius/core/
packet.rs

1use std::convert::TryInto;
2use std::fmt::Debug;
3
4use rand::Rng;
5use thiserror::Error;
6
7use crate::core::attributes::Attributes;
8use crate::core::avp::{AVPType, AVP};
9use crate::core::code::Code;
10
11#[cfg(feature = "md5")]
12use md5::compute;
13
14#[cfg(feature = "openssl")]
15use openssl::hash::{hash, MessageDigest};
16
17const MAX_PACKET_LENGTH: usize = 4096;
18const RADIUS_PACKET_HEADER_LENGTH: usize = 20; // i.e. minimum packet length
19
20#[derive(Error, Debug, PartialEq)]
21pub enum PacketError {
22    /// An error indicates the entire length of the given packet has insufficient length.
23    #[error("RADIUS packet doesn't have enough length of bytes; it has to be at least {0} bytes, but actual length was {1}")]
24    InsufficientPacketPayloadLengthError(usize, usize),
25
26    /// An error indicates the length that is instructed by a header is insufficient.
27    #[error("RADIUS packet header indicates the length as {0} bytes, but this is insufficient; this must have {1} bytes at least")]
28    InsufficientHeaderDefinedPacketLengthError(usize, usize),
29
30    /// An error indicates the length that is instructed by a header exceeds the maximum length of the RADIUS packet.
31    #[error("RADIUS packet header indicates the length as {0} bytes, but this exceeds the maximum length {1} bytes")]
32    HeaderDefinedPacketLengthExceedsMaximumLimitError(usize, usize),
33
34    /// An error that is raised when an error has been occurred on decoding bytes for a packet.
35    #[error("failed to decode the packet: {0}")]
36    DecodingError(String),
37
38    /// An error that is raised when an error has been occurred on encoding a packet into bytes.
39    #[error("failed to encode the packet: {0}")]
40    EncodingError(String),
41
42    /// An error that is raised when it received unknown packet type code of RADIUS.
43    #[error("Unknown RADIUS packet type code: {0}")]
44    UnknownCodeError(String),
45
46    /// This error is raised when computation of hash fails using openssl hash
47    #[error("computation of hash failed: {0}")]
48    HashComputationFailed(String),
49}
50
51/// This struct represents a packet of RADIUS for request and response.
52#[derive(Clone, PartialEq)]
53pub struct Packet {
54    code: Code,
55    identifier: u8,
56    authenticator: Vec<u8>,
57    secret: Vec<u8>,
58    attributes: Attributes,
59}
60
61impl Packet {
62    /// Constructor for a Packet.
63    ///
64    /// By default, this constructor makes an instance with a random identifier value.
65    /// If you'd like to set an arbitrary identifier, please use `new_with_identifier()` constructor instead or `set_identifier()` method for created instance.
66    pub fn new(code: Code, secret: &[u8]) -> Self {
67        Self::_new(code, secret, None)
68    }
69
70    /// Constructor for a Packet with arbitrary identifier value.
71    ///
72    /// If you want to make an instance with a random identifier value, please consider using `new()`.
73    pub fn new_with_identifier(code: Code, secret: &[u8], identifier: u8) -> Self {
74        Self::_new(code, secret, Some(identifier))
75    }
76
77    fn _new(code: Code, secret: &[u8], maybe_identifier: Option<u8>) -> Self {
78        let mut rng = rand::thread_rng();
79        let authenticator = (0..16).map(|_| rng.gen()).collect::<Vec<u8>>();
80        Packet {
81            code: code.to_owned(),
82            identifier: match maybe_identifier {
83                Some(ident) => ident,
84                None => rng.gen(),
85            },
86            authenticator,
87            secret: secret.to_owned(),
88            attributes: Attributes(vec![]),
89        }
90    }
91
92    pub fn get_code(&self) -> Code {
93        self.code
94    }
95
96    pub fn get_identifier(&self) -> u8 {
97        self.identifier
98    }
99
100    pub fn get_secret(&self) -> &Vec<u8> {
101        &self.secret
102    }
103
104    pub fn get_authenticator(&self) -> &Vec<u8> {
105        &self.authenticator
106    }
107
108    /// This sets an identifier value to an instance.
109    pub fn set_identifier(&mut self, identifier: u8) {
110        self.identifier = identifier;
111    }
112
113    /// This decodes bytes into a Packet.
114    pub fn decode(bs: &[u8], secret: &[u8]) -> Result<Self, PacketError> {
115        if bs.len() < RADIUS_PACKET_HEADER_LENGTH {
116            return Err(PacketError::InsufficientPacketPayloadLengthError(
117                RADIUS_PACKET_HEADER_LENGTH,
118                bs.len(),
119            ));
120        }
121
122        let len = match bs[2..4].try_into() {
123            Ok(v) => u16::from_be_bytes(v),
124            Err(e) => return Err(PacketError::DecodingError(e.to_string())),
125        } as usize;
126        if len < RADIUS_PACKET_HEADER_LENGTH {
127            return Err(PacketError::InsufficientHeaderDefinedPacketLengthError(
128                len,
129                RADIUS_PACKET_HEADER_LENGTH,
130            ));
131        }
132        if len > MAX_PACKET_LENGTH {
133            return Err(
134                PacketError::HeaderDefinedPacketLengthExceedsMaximumLimitError(
135                    len,
136                    MAX_PACKET_LENGTH,
137                ),
138            );
139        }
140        if bs.len() < len {
141            return Err(PacketError::InsufficientPacketPayloadLengthError(
142                len,
143                bs.len(),
144            ));
145        }
146
147        let attributes = match Attributes::decode(&bs[RADIUS_PACKET_HEADER_LENGTH..len]) {
148            Ok(attributes) => attributes,
149            Err(e) => return Err(PacketError::DecodingError(e)),
150        };
151
152        Ok(Packet {
153            code: Code::from(bs[0]),
154            identifier: bs[1],
155            authenticator: bs[4..RADIUS_PACKET_HEADER_LENGTH].to_owned(),
156            secret: secret.to_owned(),
157            attributes,
158        })
159    }
160
161    /// This method makes a response packet according to self (i.e. request packet).
162    pub fn make_response_packet(&self, code: Code) -> Self {
163        Packet {
164            code,
165            identifier: self.identifier,
166            authenticator: self.authenticator.clone(),
167            secret: self.secret.clone(),
168            attributes: Attributes(vec![]),
169        }
170    }
171
172    #[cfg(feature = "md5")]
173    /// This method encodes the Packet into bytes.
174    pub fn encode(&self) -> Result<Vec<u8>, PacketError> {
175        let mut bs = match self.marshal_binary() {
176            Ok(bs) => bs,
177            Err(e) => return Err(PacketError::EncodingError(e)),
178        };
179
180        match self.code {
181            Code::AccessRequest | Code::StatusServer => Ok(bs),
182            Code::AccessAccept
183            | Code::AccessReject
184            | Code::AccountingRequest
185            | Code::AccountingResponse
186            | Code::AccessChallenge
187            | Code::DisconnectRequest
188            | Code::DisconnectACK
189            | Code::DisconnectNAK
190            | Code::CoARequest
191            | Code::CoAACK
192            | Code::CoANAK => {
193                let mut buf: Vec<u8> = bs[..4].to_vec();
194                match self.code {
195                    Code::AccountingRequest // see "Request Authenticator" in https://tools.ietf.org/html/rfc2866#section-3
196                    | Code::DisconnectRequest // same as "RFC2866"; https://tools.ietf.org/html/rfc5176#section-2.3
197                    | Code::CoARequest // same as "RFC2866"; https://tools.ietf.org/html/rfc5176#section-2.3
198                    => {
199                        buf.extend(vec![
200                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
201                            0x00, 0x00, 0x00, 0x00,
202                        ]);
203                    }
204                    _ => {
205                        buf.extend(self.authenticator.clone()); // TODO take from `bs`?
206                    }
207                }
208                buf.extend(bs[RADIUS_PACKET_HEADER_LENGTH..].to_vec());
209                buf.extend(&self.secret);
210                bs.splice(4..20, compute(&buf).to_vec());
211
212                Ok(bs)
213            }
214            _ => Err(PacketError::UnknownCodeError(format!("{:?}", self.code))),
215        }
216    }
217
218    #[cfg(feature = "openssl")]
219    /// This method encodes the Packet into bytes.
220    pub fn encode(&self) -> Result<Vec<u8>, PacketError> {
221        let mut bs = match self.marshal_binary() {
222            Ok(bs) => bs,
223            Err(e) => return Err(PacketError::EncodingError(e)),
224        };
225
226        match self.code {
227            Code::AccessRequest | Code::StatusServer => Ok(bs),
228            Code::AccessAccept
229            | Code::AccessReject
230            | Code::AccountingRequest
231            | Code::AccountingResponse
232            | Code::AccessChallenge
233            | Code::DisconnectRequest
234            | Code::DisconnectACK
235            | Code::DisconnectNAK
236            | Code::CoARequest
237            | Code::CoAACK
238            | Code::CoANAK => {
239                let mut buf: Vec<u8> = bs[..4].to_vec();
240                match self.code {
241                    Code::AccountingRequest // see "Request Authenticator" in https://tools.ietf.org/html/rfc2866#section-3
242                    | Code::DisconnectRequest // same as "RFC2866"; https://tools.ietf.org/html/rfc5176#section-2.3
243                    | Code::CoARequest // same as "RFC2866"; https://tools.ietf.org/html/rfc5176#section-2.3
244                    => {
245                        buf.extend(vec![
246                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
247                            0x00, 0x00, 0x00, 0x00,
248                        ]);
249                    }
250                    _ => {
251                        buf.extend(self.authenticator.clone()); // TODO take from `bs`?
252                    }
253                }
254                buf.extend(bs[RADIUS_PACKET_HEADER_LENGTH..].to_vec());
255                buf.extend(&self.secret);
256                let hash_val = hash(MessageDigest::md5(), &buf);
257                let enc = if let Err(_err) = hash_val {
258                    return Err(PacketError::HashComputationFailed(_err.to_string()));
259                } else {
260                    hash_val.unwrap()
261                };
262                bs.splice(4..20, enc.to_vec());
263
264                Ok(bs)
265            }
266            _ => Err(PacketError::UnknownCodeError(format!("{:?}", self.code))),
267        }
268    }
269
270    /*
271     * Binary structure:
272     *   0                   1                   2                   3
273     *   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
274     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
275     *  |     Code      |  Identifier   |            Length             |
276     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
277     *  |                                                               |
278     *  |                         Authenticator                         |
279     *  |                                                               |
280     *  |                                                               |
281     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
282     *  |  Attributes ...
283     *  +-+-+-+-+-+-+-+-+-+-+-+-+-
284     */
285    fn marshal_binary(&self) -> Result<Vec<u8>, String> {
286        let encoded_avp = match self.attributes.encode() {
287            Ok(encoded) => encoded,
288            Err(e) => return Err(e),
289        };
290
291        let size = RADIUS_PACKET_HEADER_LENGTH as u16 + encoded_avp.len() as u16;
292        if size as usize > MAX_PACKET_LENGTH {
293            return Err("packet is too large".to_owned());
294        }
295
296        let mut bs: Vec<u8> = Vec::new();
297        bs.push(self.code as u8);
298        bs.push(self.identifier);
299        bs.extend(u16::to_be_bytes(size).to_vec());
300        bs.extend(self.authenticator.to_vec());
301        bs.extend(match self.attributes.encode() {
302            Ok(encoded) => encoded,
303            Err(e) => return Err(e),
304        });
305        Ok(bs)
306    }
307
308    #[cfg(feature = "md5")]
309    /// Returns whether the Packet is authentic response or not.
310    pub fn is_authentic_response(response: &[u8], request: &[u8], secret: &[u8]) -> bool {
311        if response.len() < RADIUS_PACKET_HEADER_LENGTH
312            || request.len() < RADIUS_PACKET_HEADER_LENGTH
313            || secret.is_empty()
314        {
315            return false;
316        }
317
318        compute(
319            [
320                &response[..4],
321                &request[4..RADIUS_PACKET_HEADER_LENGTH],
322                &response[RADIUS_PACKET_HEADER_LENGTH..],
323                secret,
324            ]
325            .concat(),
326        )
327        .to_vec()
328        .eq(&response[4..RADIUS_PACKET_HEADER_LENGTH].to_vec())
329    }
330
331    #[cfg(feature = "openssl")]
332    /// Returns whether the Packet is authentic response or not.
333    pub fn is_authentic_response(response: &[u8], request: &[u8], secret: &[u8]) -> bool {
334        if response.len() < RADIUS_PACKET_HEADER_LENGTH
335            || request.len() < RADIUS_PACKET_HEADER_LENGTH
336            || secret.is_empty()
337        {
338            return false;
339        }
340
341        let hash_val = hash(
342            MessageDigest::md5(),
343            &[
344                &response[..4],
345                &request[4..RADIUS_PACKET_HEADER_LENGTH],
346                &response[RADIUS_PACKET_HEADER_LENGTH..],
347                secret,
348            ]
349            .concat(),
350        );
351        let enc = hash_val.expect("Hash computation failed using openssl md5 digest algorithm");
352        enc.to_vec()
353            .eq(&response[4..RADIUS_PACKET_HEADER_LENGTH].to_vec())
354    }
355
356    #[cfg(feature = "md5")]
357    /// Returns whether the Packet is authentic request or not.
358    pub fn is_authentic_request(request: &[u8], secret: &[u8]) -> bool {
359        if request.len() < RADIUS_PACKET_HEADER_LENGTH || secret.is_empty() {
360            return false;
361        }
362
363        match Code::from(request[0]) {
364            Code::AccessRequest | Code::StatusServer => true,
365            Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => compute(
366                [
367                    &request[..4],
368                    &[
369                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
370                        0x00, 0x00, 0x00, 0x00,
371                    ],
372                    &request[RADIUS_PACKET_HEADER_LENGTH..],
373                    secret,
374                ]
375                .concat(),
376            )
377            .to_vec()
378            .eq(&request[4..RADIUS_PACKET_HEADER_LENGTH].to_vec()),
379            _ => false,
380        }
381    }
382
383    #[cfg(feature = "openssl")]
384    /// Returns whether the Packet is authentic request or not.
385    pub fn is_authentic_request(request: &[u8], secret: &[u8]) -> bool {
386        if request.len() < RADIUS_PACKET_HEADER_LENGTH || secret.is_empty() {
387            return false;
388        }
389
390        match Code::from(request[0]) {
391            Code::AccessRequest | Code::StatusServer => true,
392            Code::AccountingRequest | Code::DisconnectRequest | Code::CoARequest => {
393                let hash_val = hash(
394                    MessageDigest::md5(),
395                    &[
396                        &request[..4],
397                        &[
398                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
399                            0x00, 0x00, 0x00, 0x00,
400                        ],
401                        &request[RADIUS_PACKET_HEADER_LENGTH..],
402                        secret,
403                    ]
404                    .concat(),
405                );
406                let enc =
407                    hash_val.expect("Hash computation failed using openssl md5 digest algorithm");
408
409                enc.to_vec()
410                    .eq(&request[4..RADIUS_PACKET_HEADER_LENGTH].to_vec())
411            }
412            _ => false,
413        }
414    }
415
416    /// Add an AVP to the list of AVPs.
417    pub fn add(&mut self, avp: AVP) {
418        self.attributes.add(avp);
419    }
420
421    /// Add AVPs to the list of AVPs.
422    pub fn extend(&mut self, avps: Vec<AVP>) {
423        self.attributes.extend(avps)
424    }
425
426    /// Delete all of AVPs from the list according to given AVP type.
427    pub fn delete(&mut self, typ: AVPType) {
428        self.attributes.del(typ);
429    }
430
431    /// Returns an AVP that matches at first with the given AVP type. If there are not any matched ones, this returns `None`.
432    pub fn lookup(&self, typ: AVPType) -> Option<&AVP> {
433        self.attributes.lookup(typ)
434    }
435
436    /// Returns AVPs that match with the given AVP type.
437    pub fn lookup_all(&self, typ: AVPType) -> Vec<&AVP> {
438        self.attributes.lookup_all(typ)
439    }
440}
441
442impl Debug for Packet {
443    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
444        f.debug_struct("Packet")
445            .field("code", &self.code)
446            .field("identifier", &self.identifier)
447            .field("authenticator", &self.authenticator)
448            .field("secret", &"*redacted*")
449            .field("attributes", &self.attributes)
450            .finish()
451    }
452}
453
454#[cfg(test)]
455mod tests {
456    use std::net::Ipv4Addr;
457
458    use crate::core::avp::AVP;
459    use crate::core::code::Code;
460    use crate::core::packet::{
461        Packet, PacketError, MAX_PACKET_LENGTH, RADIUS_PACKET_HEADER_LENGTH,
462    };
463    use crate::core::rfc2865;
464
465    #[test]
466    fn test_for_rfc2865_7_1() -> Result<(), PacketError> {
467        // ref: https://tools.ietf.org/html/rfc2865#section-7.1
468
469        let secret: Vec<u8> = "xyzzy5461".as_bytes().to_vec();
470        let request: Vec<u8> = vec![
471            0x01, 0x00, 0x00, 0x38, 0x0f, 0x40, 0x3f, 0x94, 0x73, 0x97, 0x80, 0x57, 0xbd, 0x83,
472            0xd5, 0xcb, 0x98, 0xf4, 0x22, 0x7a, 0x01, 0x06, 0x6e, 0x65, 0x6d, 0x6f, 0x02, 0x12,
473            0x0d, 0xbe, 0x70, 0x8d, 0x93, 0xd4, 0x13, 0xce, 0x31, 0x96, 0xe4, 0x3f, 0x78, 0x2a,
474            0x0a, 0xee, 0x04, 0x06, 0xc0, 0xa8, 0x01, 0x10, 0x05, 0x06, 0x00, 0x00, 0x00, 0x03,
475        ];
476
477        let request_packet = Packet::decode(&request, &secret)?;
478        assert_eq!(request_packet.code, Code::AccessRequest);
479        assert_eq!(request_packet.identifier, 0);
480        assert_eq!(
481            rfc2865::lookup_user_name(&request_packet).unwrap().unwrap(),
482            "nemo"
483        );
484        assert_eq!(
485            rfc2865::lookup_all_user_name(&request_packet).unwrap(),
486            vec!["nemo"],
487        );
488        assert_eq!(
489            rfc2865::lookup_user_password(&request_packet)
490                .unwrap()
491                .unwrap(),
492            b"arctangent"
493        );
494        assert_eq!(
495            rfc2865::lookup_nas_ip_address(&request_packet)
496                .unwrap()
497                .unwrap(),
498            Ipv4Addr::from([192, 168, 1, 16]),
499        );
500        assert_eq!(
501            rfc2865::lookup_nas_port(&request_packet).unwrap().unwrap(),
502            3
503        );
504        assert_eq!(request_packet.encode().unwrap(), request);
505        assert!(Packet::is_authentic_request(&request, &secret));
506
507        let response: Vec<u8> = vec![
508            0x02, 0x00, 0x00, 0x26, 0x86, 0xfe, 0x22, 0x0e, 0x76, 0x24, 0xba, 0x2a, 0x10, 0x05,
509            0xf6, 0xbf, 0x9b, 0x55, 0xe0, 0xb2, 0x06, 0x06, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x06,
510            0x00, 0x00, 0x00, 0x00, 0x0e, 0x06, 0xc0, 0xa8, 0x01, 0x03,
511        ];
512        let mut response_packet = request_packet.make_response_packet(Code::AccessAccept);
513        rfc2865::add_service_type(&mut response_packet, rfc2865::SERVICE_TYPE_LOGIN_USER);
514        rfc2865::add_login_service(&mut response_packet, rfc2865::LOGIN_SERVICE_TELNET);
515        rfc2865::add_login_ip_host(&mut response_packet, &Ipv4Addr::from([192, 168, 1, 3]));
516        assert_eq!(response_packet.encode().unwrap(), response);
517        assert!(Packet::is_authentic_response(&response, &request, &secret));
518
519        // test removing a AVP
520        assert!(rfc2865::lookup_service_type(&response_packet).is_some());
521        rfc2865::delete_service_type(&mut response_packet);
522        assert!(rfc2865::lookup_service_type(&response_packet).is_none());
523
524        Ok(())
525    }
526
527    #[test]
528    fn test_for_rfc2865_7_2() -> Result<(), PacketError> {
529        let secret: Vec<u8> = "xyzzy5461".as_bytes().to_vec();
530        let request: Vec<u8> = vec![
531            0x01, 0x01, 0x00, 0x47, 0x2a, 0xee, 0x86, 0xf0, 0x8d, 0x0d, 0x55, 0x96, 0x9c, 0xa5,
532            0x97, 0x8e, 0x0d, 0x33, 0x67, 0xa2, 0x01, 0x08, 0x66, 0x6c, 0x6f, 0x70, 0x73, 0x79,
533            0x03, 0x13, 0x16, 0xe9, 0x75, 0x57, 0xc3, 0x16, 0x18, 0x58, 0x95, 0xf2, 0x93, 0xff,
534            0x63, 0x44, 0x07, 0x72, 0x75, 0x04, 0x06, 0xc0, 0xa8, 0x01, 0x10, 0x05, 0x06, 0x00,
535            0x00, 0x00, 0x14, 0x06, 0x06, 0x00, 0x00, 0x00, 0x02, 0x07, 0x06, 0x00, 0x00, 0x00,
536            0x01,
537        ];
538
539        let request_packet = Packet::decode(&request, &secret)?;
540        assert_eq!(request_packet.get_code(), Code::AccessRequest);
541        assert_eq!(request_packet.identifier, 1);
542        assert_eq!(
543            rfc2865::lookup_user_name(&request_packet).unwrap().unwrap(),
544            "flopsy"
545        );
546        assert_eq!(
547            rfc2865::lookup_nas_ip_address(&request_packet)
548                .unwrap()
549                .unwrap(),
550            Ipv4Addr::from([192, 168, 1, 16]),
551        );
552        assert_eq!(
553            rfc2865::lookup_nas_port(&request_packet).unwrap().unwrap(),
554            20
555        );
556        assert_eq!(
557            rfc2865::lookup_service_type(&request_packet)
558                .unwrap()
559                .unwrap(),
560            rfc2865::SERVICE_TYPE_FRAMED_USER,
561        );
562        assert_eq!(
563            rfc2865::lookup_framed_protocol(&request_packet)
564                .unwrap()
565                .unwrap(),
566            rfc2865::FRAMED_PROTOCOL_PPP,
567        );
568
569        let response: Vec<u8> = vec![
570            0x02, 0x01, 0x00, 0x38, 0x15, 0xef, 0xbc, 0x7d, 0xab, 0x26, 0xcf, 0xa3, 0xdc, 0x34,
571            0xd9, 0xc0, 0x3c, 0x86, 0x01, 0xa4, 0x06, 0x06, 0x00, 0x00, 0x00, 0x02, 0x07, 0x06,
572            0x00, 0x00, 0x00, 0x01, 0x08, 0x06, 0xff, 0xff, 0xff, 0xfe, 0x0a, 0x06, 0x00, 0x00,
573            0x00, 0x00, 0x0d, 0x06, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x06, 0x00, 0x00, 0x05,
574            //    ^ incorrectly a 2 in the document
575            0xdc,
576        ];
577        let response_packet = Packet::decode(&response, &secret).unwrap();
578
579        assert_eq!(response_packet.get_code(), Code::AccessAccept);
580        assert_eq!(response_packet.get_identifier(), 1);
581        assert_eq!(
582            rfc2865::lookup_service_type(&response_packet)
583                .unwrap()
584                .unwrap(),
585            rfc2865::SERVICE_TYPE_FRAMED_USER
586        );
587        assert_eq!(
588            rfc2865::lookup_framed_protocol(&response_packet)
589                .unwrap()
590                .unwrap(),
591            rfc2865::FRAMED_PROTOCOL_PPP,
592        );
593        assert_eq!(
594            rfc2865::lookup_framed_ip_address(&response_packet)
595                .unwrap()
596                .unwrap(),
597            Ipv4Addr::from([255, 255, 255, 254]),
598        );
599        assert_eq!(
600            rfc2865::lookup_framed_routing(&response_packet)
601                .unwrap()
602                .unwrap(),
603            rfc2865::FRAMED_ROUTING_NONE,
604        );
605        assert_eq!(
606            rfc2865::lookup_framed_compression(&response_packet)
607                .unwrap()
608                .unwrap(),
609            rfc2865::FRAMED_COMPRESSION_VAN_JACOBSON_TCP_IP,
610        );
611        assert_eq!(
612            rfc2865::lookup_framed_mtu(&response_packet)
613                .unwrap()
614                .unwrap(),
615            1500,
616        );
617
618        Ok(())
619    }
620
621    #[test]
622    fn test_passwords() {
623        let passwords = vec![
624            b"".to_vec(),
625            b"qwerty".to_vec(),
626            b"helloworld1231231231231233489hegufudhsgdsfygdf8g".to_vec(),
627        ];
628
629        let secret = b"xyzzy5461";
630
631        for password in passwords {
632            let mut request_packet = Packet::new(Code::AccessRequest, secret);
633            rfc2865::add_user_password(&mut request_packet, &password).unwrap();
634
635            let encoded = request_packet.encode().unwrap();
636
637            let decoded = Packet::decode(&encoded, secret).unwrap();
638            assert_eq!(
639                rfc2865::lookup_user_password(&decoded).unwrap().unwrap(),
640                password
641            );
642        }
643    }
644
645    #[test]
646    fn test_parse_invalid() {
647        struct TestCase<'a> {
648            plain_text: &'a str,
649            expected_error: PacketError,
650        }
651
652        let test_cases = &[
653            TestCase {
654                plain_text: "\x01",
655                expected_error: PacketError::InsufficientPacketPayloadLengthError(RADIUS_PACKET_HEADER_LENGTH, 1),
656            },
657            TestCase {
658                plain_text: "\x01\x7f\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
659                expected_error: PacketError::InsufficientHeaderDefinedPacketLengthError(0, RADIUS_PACKET_HEADER_LENGTH),
660            },
661            TestCase {
662                plain_text: "\x01\x7f\x7f\x7f\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01",
663                expected_error: PacketError::HeaderDefinedPacketLengthExceedsMaximumLimitError(32639, MAX_PACKET_LENGTH),
664            },
665            TestCase {
666                plain_text: "\x00\x7f\x00\x16\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00",
667                expected_error: PacketError::InsufficientPacketPayloadLengthError(22, 21),
668            },
669            TestCase {
670                plain_text: "\x01\x01\x00\x16\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01\x00",
671                expected_error: PacketError::DecodingError("invalid attribute length".to_owned()),
672            }
673        ];
674
675        let secret = b"12345";
676        for test_case in test_cases {
677            let result = Packet::decode(test_case.plain_text.as_bytes(), secret);
678            assert!(result.is_err());
679            assert_eq!(result.err().unwrap(), test_case.expected_error);
680        }
681    }
682
683    #[test]
684    fn test_packet_attribute_length_boundary() {
685        let mut packet = Packet::new(Code::AccessRequest, b"12345");
686        packet.add(AVP {
687            typ: 1,
688            value: vec![1; 253],
689        });
690        let encoded = packet.encode();
691        assert!(encoded.is_ok());
692
693        let mut packet = Packet::new(Code::AccessRequest, b"12345");
694        packet.add(AVP {
695            typ: 1,
696            value: vec![1; 254],
697        });
698        let encoded = packet.encode();
699        assert!(encoded.is_err());
700        assert_eq!(
701            encoded.err().unwrap(),
702            PacketError::EncodingError("attribute is too large".to_owned()),
703        );
704    }
705
706    #[test]
707    fn test_with_arbitrary_identifier() {
708        let mut packet = Packet::new(Code::AccessRequest, b"12345");
709        let random_ident = packet.get_identifier();
710        let expected_ident = random_ident + 1;
711        packet.set_identifier(expected_ident);
712        assert_eq!(packet.get_identifier(), expected_ident);
713
714        packet = Packet::new_with_identifier(Code::AccessRequest, b"12345", expected_ident);
715        assert_eq!(packet.get_identifier(), expected_ident);
716    }
717}