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

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

            
29
pub use addrpolicy::{AddrPolicy, AddrPortPattern, IpPattern};
30
pub use portpolicy::PortPolicy;
31

            
32
use crate::NormalItemArgument;
33
use crate::parse2::{ArgumentError, ArgumentStream, ItemArgumentParseable};
34

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

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

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

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

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

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

            
178
impl NormalItemArgument for PortRange {}
179

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

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

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

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

            
223
653012
        self.0.push(item);
224
653012
        Ok(())
225
653048
    }
226

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

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

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

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

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

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

            
287
88
        (!self.is_empty()).then_some(DisplayWrapper(self))
288
88
    }
289
}
290

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

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

            
323
19504
        out
324
19504
    }
325
}
326

            
327
impl FromStr for PortRanges {
328
    type Err = PolicyError;
329

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

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

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

            
365
impl NormalItemArgument for RuleKind {}
366

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

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

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

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

            
429
        assert!(PortRange::new_all().contains(1));
430
        assert!(PortRange::new_all().contains(65535));
431
        assert!(PortRange::new_all().contains(7777));
432

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

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

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

            
454
        chk(1, 65535, "1-65535");
455
        chk(10, 20, "10-20");
456
        chk(20, 20, "20");
457
    }
458

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

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

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