tor_netdoc/types/
policy.rs1mod addrpolicy;
20mod portpolicy;
21
22use std::fmt::Display;
23use std::str::FromStr;
24use thiserror::Error;
25
26pub use addrpolicy::{AddrPolicy, AddrPortPattern, RuleKind};
27pub use portpolicy::PortPolicy;
28
29#[derive(Debug, Error, Clone, PartialEq, Eq)]
31#[non_exhaustive]
32pub enum PolicyError {
33 #[error("Invalid port")]
35 InvalidPort,
36 #[error("Invalid port range")]
38 InvalidRange,
39 #[error("Invalid address")]
41 InvalidAddress,
42 #[error("mask with star")]
44 MaskWithStar,
45 #[error("invalid mask")]
47 InvalidMask,
48 #[error("Invalid policy")]
50 InvalidPolicy,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Hash)]
68#[allow(clippy::exhaustive_structs)]
69pub struct PortRange {
70 pub lo: u16,
72 pub hi: u16,
74}
75
76impl PortRange {
77 fn new_unchecked(lo: u16, hi: u16) -> Self {
80 assert!(lo != 0);
81 assert!(lo <= hi);
82 PortRange { lo, hi }
83 }
84 pub fn new_all() -> Self {
86 PortRange::new_unchecked(1, 65535)
87 }
88 pub fn new(lo: u16, hi: u16) -> Option<Self> {
94 if lo != 0 && lo <= hi {
95 Some(PortRange { lo, hi })
96 } else {
97 None
98 }
99 }
100 pub fn contains(&self, port: u16) -> bool {
102 self.lo <= port && port <= self.hi
103 }
104 pub fn is_all(&self) -> bool {
106 self.lo == 1 && self.hi == 65535
107 }
108
109 fn compare_to_port(&self, port: u16) -> std::cmp::Ordering {
115 use std::cmp::Ordering::*;
116 if port < self.lo {
117 Greater
118 } else if port <= self.hi {
119 Equal
120 } else {
121 Less
122 }
123 }
124}
125
126impl Display for PortRange {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 if self.lo == self.hi {
132 write!(f, "{}", self.lo)
133 } else {
134 write!(f, "{}-{}", self.lo, self.hi)
135 }
136 }
137}
138
139impl FromStr for PortRange {
140 type Err = PolicyError;
141 fn from_str(s: &str) -> Result<Self, PolicyError> {
142 let idx = s.find('-');
143 let (lo, hi) = if let Some(pos) = idx {
145 (
147 s[..pos]
148 .parse::<u16>()
149 .map_err(|_| PolicyError::InvalidPort)?,
150 s[pos + 1..]
151 .parse::<u16>()
152 .map_err(|_| PolicyError::InvalidPort)?,
153 )
154 } else {
155 let v = s.parse::<u16>().map_err(|_| PolicyError::InvalidPort)?;
157 (v, v)
158 };
159 PortRange::new(lo, hi).ok_or(PolicyError::InvalidRange)
160 }
161}
162
163#[cfg(test)]
164mod test {
165 #![allow(clippy::bool_assert_comparison)]
167 #![allow(clippy::clone_on_copy)]
168 #![allow(clippy::dbg_macro)]
169 #![allow(clippy::mixed_attributes_style)]
170 #![allow(clippy::print_stderr)]
171 #![allow(clippy::print_stdout)]
172 #![allow(clippy::single_char_pattern)]
173 #![allow(clippy::unwrap_used)]
174 #![allow(clippy::unchecked_duration_subtraction)]
175 #![allow(clippy::useless_vec)]
176 #![allow(clippy::needless_pass_by_value)]
177 use super::*;
179 use crate::Result;
180 #[test]
181 fn parse_portrange() -> Result<()> {
182 assert_eq!(
183 "1-100".parse::<PortRange>()?,
184 PortRange::new(1, 100).unwrap()
185 );
186 assert_eq!(
187 "01-100".parse::<PortRange>()?,
188 PortRange::new(1, 100).unwrap()
189 );
190 assert_eq!("1-65535".parse::<PortRange>()?, PortRange::new_all());
191 assert_eq!(
192 "10-30".parse::<PortRange>()?,
193 PortRange::new(10, 30).unwrap()
194 );
195 assert_eq!(
196 "9001".parse::<PortRange>()?,
197 PortRange::new(9001, 9001).unwrap()
198 );
199 assert_eq!(
200 "9001-9001".parse::<PortRange>()?,
201 PortRange::new(9001, 9001).unwrap()
202 );
203
204 assert!("hello".parse::<PortRange>().is_err());
205 assert!("0".parse::<PortRange>().is_err());
206 assert!("65536".parse::<PortRange>().is_err());
207 assert!("65537".parse::<PortRange>().is_err());
208 assert!("1-2-3".parse::<PortRange>().is_err());
209 assert!("10-5".parse::<PortRange>().is_err());
210 assert!("1-".parse::<PortRange>().is_err());
211 assert!("-2".parse::<PortRange>().is_err());
212 assert!("-".parse::<PortRange>().is_err());
213 assert!("*".parse::<PortRange>().is_err());
214 Ok(())
215 }
216
217 #[test]
218 fn pr_manip() {
219 assert!(PortRange::new_all().is_all());
220 assert!(!PortRange::new(2, 65535).unwrap().is_all());
221
222 assert!(PortRange::new_all().contains(1));
223 assert!(PortRange::new_all().contains(65535));
224 assert!(PortRange::new_all().contains(7777));
225
226 assert!(PortRange::new(20, 30).unwrap().contains(20));
227 assert!(PortRange::new(20, 30).unwrap().contains(25));
228 assert!(PortRange::new(20, 30).unwrap().contains(30));
229 assert!(!PortRange::new(20, 30).unwrap().contains(19));
230 assert!(!PortRange::new(20, 30).unwrap().contains(31));
231
232 use std::cmp::Ordering::*;
233 assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(7), Greater);
234 assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(20), Equal);
235 assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(25), Equal);
236 assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(30), Equal);
237 assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(100), Less);
238 }
239
240 #[test]
241 fn pr_fmt() {
242 fn chk(a: u16, b: u16, s: &str) {
243 let pr = PortRange::new(a, b).unwrap();
244 assert_eq!(format!("{}", pr), s);
245 }
246
247 chk(1, 65535, "1-65535");
248 chk(10, 20, "10-20");
249 chk(20, 20, "20");
250 }
251}