Adds DNS hostname to NetAddress
authorWillem Van Lint <willemvanlint@gmail.com>
Tue, 21 Jun 2022 05:50:50 +0000 (22:50 -0700)
committerWillem Van Lint <willemvanlint@gmail.com>
Mon, 4 Jul 2022 17:19:16 +0000 (10:19 -0700)
lightning/src/ln/channelmanager.rs
lightning/src/ln/msgs.rs
lightning/src/util/ser.rs

index c277a7438dadbcd71bcf2a8a3d52c98a677f3d45..3ca71a6a9824151b6c2ed3be8f32dca4cddc26da 100644 (file)
@@ -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
@@ -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!");
                }
 
index 0e5b2e07e7a4cea649bc44939a67f2c940f71779..3e44cfcf024034a7de39a614e50cc5a974cfc632 100644 (file)
@@ -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};
 
@@ -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
@@ -452,6 +459,7 @@ impl NetAddress {
                        &NetAddress::IPv6 {..} => { 2 },
                        &NetAddress::OnionV2(_) => { 3 },
                        &NetAddress::OnionV3 {..} => { 4 },
+                       &NetAddress::Hostname {..} => { 5 },
                }
        }
 
@@ -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;
 }
 
 impl Writeable for NetAddress {
@@ -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(())
        }
@@ -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)),
                }
        }
@@ -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;
@@ -1843,6 +1866,7 @@ mod tests {
 
        use io::Cursor;
        use prelude::*;
+       use core::convert::TryFrom;
 
        #[test]
        fn encoding_channel_reestablish_no_secret() {
@@ -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"));
@@ -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;
@@ -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());
                }
@@ -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) {
index 428adbc5e6638b5a0489eeb855d80f7c42f5ce86..e60f133f5e1638c085e4476d706c8bea32193998 100644 (file)
@@ -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};
@@ -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 {
+               (&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> {
@@ -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");
+       }
+}