Skip to main content

tor_netdoc/types/policy/
addrpolicy.rs

1//! Implements address policies, based on a series of accept/reject
2//! rules.
3
4use 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/// A sequence of rules that are applied to an address:port until one
22/// matches.
23///
24/// Each rule is of the form "accept PATTERN" or "reject PATTERN",
25/// where every pattern describes a set of addresses and ports.
26/// Address sets are given as a prefix of 0-128 bits that the address
27/// must have; port sets are given as a low-bound and high-bound that
28/// the target port might lie between.
29///
30/// Relays use this type for defining their own policies, and for
31/// publishing their IPv4 policies.  Clients instead use
32/// [super::portpolicy::PortPolicy] objects to view a summary of the
33/// relays' declared policies.
34///
35/// An example IPv4 policy might be:
36///
37/// ```ignore
38///  reject *:25
39///  reject 127.0.0.0/8:*
40///  reject 192.168.0.0/16:*
41///  accept *:80
42///  accept *:443
43///  accept *:9000-65535
44///  reject *:*
45/// ```
46#[derive(Clone, Debug, Default, PartialEq, Eq)]
47pub struct AddrPolicy {
48    /// A list of rules to apply to find out whether an address is
49    /// contained by this policy.
50    ///
51    /// The rules apply in order; the first one to match determines
52    /// whether the address is accepted or rejected.
53    rules: Vec<AddrPolicyRule>,
54}
55
56impl AddrPolicy {
57    /// Apply this policy to an address:port combination
58    ///
59    /// We do this by applying each rule in sequence, until one
60    /// matches.
61    ///
62    /// Returns None if no rule matches.
63    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    /// As allows, but accept a SocketAddr.
71    pub fn allows_sockaddr(&self, addr: &SocketAddr) -> Option<RuleKind> {
72        self.allows(&addr.ip(), addr.port())
73    }
74
75    /// Create a new AddrPolicy that matches nothing.
76    pub fn new() -> Self {
77        AddrPolicy::default()
78    }
79
80    /// Add a new rule to this policy.
81    ///
82    /// The newly added rule is applied _after_ all previous rules.
83    /// It matches all addresses and ports covered by AddrPortPattern.
84    ///
85    /// If accept is true, the rule is to accept addresses that match;
86    /// if accept is false, the rule rejects such addresses.
87    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        // We must use `FromStr`, not argument parsing, because
101        // RuleKind is the keyword and not an argument.
102        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        // The order of this field is significant, meaning we have to emit the
119        // values as they are.  The spec also strongly recommends a trailing
120        // `accept *:*` or `reject *:*`.  To comply with this, we check for
121        // an existing final rule with an ALL pattern and add a `reject *:*`
122        // if that is not the case.  This is not super nice and ideally we would
123        // do this somewhere in the type construction, but we cannot do some
124        // right now because the legacy parser accumulates it as it is.
125        const ALL: AddrPortPattern = AddrPortPattern::new_all();
126        const DEFAULT_DENY: AddrPolicyRule = AddrPolicyRule {
127            kind: RuleKind::Reject,
128            pattern: ALL,
129        };
130
131        // Add default deny in case of an absent trailing ALL.
132        let default_deny = match self.rules.last() {
133            // Do nothing if there already is a trailing ALL.
134            Some(AddrPolicyRule {
135                kind: _,
136                pattern: ALL,
137            }) => None,
138            // Add a default deny to the end.
139            _ => 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/// A single rule in an address policy.
150///
151/// Contains a pattern and what to do with things that match it.
152#[derive(Clone, Debug, PartialEq, Eq)]
153struct AddrPolicyRule {
154    /// What do we do with items that match the pattern?
155    kind: RuleKind,
156    /// What pattern are we trying to match?
157    pattern: AddrPortPattern,
158}
159
160/*
161impl Display for AddrPolicyRule {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        let cmd = match self.kind {
164            RuleKind::Accept => "accept",
165            RuleKind::Reject => "reject",
166        };
167        write!(f, "{} {}", cmd, self.pattern)
168    }
169}
170*/
171
172/// A pattern that may or may not match an address and port.
173///
174/// Each AddrPortPattern has an IP pattern, which matches a set of
175/// addresses by prefix, and a port pattern, which matches a range of
176/// ports.
177///
178/// # Example
179///
180/// ```
181/// use tor_netdoc::types::policy::AddrPortPattern;
182/// use std::net::{IpAddr,Ipv4Addr};
183/// let localhost = IpAddr::V4(Ipv4Addr::new(127,3,4,5));
184/// let not_localhost = IpAddr::V4(Ipv4Addr::new(192,0,2,16));
185/// let pat: AddrPortPattern = "127.0.0.0/8:*".parse().unwrap();
186///
187/// assert!(pat.matches(&localhost, 22));
188/// assert!(! pat.matches(&not_localhost, 22));
189/// ```
190#[derive(
191    Clone, Debug, Eq, PartialEq, serde_with::SerializeDisplay, serde_with::DeserializeFromStr,
192)]
193pub struct AddrPortPattern {
194    /// A pattern to match somewhere between zero and all IP addresses.
195    pattern: IpPattern,
196    /// A pattern to match a range of ports.
197    ports: PortRange,
198}
199
200impl AddrPortPattern {
201    /// Return an AddrPortPattern matching all targets.
202    pub const fn new_all() -> Self {
203        Self {
204            pattern: IpPattern::All,
205            ports: PortRange::new_all(),
206        }
207    }
208
209    /// Return true iff this pattern matches a given address and port.
210    pub fn matches(&self, addr: &IpAddr, port: u16) -> bool {
211        self.pattern.matches(addr) && self.ports.contains(port)
212    }
213    /// As matches, but accept a SocketAddr.
214    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/// A pattern that matches one or more IP addresses.
247#[derive(Clone, Debug, Eq, PartialEq)]
248enum IpPattern {
249    /// Match all addresses.
250    All,
251    /// Match addresses of a particular IP version, beginning with a given prefix.
252    Net(IpNet),
253}
254
255impl IpPattern {
256    /// Construct an IpPattern that matches the first `prefix_len` bits of `addr`.
257    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    /// Return true iff `addr` is matched by this pattern.
264    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            // We want to omit the /prefix_len if it's the maximum, for brevity
278            Net(IpNet::V4(n)) if n.prefix_len() == 32 => write!(f, "{}", n.addr()),
279            Net(IpNet::V4(n)) => write!(f, "{}", n),
280            // We want to include the [ ] around IPv6 addresses, which ipnet omits
281            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
287/// Helper: try to parse a plain ipv4 address, or an IPv6 address
288/// wrapped in brackets.
289fn 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    // @@ begin test lint list maintained by maint/add_warning @@
328    #![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)] // See arti#2571
340    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
341    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        // Patterns with excessive prefix length for the address.
364        // It's not clear that it's correct to accept these.
365        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; // drop mut
425        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        // Do round-trip encoding.
527        let mut enc = NetdocEncoder::default();
528        wrapper.encode_unsigned(&mut enc).unwrap();
529        assert_eq!(RULES, enc.finish().unwrap());
530
531        // Test default deny.
532        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 *:*"), // do not add default deny to existing
545            (reject_all.clone(), "reject *:*"), // do not add default deny to existing
546            (AddrPolicy::new(), "reject *:*"),  // add default deny to empty
547        ];
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}