Merge pull request #1553 from wvanlint/dns_hostname
[rust-lightning] / lightning / src / routing / gossip.rs
index f404e77b2b825ba894e4a193d693eb222cbbf769..716ca2b305f3a48da3b5c698c571b11fed2fcdd0 100644 (file)
@@ -250,7 +250,7 @@ where C::Target: chain::Access, L::Target: Logger
 
 impl<L: Deref> EventHandler for NetworkGraph<L> where L::Target: Logger {
        fn handle_event(&self, event: &Event) {
-               if let Event::PaymentPathFailed { payment_hash: _, rejected_by_dest: _, network_update, .. } = event {
+               if let Event::PaymentPathFailed { network_update, .. } = event {
                        if let Some(network_update) = network_update {
                                match *network_update {
                                        NetworkUpdate::ChannelUpdateMessage { ref msg } => {
@@ -745,13 +745,13 @@ impl<'a> DirectedChannelInfo<'a> {
                let (htlc_maximum_msat, effective_capacity) = match (htlc_maximum_msat, capacity_msat) {
                        (Some(amount_msat), Some(capacity_msat)) => {
                                let htlc_maximum_msat = cmp::min(amount_msat, capacity_msat);
-                               (htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat })
+                               (htlc_maximum_msat, EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: Some(htlc_maximum_msat) })
                        },
                        (Some(amount_msat), None) => {
                                (amount_msat, EffectiveCapacity::MaximumHTLC { amount_msat })
                        },
                        (None, Some(capacity_msat)) => {
-                               (capacity_msat, EffectiveCapacity::Total { capacity_msat })
+                               (capacity_msat, EffectiveCapacity::Total { capacity_msat, htlc_maximum_msat: None })
                        },
                        (None, None) => (EffectiveCapacity::Unknown.as_msat(), EffectiveCapacity::Unknown),
                };
@@ -850,6 +850,8 @@ pub enum EffectiveCapacity {
        Total {
                /// The funding amount denominated in millisatoshi.
                capacity_msat: u64,
+               /// The maximum HTLC amount denominated in millisatoshi.
+               htlc_maximum_msat: Option<u64>
        },
        /// A capacity sufficient to route any payment, typically used for private channels provided by
        /// an invoice.
@@ -869,7 +871,7 @@ impl EffectiveCapacity {
                match self {
                        EffectiveCapacity::ExactLiquidity { liquidity_msat } => *liquidity_msat,
                        EffectiveCapacity::MaximumHTLC { amount_msat } => *amount_msat,
-                       EffectiveCapacity::Total { capacity_msat } => *capacity_msat,
+                       EffectiveCapacity::Total { capacity_msat, .. } => *capacity_msat,
                        EffectiveCapacity::Infinite => u64::max_value(),
                        EffectiveCapacity::Unknown => UNKNOWN_CHANNEL_CAPACITY_MSAT,
                }
@@ -904,7 +906,7 @@ pub struct NodeAnnouncementInfo {
        /// Moniker assigned to the node.
        /// May be invalid or malicious (eg control chars),
        /// should not be exposed to the user.
-       pub alias: [u8; 32],
+       pub alias: NodeAlias,
        /// Internet-level addresses via which one can connect to the node
        pub addresses: Vec<NetAddress>,
        /// An initial announcement of the node
@@ -923,6 +925,51 @@ impl_writeable_tlv_based!(NodeAnnouncementInfo, {
        (10, addresses, vec_type),
 });
 
+/// A user-defined name for a node, which may be used when displaying the node in a graph.
+///
+/// Since node aliases are provided by third parties, they are a potential avenue for injection
+/// attacks. Care must be taken when processing.
+#[derive(Clone, Debug, PartialEq)]
+pub struct NodeAlias(pub [u8; 32]);
+
+impl fmt::Display for NodeAlias {
+       fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+               let control_symbol = core::char::REPLACEMENT_CHARACTER;
+               let first_null = self.0.iter().position(|b| *b == 0).unwrap_or(self.0.len());
+               let bytes = self.0.split_at(first_null).0;
+               match core::str::from_utf8(bytes) {
+                       Ok(alias) => {
+                               for c in alias.chars() {
+                                       let mut bytes = [0u8; 4];
+                                       let c = if !c.is_control() { c } else { control_symbol };
+                                       f.write_str(c.encode_utf8(&mut bytes))?;
+                               }
+                       },
+                       Err(_) => {
+                               for c in bytes.iter().map(|b| *b as char) {
+                                       // Display printable ASCII characters
+                                       let mut bytes = [0u8; 4];
+                                       let c = if c >= '\x20' && c <= '\x7e' { c } else { control_symbol };
+                                       f.write_str(c.encode_utf8(&mut bytes))?;
+                               }
+                       },
+               };
+               Ok(())
+       }
+}
+
+impl Writeable for NodeAlias {
+       fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+               self.0.write(w)
+       }
+}
+
+impl Readable for NodeAlias {
+       fn read<R: io::Read>(r: &mut R) -> Result<Self, DecodeError> {
+               Ok(NodeAlias(Readable::read(r)?))
+       }
+}
+
 #[derive(Clone, Debug, PartialEq)]
 /// Details about a node in the network, known from the network announcement.
 pub struct NodeInfo {
@@ -1126,7 +1173,7 @@ impl<L: Deref> NetworkGraph<L> where L::Target: Logger {
                                        features: msg.features.clone(),
                                        last_update: msg.timestamp,
                                        rgb: msg.rgb,
-                                       alias: msg.alias,
+                                       alias: NodeAlias(msg.alias),
                                        addresses: msg.addresses.clone(),
                                        announcement_message: if should_relay { full_msg.cloned() } else { None },
                                });
@@ -1627,12 +1674,11 @@ mod tests {
        use chain;
        use ln::PaymentHash;
        use ln::features::{ChannelFeatures, InitFeatures, NodeFeatures};
-       use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, MAX_EXCESS_BYTES_FOR_RELAY};
+       use routing::gossip::{P2PGossipSync, NetworkGraph, NetworkUpdate, NodeAlias, MAX_EXCESS_BYTES_FOR_RELAY};
        use ln::msgs::{Init, OptionalField, RoutingMessageHandler, UnsignedNodeAnnouncement, NodeAnnouncement,
                UnsignedChannelAnnouncement, ChannelAnnouncement, UnsignedChannelUpdate, ChannelUpdate,
                ReplyChannelRange, QueryChannelRange, QueryShortChannelIds, MAX_VALUE_MSAT};
        use util::test_utils;
-       use util::logger::Logger;
        use util::ser::{ReadableArgs, Writeable};
        use util::events::{Event, EventHandler, MessageSendEvent, MessageSendEventsProvider};
        use util::scid_utils::scid_from_parts;
@@ -1829,7 +1875,7 @@ mod tests {
        #[test]
        fn handling_channel_announcements() {
                let secp_ctx = Secp256k1::new();
-               let logger: Arc<Logger> = Arc::new(test_utils::TestLogger::new());
+               let logger = test_utils::TestLogger::new();
 
                let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
                let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
@@ -1839,8 +1885,8 @@ mod tests {
 
                // Test if the UTXO lookups were not supported
                let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
-               let network_graph = NetworkGraph::new(genesis_hash, Arc::clone(&logger));
-               let mut gossip_sync = P2PGossipSync::new(&network_graph, None, Arc::clone(&logger));
+               let network_graph = NetworkGraph::new(genesis_hash, &logger);
+               let mut gossip_sync = P2PGossipSync::new(&network_graph, None, &logger);
                match gossip_sync.handle_channel_announcement(&valid_announcement) {
                        Ok(res) => assert!(res),
                        _ => panic!()
@@ -1861,10 +1907,10 @@ mod tests {
                };
 
                // Test if an associated transaction were not on-chain (or not confirmed).
-               let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
+               let chain_source = test_utils::TestChainSource::new(Network::Testnet);
                *chain_source.utxo_ret.lock().unwrap() = Err(chain::AccessError::UnknownTx);
-               let network_graph = NetworkGraph::new(genesis_hash, Arc::clone(&logger));
-               gossip_sync = P2PGossipSync::new(&network_graph, Some(chain_source.clone()), Arc::clone(&logger));
+               let network_graph = NetworkGraph::new(genesis_hash, &logger);
+               gossip_sync = P2PGossipSync::new(&network_graph, Some(&chain_source), &logger);
 
                let valid_announcement = get_signed_channel_announcement(|unsigned_announcement| {
                        unsigned_announcement.short_channel_id += 1;
@@ -1945,11 +1991,11 @@ mod tests {
        #[test]
        fn handling_channel_update() {
                let secp_ctx = Secp256k1::new();
-               let logger: Arc<Logger> = Arc::new(test_utils::TestLogger::new());
-               let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
+               let logger = test_utils::TestLogger::new();
+               let chain_source = test_utils::TestChainSource::new(Network::Testnet);
                let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
-               let network_graph = NetworkGraph::new(genesis_hash, Arc::clone(&logger));
-               let gossip_sync = P2PGossipSync::new(&network_graph, Some(chain_source.clone()), Arc::clone(&logger));
+               let network_graph = NetworkGraph::new(genesis_hash, &logger);
+               let gossip_sync = P2PGossipSync::new(&network_graph, Some(&chain_source), &logger);
 
                let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
                let node_2_privkey = &SecretKey::from_slice(&[41; 32]).unwrap();
@@ -2151,10 +2197,10 @@ mod tests {
        fn test_channel_timeouts() {
                // Test the removal of channels with `remove_stale_channels`.
                let logger = test_utils::TestLogger::new();
-               let chain_source = Arc::new(test_utils::TestChainSource::new(Network::Testnet));
+               let chain_source = test_utils::TestChainSource::new(Network::Testnet);
                let genesis_hash = genesis_block(Network::Testnet).header.block_hash();
                let network_graph = NetworkGraph::new(genesis_hash, &logger);
-               let gossip_sync = P2PGossipSync::new(&network_graph, Some(chain_source.clone()), &logger);
+               let gossip_sync = P2PGossipSync::new(&network_graph, Some(&chain_source), &logger);
                let secp_ctx = Secp256k1::new();
 
                let node_1_privkey = &SecretKey::from_slice(&[42; 32]).unwrap();
@@ -2731,6 +2777,29 @@ mod tests {
                });
                assert!(result.is_err());
        }
+
+       #[test]
+       fn displays_node_alias() {
+               let format_str_alias = |alias: &str| {
+                       let mut bytes = [0u8; 32];
+                       bytes[..alias.as_bytes().len()].copy_from_slice(alias.as_bytes());
+                       format!("{}", NodeAlias(bytes))
+               };
+
+               assert_eq!(format_str_alias("I\u{1F496}LDK! \u{26A1}"), "I\u{1F496}LDK! \u{26A1}");
+               assert_eq!(format_str_alias("I\u{1F496}LDK!\0\u{26A1}"), "I\u{1F496}LDK!");
+               assert_eq!(format_str_alias("I\u{1F496}LDK!\t\u{26A1}"), "I\u{1F496}LDK!\u{FFFD}\u{26A1}");
+
+               let format_bytes_alias = |alias: &[u8]| {
+                       let mut bytes = [0u8; 32];
+                       bytes[..alias.len()].copy_from_slice(alias);
+                       format!("{}", NodeAlias(bytes))
+               };
+
+               assert_eq!(format_bytes_alias(b"\xFFI <heart> LDK!"), "\u{FFFD}I <heart> LDK!");
+               assert_eq!(format_bytes_alias(b"\xFFI <heart>\0LDK!"), "\u{FFFD}I <heart>");
+               assert_eq!(format_bytes_alias(b"\xFFI <heart>\tLDK!"), "\u{FFFD}I <heart>\u{FFFD}LDK!");
+       }
 }
 
 #[cfg(all(test, feature = "_bench_unstable"))]