1
//! Exit policies: match patterns of addresses and/or ports.
2
//!
3
//! Every Tor relays has a set of address:port combinations that it
4
//! actually allows connections to.  The set, abstractly, is the
5
//! relay's "exit policy".
6
//!
7
//! Address policies can be transmitted in two forms.  One is a "full
8
//! policy", that includes a list of rules that are applied in order
9
//! to represent addresses and ports.  We represent this with the
10
//! AddrPolicy type.
11
//!
12
//! In microdescriptors, and for IPv6 policies, policies are just
13
//! given a list of ports for which _most_ addresses are permitted.
14
//! We represent this kind of policy with the PortPolicy type.
15
//!
16
//! TODO: This module probably belongs in a crate of its own, with
17
//! possibly only the parsing code in this crate.
18

            
19
mod addrpolicy;
20
mod portpolicy;
21
mod summary;
22

            
23
use std::fmt;
24
use std::ops::RangeInclusive;
25
use std::str::FromStr;
26
use std::{collections::BTreeSet, fmt::Display};
27
use thiserror::Error;
28
use tor_basic_utils::iter_join;
29

            
30
pub use addrpolicy::{AddrPolicy, AddrPortPattern, IpPattern};
31
pub use portpolicy::PortPolicy;
32
pub use summary::{PortPolicies, PortSummaryThresholds};
33

            
34
use crate::NormalItemArgument;
35
use crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable};
36

            
37
/// Error from an unparsable or invalid policy.
38
#[derive(Debug, Error, Clone, PartialEq, Eq)]
39
#[non_exhaustive]
40
pub enum PolicyError {
41
    /// A port was not a number in the range 1..65535
42
    #[error("Invalid port")]
43
    InvalidPort,
44
    /// A port range had its starting-point higher than its ending point.
45
    #[error("Invalid port range")]
46
    InvalidRange,
47
    /// An address could not be interpreted.
48
    #[error("Invalid address")]
49
    InvalidAddress,
50
    /// Tried to use a bitmask or prefix len with the address "*".
51
    // TODO maybe rename this, we never use masks, only prefix lengths
52
    #[error("mask or prefix length with star")]
53
    MaskWithStar,
54
    /// A bit mask was out of range.
55
    // TODO maybe rename this, we never use masks, only prefix lengths
56
    #[error("invalid prefix length or mask")]
57
    InvalidMask,
58
    /// A policy could not be parsed for some other reason.
59
    #[error("Invalid policy")]
60
    InvalidPolicy,
61
}
62

            
63
/// A PortRange is a set of consecutively numbered TCP or UDP ports.
64
///
65
/// # Example
66
/// ```
67
/// use tor_netdoc::types::policy::PortRange;
68
///
69
/// let r: PortRange = "22-8000".parse().unwrap();
70
/// assert!(r.contains(128));
71
/// assert!(r.contains(22));
72
/// assert!(r.contains(8000));
73
///
74
/// assert!(! r.contains(21));
75
/// assert!(! r.contains(8001));
76
/// ```
77
#[derive(derive_more::Debug, Clone, Copy, PartialEq, Eq, Hash)]
78
#[allow(clippy::exhaustive_structs)]
79
#[debug("PortRange({})", &self)]
80
pub struct PortRange {
81
    /// The first port in this range.
82
    lo: u16,
83
    /// The last port in this range.
84
    hi: u16,
85
}
86

            
87
impl PortRange {
88
    /// Create a new port range spanning from lo to hi, asserting that
89
    /// the correct invariants hold.
90
121171
    const fn new_unchecked(lo: u16, hi: u16) -> Self {
91
121171
        assert!(lo != 0);
92
121171
        assert!(lo <= hi);
93
121171
        PortRange { lo, hi }
94
121171
    }
95
    /// Create a port range containing all ports.
96
29289
    pub const fn new_all() -> Self {
97
29289
        PortRange::new_unchecked(1, 65535)
98
29289
    }
99
    /// Create a new PortRange.
100
    ///
101
    /// The Portrange contains all ports between `lo` and `hi` inclusive.
102
    ///
103
    /// Returns None if lo is greater than hi, or if either is zero.
104
716146
    pub fn new(lo: u16, hi: u16) -> Option<Self> {
105
716146
        if lo != 0 && lo <= hi {
106
716140
            Some(PortRange { lo, hi })
107
        } else {
108
6
            None
109
        }
110
716146
    }
111
    /// Create a new `PortRange` from a `RangeInclusive`
112
    ///
113
    /// Returns `None` if the range is ill-formed, or contains zero.
114
25406
    pub fn from_range(r: RangeInclusive<u16>) -> Option<Self> {
115
25406
        Self::new(*r.start(), *r.end())
116
25406
    }
117
    /// Create a new `PortRange` from a `RangeInclusive`
118
    ///
119
    /// Returns `None` if the range is ill-formed, or contains zero.
120
44421
    pub fn to_range(self) -> RangeInclusive<u16> {
121
44421
        self.lo..=self.hi
122
44421
    }
123
    /// Return true if a port is in this range.
124
5604
    pub fn contains(&self, port: u16) -> bool {
125
5604
        self.lo <= port && port <= self.hi
126
5604
    }
127
    /// Return true if this range contains all ports.
128
277
    pub fn is_all(&self) -> bool {
129
277
        self.lo == 1 && self.hi == 65535
130
277
    }
131

            
132
    /// Helper for binary search: compare this range to a port.
133
    ///
134
    /// This range is "equal" to all ports that it contains.  It is
135
    /// "greater" than all ports that precede its starting point, and
136
    /// "less" than all ports that follow its ending point.
137
29568384
    fn compare_to_port(&self, port: u16) -> std::cmp::Ordering {
138
        use std::cmp::Ordering::*;
139
29568384
        if port < self.lo {
140
3320808
            Greater
141
26247576
        } else if port <= self.hi {
142
19880565
            Equal
143
        } else {
144
6367011
            Less
145
        }
146
29568384
    }
147
}
148

            
149
/// A PortRange is displayed as a number if it contains a single port,
150
/// and as a start point and end point separated by a dash if it contains
151
/// more than one port.
152
impl Display for PortRange {
153
83230
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154
83230
        if self.lo == self.hi {
155
32238
            write!(f, "{}", self.lo)
156
        } else {
157
50992
            write!(f, "{}-{}", self.lo, self.hi)
158
        }
159
83230
    }
