1pub mod error;
2pub mod ping;
3pub mod policy;
4pub mod report;
5
6use std::{borrow::Cow, str::FromStr};
7
8use roxmltree::{Document, StringStorage};
9
10use crate::{error::FormatError, policy::Policy, report::Report};
11
12#[derive(Debug)]
22pub struct NessusClientDataV2<'input> {
23 pub policy: Policy<'input>,
25 pub report: Option<Report<'input>>,
28}
29
30impl<'input> NessusClientDataV2<'input> {
31 pub fn parse(xml: &'input str) -> Result<Self, FormatError> {
48 let doc = Document::parse(xml)?;
49
50 let root = doc.root_element();
51
52 if root.tag_name().name() != "NessusClientData_v2" {
53 return Err(FormatError::UnsupportedVersion);
54 }
55
56 let mut policy = None;
57 let mut report = None;
58
59 for child in root.children() {
60 match child.tag_name().name() {
61 "Policy" => {
62 if policy.is_some() {
63 return Err(FormatError::RepeatedTag("Policy"));
64 }
65 policy = Some(Policy::from_xml_node(child)?);
66 }
67 "Report" => {
68 if report.is_some() {
69 return Err(FormatError::RepeatedTag("Report"));
70 }
71 report = Some(Report::from_xml_node(child)?);
72 }
73 _ => assert_empty_text(child)?,
74 }
75 }
76
77 let policy = policy.ok_or(FormatError::MissingTag("Policy"))?;
78
79 Ok(Self { policy, report })
80 }
81}
82
83fn assert_empty_text(node: roxmltree::Node<'_, '_>) -> Result<(), FormatError> {
84 let Some(text) = node.text() else {
85 return Err(FormatError::UnexpectedNodeKind);
86 };
87
88 if !text.trim().is_empty() {
89 return Err(FormatError::UnexpectedNode(
90 format!("{}: {text}", node.tag_name().name()).into_boxed_str(),
91 ));
92 }
93
94 Ok(())
95}
96
97trait StringStorageExt<'input> {
98 fn to_str(&self) -> Result<&'input str, FormatError>;
99 fn to_cow(&self) -> Cow<'input, str>;
100}
101
102impl<'input> StringStorageExt<'input> for StringStorage<'input> {
103 fn to_str(&self) -> Result<&'input str, FormatError> {
104 match self {
105 StringStorage::Borrowed(s) => Ok(s),
106 StringStorage::Owned(s) => Err(FormatError::UnexpectedXmlAttribute(s.as_ref().into())),
107 }
108 }
109
110 fn to_cow(&self) -> Cow<'input, str> {
111 match self {
112 StringStorage::Borrowed(s) => Cow::Borrowed(s),
113 StringStorage::Owned(s) => Cow::Owned(String::from(s.as_ref())),
114 }
115 }
116}
117
118#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
124pub struct MacAddress {
125 bytes: [u8; 6],
126}
127
128impl MacAddress {
129 #[must_use]
130 pub const fn bytes(self) -> [u8; 6] {
131 self.bytes
132 }
133}
134
135impl FromStr for MacAddress {
136 type Err = FormatError;
137
138 fn from_str(s: &str) -> Result<Self, Self::Err> {
139 let mut octets = s.split(':');
140
141 let mac_address = Self {
142 bytes: [
143 parse_octet(octets.next().ok_or(FormatError::MacAdressParse)?)?,
144 parse_octet(octets.next().ok_or(FormatError::MacAdressParse)?)?,
145 parse_octet(octets.next().ok_or(FormatError::MacAdressParse)?)?,
146 parse_octet(octets.next().ok_or(FormatError::MacAdressParse)?)?,
147 parse_octet(octets.next().ok_or(FormatError::MacAdressParse)?)?,
148 parse_octet(octets.next().ok_or(FormatError::MacAdressParse)?)?,
149 ],
150 };
151
152 if octets.next().is_some() {
153 Err(FormatError::MacAdressParse)
154 } else {
155 Ok(mac_address)
156 }
157 }
158}
159
160fn parse_octet(input: &str) -> Result<u8, FormatError> {
161 let &[a, b] = input.as_bytes() else {
162 return Err(FormatError::MacAdressParse);
163 };
164
165 Ok((parse_hex_digit(a)? << 4) | parse_hex_digit(b)?)
166}
167
168const fn parse_hex_digit(ch: u8) -> Result<u8, FormatError> {
169 match ch {
170 b'0'..=b'9' => Ok(ch - b'0'),
171 b'A'..=b'F' => Ok((ch - b'A') + 10),
172 b'a'..=b'f' => Ok((ch - b'a') + 10),
173 _ => Err(FormatError::MacAdressParse),
174 }
175}
176
177impl std::fmt::Display for MacAddress {
178 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
179 let _ = write!(
180 f,
181 "{:<02X}:{:<02X}:{:<02X}:{:<02X}:{:<02X}:{:<02X}",
182 self.bytes[0],
183 self.bytes[1],
184 self.bytes[2],
185 self.bytes[3],
186 self.bytes[4],
187 self.bytes[5]
188 );
189
190 Ok(())
191 }
192}
193
194impl std::fmt::Debug for MacAddress {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 write!(f, "\"{self}\"")
197 }
198}