Skip to content

Commit 33f8be2

Browse files
committed
Adds DNS hostname to NetAddress
Supports Bolt 7 DNS hostnames specified by lightning/bolts#911.
1 parent abf6564 commit 33f8be2

File tree

4 files changed

+151
-19
lines changed

4 files changed

+151
-19
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2859,15 +2859,15 @@ impl<Signer: Sign, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelMana
28592859

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

28722872
/// Regenerates channel_announcements and generates a signed node_announcement from the given
28732873
/// 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
28842884
/// tying these addresses together and to this node. If you wish to preserve user privacy,
28852885
/// addresses should likely contain only Tor Onion addresses.
28862886
///
2887-
/// Panics if `addresses` is absurdly large (more than 500).
2887+
/// Panics if `addresses` is absurdly large (more than 100).
28882888
///
28892889
/// [`get_and_clear_pending_msg_events`]: MessageSendEventsProvider::get_and_clear_pending_msg_events
28902890
pub fn broadcast_node_announcement(&self, rgb: [u8; 3], alias: [u8; 32], mut addresses: Vec<NetAddress>) {
28912891
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
28922892

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

lightning/src/ln/msgs.rs

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ use io_extras::read_to_end;
4040

4141
use util::events::MessageSendEventsProvider;
4242
use util::logger;
43-
use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt};
43+
use util::ser::{Readable, Writeable, Writer, FixedLengthReader, HighZeroBytesDroppedVarInt, ShortAsciiString};
4444

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

@@ -442,6 +442,14 @@ pub enum NetAddress {
442442
/// The port on which the node is listening
443443
port: u16,
444444
},
445+
/// A DNS hostname/port on which the peer is listening.
446+
DNSHostname {
447+
/// The hostname as an ASCII string with a variable length below or equal to 255.
448+
/// Can be a Punycode encoding.
449+
hostname: ShortAsciiString,
450+
/// The port on which the node is listening.
451+
port: u16,
452+
},
445453
}
446454
impl NetAddress {
447455
/// Gets the ID of this address type. Addresses in node_announcement messages should be sorted
@@ -452,6 +460,7 @@ impl NetAddress {
452460
&NetAddress::IPv6 {..} => { 2 },
453461
&NetAddress::OnionV2(_) => { 3 },
454462
&NetAddress::OnionV3 {..} => { 4 },
463+
&NetAddress::DNSHostname {..} => { 5 },
455464
}
456465
}
457466

@@ -462,11 +471,12 @@ impl NetAddress {
462471
&NetAddress::IPv6 { .. } => { 18 },
463472
&NetAddress::OnionV2(_) => { 12 },
464473
&NetAddress::OnionV3 { .. } => { 37 },
474+
&NetAddress::DNSHostname { ref hostname, .. } => { u16::from(hostname.len()) + 3 },
465475
}
466476
}
467477

468478
/// The maximum length of any address descriptor, not including the 1-byte type
469-
pub(crate) const MAX_LEN: u16 = 37;
479+
pub(crate) const MAX_LEN: u16 = u8::MAX as u16 + 3;
470480
}
471481

