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