tor_netdoc/types/
family.rs1use std::sync::Arc;
7
8use crate::types::misc::LongIdent;
9use crate::{Error, NetdocErrorKind, NormalItemArgument, Pos, Result};
10use base64ct::Encoding;
11use derive_deftly::Deftly;
12use tor_basic_utils::intern::InternCache;
13use tor_llcrypto::pk::ed25519::{ED25519_ID_LEN, Ed25519Identity};
14use tor_llcrypto::pk::rsa::RsaIdentity;
15
16#[derive(Clone, Debug, Default, Hash, Eq, PartialEq, Deftly)]
29#[derive_deftly(ItemValueParseable)]
30pub struct RelayFamily(Vec<LongIdent>);
31
32static FAMILY_CACHE: InternCache<RelayFamily> = InternCache::new();
37
38impl RelayFamily {
39 pub fn new() -> Self {
41 RelayFamily::default()
42 }
43
44 pub fn push(&mut self, rsa_id: RsaIdentity) {
46 self.0.push(rsa_id.into());
47 }
48
49 fn normalize(&mut self) {
51 self.0.sort_by_key(|v| v.0);
52 self.0.dedup();
53 }
54
55 pub fn intern(mut self) -> Arc<Self> {
58 self.normalize();
59 FAMILY_CACHE.intern(self)
60 }
61
62 pub fn contains(&self, rsa_id: &RsaIdentity) -> bool {
64 self.0.contains(&LongIdent(*rsa_id))
65 }
66
67 pub fn members(&self) -> impl Iterator<Item = &RsaIdentity> {
70 self.0.iter().map(|id| &id.0)
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.0.is_empty()
76 }
77}
78
79impl std::str::FromStr for RelayFamily {
80 type Err = Error;
81 fn from_str(s: &str) -> Result<Self> {
82 let v: Result<Vec<LongIdent>> = s
83 .split(crate::parse::tokenize::is_sp)
84 .map(|e| e.parse::<LongIdent>())
85 .filter(Result::is_ok)
86 .collect();
87 Ok(RelayFamily(v?))
88 }
89}
90
91#[derive(Clone, Debug, Eq, PartialEq)]
99#[non_exhaustive]
100pub enum RelayFamilyId {
101 Ed25519(Ed25519Identity),
103 Unrecognized(String),
105}
106
107const ED25519_ID_PREFIX: &str = "ed25519:";
109
110impl std::str::FromStr for RelayFamilyId {
111 type Err = Error;
112
113 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
114 let mut buf = [0_u8; ED25519_ID_LEN];
115 if let Some(s) = s.strip_prefix(ED25519_ID_PREFIX) {
116 if let Ok(decoded) = base64ct::Base64Unpadded::decode(s, &mut buf) {
117 if let Some(ed_id) = Ed25519Identity::from_bytes(decoded) {
118 return Ok(RelayFamilyId::Ed25519(ed_id));
119 }
120 }
121 return Err(NetdocErrorKind::BadArgument
122 .with_msg("Invalid ed25519 family ID")
123 .at_pos(Pos::at(s)));
124 }
125 Ok(RelayFamilyId::Unrecognized(s.to_string()))
126 }
127}
128
129impl std::fmt::Display for RelayFamilyId {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 match self {
132 RelayFamilyId::Ed25519(id) => write!(f, "{}{}", ED25519_ID_PREFIX, id),
133 RelayFamilyId::Unrecognized(s) => write!(f, "{}", s),
134 }
135 }
136}
137
138impl PartialOrd for RelayFamilyId {
139 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
140 Some(Ord::cmp(self, other))
141 }
142}
143impl Ord for RelayFamilyId {
144 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
145 Ord::cmp(&self.to_string(), &other.to_string())
148 }
149}
150
151impl NormalItemArgument for RelayFamilyId {}
152
153#[derive(Clone, Debug, Default, Eq, PartialEq, Deftly, derive_more::AsRef)]
155#[derive_deftly(ItemValueParseable)]
156pub struct RelayFamilyIds(
157 Vec<RelayFamilyId>,
160);
161
162impl RelayFamilyIds {
163 pub fn new() -> Self {
165 Self::default()
166 }
167
168 pub fn push(&mut self, family_id: RelayFamilyId) {
170 self.0.push(family_id);
171 }
172
173 pub fn sort(&mut self) {
175 self.0.sort();
176 }
177
178 pub fn dedup(&mut self) {
180 self.0.dedup();
181 }
182}
183
184impl FromIterator<RelayFamilyId> for RelayFamilyIds {
185 fn from_iter<T: IntoIterator<Item = RelayFamilyId>>(iter: T) -> Self {
186 Self(iter.into_iter().collect())
187 }
188}
189
190#[cfg(test)]
191mod test {
192 #![allow(clippy::bool_assert_comparison)]
194 #![allow(clippy::clone_on_copy)]
195 #![allow(clippy::dbg_macro)]
196 #![allow(clippy::mixed_attributes_style)]
197 #![allow(clippy::print_stderr)]
198 #![allow(clippy::print_stdout)]
199 #![allow(clippy::single_char_pattern)]
200 #![allow(clippy::unwrap_used)]
201 #![allow(clippy::unchecked_time_subtraction)]
202 #![allow(clippy::useless_vec)]
203 #![allow(clippy::needless_pass_by_value)]
204 use std::str::FromStr;
206
207 use super::*;
208 use crate::Result;
209 #[test]
210 fn family() -> Result<()> {
211 let f = "nickname1 nickname2 $ffffffffffffffffffffffffffffffffffffffff=foo eeeeeeeeeeeeeeeeeeeEEEeeeeeeeeeeeeeeeeee ddddddddddddddddddddddddddddddddd $cccccccccccccccccccccccccccccccccccccccc~blarg ".parse::<RelayFamily>()?;
212 let v = vec![
213 RsaIdentity::from_bytes(
214 &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
215 )
216 .unwrap(),
217 RsaIdentity::from_bytes(
218 &hex::decode("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap()[..],
219 )
220 .unwrap(),
221 RsaIdentity::from_bytes(
222 &hex::decode("cccccccccccccccccccccccccccccccccccccccc").unwrap()[..],
223 )
224 .unwrap(),
225 ];
226 assert_eq!(f.members().cloned().collect::<Vec<_>>(), v);
227 Ok(())
228 }
229
230 #[test]
231 fn test_contains() -> Result<()> {
232 let family =
233 "ffffffffffffffffffffffffffffffffffffffff eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
234 .parse::<RelayFamily>()?;
235 let in_family = RsaIdentity::from_bytes(
236 &hex::decode("ffffffffffffffffffffffffffffffffffffffff").unwrap()[..],
237 )
238 .unwrap();
239 let not_in_family = RsaIdentity::from_bytes(
240 &hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap()[..],
241 )
242 .unwrap();
243 assert!(family.contains(&in_family), "Relay not found in family");
244 assert!(
245 !family.contains(¬_in_family),
246 "Extra relay found in family"
247 );
248 Ok(())
249 }
250
251 #[test]
252 fn mutable() {
253 let mut family = RelayFamily::default();
254 let key = RsaIdentity::from_hex("ffffffffffffffffffffffffffffffffffffffff").unwrap();
255 assert!(!family.contains(&key));
256 family.push(key);
257 assert!(family.contains(&key));
258 }
259
260 #[test]
261 fn family_ids() {
262 let ed_str_rep = "ed25519:7sToQRuge1bU2hS0CG0ViMndc4m82JhO4B4kdrQey80";
263 let ed_id = RelayFamilyId::from_str(ed_str_rep).unwrap();
264 assert!(matches!(ed_id, RelayFamilyId::Ed25519(_)));
265 assert_eq!(ed_id.to_string().as_str(), ed_str_rep);
266
267 let other_str_rep = "hello-world";
268 let other_id = RelayFamilyId::from_str(other_str_rep).unwrap();
269 assert!(matches!(other_id, RelayFamilyId::Unrecognized(_)));
270 assert_eq!(other_id.to_string().as_str(), other_str_rep);
271
272 assert_eq!(ed_id, ed_id);
273 assert_ne!(ed_id, other_id);
274 }
275
276 #[test]
277 fn parse2() {
278 #[derive(Debug, PartialEq, Eq, derive_deftly::Deftly)]
279 #[derive_deftly(NetdocParseable)]
280 struct Wrapper {
281 family: RelayFamily,
282 }
283
284 const LINE: &str = "family $0000000000000000000000000000000000000000 $FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF";
285 let parsed =
286 crate::parse2::parse_netdoc::<Wrapper>(&crate::parse2::ParseInput::new(LINE, ""))
287 .unwrap();
288 assert_eq!(
289 parsed,
290 Wrapper {
291 family: RelayFamily(vec![
292 RsaIdentity::from_hex("0000000000000000000000000000000000000000")
293 .unwrap()
294 .into(),
295 RsaIdentity::from_hex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
296 .unwrap()
297 .into()
298 ])
299 }
300 );
301 }
302}