1 //! Link specifier objects
2 //!
3 //! (These are in a separate crate, since they get used both by
4 //! directory code and protocol code.)
5 
6 use std::net::{IpAddr, SocketAddr};
7 
8 use tor_bytes::{Error, Readable, Reader, Result, Writeable, Writer};
9 use tor_llcrypto::pk::ed25519;
10 use tor_llcrypto::pk::rsa::RsaIdentity;
11 
12 /// A piece of information about a relay and how to connect to it.
13 #[non_exhaustive]
14 #[derive(Debug, Clone, PartialEq)]
15 pub enum LinkSpec {
16     /// The TCP address of an OR Port for a relay
17     OrPort(IpAddr, u16),
18     /// The RSA identity fingerprint of the relay
19     RsaId(RsaIdentity),
20     /// The Ed25519 identity of the relay
21     Ed25519Id(ed25519::Ed25519Identity),
22     /// A link specifier that we didn't recognize
23     Unrecognized(u8, Vec<u8>),
24 }
25 
26 /// Indicates an IPv4 ORPORT link specifier.
27 const LSTYPE_ORPORT_V4: u8 = 0;
28 /// Indicates an IPv6 ORPORT link specifier.
29 const LSTYPE_ORPORT_V6: u8 = 1;
30 /// Indicates an RSA ID fingerprint link specifier
31 const LSTYPE_RSAID: u8 = 2;
32 /// Indicates an Ed25519 link specifier
33 const LSTYPE_ED25519ID: u8 = 3;
34 
35 impl Readable for LinkSpec {
take_from(r: &mut Reader<'_>) -> Result<Self>36     fn take_from(r: &mut Reader<'_>) -> Result<Self> {
37         /// Return the expected length of the link specifier whose type is tp.
38         fn lstype_len(tp: u8) -> Option<usize> {
39             match tp {
40                 LSTYPE_ORPORT_V4 => Some(6),
41                 LSTYPE_ORPORT_V6 => Some(18),
42                 LSTYPE_RSAID => Some(20),
43                 LSTYPE_ED25519ID => Some(32),
44                 _ => None,
45             }
46         }
47         let lstype = r.take_u8()?;
48         let lslen = r.take_u8()? as usize;
49         if let Some(wantlen) = lstype_len(lstype) {
50             if wantlen != lslen {
51                 return Err(Error::BadMessage("Wrong length for link specifier"));
52             }
53         }
54         Ok(match lstype {
55             LSTYPE_ORPORT_V4 => {
56                 let addr = IpAddr::V4(r.extract()?);
57                 LinkSpec::OrPort(addr, r.take_u16()?)
58             }
59             LSTYPE_ORPORT_V6 => {
60                 let addr = IpAddr::V6(r.extract()?);
61                 LinkSpec::OrPort(addr, r.take_u16()?)
62             }
63             LSTYPE_RSAID => LinkSpec::RsaId(r.extract()?),
64             LSTYPE_ED25519ID => LinkSpec::Ed25519Id(r.extract()?),
65             _ => LinkSpec::Unrecognized(lstype, r.take(lslen)?.into()),
66         })
67     }
68 }
69 impl Writeable for LinkSpec {
write_onto<B: Writer + ?Sized>(&self, w: &mut B)70     fn write_onto<B: Writer + ?Sized>(&self, w: &mut B) {
71         use LinkSpec::*;
72         match self {
73             OrPort(IpAddr::V4(v4), port) => {
74                 w.write_u8(LSTYPE_ORPORT_V4);
75                 w.write_u8(6); // Length
76                 w.write(v4);
77                 w.write_u16(*port);
78             }
79             OrPort(IpAddr::V6(v6), port) => {
80                 w.write_u8(LSTYPE_ORPORT_V6);
81                 w.write_u8(18); // Length
82                 w.write(v6);
83                 w.write_u16(*port);
84             }
85             RsaId(r) => {
86                 w.write_u8(LSTYPE_RSAID);
87                 w.write_u8(20); // Length
88                 w.write(r);
89             }
90             Ed25519Id(e) => {
91                 w.write_u8(LSTYPE_ED25519ID);
92                 w.write_u8(32); // Length
93                 w.write(e);
94             }
95             Unrecognized(tp, vec) => {
96                 w.write_u8(*tp);
97                 assert!(vec.len() < std::u8::MAX as usize);
98                 w.write_u8(vec.len() as u8);
99                 w.write_all(&vec[..]);
100             }
101         }
102     }
103 }
104 
105 impl From<&SocketAddr> for LinkSpec {
from(sa: &SocketAddr) -> Self106     fn from(sa: &SocketAddr) -> Self {
107         LinkSpec::OrPort(sa.ip(), sa.port())
108     }
109 }
110 impl From<SocketAddr> for LinkSpec {
from(sa: SocketAddr) -> Self111     fn from(sa: SocketAddr) -> Self {
112         (&sa).into()
113     }
114 }
115 impl From<RsaIdentity> for LinkSpec {
from(id: RsaIdentity) -> Self116     fn from(id: RsaIdentity) -> Self {
117         LinkSpec::RsaId(id)
118     }
119 }
120 impl From<ed25519::Ed25519Identity> for LinkSpec {
from(id: ed25519::Ed25519Identity) -> Self121     fn from(id: ed25519::Ed25519Identity) -> Self {
122         LinkSpec::Ed25519Id(id)
123     }
124 }
125 impl From<ed25519::PublicKey> for LinkSpec {
from(pk: ed25519::PublicKey) -> Self126     fn from(pk: ed25519::PublicKey) -> Self {
127         LinkSpec::Ed25519Id(pk.into())
128     }
129 }
130 
131 impl LinkSpec {
132     /// Helper: return the position in the list of identifiers
133     /// in which a given linkspec should occur.
sort_pos(&self) -> u8134     fn sort_pos(&self) -> u8 {
135         use LinkSpec::*;
136         match self {
137             OrPort(IpAddr::V4(_), _) => 0,
138             RsaId(_) => 1,
139             Ed25519Id(_) => 2,
140             OrPort(IpAddr::V6(_), _) => 3,
141             Unrecognized(n, _) => *n,
142         }
143     }
144 
145     /// Sort a slice of LinkSpec based on the order in which they should
146     /// appear in an EXTEND cell.
sort_by_type(lst: &mut [Self])147     pub fn sort_by_type(lst: &mut [Self]) {
148         lst.sort_by_key(LinkSpec::sort_pos);
149     }
150 }
151 
152 #[cfg(test)]
153 mod test {
154     #![allow(clippy::unwrap_used)]
155     use super::*;
156     use hex_literal::hex;
157     use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
158     use tor_bytes::{Reader, Writer};
159 
160     #[test]
test_parse_enc()161     fn test_parse_enc() {
162         fn t(b: &[u8], val: &LinkSpec) {
163             let mut r = Reader::from_slice(b);
164             let got: LinkSpec = r.extract().unwrap();
165             assert_eq!(r.remaining(), 0);
166             assert_eq!(&got, val);
167             let mut v = Vec::new();
168             v.write(val);
169             assert_eq!(&v[..], b);
170         }
171 
172         t(
173             &hex!("00 06 01020304 0050"),
174             &LinkSpec::OrPort(IpAddr::V4(Ipv4Addr::new(1, 2, 3, 4)), 80),
175         );
176         t(
177             &hex!("01 12 0001 0002 0003 0004 0005 0006 0007 0008 01bb"),
178             &LinkSpec::OrPort(IpAddr::V6(Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8)), 443),
179         );
180         t(
181             &[
182                 2, 20, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 33, 33, 33, 33, 33,
183                 33, 33, 33, 33,
184             ],
185             &LinkSpec::RsaId(RsaIdentity::from_bytes(b"hello world!!!!!!!!!").unwrap()),
186         );
187         let key = ed25519::PublicKey::from_bytes(&hex!(
188             "B440EEDB32D5C89EF21D6B16BE85A658774CE5992355737411678EE1041BDFBA"
189         ))
190         .unwrap()
191         .into();
192         t(
193             &hex!("03 20 B440EEDB32D5C89EF21D6B16BE85A658774CE5992355737411678EE1041BDFBA"),
194             &LinkSpec::Ed25519Id(key),
195         );
196 
197         t(
198             &[77, 7, 115, 116, 114, 97, 110, 103, 101],
199             &LinkSpec::Unrecognized(77, (&b"strange"[..]).into()),
200         );
201     }
202 
203     #[test]
test_parse_bad()204     fn test_parse_bad() {
205         use tor_bytes::Error;
206 
207         fn t(b: &[u8]) -> Error {
208             let mut r = Reader::from_slice(b);
209             let got: Result<LinkSpec> = r.extract();
210             got.err().unwrap()
211         }
212 
213         assert!(matches!(t(&hex!("00 03")), Error::BadMessage(_)));
214         assert!(matches!(t(&hex!("00 06 01020304")), Error::Truncated));
215         assert!(matches!(t(&hex!("99 07 010203")), Error::Truncated));
216     }
217 }
218