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