160
}
161

            
162
impl FromStr for PortRange {
163
    type Err = PolicyError;
164
690710
    fn from_str(s: &str) -> Result<Self, PolicyError> {
165
690710
        let (lo, hi) = match s.split_once('-') {
166
390047
            Some((lo, hi)) => (
167
390047
                lo.parse::<u16>().map_err(|_| PolicyError::InvalidPort)?,
168
390039
                hi.parse::<u16>().map_err(|_| PolicyError::InvalidPort)?,
169
            ),
170
            None => {
171
                // There was no hyphen, so try to parse this range as a singleton.
172
300663
                let v = s.parse::<u16>().map_err(|_| PolicyError::InvalidPort)?;
173
300645
                (v, v)
174
            }
175
        };
176
690680
        PortRange::new(lo, hi).ok_or(PolicyError::InvalidRange)
177
690710
    }
178
}
179

            
180
impl NormalItemArgument for PortRange {}
181

            
182
/// A collection of port ranges in a sorted order.
183
///
184
/// Please use this when storing multiple port ranges because it optimizies
185
/// them storage wise.
186
// TODO: We should rewrite most of this, the implementation has lots of
187
// potential for off-by-one errors and such.
188
#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
189
// Invariant:
190
//
191
// The `PortRange`s are valid, nonoverlapping, non-abutting, and sorted.
192
struct PortRanges(Vec<PortRange>);
193

            
194
impl PortRanges {
195
    /// Creates a new [`PortRanges`] collection with no elements in it.
196
524071
    fn new() -> Self {
197
524071
        Self(Vec::new())
198
524071
    }
199

            
200
    /// Checks whether there are no ranges in this instance.
201
19503960
    fn is_empty(&self) -> bool {
202
19503960
        self.0.is_empty()
203
19503960
    }
204

            
205
    /// Adds a new range into this [`PortRanges`].
206
    ///
207
    /// The ranges must be valid, nonoverlapping, and pushed in a monotonically increasing order,
208
    /// meaning that inserting `400-500,450-600` or `400-500,500-600` are
209
    /// invalid, whereas `400-500,501-600` and `400-500,501-600` are.
210
730973
    fn push_ordered(&mut self, item: PortRange) -> Result<(), PolicyError> {
211
730973
        if let Some(prev) = self.0.last() {
212
            // TODO SPEC: We don't enforce this in Tor, but we probably
213
            // should.  See torspec#60.
214
214290
            if prev.hi >= item.lo {
215
16
                return Err(PolicyError::InvalidPolicy);
216
214274
            } else if prev.hi == item.lo - 1 {
217
                // We compress a-b,(b+1)-c into a-c.
218
2368
                let r = PortRange::new_unchecked(prev.lo, item.hi);
219
2368
                self.0.pop();
220
2368
                self.0.push(r);
221
2368
                return Ok(());
222
211906
            }
223
516683
        }
224

            
225
728589
        self.0.push(item);
226
728589
        Ok(())
227
730973
    }
228

            
229
    /// Checks whether `port` is contained in a range.
230
    ///
231
    /// Whether this means if `port` is allowed or rejected depends on the
232
    /// surroundings (such as which field this `PortRage` is in,
233
    /// or an associated [`RuleKind`]).
234
32963388
    fn contains(&self, port: u16) -> bool {
235
33147512
        debug_assert!(self.0.is_sorted_by(|a, b| a.lo < b.lo));
236
32963388
        self.0
237
33521514
            .binary_search_by(|range| range.compare_to_port(port))
238
32963388
            .is_ok()
239
32963388
    }
240

            
241
    /// Returns an inverted [`PortRanges`].
242
    ///
243
    /// For example, a [`PortRanges`] of `80-443` would become `1-79,444-65535`.
244
263203
    fn inverted(&self) -> PortRanges {
245
263203
        let mut prev_hi = 0;
246
263203
        let mut new_allowed = Vec::new();
247
303505
        for entry in &self.0 {
248
            // ports prev_hi+1 through entry.lo-1 were rejected.  We should
249
            // make them allowed.
250
303505
            if entry.lo > prev_hi + 1 {
251
41047
                new_allowed.push(PortRange::new_unchecked(prev_hi + 1, entry.lo - 1));
252
262640
            }
253
303505
            prev_hi = entry.hi;
254
        }
255
263203
        if prev_hi < 65535 {
256
747
            new_allowed.push(PortRange::new_unchecked(prev_hi + 1, 65535));
257
262456
        }
258
263203
        PortRanges(new_allowed)
259
263203
    }
260

            
261
    /// Inverts a [`PortRanges`] in place
262
    ///
263
    /// For example, a [`PortRanges`] of `80-443` would become `1-79,444-65535`.
264
262501
    fn invert(&mut self) {
265
262501
        *self = self.inverted();
266
262501
    }
267

            
268
    /// Returns an iterator for [`PortRanges`].
269
1352
    fn iter(&self) -> impl Iterator<Item = &PortRange> + Clone {
270
1352
        self.0.iter()
271
1352
    }
272

            
273
    /// If set of ranges is non-empty, returns a string representation
274
    ///
275
    /// We don't provide a normal `Display` impl, because it would have to
276
    /// emit the empty string for an empty range, which would be quite odd.
277
    ///
278
    /// When displaying accept/reject ranges, the caller needs to
279
    /// choose between prepending `accept` and prepending `reject`.
280
1404
    fn display(&self) -> Option<impl Display + '_> {
281
        struct DisplayWrapper<'r>(&'r PortRanges);
