mirror of
https://github.com/lightningdevkit/rust-lightning.git
synced 2025-02-25 15:20:24 +01:00
Adds DNS hostname to NetAddress
This commit is contained in:
parent
abf6564a44
commit
c30dcf183c
3 changed files with 151 additions and 20 deletions
|
@ -2859,15 +2859,15 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// Messages of up to 64KB should never end up more than half full with addresses, as that would
|
// 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
|
// broadcast_node_announcement panics) of the maximum-length addresses would fit in a 64KB
|
||||||
// message...
|
// message...
|
||||||
const HALF_MESSAGE_IS_ADDRS: u32 = ::core::u16::MAX as u32 / (NetAddress::MAX_LEN as u32 + 1) / 2;
|
const HALF_MESSAGE_IS_ADDRS: u32 = ::core::u16::MAX as u32 / (NetAddress::MAX_LEN as u32 + 1) / 2;
|
||||||
#[deny(const_err)]
|
#[deny(const_err)]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// ...by failing to compile if the number of addresses that would be half of a message is
|
// ...by failing to compile if the number of addresses that would be half of a message is
|
||||||
// smaller than 500:
|
// smaller than 100:
|
||||||
const STATIC_ASSERT: u32 = Self::HALF_MESSAGE_IS_ADDRS - 500;
|
const STATIC_ASSERT: u32 = Self::HALF_MESSAGE_IS_ADDRS - 100;
|
||||||
|
|
||||||
/// Regenerates channel_announcements and generates a signed node_announcement from the given
|
/// Regenerates channel_announcements and generates a signed node_announcement from the given
|
||||||
/// arguments, providing them in corresponding events via
|
/// 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,
|
/// tying these addresses together and to this node. If you wish to preserve user privacy,
|
||||||
/// addresses should likely contain only Tor Onion addresses.
|
/// 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
|
/// [`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>) {
|
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);
|
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!");
|
panic!("More than half the message size was taken up by public addresses!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ use io_extras::read_to_end;
|
||||||
|
|
||||||
use util::events::MessageSendEventsProvider;
|
use util::events::MessageSendEventsProvider;
|
||||||
use util::logger;
|
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};
|
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
|
||||||
|
|
||||||
|
@ -442,6 +442,13 @@ pub enum NetAddress {
|
||||||
/// The port on which the node is listening
|
/// The port on which the node is listening
|
||||||
port: u16,
|
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 {
|
impl NetAddress {
|
||||||
/// Gets the ID of this address type. Addresses in node_announcement messages should be sorted
|
/// 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::IPv6 {..} => { 2 },
|
||||||
&NetAddress::OnionV2(_) => { 3 },
|
&NetAddress::OnionV2(_) => { 3 },
|
||||||
&NetAddress::OnionV3 {..} => { 4 },
|
&NetAddress::OnionV3 {..} => { 4 },
|
||||||
|
&NetAddress::Hostname {..} => { 5 },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -462,11 +470,15 @@ impl NetAddress {
|
||||||
&NetAddress::IPv6 { .. } => { 18 },
|
&NetAddress::IPv6 { .. } => { 18 },
|
||||||
&NetAddress::OnionV2(_) => { 12 },
|
&NetAddress::OnionV2(_) => { 12 },
|
||||||
&NetAddress::OnionV3 { .. } => { 37 },
|
&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
|
/// The maximum length of any address descriptor, not including the 1-byte type.
|
||||||
pub(crate) const MAX_LEN: u16 = 37;
|
/// 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 {
|
impl Writeable for NetAddress {
|
||||||
|
@ -492,7 +504,12 @@ impl Writeable for NetAddress {
|
||||||
checksum.write(writer)?;
|
checksum.write(writer)?;
|
||||||
version.write(writer)?;
|
version.write(writer)?;
|
||||||
port.write(writer)?;
|
port.write(writer)?;
|
||||||
}
|
},
|
||||||
|
&NetAddress::Hostname { ref hostname, ref port } => {
|
||||||
|
5u8.write(writer)?;
|
||||||
|
hostname.write(writer)?;
|
||||||
|
port.write(writer)?;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -523,6 +540,12 @@ impl Readable for Result<NetAddress, u8> {
|
||||||
port: Readable::read(reader)?,
|
port: Readable::read(reader)?,
|
||||||
}))
|
}))
|
||||||
},
|
},
|
||||||
|
5 => {
|
||||||
|
Ok(Ok(NetAddress::Hostname {
|
||||||
|
hostname: Readable::read(reader)?,
|
||||||
|
port: Readable::read(reader)?,
|
||||||
|
}))
|
||||||
|
},
|
||||||
_ => return Ok(Err(byte)),
|
_ => return Ok(Err(byte)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1829,7 +1852,7 @@ mod tests {
|
||||||
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
|
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
|
||||||
use ln::msgs;
|
use ln::msgs;
|
||||||
use ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat};
|
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::hashes::hex::FromHex;
|
||||||
use bitcoin::util::address::Address;
|
use bitcoin::util::address::Address;
|
||||||
|
@ -1843,6 +1866,7 @@ mod tests {
|
||||||
|
|
||||||
use io::Cursor;
|
use io::Cursor;
|
||||||
use prelude::*;
|
use prelude::*;
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encoding_channel_reestablish_no_secret() {
|
fn encoding_channel_reestablish_no_secret() {
|
||||||
|
@ -1971,7 +1995,7 @@ mod tests {
|
||||||
do_encoding_channel_announcement(true, true);
|
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 secp_ctx = Secp256k1::new();
|
||||||
let (privkey_1, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
|
let (privkey_1, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
|
||||||
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
|
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
|
||||||
|
@ -2007,6 +2031,12 @@ mod tests {
|
||||||
port: 9735
|
port: 9735
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if hostname {
|
||||||
|
addresses.push(msgs::NetAddress::Hostname {
|
||||||
|
hostname: Hostname::try_from(String::from("host")).unwrap(),
|
||||||
|
port: 9735,
|
||||||
|
});
|
||||||
|
}
|
||||||
let mut addr_len = 0;
|
let mut addr_len = 0;
|
||||||
for addr in &addresses {
|
for addr in &addresses {
|
||||||
addr_len += addr.len() + 1;
|
addr_len += addr.len() + 1;
|
||||||
|
@ -2047,6 +2077,9 @@ mod tests {
|
||||||
if onionv3 {
|
if onionv3 {
|
||||||
target_value.append(&mut hex::decode("04fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e00020102607").unwrap());
|
target_value.append(&mut hex::decode("04fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e00020102607").unwrap());
|
||||||
}
|
}
|
||||||
|
if hostname {
|
||||||
|
target_value.append(&mut hex::decode("0504686f73742607").unwrap());
|
||||||
|
}
|
||||||
if excess_address_data {
|
if excess_address_data {
|
||||||
target_value.append(&mut hex::decode("216c280b5395a2546e7e4b2663e04f811622f15a4f92e83aa2e92ba2a573c139142c54ae63072a1ec1ee7dc0c04bde5c847806172aa05c92c22ae8e308d1d269").unwrap());
|
target_value.append(&mut hex::decode("216c280b5395a2546e7e4b2663e04f811622f15a4f92e83aa2e92ba2a573c139142c54ae63072a1ec1ee7dc0c04bde5c847806172aa05c92c22ae8e308d1d269").unwrap());
|
||||||
}
|
}
|
||||||
|
@ -2058,15 +2091,16 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn encoding_node_announcement() {
|
fn encoding_node_announcement() {
|
||||||
do_encoding_node_announcement(true, true, true, true, true, true, true);
|
do_encoding_node_announcement(true, true, true, true, true, true, true, true);
|
||||||
do_encoding_node_announcement(false, false, false, false, false, false, false);
|
do_encoding_node_announcement(false, false, false, false, false, false, false, false);
|
||||||
do_encoding_node_announcement(false, true, 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);
|
do_encoding_node_announcement(false, false, true, false, false, false, false, false);
|
||||||
do_encoding_node_announcement(false, false, false, true, 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);
|
do_encoding_node_announcement(false, false, false, false, true, false, false, false);
|
||||||
do_encoding_node_announcement(false, false, false, false, false, true, false);
|
do_encoding_node_announcement(false, false, false, false, false, true, false, false);
|
||||||
do_encoding_node_announcement(false, true, false, true, false, true, false);
|
do_encoding_node_announcement(false, false, false, false, false, false, true, false);
|
||||||
do_encoding_node_announcement(false, false, true, false, true, false, 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) {
|
fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool, excess_data: bool) {
|
||||||
|
|
|
@ -16,6 +16,8 @@ use io_extras::{copy, sink};
|
||||||
use core::hash::Hash;
|
use core::hash::Hash;
|
||||||
use sync::Mutex;
|
use sync::Mutex;
|
||||||
use core::cmp;
|
use core::cmp;
|
||||||
|
use core::convert::TryFrom;
|
||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
use bitcoin::secp256k1::{PublicKey, SecretKey};
|
use bitcoin::secp256k1::{PublicKey, SecretKey};
|
||||||
use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE};
|
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 {
|
impl Writeable for Duration {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
|
||||||
|
@ -928,3 +999,29 @@ impl Readable for Duration {
|
||||||
Ok(Duration::new(secs, nanos))
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue