diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index c674b1570ac..1ca47d5647b 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -774,8 +774,13 @@ pub enum SocketAddress { /// An old-style Tor onion address/port on which the peer is listening. /// /// This field is deprecated and the Tor network generally no longer supports V2 Onion - /// addresses. Thus, the details are not parsed here. - OnionV2([u8; 12]), + /// addresses. + OnionV2{ + /// The base32 of first 80 bits + addr: [u8; 10], + /// The port on which the node is listening + port: u16, + }, /// A new-style Tor onion address/port on which the peer is listening. /// /// To create the human-readable "hostname", concatenate the ED25519 pubkey, checksum, and version, @@ -805,7 +810,7 @@ impl SocketAddress { match self { &SocketAddress::TcpIpV4 {..} => { 1 }, &SocketAddress::TcpIpV6 {..} => { 2 }, - &SocketAddress::OnionV2(_) => { 3 }, + &SocketAddress::OnionV2 {..} => { 3 }, &SocketAddress::OnionV3 {..} => { 4 }, &SocketAddress::Hostname {..} => { 5 }, } @@ -816,7 +821,7 @@ impl SocketAddress { match self { &SocketAddress::TcpIpV4 { .. } => { 6 }, &SocketAddress::TcpIpV6 { .. } => { 18 }, - &SocketAddress::OnionV2(_) => { 12 }, + &SocketAddress::OnionV2 { .. } => { 12 }, &SocketAddress::OnionV3 { .. } => { 37 }, // Consists of 1-byte hostname length, hostname bytes, and 2-byte port. &SocketAddress::Hostname { ref hostname, .. } => { u16::from(hostname.len()) + 3 }, @@ -842,9 +847,10 @@ impl Writeable for SocketAddress { addr.write(writer)?; port.write(writer)?; }, - &SocketAddress::OnionV2(bytes) => { + &SocketAddress::OnionV2{ ref addr, ref port } => { 3u8.write(writer)?; - bytes.write(writer)?; + addr.write(writer)?; + port.write(writer)?; }, &SocketAddress::OnionV3 { ref ed25519_pubkey, ref checksum, ref version, ref port } => { 4u8.write(writer)?; @@ -879,7 +885,10 @@ impl Readable for Result { port: Readable::read(reader)?, })) }, - 3 => Ok(Ok(SocketAddress::OnionV2(Readable::read(reader)?))), + 3 => Ok(Ok(SocketAddress::OnionV2 { + addr: Readable::read(reader)?, + port: Readable::read(reader)?, + })), 4 => { Ok(Ok(SocketAddress::OnionV3 { ed25519_pubkey: Readable::read(reader)?, @@ -918,10 +927,8 @@ pub enum SocketAddressParseError { InvalidInput, /// Invalid port InvalidPort, - /// Invalid onion v2 address - InvalidOnionV2, - /// Invalid onion v3 address - InvalidOnionV3, + /// Invalid onion address + InvalidOnion, } impl fmt::Display for SocketAddressParseError { @@ -931,8 +938,7 @@ impl fmt::Display for SocketAddressParseError { SocketAddressParseError::InvalidInput => write!(f, "Invalid input format. \ Expected: \":\", \"[]:\", \".onion:\" or \":\""), SocketAddressParseError::InvalidPort => write!(f, "Invalid port"), - SocketAddressParseError::InvalidOnionV2 => write!(f, "Invalid onion v2 address"), - SocketAddressParseError::InvalidOnionV3 => write!(f, "Invalid onion v3 address"), + SocketAddressParseError::InvalidOnion => write!(f, "Invalid onion address"), } } } @@ -961,39 +967,23 @@ impl From for SocketAddress { } } -/// Parses an OnionV2 host and port into a [`SocketAddress::OnionV2`]. +/// Parses an Onion host and port into a [`SocketAddress::OnionV3`]. /// /// The host part must end with ".onion". -pub fn parse_onion_v2_address(host: &str) -> Result { +pub fn parse_onion_address(host: &str, port: u16) -> Result { if host.ends_with(".onion") { let domain = &host[..host.len() - ".onion".len()]; - if domain.len() != 16 { - return Err(SocketAddressParseError::InvalidOnionV2); + if domain.len() != 56 && domain.len() !=16 { + return Err(SocketAddressParseError::InvalidOnion); } - let onion = base32::Alphabet::RFC4648 { padding: false }.decode(&domain).map_err(|_| SocketAddressParseError::InvalidOnionV2)?; - if onion.len() != 12 { - return Err(SocketAddressParseError::InvalidOnionV2); + let onion = base32::Alphabet::RFC4648 { padding: false }.decode(&domain).map_err(|_| SocketAddressParseError::InvalidOnion)?; + if onion.len() != 35 && onion.len() != 10 { + return Err(SocketAddressParseError::InvalidOnion); } - let mut bytes = [0; 12]; - bytes.copy_from_slice(&onion); - return Ok(SocketAddress::OnionV2(bytes)); - } else { - return Err(SocketAddressParseError::InvalidInput); - } -} - -/// Parses an OnionV3 host and port into a [`SocketAddress::OnionV3`]. -/// -/// The host part must end with ".onion". -pub fn parse_onion_v3_address(host: &str, port: u16) -> Result { - if host.ends_with(".onion") { - let domain = &host[..host.len() - ".onion".len()]; - if domain.len() != 56 { - return Err(SocketAddressParseError::InvalidOnionV3); - } - let onion = base32::Alphabet::RFC4648 { padding: false }.decode(&domain).map_err(|_| SocketAddressParseError::InvalidOnionV3)?; - if onion.len() != 35 { - return Err(SocketAddressParseError::InvalidOnionV3); + if onion.len() == 10 { + let mut addr = [0; 10]; + addr.copy_from_slice(&onion); + return Ok(SocketAddress::OnionV2{ addr, port }); } let version = onion[0]; let first_checksum_flag = onion[1]; @@ -1009,9 +999,9 @@ pub fn parse_onion_v3_address(host: &str, port: u16) -> Result String { - let onion = base32::Alphabet::RFC4648 { padding: false }.encode(&bytes[..]); - format!("{onion}.onion") +pub fn to_onion_v2_string(addr: &[u8; 10], port: &u16) -> String { + let onion = base32::Alphabet::RFC4648 { padding: false }.encode(&addr[..]); + format!("{onion}.onion:{port}") } /// [`SocketAddress::OnionV3`] to onion address string @@ -1029,7 +1019,7 @@ impl Display for SocketAddress { match self { SocketAddress::TcpIpV4{addr, port} => write!(f, "{}:{port}", std::net::Ipv4Addr::from(*addr))?, SocketAddress::TcpIpV6{addr, port} => write!(f, "[{}]:{port}", std::net::Ipv6Addr::from(*addr))?, - SocketAddress::OnionV2(bytes) => write!(f, "{}", to_onion_v2_string(&bytes))?, + SocketAddress::OnionV2{addr, port} => write!(f, "{}", to_onion_v2_string(addr, port))?, SocketAddress::OnionV3 { ed25519_pubkey, checksum, @@ -1052,13 +1042,12 @@ impl FromStr for SocketAddress { Err(_) => { let trimmed_input = match s.rfind(":") { Some(pos) => pos, - None if s.ends_with(".onion") => return parse_onion_v2_address(s), - _ => return Err(SocketAddressParseError::InvalidInput), + None => return Err(SocketAddressParseError::InvalidInput), }; let host = &s[..trimmed_input]; let port: u16 = s[trimmed_input + 1..].parse().map_err(|_| SocketAddressParseError::InvalidPort)?; if host.ends_with(".onion") { - return parse_onion_v3_address(host, port); + return parse_onion_address(host, port); }; if let Ok(hostname) = Hostname::try_from(s[..trimmed_input].to_string()) { return Ok(SocketAddress::Hostname { hostname, port }); @@ -2915,9 +2904,10 @@ mod tests { }); } if onionv2 { - addresses.push(msgs::SocketAddress::OnionV2( - [255, 254, 253, 252, 251, 250, 249, 248, 247, 246, 38, 7] - )); + addresses.push(msgs::SocketAddress::OnionV2 { + addr: [255, 254, 253, 252, 251, 250, 249, 248, 247, 246], + port: 9735, + }); } if onionv3 { addresses.push(msgs::SocketAddress::OnionV3 { @@ -4125,19 +4115,19 @@ mod tests { #[test] #[cfg(feature = "std")] fn test_socket_address_from_str() { - let v4 = SocketAddress::TcpIpV4 { + let tcpip_v4 = SocketAddress::TcpIpV4 { addr: Ipv4Addr::new(127, 0, 0, 1).octets(), port: 1234, }; - assert_eq!(v4, SocketAddress::from_str("127.0.0.1:1234").unwrap()); - assert_eq!(v4, SocketAddress::from_str(&v4.to_string()).unwrap()); + assert_eq!(tcpip_v4, SocketAddress::from_str("127.0.0.1:1234").unwrap()); + assert_eq!(tcpip_v4, SocketAddress::from_str(&tcpip_v4.to_string()).unwrap()); - let v6 = SocketAddress::TcpIpV6 { + let tcpip_v6 = SocketAddress::TcpIpV6 { addr: Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1).octets(), port: 1234, }; - assert_eq!(v6, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap()); - assert_eq!(v6, SocketAddress::from_str(&v6.to_string()).unwrap()); + assert_eq!(tcpip_v6, SocketAddress::from_str("[0:0:0:0:0:0:0:1]:1234").unwrap()); + assert_eq!(tcpip_v6, SocketAddress::from_str(&tcpip_v6.to_string()).unwrap()); let hostname = SocketAddress::Hostname { hostname: Hostname::try_from("lightning-node.mydomain.com".to_string()).unwrap(), @@ -4146,21 +4136,24 @@ mod tests { assert_eq!(hostname, SocketAddress::from_str("lightning-node.mydomain.com:1234").unwrap()); assert_eq!(hostname, SocketAddress::from_str(&hostname.to_string()).unwrap()); - let v2 = SocketAddress::OnionV2 ([37, 24, 75, 5, 25, 73, 117, 194, 139, 102, 182, 107]); - assert_eq!(v2, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap()); - assert_eq!(v2, SocketAddress::from_str(&v2.to_string()).unwrap()); + let onion_v2 = SocketAddress::OnionV2{ + addr: [40, 4, 64, 185, 202, 19, 162, 75, 90, 200], + port: 80, + }; + assert_eq!(onion_v2, SocketAddress::from_str("facebookcorewwwi.onion:80").unwrap()); + assert_eq!(onion_v2, SocketAddress::from_str(&onion_v2.to_string()).unwrap()); - let v3 = SocketAddress::OnionV3 { + let onion_v3 = SocketAddress::OnionV3 { ed25519_pubkey: [37, 24, 75, 5, 25, 73, 117, 194, 139, 102, 182, 107, 4, 105, 247, 246, 85, 111, 177, 172, 49, 137, 167, 155, 64, 221, 163, 47, 31, 33, 71, 3], checksum: 48326, version: 121, port: 1234 }; - assert_eq!(v3, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap()); - assert_eq!(v3, SocketAddress::from_str(&v3.to_string()).unwrap()); + assert_eq!(onion_v3, SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion:1234").unwrap()); + assert_eq!(onion_v3, SocketAddress::from_str(&onion_v3.to_string()).unwrap()); - assert_eq!(Err(SocketAddressParseError::InvalidOnionV3), SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234")); + assert_eq!(Err(SocketAddressParseError::InvalidOnion), SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6.onion:1234")); assert_eq!(Err(SocketAddressParseError::InvalidInput), SocketAddress::from_str("127.0.0.1@1234")); assert_eq!(Err(SocketAddressParseError::InvalidInput), "".parse::()); assert!(SocketAddress::from_str("pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion.onion:9735:94").is_err()); diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index 3bb1306647d..8392a3cbe0d 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -587,7 +587,7 @@ macro_rules! impl_array { impl_array!(3, u8); // for rgb, ISO 4712 code impl_array!(4, u8); // for IPv4 -impl_array!(12, u8); // for OnionV2 +impl_array!(10, u8); // for OnionV2 impl_array!(16, u8); // for IPv6 impl_array!(32, u8); // for channel id & hmac impl_array!(PUBLIC_KEY_SIZE, u8); // for PublicKey