282

            
283
        impl Display for DisplayWrapper<'_> {
284
1352
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
285
1352
                write!(f, "{}", iter_join(",", self.0.iter()))
286
1352
            }
287
        }
288

            
289
1404
        (!self.is_empty()).then_some(DisplayWrapper(self))
290
1404
    }
291
}
292

            
293
impl FromIterator<u16> for PortRanges {
294
19684
    fn from_iter<I: IntoIterator<Item = u16>>(iter: I) -> Self {
295
        // Collect all ports into a BTreeSet to have them sorted and deduped.
296
19684
        let ports = iter.into_iter().collect::<BTreeSet<_>>();
297
19684
        let mut ports = ports.into_iter().peekable();
298

            
299
19684
        let mut out = Self::new();
300
19684
        let mut current_min = None;
301
97788
        while let Some(port) = ports.next() {
302
78104
            if current_min.is_none() {
303
47720
                current_min = Some(port);
304
47720
            }
305
78104
            if let Some(next_port) = ports.peek().copied() {
306
                // We do not have to worry about port == 65535, because then
307
                // ports.peek() will be None, as each item in the BTreeSet is
308
                // ordered and unique, implying that there won't be a successor
309
                // to a port == 65535.
310
63192
                if next_port != port + 1 {
311
32808
                    let _ = out.push_ordered(PortRange::new_unchecked(
312
32808
                        current_min.expect("Don't have min port number"),
313
32808
                        port,
314
32808
                    ));
315
32808
                    current_min = None;
316
32928
                }
317
14912
            } else {
318
14912
                let _ = out.push_ordered(PortRange::new_unchecked(
319
14912
                    current_min.expect("Don't have min port number"),
320
14912
                    port,
321
14912
                ));
322
14912
            }
323
        }
324

            
325
19684
        out
326
19684
    }
327
}
328

            
329
impl FromStr for PortRanges {
330
    type Err = PolicyError;
331

            
332
500008
    fn from_str(s: &str) -> Result<Self, Self::Err> {
333
        // Pitfall: Do not use a clever iterator here because we need the result
334
        // of .push() in order to avoid things such as `30-19`.
335
500008
        let mut ranges = Self::new();
336
658354
        for range in s.split(',') {
337
658354
            ranges.push_ordered(range.parse()?)?;
338
        }
339
499978
        Ok(ranges)
340
500008
    }
341
}
342

            
343
impl ItemArgumentParseable for PortRanges {
344
    /// [`PortRanges`] argument parser which is odd because port ranges are
345
    /// syntactically a single argument although semantically multiple ones.
346
1326
    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError> {
347
1326
        args.next()
348
1326
            .map(Self::from_str)
349
1326
            .unwrap_or(Ok(Self::new()))
350
1326
            .map_err(|_| ArgumentError::Invalid)
351
1326
    }
352
}
353

            
354
/// A kind of policy rule: either accepts or rejects addresses
355
/// matching a pattern.
356
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, derive_more::Display, derive_more::FromStr)]
357
#[display(rename_all = "lowercase")]
358
#[from_str(rename_all = "lowercase")]
359
#[allow(clippy::exhaustive_enums)]
360
pub enum RuleKind {
361
    /// A rule that accepts matching address:port combinations.
362
    Accept,
363
    /// A rule that rejects matching address:port combinations.
364
    Reject,
365
}
366

            
367
impl NormalItemArgument for RuleKind {}
368

            
369
#[cfg(test)]
370
mod test {
371
    // @@ begin test lint list maintained by maint/add_warning @@
372
    #![allow(clippy::bool_assert_comparison)]
373
    #![allow(clippy::clone_on_copy)]
374
    #![allow(clippy::dbg_macro)]
375
    #![allow(clippy::mixed_attributes_style)]
376
    #![allow(clippy::print_stderr)]
377
    #![allow(clippy::print_stdout)]
378
    #![allow(clippy::single_char_pattern)]
379
    #![allow(clippy::unwrap_used)]
380
    #![allow(clippy::unchecked_time_subtraction)]
381
    #![allow(clippy::useless_vec)]
382
    #![allow(clippy::needless_pass_by_value)]
383
    #![allow(clippy::string_slice)] // See arti#2571
384
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
385
    use super::*;
386
    use crate::Result;
387
    use crate::parse2::{self, ParseInput};
388

            
389
    #[test]
390
    fn parse_portrange() -> Result<()> {
391
        assert_eq!(
392
            "1-100".parse::<PortRange>()?,
393
            PortRange::new(1, 100).unwrap()
394
        );
395
        assert_eq!(
396
            "01-100".parse::<PortRange>()?,
397
            PortRange::new(1, 100).unwrap()
398
        );
399
        assert_eq!("1-65535".parse::<PortRange>()?, PortRange::new_all());
400
        assert_eq!(
401
            "10-30".parse::<PortRange>()?,
402
            PortRange::new(10, 30).unwrap()
403
        );
404
        assert_eq!(
405
            "9001".parse::<PortRange>()?,
406
            PortRange::new(9001, 9001).unwrap()
407
        );
408
        assert_eq!(
409
            "9001-9001".parse::<PortRange>()?,
410
            PortRange::new(9001, 9001).unwrap()
411
        );
412

            
413
        assert!("hello".parse::<PortRange>().is_err());
414
        assert!("0".parse::<PortRange>().is_err());
415
        assert!("65536".parse::<PortRange>().is_err());
416
        assert!("65537".parse::<PortRange>().is_err());
417
        assert!("1-2-3".parse::<PortRange>().is_err());
418
        assert!("10-5".parse::<PortRange>().is_err());
419
        assert!("1-".parse::<PortRange>().is_err());
420
        assert!("-2".parse::<PortRange>().is_err());
421
        assert!("-".parse::<PortRange>().is_err());
422
        assert!("*".parse::<PortRange>().is_err());
423
        Ok(())
424
    }
425

            
426
    #[test]
427
    fn pr_manip() {
428
        assert!(PortRange::new_all().is_all());
429
        assert!(!PortRange::new(2, 65535).unwrap().is_all());
430

            
431
        assert!(PortRange::new_all().contains(1));
432
        assert!(PortRange::new_all().contains(65535));
433
        assert!(PortRange::new_all().contains(7777));
434

            
435
        assert!(PortRange::new(20, 30).unwrap().contains(20));
436
        assert!(PortRange::new(20, 30).unwrap().contains(25));
437
        assert!(PortRange::new(20, 30).unwrap().contains(30));
438
        assert!(!PortRange::new(20, 30).unwrap().contains(19));
439
        assert!(!PortRange::new(20, 30).unwrap().contains(31));
440

            
441
        use std::cmp::Ordering::*;
442
        assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(7), Greater);
443
        assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(20), Equal);
