1
//! Provide builder functionality for routerstatuses.
2

            
3
use super::{GenericRouterStatus, MdConsensusRouterStatus};
4
use crate::doc::microdesc::MdDigest;
5
use crate::doc::netstatus::{ConsensusBuilder, RelayFlags, RelayWeight};
6
use crate::{BuildError as Error, BuildResult as Result};
7
use tor_llcrypto::pk::rsa::RsaIdentity;
8
use tor_protover::Protocols;
9

            
10
use std::net::SocketAddr;
11

            
12
#[cfg(feature = "ns_consensus")]
13
use super::NsConsensusRouterStatus;
14
#[cfg(feature = "ns_consensus")]
15
use crate::doc::routerdesc::RdDigest;
16

            
17
/// A Builder object for creating a RouterStatus and adding it to a
18
/// consensus.
19
46
#[derive(Debug, Clone)]
20
pub struct RouterStatusBuilder<D> {
21
    /// See [`GenericRouterStatus::nickname`].
22
    nickname: Option<String>,
23
    /// See [`GenericRouterStatus::identity`].
24
    identity: Option<RsaIdentity>,
25
    /// See [`GenericRouterStatus::addrs`].
26
    addrs: Vec<SocketAddr>,
27
    /// See [`GenericRouterStatus::doc_digest`].
28
    doc_digest: Option<D>,
29
    /// See [`GenericRouterStatus::flags`].
30
    flags: RelayFlags,
31
    /// See [`GenericRouterStatus::version`].
32
    version: Option<String>,
33
    /// See [`GenericRouterStatus::protos`].
34
    protos: Option<Protocols>,
35
    /// See [`GenericRouterStatus::weight`].
36
    weight: Option<RelayWeight>,
37
}
38

            
39
impl<D: Clone> RouterStatusBuilder<D> {
40
    /// Construct a new RouterStatusBuilder.
41
27747
    pub(crate) fn new() -> Self {
42
27747
        RouterStatusBuilder {
43
27747
            nickname: None,
44
27747
            identity: None,
45
27747
            addrs: Vec::new(),
46
27747
            doc_digest: None,
47
27747
            flags: RelayFlags::empty(),
48
27747
            version: None,
49
27747
            protos: None,
50
27747
            weight: None,
51
27747
        }
52
27747
    }
53

            
54
    /// Set the nickname for this routerstatus.
55
    ///
56
    /// This value defaults to "Unnamed".
57
1
    pub fn nickname(&mut self, nickname: String) -> &mut Self {
58
1
        self.nickname = Some(nickname);
59
1
        self
60
1
    }
61

            
62
    /// Set the RSA identity for this routerstatus.
63
    ///
64
    /// (The Ed25519 identity is in the microdescriptor).
65
    ///
66
    /// This value is required.
67
27774
    pub fn identity(&mut self, identity: RsaIdentity) -> &mut Self {
68
27774
        self.identity = Some(identity);
69
27774
        self
70
27774
    }
71
    /// Add an OrPort at `addr` to this routerstatus.
72
    ///
73
    /// At least one value here is required.
74
27752
    pub fn add_or_port(&mut self, addr: SocketAddr) -> &mut Self {
75
27752
        self.addrs.push(addr);
76
27752
        self
77
27752
    }
78
    /// Set the document digest for this routerstatus.
79
    ///
80
    /// This value is required.
81
28055
    pub fn doc_digest(&mut self, doc_digest: D) -> &mut Self {
82
28055
        self.doc_digest = Some(doc_digest);
83
28055
        self
84
28055
    }
85
    /// Replace the current flags in this routerstatus with `flags`.
86
27793
    pub fn set_flags(&mut self, flags: RelayFlags) -> &mut Self {
87
27793
        self.flags = flags;
88
27793
        self
89
27793
    }
90
    /// Make all the flags in `flags` become set on this routerstatus,
91
    /// in addition to the flags already set.
92
47
    pub fn add_flags(&mut self, flags: RelayFlags) -> &mut Self {
93
47
        self.flags |= flags;
94
47
        self
95
47
    }
96
    /// Set the version of the relay described in this routerstatus.
97
    ///
98
    /// This value is optional.
99
1
    pub fn version(&mut self, version: String) -> &mut Self {
100
1
        self.version = Some(version);
101
1
        self
102
1
    }
103
    /// Set the list of subprotocols supported by the relay described
104
    /// by this routerstatus.
105
    ///
106
    /// This value is required.
107
28369
    pub fn protos(&mut self, protos: Protocols) -> &mut Self {
108
28369
        self.protos = Some(protos);
109
28369
        self
110
28369
    }
111
    /// Set the weight of this routerstatus for random selection.
112
    ///
113
    /// This value is optional; it defaults to 0.
114
28462
    pub fn weight(&mut self, weight: RelayWeight) -> &mut Self {
115
28462
        self.weight = Some(weight);
116
28462
        self
117
28462
    }
118
    /// Try to build a GenericRouterStatus from this builder.
119
38829
    fn finish(&self) -> Result<GenericRouterStatus<D>> {
120
38829
        let nickname = self.nickname.clone().unwrap_or_else(|| "Unnamed".into());
121
38829
        let identity = self
122
38829
            .identity
123
38829
            .ok_or(Error::CannotBuild("Missing RSA identity"))?;
124
38829
        if self.addrs.is_empty() {
125
            return Err(Error::CannotBuild("No addresses"));
126
38829
        }
127
38829
        let doc_digest = self
128
38829
            .doc_digest
129
38829
            .as_ref()
130
38829
            .ok_or(Error::CannotBuild("Missing document digest"))?
131
38829
            .clone();
132
38829
        let protos = self
133
38829
            .protos
134
38829
            .as_ref()
135
38829
            .ok_or(Error::CannotBuild("Missing protocols"))?
136
38829
            .clone();
137
38829
        let weight = self.weight.unwrap_or(RelayWeight::Unmeasured(0));
138
38829
        let version = self.version.as_deref().map(str::parse).transpose()?;
139

            
140
38744
        Ok(GenericRouterStatus {
141
38744
            nickname,
142
38744
            identity,
143
38744
            addrs: self.addrs.clone(),
144
38744
            doc_digest,
145
38744
            version,
146
38744
            protos,
147
38744
            flags: self.flags,
148
38744
            weight,
149
38744
        })
150
38744
    }
151
}
152

            
153
#[cfg(feature = "ns_consensus")]
154
impl RouterStatusBuilder<RdDigest> {
155
    /// Try to finish this builder and add its RouterStatus to a
156
    /// provided ConsensusBuilder.
157
    pub fn build_into(
158
        &self,
159
        builder: &mut ConsensusBuilder<NsConsensusRouterStatus>,
160
    ) -> Result<()> {
161
        builder.add_rs(self.build()?);
162
        Ok(())
163
    }
164
    /// Return a router status built by this object.
165
    pub fn build(&self) -> Result<NsConsensusRouterStatus> {
166
        Ok(self.finish()?.into())
167
    }
168
}
169

            
170
impl RouterStatusBuilder<MdDigest> {
171
    /// Try to finish this builder and add its RouterStatus to a
172
    /// provided ConsensusBuilder.x
173
    pub fn build_into(
174
        &self,
175
        builder: &mut ConsensusBuilder<MdConsensusRouterStatus>,
176
    ) -> Result<()> {
177
38727
        builder.add_rs(self.build()?);
178
38727
        Ok(())
179
38727
    }
180

            
181
    /// Return a router status built by this object.
182
38829
    pub fn build(&self) -> Result<MdConsensusRouterStatus> {
183
38829
        Ok(self.finish()?.into())
184
38829
    }
185
}