Skip to content

Commit

Permalink
Perform character set and length validation only
Browse files Browse the repository at this point in the history
  • Loading branch information
wvanlint committed Jul 3, 2022
1 parent d969b5b commit 2767a41
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 30 deletions.
8 changes: 4 additions & 4 deletions lightning/src/ln/msgs.rs
Expand Up @@ -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};

Expand Down Expand Up @@ -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,
},
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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,
});
}
Expand Down
59 changes: 33 additions & 26 deletions lightning/src/util/ser.rs
Expand Up @@ -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<ShortAsciiString> for String {
fn from(short_s: ShortAsciiString) -> Self {
short_s.0
impl From<Hostname> for String {
fn from(hostname: Hostname) -> Self {
hostname.0
}
}
impl TryFrom<Vec<u8>> for ShortAsciiString {
impl TryFrom<Vec<u8>> for Hostname {
type Error = ();

fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
if let Ok(s) = String::from_utf8(bytes) {
ShortAsciiString::try_from(s)
Hostname::try_from(s)
} else {
Err(())
}
}
}
impl TryFrom<String> for ShortAsciiString {
impl TryFrom<String> for Hostname {
type Error = ();

fn try_from(s: String) -> Result<Self, Self::Error> {
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<W: Writer>(&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: Read>(r: &mut R) -> Result<ShortAsciiString, DecodeError> {
fn read<R: Read>(r: &mut R) -> Result<Hostname, DecodeError> {
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)
}
}

Expand All @@ -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<u8> = 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");
}
}

0 comments on commit 2767a41

Please sign in to comment.