1use std::fmt::Display;
5use std::net::{IpAddr, SocketAddr};
6use std::str::FromStr;
7
8use itertools::chain;
9
10use crate::NormalItemArgument;
11use crate::encode::NetdocEncodableFields;
12use crate::parse2::{
13 ErrorProblem as EP, ItemArgumentParseable, ItemStream, KeywordRef, NetdocParseableFields,
14 UnparsedItem,
15};
16
17use ipnet::IpNet;
18
19use super::{PolicyError, PortRange, RuleKind};
20
21#[derive(Clone, Debug, Default, PartialEq, Eq)]
47pub struct AddrPolicy {
48 rules: Vec<AddrPolicyRule>,
54}
55
56impl AddrPolicy {
57 pub fn allows(&self, addr: &IpAddr, port: u16) -> Option<RuleKind> {
64 self.rules
65 .iter()
66 .find(|rule| rule.pattern.matches(addr, port))
67 .map(|AddrPolicyRule { kind, .. }| *kind)
68 }
69
70 pub fn allows_sockaddr(&self, addr: &SocketAddr) -> Option<RuleKind> {
72 self.allows(&addr.ip(), addr.port())
73 }
74
75 pub fn new() -> Self {
77 AddrPolicy::default()
78 }
79
80 pub fn push(&mut self, kind: RuleKind, pattern: AddrPortPattern) {
88 self.rules.push(AddrPolicyRule { kind, pattern });
89 }
90}
91
92impl NetdocParseableFields for AddrPolicy {
93 type Accumulator = AddrPolicy;
94
95 fn is_item_keyword(kw: KeywordRef<'_>) -> bool {
96 matches!(kw.as_str(), "accept" | "reject")
97 }
98
99 fn accumulate_item(acc: &mut Self::Accumulator, mut item: UnparsedItem<'_>) -> Result<(), EP> {
100 let rule = RuleKind::from_str(item.keyword().as_str())
103 .map_err(|_| EP::Internal("accept/reject not a RuleKind?"))?;
104 let args = item.args_mut();
105 let pattern =
106 AddrPortPattern::from_args(args).map_err(args.error_handler("accept/reject"))?;
107 acc.push(rule, pattern);
108 Ok(())
109 }
110
111 fn finish(acc: Self::Accumulator, _: &ItemStream) -> Result<Self, EP> {
112 Ok(acc)
113 }
114}
115
116impl NetdocEncodableFields for AddrPolicy {
117 fn encode_fields(&self, out: &mut crate::encode::NetdocEncoder) -> Result<(), tor_error::Bug> {
118 const ALL: AddrPortPattern = AddrPortPattern::new_all();
126 const DEFAULT_DENY: AddrPolicyRule = AddrPolicyRule {
127 kind: RuleKind::Reject,
128 pattern: ALL,
129 };
130
131 let default_deny = match self.rules.last() {
133 Some(AddrPolicyRule {
135 kind: _,
136 pattern: ALL,
137 }) => None,
138 _ => Some(&DEFAULT_DENY),
140 };
141
142 for rule in chain!(&self.rules, default_deny) {
143 out.push_raw_string(&format_args!("{} {}\n", rule.kind, rule.pattern));
144 }
145 Ok(())
146 }
147}
148
149#[derive(Clone, Debug, PartialEq, Eq)]
153struct AddrPolicyRule {
154 kind: RuleKind,
156 pattern: AddrPortPattern,
158}
159
160#[derive(
191 Clone, Debug, Eq, PartialEq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr,
192)]
193pub struct AddrPortPattern {
194 pattern: IpPattern,
196 ports: PortRange,
198}
199
200impl AddrPortPattern {
201 pub const fn new_all() -> Self {
203 Self {
204 pattern: IpPattern::All,
205 ports: PortRange::new_all(),
206 }
207 }
208
209 pub fn matches(&self, addr: &IpAddr, port: u16) -> bool {
211 self.pattern.matches(addr) && self.ports.contains(port)
212 }
213 pub fn matches_sockaddr(&self, addr: &SocketAddr) -> bool {
215 self.matches(&addr.ip(), addr.port())
216 }
217}
218
219impl Display for AddrPortPattern {
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 if self.ports.is_all() {
222 write!(f, "{}:*", self.pattern)
223 } else {
224 write!(f, "{}:{}", self.pattern, self.ports)
225 }
226 }
227}
228
229impl FromStr for AddrPortPattern {
230 type Err = PolicyError;
231 fn from_str(s: &str) -> Result<Self, PolicyError> {
232 let (pattern, ports_s) = s.rsplit_once(':').ok_or(PolicyError::InvalidPolicy)?;
233 let pattern: IpPattern = pattern.parse()?;
234 let ports: PortRange = if ports_s == "*" {
235 PortRange::new_all()
236 } else {
237 ports_s.parse()?
238 };
239
240 Ok(AddrPortPattern { pattern, ports })
241 }
242}
243
244impl NormalItemArgument for AddrPortPattern {}
245
246#[derive(Clone, Debug, Eq, PartialEq)]
248enum IpPattern {
249 All,
251 Net(IpNet),
253}
254
255impl IpPattern {
256 fn from_addr_and_prefix_len(addr: IpAddr, prefix_len: u8) -> Result<Self, PolicyError> {
258 IpNet::new(addr, prefix_len)
259 .map(IpPattern::Net)
260 .map_err(|_: ipnet::PrefixLenError| PolicyError::InvalidMask)
261 }
262
263 fn matches(&self, addr: &IpAddr) -> bool {
265 match self {
266 IpPattern::All => true,
267 IpPattern::Net(n) => n.contains(addr),
268 }
269 }
270}
271
272impl Display for IpPattern {
273 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
274 use IpPattern::*;
275 match self {
276 All => write!(f, "*"),
277 Net(IpNet::V4(n)) if n.prefix_len() == 32 => write!(f, "{}", n.addr()),
279 Net(IpNet::V4(n)) => write!(f, "{}", n),
280 Net(IpNet::V6(n)) if n.prefix_len() == 128 => write!(f, "[{}]", n.addr()),
282 Net(IpNet::V6(n)) => write!(f, "[{}]/{}", n.addr(), n.prefix_len()),
283 }
284 }
285}
286
287fn parse_addr(mut s: &str) -> Result<IpAddr, PolicyError> {
290 let trimmed = s.strip_prefix('[').and_then(|s| s.strip_suffix(']'));
291 if let Some(trimmed) = trimmed {
292 s = trimmed;
293 }
294 let addr: IpAddr = s.parse().map_err(|_| PolicyError::InvalidAddress)?;
295 if addr.is_ipv6() != trimmed.is_some() {
296 return Err(PolicyError::InvalidAddress);
297 }
298 Ok(addr)
299}
300
301impl FromStr for IpPattern {
302 type Err = PolicyError;
303 fn from_str(s: &str) -> Result<Self, PolicyError> {
304 let (ip_s, plen_s) = match s.split_once('/') {
305 Some((ip_s, plen_s)) => (ip_s, Some(plen_s)),
306 None => (s, None),
307 };
308 match (ip_s, plen_s) {
309 ("*", Some(_)) => Err(PolicyError::MaskWithStar),
310 ("*", None) => Ok(IpPattern::All),
311 (s, Some(m)) => {
312 let a: IpAddr = parse_addr(s)?;
313 let m: u8 = m.parse().map_err(|_| PolicyError::InvalidMask)?;
314 IpPattern::from_addr_and_prefix_len(a, m)
315 }
316 (s, None) => {
317 let a: IpAddr = parse_addr(s)?;
318 let m = if a.is_ipv4() { 32 } else { 128 };
319 IpPattern::from_addr_and_prefix_len(a, m)
320 }
321 }
322 }
323}
324
325#[cfg(test)]
326mod test {
327 #![allow(clippy::bool_assert_comparison)]
329 #![allow(clippy::clone_on_copy)]
330 #![allow(clippy::dbg_macro)]
331 #![allow(clippy::mixed_attributes_style)]
332 #![allow(clippy::print_stderr)]
333 #![allow(clippy::print_stdout)]
334 #![allow(clippy::single_char_pattern)]
335 #![allow(clippy::unwrap_used)]
336 #![allow(clippy::unchecked_time_subtraction)]
337 #![allow(clippy::useless_vec)]
338 #![allow(clippy::needless_pass_by_value)]
339 #![allow(clippy::string_slice)] use crate::encode::{NetdocEncodable, NetdocEncoder};
342
343 use super::*;
344
345 #[test]
346 fn test_roundtrip_rules() {
347 fn check2(inp: &str, outp: &str) {
348 let policy = inp.parse::<AddrPortPattern>().expect(inp);
349 assert_eq!(format!("{}", policy), outp);
350 }
351 let check = |inp| check2(inp, inp);
352
353 check2("127.0.0.2/32:77-10000", "127.0.0.2:77-10000");
354 check2("127.0.0.2/32:*", "127.0.0.2:*");
355 check("127.0.0.0/16:9-100");
356 check("127.0.0.0/0:443");
357 check("*:443");
358 check("[::1]:443");
359 check("[ffaa::]/16:80");
360 check2("[ffaa::77]/128:80", "[ffaa::77]:80");
361 check("[::]/0:443");
362
363 check("127.0.0.1/8:443");
366 check("[::1]/8:443");
367 }
368
369 #[test]
370 fn test_bad_rules() {
371 fn check(s: &str) {
372 let _: PolicyError = s.parse::<AddrPortPattern>().expect_err(s);
373 }
374
375 check("marzipan:80");
376 check("1.2.3.4:90-80");
377 check("1.2.3.4/100:8888");
378 check("[1.2.3.4]/16:80");
379 check("[::1]/130:8888");
380 }
381
382 #[test]
383 fn test_rule_matches() {
384 fn check(addr: &str, yes: &[&str], no: &[&str]) {
385 use std::net::SocketAddr;
386 let policy = addr.parse::<AddrPortPattern>().unwrap();
387 for s in yes {
388 let sa = s.parse::<SocketAddr>().unwrap();
389 assert!(policy.matches_sockaddr(&sa));
390 }
391 for s in no {
392 let sa = s.parse::<SocketAddr>().unwrap();
393 assert!(!policy.matches_sockaddr(&sa));
394 }
395 }
396
397 check(
398 "1.2.3.4/16:80",
399 &["1.2.3.4:80", "1.2.44.55:80"],
400 &["9.9.9.9:80", "1.3.3.4:80", "1.2.3.4:81"],
401 );
402 check(
403 "*:443-8000",
404 &["1.2.3.4:443", "[::1]:500"],
405 &["9.0.0.0:80", "[::1]:80"],
406 );
407 check(
408 "[face::]/8:80",
409 &["[fab0::7]:80"],
410 &["[dd00::]:80", "[face::7]:443"],
411 );
412
413 check("0.0.0.0/0:*", &["127.0.0.1:80"], &["[f00b::]:80"]);
414 check("[::]/0:*", &["[f00b::]:80"], &["127.0.0.1:80"]);
415 }
416
417 #[test]
418 fn test_policy_matches() -> Result<(), PolicyError> {
419 let mut policy = AddrPolicy::default();
420 policy.push(RuleKind::Accept, "*:443".parse()?);
421 policy.push(RuleKind::Accept, "[::1]:80".parse()?);
422 policy.push(RuleKind::Reject, "*:80".parse()?);
423
424 let policy = policy; assert_eq!(
426 policy.allows_sockaddr(&"[::6]:443".parse().unwrap()),
427 Some(RuleKind::Accept)
428 );
429 assert_eq!(
430 policy.allows_sockaddr(&"127.0.0.1:443".parse().unwrap()),
431 Some(RuleKind::Accept)
432 );
433 assert_eq!(
434 policy.allows_sockaddr(&"[::1]:80".parse().unwrap()),
435 Some(RuleKind::Accept)
436 );
437 assert_eq!(
438 policy.allows_sockaddr(&"[::2]:80".parse().unwrap()),
439 Some(RuleKind::Reject)
440 );
441 assert_eq!(
442 policy.allows_sockaddr(&"127.0.0.1:80".parse().unwrap()),
443 Some(RuleKind::Reject)
444 );
445 assert_eq!(
446 policy.allows_sockaddr(&"127.0.0.1:66".parse().unwrap()),
447 None
448 );
449 Ok(())
450 }
451
452 #[test]
453 fn serde() {
454 #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)]
455 struct X {
456 p1: AddrPortPattern,
457 p2: AddrPortPattern,
458 }
459
460 let x = X {
461 p1: "127.0.0.1/8:9-10".parse().unwrap(),
462 p2: "*:80".parse().unwrap(),
463 };
464
465 let encoded = serde_json::to_string(&x).unwrap();
466 let expected = r#"{"p1":"127.0.0.1/8:9-10","p2":"*:80"}"#;
467 let x2: X = serde_json::from_str(&encoded).unwrap();
468 let x3: X = serde_json::from_str(expected).unwrap();
469 assert_eq!(&x2, &x3);
470 assert_eq!(&x2, &x);
471 }
472
473 #[test]
474 fn parse2() {
475 use crate::parse2::{self, ParseInput};
476 use derive_deftly::Deftly;
477
478 const RULES: &str = "\
479 intro\n\
480 reject *:25\n\
481 reject 127.0.0.0/8:*\n\
482 reject 192.168.0.0/16:*\n\
483 accept *:80\n\
484 accept *:443\n\
485 accept *:9000-65535\n\
486 reject *:*\n";
487
488 #[derive(Deftly)]
489 #[derive_deftly(NetdocParseable, NetdocEncodable)]
490 struct Wrapper {
491 #[allow(dead_code)]
492 intro: (),
493 #[deftly(netdoc(flatten))]
494 ipv4_policy: AddrPolicy,
495 }
496
497 let wrapper = parse2::parse_netdoc::<Wrapper>(&ParseInput::new(RULES, "")).unwrap();
498 let ap = wrapper.ipv4_policy.clone();
499
500 assert_eq!(
501 ap.allows_sockaddr(&"1.1.1.1:80".parse().unwrap()),
502 Some(RuleKind::Accept)
503 );
504 assert_eq!(
505 ap.allows_sockaddr(&"1.1.1.1:443".parse().unwrap()),
506 Some(RuleKind::Accept)
507 );
508 assert_eq!(
509 ap.allows_sockaddr(&"1.1.1.1:9005".parse().unwrap()),
510 Some(RuleKind::Accept)
511 );
512
513 assert_eq!(
514 ap.allows_sockaddr(&"1.1.1.1:25".parse().unwrap()),
515 Some(RuleKind::Reject)
516 );
517 assert_eq!(
518 ap.allows_sockaddr(&"127.0.0.1:80".parse().unwrap()),
519 Some(RuleKind::Reject)
520 );
521 assert_eq!(
522 ap.allows_sockaddr(&"1.1.1.1:70".parse().unwrap()),
523 Some(RuleKind::Reject)
524 );
525
526 let mut enc = NetdocEncoder::default();
528 wrapper.encode_unsigned(&mut enc).unwrap();
529 assert_eq!(RULES, enc.finish().unwrap());
530
531 let accept_all = {
533 let mut ap = AddrPolicy::new();
534 ap.push(RuleKind::Accept, AddrPortPattern::new_all());
535 ap
536 };
537 let reject_all = {
538 let mut ap = AddrPolicy::new();
539 ap.push(RuleKind::Reject, AddrPortPattern::new_all());
540 ap
541 };
542
543 let tests = [
544 (accept_all.clone(), "accept *:*"), (reject_all.clone(), "reject *:*"), (AddrPolicy::new(), "reject *:*"), ];
548 for (input, expected_tail) in tests {
549 let mut enc = NetdocEncoder::default();
550 Wrapper {
551 intro: (),
552 ipv4_policy: input,
553 }
554 .encode_unsigned(&mut enc)
555 .unwrap();
556 assert_eq!(expected_tail, enc.finish().unwrap().lines().last().unwrap());
557 }
558 }
559}