stun_types/attribute/
software.rs1use std::convert::TryFrom;
10
11use crate::message::{StunParseError, StunWriteError};
12
13use super::{
14 Attribute, AttributeExt, AttributeFromRaw, AttributeStaticType, AttributeType, AttributeWrite,
15 AttributeWriteExt, RawAttribute,
16};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct Software {
21 software: String,
22}
23
24impl AttributeStaticType for Software {
25 const TYPE: AttributeType = AttributeType(0x8022);
26}
27
28impl Attribute for Software {
29 fn get_type(&self) -> AttributeType {
30 Self::TYPE
31 }
32
33 fn length(&self) -> u16 {
34 self.software.len() as u16
35 }
36}
37
38impl AttributeWrite for Software {
39 fn to_raw(&self) -> RawAttribute<'_> {
40 RawAttribute::new(Software::TYPE, self.software.as_bytes())
41 }
42
43 fn write_into_unchecked(&self, dest: &mut [u8]) {
44 let len = self.padded_len();
45 let offset = self.write_header_unchecked(dest);
46 dest[offset..offset + self.software.len()].copy_from_slice(self.software.as_bytes());
47 let offset = offset + self.software.len();
48 if len > offset {
49 dest[offset..len].fill(0);
50 }
51 }
52}
53
54impl AttributeFromRaw<'_> for Software {
55 fn from_raw_ref(raw: &RawAttribute) -> Result<Self, StunParseError>
56 where
57 Self: Sized,
58 {
59 Self::try_from(raw)
60 }
61}
62
63impl TryFrom<&RawAttribute<'_>> for Software {
64 type Error = StunParseError;
65
66 fn try_from(raw: &RawAttribute) -> Result<Self, Self::Error> {
67 raw.check_type_and_len(Self::TYPE, ..=763)?;
68 Ok(Self {
69 software: std::str::from_utf8(&raw.value)
70 .map_err(|_| StunParseError::InvalidAttributeData)?
71 .to_owned(),
72 })
73 }
74}
75
76impl Software {
77 pub fn new(software: &str) -> Result<Self, StunWriteError> {
91 if software.len() > 763 {
93 return Err(StunWriteError::TooLarge {
94 expected: 763,
95 actual: software.len(),
96 });
97 }
98 Ok(Self {
99 software: software.to_owned(),
100 })
101 }
102
103 pub fn software(&self) -> &str {
113 &self.software
114 }
115}
116
117impl std::fmt::Display for Software {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(f, "{}: '{}'", Software::TYPE, self.software)
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use byteorder::{BigEndian, ByteOrder};
127 use tracing::trace;
128
129 #[test]
130 fn software() {
131 let _log = crate::tests::test_init_log();
132 let software = Software::new("software").unwrap();
133 trace!("{software}");
134 assert_eq!(software.software(), "software");
135 assert_eq!(software.length() as usize, "software".len());
136 let raw = RawAttribute::from(&software);
137 trace!("{raw}");
138 assert_eq!(raw.get_type(), Software::TYPE);
139 let software2 = Software::try_from(&raw).unwrap();
140 assert_eq!(software2.software(), "software");
141 let mut data: Vec<_> = raw.into();
143 BigEndian::write_u16(&mut data[0..2], 0);
144 assert!(matches!(
145 Software::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
146 Err(StunParseError::WrongAttributeImplementation)
147 ));
148 }
149
150 #[test]
151 fn software_not_utf8() {
152 let _log = crate::tests::test_init_log();
153 let attr = Software::new("software").unwrap();
154 let raw = RawAttribute::from(&attr);
155 let mut data = raw.to_bytes();
156 data[6] = 0x88;
157 assert!(matches!(
158 Software::try_from(&RawAttribute::from_bytes(data.as_ref()).unwrap()),
159 Err(StunParseError::InvalidAttributeData)
160 ));
161 }
162
163 #[test]
164 fn software_new_too_large() {
165 let _log = crate::tests::test_init_log();
166 let mut large = String::new();
167 for _i in 0..95 {
168 large.push_str("abcdefgh");
169 }
170 large.push_str("abcd");
171 assert!(matches!(
172 Software::new(&large),
173 Err(StunWriteError::TooLarge {
174 expected: 763,
175 actual: 764
176 })
177 ));
178 }
179}