Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds DNS hostname to NetAddress #1553

Merged
merged 1 commit into from
Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions lightning/src/ln/channelmanager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2859,15 +2859,15 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana

#[allow(dead_code)]
// Messages of up to 64KB should never end up more than half full with addresses, as that would
// be absurd. We ensure this by checking that at least 500 (our stated public contract on when
// be absurd. We ensure this by checking that at least 100 (our stated public contract on when
// broadcast_node_announcement panics) of the maximum-length addresses would fit in a 64KB
// message...
const HALF_MESSAGE_IS_ADDRS: u32 = ::core::u16::MAX as u32 / (NetAddress::MAX_LEN as u32 + 1) / 2;
#[deny(const_err)]
#[allow(dead_code)]
// ...by failing to compile if the number of addresses that would be half of a message is
// smaller than 500:
const STATIC_ASSERT: u32 = Self::HALF_MESSAGE_IS_ADDRS - 500;
// smaller than 100:
const STATIC_ASSERT: u32 = Self::HALF_MESSAGE_IS_ADDRS - 100;

/// Regenerates channel_announcements and generates a signed node_announcement from the given
/// arguments, providing them in corresponding events via
Expand All @@ -2884,13 +2884,13 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
/// tying these addresses together and to this node. If you wish to preserve user privacy,
/// addresses should likely contain only Tor Onion addresses.
///
/// Panics if `addresses` is absurdly large (more than 500).
/// Panics if `addresses` is absurdly large (more than 100).
///
/// [`get_and_clear_pending_msg_events`]: MessageSendEventsProvider::get_and_clear_pending_msg_events
pub fn broadcast_node_announcement(&self, rgb: [u8; 3], alias: [u8; 32], mut addresses: Vec<NetAddress>) {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);

if addresses.len() > 500 {
if addresses.len() > 100 {
panic!("More than half the message size was taken up by public addresses!");
}

Expand Down
64 changes: 49 additions & 15 deletions lightning/src/ln/msgs.rs
Original file line number Diff line number Diff line change
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};
use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, Hostname};

use ln::{PaymentPreimage, PaymentHash, PaymentSecret};

Expand Down Expand Up @@ -442,6 +442,13 @@ pub enum NetAddress {
/// The port on which the node is listening
port: u16,
},
/// A hostname/port on which the peer is listening.
Hostname {
/// The hostname on which the node is listening.
hostname: Hostname,
/// The port on which the node is listening.
port: u16,
},
}
impl NetAddress {
/// Gets the ID of this address type. Addresses in node_announcement messages should be sorted
Expand All @@ -452,6 +459,7 @@ impl NetAddress {
&NetAddress::IPv6 {..} => { 2 },
&NetAddress::OnionV2(_) => { 3 },
&NetAddress::OnionV3 {..} => { 4 },
&NetAddress::Hostname {..} => { 5 },
}
}

Expand All @@ -462,11 +470,15 @@ impl NetAddress {
&NetAddress::IPv6 { .. } => { 18 },
&NetAddress::OnionV2(_) => { 12 },
&NetAddress::OnionV3 { .. } => { 37 },
// Consists of 1-byte hostname length, hostname bytes, and 2-byte port.
&NetAddress::Hostname { ref hostname, .. } => { u16::from(hostname.len()) + 3 },
}
}

/// The maximum length of any address descriptor, not including the 1-byte type
pub(crate) const MAX_LEN: u16 = 37;
/// The maximum length of any address descriptor, not including the 1-byte type.
/// This maximum length is reached by a hostname address descriptor:
/// a hostname with a maximum length of 255, its 1-byte length and a 2-byte port.
pub(crate) const MAX_LEN: u16 = 258;
tnull marked this conversation as resolved.
Show resolved Hide resolved
}

impl Writeable for NetAddress {
Expand All @@ -492,7 +504,12 @@ impl Writeable for NetAddress {
checksum.write(writer)?;
version.write(writer)?;
port.write(writer)?;
}
},
&NetAddress::Hostname { ref hostname, ref port } => {
5u8.write(writer)?;
hostname.write(writer)?;
port.write(writer)?;
},
}
Ok(())
}
Expand Down Expand Up @@ -523,6 +540,12 @@ impl Readable for Result<NetAddress, u8> {
port: Readable::read(reader)?,
}))
},
5 => {
Ok(Ok(NetAddress::Hostname {
hostname: Readable::read(reader)?,
port: Readable::read(reader)?,
}))
},
_ => return Ok(Err(byte)),
}
}
Expand Down Expand Up @@ -1829,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};
use util::ser::{Writeable, Readable, Hostname};

