Skip to content

Commit d2a7ee2

Browse files
authored
Merge pull request #1544 from jkczyz/2022-06-node-alias
Define `NodeAlias` struct and `Display` impl
2 parents e533446 + 21aff6f commit d2a7ee2

File tree

1 file changed

+71
-3
lines changed

1 file changed

+71
-3
lines changed

lightning/src/routing/gossip.rs

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ pub struct NodeAnnouncementInfo {
904904
/// Moniker assigned to the node.
905905
/// May be invalid or malicious (eg control chars),
906906
/// should not be exposed to the user.
907-
pub alias: [u8; 32],
907+
pub alias: NodeAlias,
908908
/// Internet-level addresses via which one can connect to the node
909909
pub addresses: Vec<NetAddress>,
910910
/// An initial announcement of the node
@@ -923,6 +923,51 @@ impl_writeable_tlv_based!(NodeAnnouncementInfo, {
923923
(10, addresses, vec_type),
924924
});
925925

926+
/// A user-defined name for a node, which may be used when displaying the node in a graph.
927+
///
928+
/// Since node aliases are provided by third parties, they are a potential avenue for injection
929+
/// attacks. Care must be taken when processing.
930+
#[derive(Clone, Debug, PartialEq)]
931+
pub struct NodeAlias(pub [u8; 32]);
932+
933+
impl fmt::Display for NodeAlias {
934+
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
935+
let control_symbol = core::char::REPLACEMENT_CHARACTER;
936+
let first_null = self.0.iter().position(|b| *b == 0).unwrap_or(self.0.len());
937+
let bytes = self.0.split_at(first_null).0;
938+
match core::str::from_utf8(bytes) {
939+
Ok(alias) => {
940+
for c in alias.chars() {
941+
let mut bytes = [0u8; 4];
942+
let c = if !c.is_control() { c } else { control_symbol };
943+
f.write_str(c.encode_utf8(&mut bytes))?;
944+
}
945+
},
946+
Err(_) => {
947+
for c in bytes.iter().map(|b| *b as char) {
948+
// Display printable ASCII characters
949+
let mut bytes = [0u8; 4];
950+
let c = if c >= '\x20' && c <= '\x7e' { c } else { control_symbol };
951+
f.write_str(c.encode_utf8(&mut bytes))?;
952+
}
953+
},
954+
};
955+
Ok(())
956+
}
957+
}
958+
959+
impl Writeable for NodeAlias {
960+
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
961+
self.0.write(w)
962+
}
963+
}
964+
965+
impl Readable for NodeAlias {
966+
fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
967+
Ok(NodeAlias(Readable::read(r)?))
968+
}
969+
}
970+
926971
#[derive(Clone, Debug, PartialEq)]
927972
/// Details about a node in the network, known from the network announcement.
928973
pub struct NodeInfo {
@@ -1126,7 +1171,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
11261171
features: msg.features.clone(),
11271172
last_update: msg.timestamp,
11281173
rgb: msg.rgb,
1129-
alias: msg.alias,
1174+
alias: NodeAlias(msg.alias),
11301175
addresses: msg.addresses.clone(),
11311176
announcement_message: if should_relay { full_msg.cloned() } else { None },
11321177
});
@@ -1627,7 +1672,7 @@ mod tests {
16271672
use chain;
16281673
use ln::PaymentHash;
16291674
use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
1630-
use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, MAX_EXCESS_BYTES_FOR_RELAY};
1675+
use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, NodeAlias, MAX_EXCESS_BYTES_FOR_RELAY};
16311676
use ln::msgs::{Init, OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
16321677
UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
16331678
ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
@@ -2730,6 +2775,29 @@ mod tests {
27302775
});
27312776
assert!(result.is_err());
27322777
}
2778+
2779+
#[test]
2780+
fn displays_node_alias() {
2781+
let format_str_alias = |alias: &str| {
2782+
let mut bytes = [0u8; 32];
2783+
bytes[..alias.as_bytes().len()].copy_from_slice(alias.as_bytes());
2784+
format!("{}", NodeAlias(bytes))
2785+
};
2786+
2787+
assert_eq!(format_str_alias("I\u{1F496}LDK! \u{26A1}"), "I\u{1F496}LDK! \u{26A1}");
2788+
assert_eq!(format_str_alias("I\u{1F496}LDK!\0\u{26A1}"), "I\u{1F496}LDK!");
2789+
assert_eq!(format_str_alias("I\u{1F496}LDK!\t\u{26A1}"), "I\u{1F496}LDK!\u{FFFD}\u{26A1}");
2790+
2791+
let format_bytes_alias = |alias: &[u8]| {
2792+
let mut bytes = [0u8; 32];
2793+
bytes[..alias.len()].copy_from_slice(alias);
2794+
format!("{}", NodeAlias(bytes))
2795+
};
2796+
2797+
assert_eq!(format_bytes_alias(b"\xFFI <heart> LDK!"), "\u{FFFD}I <heart> LDK!");
2798+
assert_eq!(format_bytes_alias(b"\xFFI <heart>\0LDK!"), "\u{FFFD}I <heart>");
2799+
assert_eq!(format_bytes_alias(b"\xFFI <heart>\tLDK!"), "\u{FFFD}I <heart>\u{FFFD}LDK!");
2800+
}
27332801
}
27342802

27352803
#[cfg(all(test, feature = "_bench_unstable"))]

0 commit comments

Comments
 (0)