From 2767a4167c49b4310b66ac36b23b2d03e439e8ec Mon Sep 17 00:00:00 2001 From: Willem Van Lint Date: Sun, 3 Jul 2022 14:50:51 -0700 Subject: [PATCH] Perform character set and length validation only --- lightning/src/ln/msgs.rs | 8 +++--- lightning/src/util/ser.rs | 59 ++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/lightning/src/ln/msgs.rs b/lightning/src/ln/msgs.rs index 64e37c92828..3e44cfcf024 100644 --- a/lightning/src/ln/msgs.rs +++ b/lightning/src/ln/msgs.rs @@ -40,7 +40,7 @@ use io_extras::read_to_end; use util::events::MessageSendEventsProvider; use util::logger; -use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, ShortAsciiString}; +use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname}; use ln::{PaymentPreimage, PaymentHash, PaymentSecret}; @@ -445,7 +445,7 @@ pub enum NetAddress { /// A hostname/port on which the peer is listening. Hostname { /// The hostname on which the node is listening. - hostname: ShortAsciiString, + hostname: Hostname, /// The port on which the node is listening. port: u16, }, @@ -1852,7 +1852,7 @@ mod tests { use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures}; use ln::msgs; use ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat}; - use util::ser::{Writeable, Readable, ShortAsciiString}; + use util::ser::{Writeable, Readable, Hostname}; use bitcoin::hashes::hex::FromHex; use bitcoin::util::address::Address; @@ -2033,7 +2033,7 @@ mod tests { } if hostname { addresses.push(msgs::NetAddress::Hostname { - hostname: ShortAsciiString::try_from(String::from("host")).unwrap(), + hostname: Hostname::try_from(String::from("host")).unwrap(), port: 9735, }); } diff --git a/lightning/src/util/ser.rs b/lightning/src/util/ser.rs index e147958aa1d..e60f133f5e1 100644 --- a/lightning/src/util/ser.rs +++ b/lightning/src/util/ser.rs @@ -915,67 +915,72 @@ impl Readable for String { } } -/// Represents a printable ASCII string whose length can be represented by a single byte. +/// Represents a hostname for serialization purposes. +/// Only the character set and length will be validated. +/// The character set consists of ASCII alphanumeric characters, hyphens, and periods. +/// Its length is guaranteed to be representable by a single byte. /// This serialization is used by BOLT 7 hostnames. #[derive(Clone, Debug, PartialEq)] -pub struct ShortAsciiString(String); -impl ShortAsciiString { - /// Returns the length of the short ASCII string. +pub struct Hostname(String); +impl Hostname { + /// Returns the length of the hostname. pub fn len(&self) -> u8 { (&self.0).len() as u8 } } -impl Deref for ShortAsciiString { +impl Deref for Hostname { type Target = String; fn deref(&self) -> &Self::Target { &self.0 } } -impl From for String { - fn from(short_s: ShortAsciiString) -> Self { - short_s.0 +impl From for String { + fn from(hostname: Hostname) -> Self { + hostname.0 } } -impl TryFrom> for ShortAsciiString { +impl TryFrom> for Hostname { type Error = (); fn try_from(bytes: Vec) -> Result { if let Ok(s) = String::from_utf8(bytes) { - ShortAsciiString::try_from(s) + Hostname::try_from(s) } else { Err(()) } } } -impl TryFrom for ShortAsciiString { +impl TryFrom for Hostname { type Error = (); fn try_from(s: String) -> Result { if s.len() <= 255 && s.chars().all(|c| - c.is_ascii() && !c.is_ascii_control() + c.is_ascii_alphanumeric() || + c == '.' || + c == '-' ) { - Ok(ShortAsciiString(s)) + Ok(Hostname(s)) } else { Err(()) } } } -impl Writeable for ShortAsciiString { +impl Writeable for Hostname { #[inline] fn write(&self, w: &mut W) -> Result<(), io::Error> { self.len().write(w)?; w.write_all(self.as_bytes()) } } -impl Readable for ShortAsciiString { +impl Readable for Hostname { #[inline] - fn read(r: &mut R) -> Result { + fn read(r: &mut R) -> Result { let len: u8 = Readable::read(r)?; let mut vec = Vec::with_capacity(len.into()); vec.resize(len.into(), 0); r.read_exact(&mut vec)?; - ShortAsciiString::try_from(vec).map_err(|_| DecodeError::InvalidValue) + Hostname::try_from(vec).map_err(|_| DecodeError::InvalidValue) } } @@ -998,23 +1003,25 @@ impl Readable for Duration { #[cfg(test)] mod tests { use core::convert::TryFrom; - use util::ser::{Readable, ShortAsciiString, Writeable}; + use util::ser::{Readable, Hostname, Writeable}; #[test] - fn short_ascii_string_conversion() { - assert_eq!(ShortAsciiString::try_from(String::from("test")).unwrap().as_str(), "test"); + fn hostname_conversion() { + assert_eq!(Hostname::try_from(String::from("a-test.com")).unwrap().as_str(), "a-test.com"); - assert!(ShortAsciiString::try_from(String::from("⚡")).is_err()); + assert!(Hostname::try_from(String::from("\"")).is_err()); + assert!(Hostname::try_from(String::from("$")).is_err()); + assert!(Hostname::try_from(String::from("⚡")).is_err()); let mut large_vec = Vec::with_capacity(256); large_vec.resize(256, b'A'); - assert!(ShortAsciiString::try_from(String::from_utf8(large_vec).unwrap()).is_err()); + assert!(Hostname::try_from(String::from_utf8(large_vec).unwrap()).is_err()); } #[test] - fn short_ascii_string_serialization() { - let short_s = ShortAsciiString::try_from(String::from("test")).unwrap(); + fn hostname_serialization() { + let hostname = Hostname::try_from(String::from("test")).unwrap(); let mut buf: Vec = Vec::new(); - short_s.write(&mut buf).unwrap(); - assert_eq!(ShortAsciiString::read(&mut buf.as_slice()).unwrap().as_str(), "test"); + hostname.write(&mut buf).unwrap(); + assert_eq!(Hostname::read(&mut buf.as_slice()).unwrap().as_str(), "test"); } }