444
        assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(25), Equal);
445
        assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(30), Equal);
446
        assert_eq!(PortRange::new(20, 30).unwrap().compare_to_port(100), Less);
447
    }
448

            
449
    #[test]
450
    fn pr_fmt() {
451
        fn chk(a: u16, b: u16, s: &str) {
452
            let pr = PortRange::new(a, b).unwrap();
453
            assert_eq!(format!("{}", pr), s);
454
        }
455

            
456
        chk(1, 65535, "1-65535");
457
        chk(10, 20, "10-20");
458
        chk(20, 20, "20");
459
    }
460

            
461
    #[test]
462
    fn port_ranges() {
463
        const INPUT: &str = "22,80,443,8000-9000,9002";
464
        let ranges = PortRanges::from_str(INPUT).unwrap();
465
        assert_eq!(
466
            ranges.0,
467
            [
468
                PortRange::new(22, 22).unwrap(),
469
                PortRange::new(80, 80).unwrap(),
470
                PortRange::new(443, 443).unwrap(),
471
                PortRange::new(8000, 9000).unwrap(),
472
                PortRange::new(9002, 9002).unwrap(),
473
            ]
474
        );
475
        assert!(ranges.contains(22));
476
        assert!(ranges.contains(80));
477
        assert!(ranges.contains(443));
478
        assert!(ranges.contains(8000));
479
        assert!(ranges.contains(8500));
480
        assert!(ranges.contains(9000));
481
        assert!(!ranges.contains(9001));
482
        assert!(ranges.contains(9002));
483

            
484
        let mut ranges_inverse = ranges.clone();
485
        ranges_inverse.invert();
486
        assert_eq!(
487
            ranges_inverse.0,
488
            [
489
                PortRange::new(1, 21).unwrap(),
490
                PortRange::new(23, 79).unwrap(),
491
                PortRange::new(81, 442).unwrap(),
492
                PortRange::new(444, 7999).unwrap(),
493
                PortRange::new(9001, 9001).unwrap(),
494
                PortRange::new(9003, 65535).unwrap(),
495
            ]
496
        );
497

            
498
        #[derive(derive_deftly::Deftly)]
499
        #[derive_deftly(NetdocParseable)]
500
        struct Dummy {
501
            #[deftly(netdoc(single_arg))]
502
            dummy: PortRanges,
503
        }
504
        let ranges2 =
505
            parse2::parse_netdoc::<Dummy>(&ParseInput::new(&format!("dummy {INPUT}\n"), ""))
506
                .unwrap();
507
        assert_eq!(ranges, ranges2.dummy);
508
    }
509
}