use bitcoin::hashes::hex::FromHex;
use bitcoin::util::address::Address;
Expand All @@ -1843,6 +1866,7 @@ mod tests {

use io::Cursor;
use prelude::*;
use core::convert::TryFrom;

#[test]
fn encoding_channel_reestablish_no_secret() {
Expand Down Expand Up @@ -1971,7 +1995,7 @@ mod tests {
do_encoding_channel_announcement(true, true);
}

fn do_encoding_node_announcement(unknown_features_bits: bool, ipv4: bool, ipv6: bool, onionv2: bool, onionv3: bool, excess_address_data: bool, excess_data: bool) {
fn do_encoding_node_announcement(unknown_features_bits: bool, ipv4: bool, ipv6: bool, onionv2: bool, onionv3: bool, hostname: bool, excess_address_data: bool, excess_data: bool) {
let secp_ctx = Secp256k1::new();
let (privkey_1, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
Expand Down Expand Up @@ -2007,6 +2031,12 @@ mod tests {
port: 9735
});
}
if hostname {
addresses.push(msgs::NetAddress::Hostname {
hostname: Hostname::try_from(String::from("host")).unwrap(),
port: 9735,
});
}
let mut addr_len = 0;
for addr in &addresses {
addr_len += addr.len() + 1;
Expand Down Expand Up @@ -2047,6 +2077,9 @@ mod tests {
if onionv3 {
target_value.append(&mut hex::decode("04fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e00020102607").unwrap());
}
if hostname {
target_value.append(&mut hex::decode("0504686f73742607").unwrap());
}
if excess_address_data {
target_value.append(&mut hex::decode("216c280b5395a2546e7e4b2663e04f811622f15a4f92e83aa2e92ba2a573c139142c54ae63072a1ec1ee7dc0c04bde5c847806172aa05c92c22ae8e308d1d269").unwrap());
}
Expand All @@ -2058,15 +2091,16 @@ mod tests {

#[test]
fn encoding_node_announcement() {
do_encoding_node_announcement(true, true, true, true, true, true, true);
do_encoding_node_announcement(false, false, false, false, false, false, false);
do_encoding_node_announcement(false, true, false, false, false, false, false);
do_encoding_node_announcement(false, false, true, false, false, false, false);
do_encoding_node_announcement(false, false, false, true, false, false, false);
do_encoding_node_announcement(false, false, false, false, true, false, false);
do_encoding_node_announcement(false, false, false, false, false, true, false);
do_encoding_node_announcement(false, true, false, true, false, true, false);
do_encoding_node_announcement(false, false, true, false, true, false, false);
do_encoding_node_announcement(true, true, true, true, true, true, true, true);
do_encoding_node_announcement(false, false, false, false, false, false, false, false);
do_encoding_node_announcement(false, true, false, false, false, false, false, false);
do_encoding_node_announcement(false, false, true, false, false, false, false, false);
do_encoding_node_announcement(false, false, false, true, false, false, false, false);
do_encoding_node_announcement(false, false, false, false, true, false, false, false);
do_encoding_node_announcement(false, false, false, false, false, true, false, false);
do_encoding_node_announcement(false, false, false, false, false, false, true, false);
do_encoding_node_announcement(false, true, false, true, false, false, true, false);
do_encoding_node_announcement(false, false, true, false, true, false, false, false);
}

fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool, excess_data: bool) {
Expand Down
97 changes: 97 additions & 0 deletions lightning/src/util/ser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ use io_extras::{copy, sink};
use core::hash::Hash;
use sync::Mutex;
use core::cmp;
use core::convert::TryFrom;
use core::ops::Deref;

use bitcoin::secp256k1::{PublicKey, SecretKey};
use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE};
Expand Down Expand Up @@ -913,6 +915,75 @@ impl Readable for String {
}
}

/// 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 Hostname(String);
impl Hostname {
/// Returns the length of the hostname.
pub fn len(&self) -> u8 {
tnull marked this conversation as resolved.
Show resolved Hide resolved
(&self.0).len() as u8
}
}
impl Deref for Hostname {
type Target = String;

fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Hostname> for String {
fn from(hostname: Hostname) -> Self {
hostname.0
}
}
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) {
Hostname::try_from(s)
} else {
Err(())
}
}
}
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_alphanumeric() ||
c == '.' ||
c == '-'
) {
Ok(Hostname(s))
} else {
Err(())
}
}
}
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 Hostname {
#[inline]
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)?;
Hostname::try_from(vec).map_err(|_| DecodeError::InvalidValue)
}
}

impl Writeable for Duration {
#[inline]
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
Expand All @@ -928,3 +999,29 @@ impl Readable for Duration {
Ok(Duration::new(secs, nanos))
}
}

#[cfg(test)]
mod tests {
use core::convert::TryFrom;
use util::ser::{Readable, Hostname, Writeable};

#[test]
fn hostname_conversion() {
assert_eq!(Hostname::try_from(String::from("a-test.com")).unwrap().as_str(), "a-test.com");

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!(Hostname::try_from(String::from_utf8(large_vec).unwrap()).is_err());
}

#[test]
fn hostname_serialization() {
let hostname = Hostname::try_from(String::from("test")).unwrap();
let mut buf: Vec<u8> = Vec::new();
hostname.write(&mut buf).unwrap();
assert_eq!(Hostname::read(&mut buf.as_slice()).unwrap().as_str(), "test");
}
}