summaryrefslogtreecommitdiff
path: root/tor-circmgr/src/path/dirpath.rs
blob: 1be123535baf75b2f4a62b2af65b2b614bacd951 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//! Code to construct paths to a directory for non-anonymous downloads
use super::*;
use crate::{DirInfo, Error};
use tor_netdir::{Relay, WeightRole};

use rand::seq::SliceRandom;

/// A PathBuilder that can connect to a directory.
#[non_exhaustive]
pub struct DirPathBuilder {}

impl Default for DirPathBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl DirPathBuilder {
    /// Create a new DirPathBuilder.
    pub fn new() -> Self {
        DirPathBuilder {}
    }

    /// Try to create and return a path corresponding to the requirements of
    /// this builder.
    pub fn pick_path<'a, R: Rng>(&self, rng: &mut R, netdir: DirInfo<'a>) -> Result<TorPath<'a>> {
        // TODO: this will need to learn about directory guards.
        match netdir {
            DirInfo::Fallbacks(f) => {
                let relay = f.choose(rng);
                if let Some(r) = relay {
                    return Ok(TorPath::new_fallback_one_hop(r));
                }
            }
            DirInfo::Directory(netdir) => {
                let relay = netdir.pick_relay(rng, WeightRole::BeginDir, Relay::is_dir_cache);
                if let Some(r) = relay {
                    return Ok(TorPath::new_one_hop(r));
                }
            }
        }
        Err(Error::NoRelays(
            "No relays found for use as directory cache".into(),
        ))
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use tor_netdir::testnet;

    #[test]
    fn dirpath_relay() {
        let netdir = testnet::construct_netdir();
        let mut rng = rand::thread_rng();
        let dirinfo = (&netdir).into();

        for _ in 0..1000 {
            let p = DirPathBuilder::default().pick_path(&mut rng, dirinfo);
            let p = p.unwrap();
            assert!(p.exit_relay().is_none());
            assert_eq!(p.len(), 1);
            assert_same_path_when_owned(&p);
            if let crate::path::TorPathInner::OneHop(r) = p.inner {
                assert!(r.is_dir_cache());
            } else {
                panic!("Generated the wrong kind of path.");
            }
        }
    }

    #[test]
    fn dirpath_fallback() {
        let fb = vec![
            FallbackDir::builder()
                .rsa_identity([0x01; 20].into())
                .ed_identity([0x01; 32].into())
                .orport("127.0.0.1:9000".parse().unwrap())
                .build()
                .unwrap(),
            FallbackDir::builder()
                .rsa_identity([0x03; 20].into())
                .ed_identity([0x03; 32].into())
                .orport("127.0.0.1:9003".parse().unwrap())
                .build()
                .unwrap(),
        ];
        let dirinfo = (&fb[..]).into();
        let mut rng = rand::thread_rng();

        for _ in 0..10 {
            let p = DirPathBuilder::default().pick_path(&mut rng, dirinfo);
            let p = p.unwrap();
            assert!(p.exit_relay().is_none());
            assert_eq!(p.len(), 1);
            assert_same_path_when_owned(&p);

            if let crate::path::TorPathInner::FallbackOneHop(f) = p.inner {
                assert!(std::ptr::eq(f, &fb[0]) || std::ptr::eq(f, &fb[1]));
            } else {
                panic!("Generated the wrong kind of path.");
            }
        }
    }

    #[test]
    fn dirpath_no_fallbacks() {
        let fb = vec![];
        let dirinfo = DirInfo::Fallbacks(&fb[..]);
        let mut rng = rand::thread_rng();

        let err = DirPathBuilder::default().pick_path(&mut rng, dirinfo);
        assert!(matches!(err, Err(Error::NoRelays(_))));
    }
}