@@ -904,7 +904,7 @@ pub struct NodeAnnouncementInfo {
904
904
/// Moniker assigned to the node.
905
905
/// May be invalid or malicious (eg control chars),
906
906
/// should not be exposed to the user.
907
- pub alias : [ u8 ; 32 ] ,
907
+ pub alias : NodeAlias ,
908
908
/// Internet-level addresses via which one can connect to the node
909
909
pub addresses : Vec < NetAddress > ,
910
910
/// An initial announcement of the node
@@ -923,6 +923,51 @@ impl_writeable_tlv_based!(NodeAnnouncementInfo, {
923
923
( 10 , addresses, vec_type) ,
924
924
} ) ;
925
925
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
+
926
971
#[ derive( Clone , Debug , PartialEq ) ]
927
972
/// Details about a node in the network, known from the network announcement.
928
973
pub struct NodeInfo {
@@ -1126,7 +1171,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
1126
1171
features : msg. features . clone ( ) ,
1127
1172
last_update : msg. timestamp ,
1128
1173
rgb : msg. rgb ,
1129
- alias : msg. alias ,
1174
+ alias : NodeAlias ( msg. alias ) ,
1130
1175
addresses : msg. addresses . clone ( ) ,
1131
1176
announcement_message : if should_relay { full_msg. cloned ( ) } else { None } ,
1132
1177
} ) ;
@@ -1627,7 +1672,7 @@ mod tests {
1627
1672
use chain;
1628
1673
use ln:: PaymentHash ;
1629
1674
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 } ;
1631
1676
use ln:: msgs:: { Init , OptionalField , RoutingMessageHandler , UnsignedNodeAnnouncement , NodeAnnouncement ,
1632
1677
UnsignedChannelAnnouncement , ChannelAnnouncement , UnsignedChannelUpdate , ChannelUpdate ,
1633
1678
ReplyChannelRange , QueryChannelRange , QueryShortChannelIds , MAX_VALUE_MSAT } ;
@@ -2730,6 +2775,29 @@ mod tests {
2730
2775
} ) ;
2731
2776
assert ! ( result. is_err( ) ) ;
2732
2777
}
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"\xFF I <heart> LDK!" ) , "\u{FFFD} I <heart> LDK!" ) ;
2798
+ assert_eq ! ( format_bytes_alias( b"\xFF I <heart>\0 LDK!" ) , "\u{FFFD} I <heart>" ) ;
2799
+ assert_eq ! ( format_bytes_alias( b"\xFF I <heart>\t LDK!" ) , "\u{FFFD} I <heart>\u{FFFD} LDK!" ) ;
2800
+ }
2733
2801
}
2734
2802
2735
2803
#[ cfg( all( test, feature = "_bench_unstable" ) ) ]
0 commit comments