472482
impl Writeable for NetAddress {
@@ -492,7 +502,12 @@ impl Writeable for NetAddress {
492502
checksum.write(writer)?;
493503
version.write(writer)?;
494504
port.write(writer)?;
495-
}
505+
},
506+
&NetAddress::DNSHostname { ref hostname, ref port } => {
507+
5u8.write(writer)?;
508+
hostname.write(writer)?;
509+
port.write(writer)?;
510+
},
496511
}
497512
Ok(())
498513
}
@@ -523,6 +538,12 @@ impl Readable for Result<NetAddress, u8> {
523538
port: Readable::read(reader)?,
524539
}))
525540
},
541+
5 => {
542+
Ok(Ok(NetAddress::DNSHostname {
543+
hostname: Readable::read(reader)?,
544+
port: Readable::read(reader)?,
545+
}))
546+
},
526547
_ => return Ok(Err(byte)),
527548
}
528549
}
@@ -1829,7 +1850,7 @@ mod tests {
18291850
use ln::features::{ChannelFeatures, ChannelTypeFeatures, InitFeatures, NodeFeatures};
18301851
use ln::msgs;
18311852
use ln::msgs::{FinalOnionHopData, OptionalField, OnionErrorPacket, OnionHopDataFormat};
1832-
use util::ser::{Writeable, Readable};
1853+
use util::ser::{Writeable, Readable, ShortAsciiString};
18331854

18341855
use bitcoin::hashes::hex::FromHex;
18351856
use bitcoin::util::address::Address;
@@ -1843,6 +1864,7 @@ mod tests {
18431864

18441865
use io::Cursor;
18451866
use prelude::*;
1867+
use std::convert::TryFrom;
18461868

18471869
#[test]
18481870
fn encoding_channel_reestablish_no_secret() {
@@ -1971,7 +1993,7 @@ mod tests {
19711993
do_encoding_channel_announcement(true, true);
19721994
}
19731995

1974-
fn do_encoding_node_announcement(unknown_features_bits: bool, ipv4: bool, ipv6: bool, onionv2: bool, onionv3: bool, excess_address_data: bool, excess_data: bool) {
1996+
fn do_encoding_node_announcement(unknown_features_bits: bool, ipv4: bool, ipv6: bool, onionv2: bool, onionv3: bool, dns_hostname: bool, excess_address_data: bool, excess_data: bool) {
19751997
let secp_ctx = Secp256k1::new();
19761998
let (privkey_1, pubkey_1) = get_keys_from!("0101010101010101010101010101010101010101010101010101010101010101", secp_ctx);
19771999
let sig_1 = get_sig_on!(privkey_1, secp_ctx, String::from("01010101010101010101010101010101"));
@@ -2007,6 +2029,12 @@ mod tests {
20072029
port: 9735
20082030
});
20092031
}
2032+
if dns_hostname {
2033+
addresses.push(msgs::NetAddress::DNSHostname {
2034+
hostname: ShortAsciiString::try_from("host").unwrap(),
2035+
port: 9735,
2036+
});
2037+
}
20102038
let mut addr_len = 0;
20112039
for addr in &addresses {
20122040
addr_len += addr.len() + 1;
@@ -2047,6 +2075,9 @@ mod tests {
20472075
if onionv3 {
20482076
target_value.append(&mut hex::decode("04fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e00020102607").unwrap());
20492077
}
2078+
if dns_hostname {
2079+
target_value.append(&mut hex::decode("0504686f73742607").unwrap());
2080+
}
20502081
if excess_address_data {
20512082
target_value.append(&mut hex::decode("216c280b5395a2546e7e4b2663e04f811622f15a4f92e83aa2e92ba2a573c139142c54ae63072a1ec1ee7dc0c04bde5c847806172aa05c92c22ae8e308d1d269").unwrap());
20522083
}
@@ -2058,15 +2089,16 @@ mod tests {
20582089

20592090
#[test]
20602091
fn encoding_node_announcement() {
2061-
do_encoding_node_announcement(true, true, true, true, true, true, true);
2062-
do_encoding_node_announcement(false, false, false, false, false, false, false);
2063-
do_encoding_node_announcement(false, true, false, false, false, false, false);
2064-
do_encoding_node_announcement(false, false, true, false, false, false, false);
2065-
do_encoding_node_announcement(false, false, false, true, false, false, false);
2066-
do_encoding_node_announcement(false, false, false, false, true, false, false);
2067-
do_encoding_node_announcement(false, false, false, false, false, true, false);
2068-
do_encoding_node_announcement(false, true, false, true, false, true, false);
2069-
do_encoding_node_announcement(false, false, true, false, true, false, false);
2092+
do_encoding_node_announcement(true, true, true, true, true, true, true, true);
2093+
do_encoding_node_announcement(false, false, false, false, false, false, false, false);
2094+
do_encoding_node_announcement(false, true, false, false, false, false, false, false);
2095+
do_encoding_node_announcement(false, false, true, false, false, false, false, false);
2096+
do_encoding_node_announcement(false, false, false, true, false, false, false, false);
2097+
do_encoding_node_announcement(false, false, false, false, true, false, false, false);
2098+
do_encoding_node_announcement(false, false, false, false, false, true, false, false);
2099+
do_encoding_node_announcement(false, false, false, false, false, false, true, false);
2100+
do_encoding_node_announcement(false, true, false, true, false, false, true, false);
2101+
do_encoding_node_announcement(false, false, true, false, true, false, false, false);
20702102
}
20712103

20722104
fn do_encoding_channel_update(direction: bool, disable: bool, htlc_maximum_msat: bool, excess_data: bool) {

lightning/src/util/errors.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ impl fmt::Debug for APIError {
7878
}
7979
}
8080

81+
/// Indicates an error on conversions by core::convert traits.
82+
#[derive(Debug)]
83+
pub struct ConversionError;
84+
8185
#[inline]
8286
pub(crate) fn get_onion_debug_field(error_code: u16) -> (&'static str, usize) {
8387
match error_code & 0xff {

lightning/src/util/ser.rs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ use io_extras::{copy, sink};
1616
use core::hash::Hash;
1717
use sync::Mutex;
1818
use core::cmp;
19+
use core::convert::TryFrom;
20+
use core::ops::Deref;
1921

2022
use bitcoin::secp256k1::{PublicKey, SecretKey};
2123
use bitcoin::secp256k1::constants::{PUBLIC_KEY_SIZE, SECRET_KEY_SIZE, COMPACT_SIGNATURE_SIZE};
@@ -32,6 +34,7 @@ use ln::msgs::DecodeError;
3234
use ln::{PaymentPreimage, PaymentHash, PaymentSecret};
3335

3436
use util::byte_utils::{be48_to_array, slice_to_be48};
37+
use util::errors::ConversionError;
3538

3639
/// serialization buffer size
3740
pub const MAX_BUF_SIZE: usize = 64 * 1024;
@@ -913,6 +916,67 @@ impl Readable for String {
913916
}
914917
}
915918

919+
/// Wraps a string with additional validation for serialization purposes.
920+
/// The string is guaranteed to contain only ASCII characters and its length
921+
/// is guaranteed to be representable by a single byte.
922+
/// This serialization is used for example by Bolt 7 DNS hostnames.
923+
#[derive(Clone, Debug, PartialEq)]
924+
pub struct ShortAsciiString(String);
925+
impl ShortAsciiString {
926+
/// Returns the length of the string with an appropriate return type for the validated length.
927+
pub fn len(&self) -> u8 {
928+
(&self.0).len() as u8
929+
}
930+
}
931+
impl Deref for ShortAsciiString {
932+
type Target = String;
933+
934+
fn deref(&self) -> &Self::Target {
935+
&self.0
936+
}
937+
}
938+
impl From<ShortAsciiString> for String {
939+
fn from(short_s: ShortAsciiString) -> Self {
940+
short_s.0
941+
}
942+
}
943+
impl TryFrom<String> for ShortAsciiString {
944+
type Error = ConversionError;
945+
946+
fn try_from(s: String) -> Result<Self, Self::Error> {
947+
if s.is_ascii() && s.len() <= u8::MAX.into() {
948+
Ok(ShortAsciiString(s))
949+
} else {
950+
Err(ConversionError)
951+
}
952+
}
953+
}
954+
impl TryFrom<&str> for ShortAsciiString {
955+
type Error = ConversionError;
956+
957+
fn try_from(s: &str) -> Result<Self, Self::Error> {
958+
ShortAsciiString::try_from(String::from(s))
959+
}
960+
}
961+
impl Writeable for ShortAsciiString {
962+
#[inline]
963+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
964+
self.len().write(w)?;
965+
w.write_all(self.as_bytes())
966+
}
967+
}
968+
impl Readable for ShortAsciiString {
969+
#[inline]
970+
fn read<R: Read>(r: &mut R) -> Result<ShortAsciiString, DecodeError> {
971+
let len: u8 = Readable::read(r)?;
972+
let mut vec = Vec::with_capacity(len as usize);
973+
vec.resize(len as usize, 0);
974+
r.read_exact(&mut vec)?;
975+
let s = String::from_utf8(vec).map_err(|_| DecodeError::InvalidValue)?;
976+
ShortAsciiString::try_from(s).map_err(|_| DecodeError::InvalidValue)
977+
}
978+
}
979+
916980
impl Writeable for Duration {
917981
#[inline]
918982
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
@@ -928,3 +992,35 @@ impl Readable for Duration {
928992
Ok(Duration::new(secs, nanos))
929993
}
930994
}
995+
996+
#[cfg(test)]
997+
mod tests {
998+
use core::convert::TryFrom;
999+
use std::io::Cursor;
1000+
use util::ser::{Readable, ShortAsciiString, Writeable};
1001+
1002+
#[test]
1003+
fn short_ascii_string_conversion() {
1004+
let short_s = ShortAsciiString::try_from("test").unwrap();
1005+
assert_eq!(short_s.as_str(), "test");
1006+
1007+
ShortAsciiString::try_from("⚡").expect_err(
1008+
"Expected non-ASCII string to fail conversion"
1009+
);
1010+
1011+
let mut large_vec = Vec::with_capacity(u8::MAX as usize + 1);
1012+
large_vec.resize(u8::MAX as usize + 1, u8::try_from('A').unwrap());
1013+
ShortAsciiString::try_from(String::from_utf8(large_vec).unwrap()).expect_err(
1014+
"Expected string with exceeding length to fail conversion"
1015+
);
1016+
}
1017+
1018+
#[test]
1019+
fn short_ascii_string_serialization() {
1020+
let short_s = ShortAsciiString::try_from("test").unwrap();
1021+
let mut buf = Cursor::new(Vec::new());
1022+
short_s.write(&mut buf).unwrap();
1023+
buf.set_position(0);
1024+
assert_eq!(ShortAsciiString::read(&mut buf).unwrap().as_str(), "test");
1025+
}
1026+
}

0 commit comments

Comments
 (0)