stun_rs/attributes/discovery/
change_request.rs1use crate::attributes::{
2 stunt_attribute, AsVerifiable, AttributeDecoderContext, AttributeEncoderContext,
3 DecodeAttributeValue, EncodeAttributeValue,
4};
5use enumflags2::{bitflags, BitFlags};
6
7const CHANGE_REQUEST: u16 = 0x0003;
8
9#[bitflags]
13#[repr(u32)]
14#[derive(Copy, Clone, Debug, Eq, PartialEq)]
15pub enum ChangeRequestFlags {
16 ChangePort = 1 << 1,
18 ChangeIp = 1 << 2,
20}
21
22#[derive(Debug, Clone, Copy)]
42pub struct ChangeRequest(u32);
43
44impl ChangeRequest {
45 pub fn new(flags: Option<BitFlags<ChangeRequestFlags>>) -> Self {
52 let flags = match flags {
53 Some(flags) => flags.bits(),
54 None => 0,
55 };
56 ChangeRequest(flags)
57 }
58
59 pub fn flags(&self) -> BitFlags<ChangeRequestFlags> {
61 BitFlags::<ChangeRequestFlags>::from_bits_truncate(self.0)
62 }
63}
64
65impl DecodeAttributeValue for ChangeRequest {
66 fn decode(ctx: AttributeDecoderContext) -> Result<(Self, usize), crate::StunError> {
67 use crate::Decode;
68 let (value, size) = u32::decode(ctx.raw_value())?;
69 Ok((ChangeRequest(value), size))
70 }
71}
72
73impl EncodeAttributeValue for ChangeRequest {
74 fn encode(&self, mut ctx: AttributeEncoderContext) -> Result<usize, crate::StunError> {
75 use crate::Encode;
76 self.0.encode(ctx.raw_value_mut())
77 }
78}
79
80impl AsVerifiable for ChangeRequest {}
81
82stunt_attribute!(ChangeRequest, CHANGE_REQUEST);
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87 use crate::attributes::discovery::change_request::ChangeRequestFlags::*;
88 use crate::error::StunErrorType;
89 use crate::StunAttribute;
90
91 #[test]
92 fn change_request_attribute() {
93 let attr = ChangeRequest::new(None);
94 assert!(!attr.flags().contains(ChangePort));
95 assert!(!attr.flags().contains(ChangeIp));
96
97 let attr = ChangeRequest::new(Some(ChangeIp | ChangePort));
98 assert!(attr.flags().contains(ChangePort));
99 assert!(attr.flags().contains(ChangeIp));
100 }
101
102 #[test]
103 fn decode_change_request_attribute() {
104 let dummy_msg = [];
105 let raw_value = [0x00, 0x00, 0x04];
106 let ctx = AttributeDecoderContext::new(None, &dummy_msg, &raw_value);
107 assert_eq!(
108 ChangeRequest::decode(ctx).expect_err("Error expected"),
109 StunErrorType::SmallBuffer
110 );
111
112 let raw_value = [0x00, 0x00, 0x00, 0x04];
113 let ctx = AttributeDecoderContext::new(None, &dummy_msg, &raw_value);
114 let (attr, size) = ChangeRequest::decode(ctx).unwrap();
115 assert!(!attr.flags().contains(ChangePort));
116 assert!(attr.flags().contains(ChangeIp));
117 assert_eq!(size, 4);
118 }
119
120 #[test]
121 fn encode_change_request_attribute() {
122 let dummy_msg = [];
123 let attr = ChangeRequest::new(Some(ChangePort.into()));
124 let mut raw_value = [0x00; 3];
125 let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut raw_value);
126 let result = attr.encode(ctx);
127 assert_eq!(
128 result.expect_err("Error expected"),
129 StunErrorType::SmallBuffer
130 );
131
132 let mut raw_value = [0x00; 4];
133 let ctx = AttributeEncoderContext::new(None, &dummy_msg, &mut raw_value);
134 let size = attr.encode(ctx).unwrap();
135 assert_eq!(raw_value, [0x00, 0x00, 0x00, 0x02]);
136 assert_eq!(size, 4);
137 }
138
139 #[test]
140 fn change_request_stunt_attribute() {
141 let attr = StunAttribute::ChangeRequest(ChangeRequest::new(Some(ChangePort.into())));
142 assert!(attr.is_change_request());
143 assert!(attr.as_change_request().is_ok());
144 assert!(attr.as_error_code().is_err());
145
146 assert!(attr.attribute_type().is_comprehension_required());
147 assert!(!attr.attribute_type().is_comprehension_optional());
148
149 let dbg_fmt = format!("{:?}", attr);
150 assert_eq!("ChangeRequest(ChangeRequest(2))", dbg_fmt);
151 }
152}