diff --git a/src/consensus/encode.rs b/src/consensus/encode.rs index 9507734f..2fc39b68 100644 --- a/src/consensus/encode.rs +++ b/src/consensus/encode.rs @@ -42,7 +42,7 @@ use util::psbt; use blockdata::transaction::{TxOut, Transaction, TxIn}; use network::message_blockdata::Inventory; -use network::address::Address; +use network::address::{Address, AddrV2Message}; /// Encoding error #[derive(Debug)] @@ -530,6 +530,7 @@ macro_rules! impl_array { impl_array!(2); impl_array!(4); impl_array!(8); +impl_array!(10); impl_array!(12); impl_array!(16); impl_array!(32); @@ -600,6 +601,7 @@ impl_vec!(Inventory); impl_vec!(Vec); impl_vec!((u32, Address)); impl_vec!(u64); +impl_vec!(AddrV2Message); fn consensus_encode_with_size(data: &[u8], mut s: S) -> Result { let vi_len = VarInt(data.len() as u64).consensus_encode(&mut s)?; diff --git a/src/network/address.rs b/src/network/address.rs index 6edc5bb5..b21b992c 100644 --- a/src/network/address.rs +++ b/src/network/address.rs @@ -20,10 +20,10 @@ use std::io; use std::fmt; -use std::net::{SocketAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6}; +use std::net::{SocketAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6, Ipv4Addr}; use network::constants::ServiceFlags; -use consensus::encode::{self, Decodable, Encodable}; +use consensus::encode::{self, Decodable, Encodable, VarInt, ReadExt, WriteExt}; /// A message which can be sent on the Bitcoin network #[derive(Clone, PartialEq, Eq, Hash)] @@ -110,12 +110,167 @@ impl fmt::Debug for Address { } } +/// Address received from BIP155 addrv2 message +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub struct AddrV2Message { + /// Time that this node was last seen as connected to the network + pub time: u32, + /// Service bits + pub services: ServiceFlags, + /// Network ID + Network Address + pub addr: AddrV2, + /// Network port, 0 if not applicable + pub port: u16 +} + +/// Supported networks for use in BIP155 addrv2 message +#[derive(Clone, PartialEq, Eq, Hash, Debug)] +pub enum AddrV2 { + /// IPV4 + Ipv4(Ipv4Addr), + /// IPV6 + Ipv6(Ipv6Addr), + /// TORV2 + TorV2([u8; 10]), + /// TORV3 + TorV3([u8; 32]), + /// I2P + I2p([u8; 32]), + /// CJDNS + Cjdns(Ipv6Addr), + /// Unknown + Unknown(u8, Vec), +} + +impl Encodable for AddrV2 { + fn consensus_encode(&self, e: W) -> Result { + fn encode_addr(mut e: W, network: u8, bytes: &[u8]) -> Result { + let len = + network.consensus_encode(&mut e)? + + VarInt(bytes.len() as u64).consensus_encode(&mut e)? + + bytes.len(); + e.emit_slice(bytes)?; + Ok(len) + } + Ok(match *self { + AddrV2::Ipv4(ref addr) => encode_addr(e, 1, &addr.octets())?, + AddrV2::Ipv6(ref addr) => encode_addr(e, 2, &addr.octets())?, + AddrV2::TorV2(ref bytes) => encode_addr(e, 3, bytes)?, + AddrV2::TorV3(ref bytes) => encode_addr(e, 4, bytes)?, + AddrV2::I2p(ref bytes) => encode_addr(e, 5, bytes)?, + AddrV2::Cjdns(ref addr) => encode_addr(e, 6, &addr.octets())?, + AddrV2::Unknown(network, ref bytes) => encode_addr(e, network, bytes)? + }) + } +} + +impl Decodable for AddrV2 { + fn consensus_decode(mut d: D) -> Result { + let network_id = u8::consensus_decode(&mut d)?; + let len = VarInt::consensus_decode(&mut d)?.0; + if len > 512 { + return Err(encode::Error::ParseFailed("IP must be <= 512 bytes")); + } + Ok(match network_id { + 1 => { + if len != 4 { + return Err(encode::Error::ParseFailed("Invalid IPv4 address")); + } + let addr: [u8; 4] = Decodable::consensus_decode(&mut d)?; + AddrV2::Ipv4(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3])) + }, + 2 => { + if len != 16 { + return Err(encode::Error::ParseFailed("Invalid IPv6 address")); + } + let addr: [u16; 8] = addr_to_be(Decodable::consensus_decode(&mut d)?); + if addr[0..3] == ONION { + return Err(encode::Error::ParseFailed("OnionCat address sent with IPv6 network id")); + } + if addr[0..6] == [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF] { + return Err(encode::Error::ParseFailed("IPV4 wrapped address sent with IPv6 network id")); + } + AddrV2::Ipv6(Ipv6Addr::new( + addr[0],addr[1],addr[2],addr[3], + addr[4],addr[5],addr[6],addr[7] + )) + }, + 3 => { + if len != 10 { + return Err(encode::Error::ParseFailed("Invalid TorV2 address")); + } + let id = Decodable::consensus_decode(&mut d)?; + AddrV2::TorV2(id) + }, + 4 => { + if len != 32 { + return Err(encode::Error::ParseFailed("Invalid TorV3 address")); + } + let pubkey = Decodable::consensus_decode(&mut d)?; + AddrV2::TorV3(pubkey) + }, + 5 => { + if len != 32 { + return Err(encode::Error::ParseFailed("Invalid I2P address")); + } + let hash = Decodable::consensus_decode(&mut d)?; + AddrV2::I2p(hash) + }, + 6 => { + if len != 16 { + return Err(encode::Error::ParseFailed("Invalid CJDNS address")); + } + let addr: [u16; 8] = Decodable::consensus_decode(&mut d)?; + // check the first byte for the CJDNS marker + if addr[0] as u8 != 0xFC { + return Err(encode::Error::ParseFailed("Invalid CJDNS address")); + } + let addr = addr_to_be(addr); + AddrV2::Cjdns(Ipv6Addr::new( + addr[0],addr[1],addr[2],addr[3], + addr[4],addr[5],addr[6],addr[7] + )) + }, + _ => { + // len already checked above to be <= 512 + let mut addr = vec![0u8; len as usize]; + d.read_slice(&mut addr)?; + AddrV2::Unknown(network_id, addr) + } + }) + } +} + +impl Encodable for AddrV2Message { + fn consensus_encode(&self, mut e: W) -> Result { + let mut len = 0; + len += self.time.consensus_encode(&mut e)?; + len += VarInt(self.services.as_u64()).consensus_encode(&mut e)?; + len += self.addr.consensus_encode(&mut e)?; + len += self.port.to_be().consensus_encode(e)?; + Ok(len) + } +} + + +impl Decodable for AddrV2Message { + fn consensus_decode(mut d: D) -> Result { + Ok(AddrV2Message{ + time: Decodable::consensus_decode(&mut d)?, + services: ServiceFlags::from(VarInt::consensus_decode(&mut d)?.0), + addr: Decodable::consensus_decode(&mut d)?, + port: u16::from_be(Decodable::consensus_decode(d)?), + }) + } +} + #[cfg(test)] mod test { use std::str::FromStr; - use super::Address; + use super::{AddrV2Message, AddrV2, Address}; use network::constants::ServiceFlags; use std::net::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr}; + use hashes::hex::FromHex; use consensus::encode::{deserialize, serialize}; @@ -191,5 +346,116 @@ mod test { let addr = Address::new(&onionaddr, ServiceFlags::NONE); assert!(addr.socket_addr().is_err()); } -} + #[test] + fn serialize_addrv2_test() { + // Taken from https://github.com/bitcoin/bitcoin/blob/12a1c3ad1a43634d2a98717e49e3f02c4acea2fe/src/test/net_tests.cpp#L348 + + let ip = AddrV2::Ipv4(Ipv4Addr::new(1, 2, 3, 4)); + assert_eq!(serialize(&ip), Vec::from_hex("010401020304").unwrap()); + + let ip = AddrV2::Ipv6(Ipv6Addr::from_str("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b").unwrap()); + assert_eq!(serialize(&ip), Vec::from_hex("02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b").unwrap()); + + let ip = AddrV2::TorV2(FromHex::from_hex("f1f2f3f4f5f6f7f8f9fa").unwrap()); + assert_eq!(serialize(&ip), Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap()); + + let ip = AddrV2::TorV3(FromHex::from_hex("53cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88").unwrap()); + assert_eq!(serialize(&ip), Vec::from_hex("042053cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88").unwrap()); + + let ip = AddrV2::I2p(FromHex::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap()); + assert_eq!(serialize(&ip), Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap()); + + let ip = AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap()); + assert_eq!(serialize(&ip), Vec::from_hex("0610fc000001000200030004000500060007").unwrap()); + + let ip = AddrV2::Unknown(170, Vec::from_hex("01020304").unwrap()); + assert_eq!(serialize(&ip), Vec::from_hex("aa0401020304").unwrap()); + } + + #[test] + fn deserialize_addrv2_test() { + // Taken from https://github.com/bitcoin/bitcoin/blob/12a1c3ad1a43634d2a98717e49e3f02c4acea2fe/src/test/net_tests.cpp#L386 + + // Valid IPv4. + let ip: AddrV2 = deserialize(&Vec::from_hex("010401020304").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::Ipv4(Ipv4Addr::new(1, 2, 3, 4))); + + // Invalid IPv4, valid length but address itself is shorter. + deserialize::(&Vec::from_hex("01040102").unwrap()).unwrap_err(); + + // Invalid IPv4, with bogus length. + assert!(deserialize::(&Vec::from_hex("010501020304").unwrap()).is_err()); + + // Invalid IPv4, with extreme length. + assert!(deserialize::(&Vec::from_hex("01fd010201020304").unwrap()).is_err()); + + // Valid IPv6. + let ip: AddrV2 = deserialize(&Vec::from_hex("02100102030405060708090a0b0c0d0e0f10").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::Ipv6(Ipv6Addr::from_str("102:304:506:708:90a:b0c:d0e:f10").unwrap())); + + // Invalid IPv6, with bogus length. + assert!(deserialize::(&Vec::from_hex("020400").unwrap()).is_err()); + + // Invalid IPv6, contains embedded IPv4. + assert!(deserialize::(&Vec::from_hex("021000000000000000000000ffff01020304").unwrap()).is_err()); + + // Invalid IPv6, contains embedded TORv2. + assert!(deserialize::(&Vec::from_hex("0210fd87d87eeb430102030405060708090a").unwrap()).is_err()); + + // Valid TORv2. + let ip: AddrV2 = deserialize(&Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::TorV2(FromHex::from_hex("f1f2f3f4f5f6f7f8f9fa").unwrap())); + + // Invalid TORv2, with bogus length. + assert!(deserialize::(&Vec::from_hex("030700").unwrap()).is_err()); + + // Valid TORv3. + let ip: AddrV2 = deserialize(&Vec::from_hex("042079bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::TorV3(FromHex::from_hex("79bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f").unwrap())); + + // Invalid TORv3, with bogus length. + assert!(deserialize::(&Vec::from_hex("040000").unwrap()).is_err()); + + // Valid I2P. + let ip: AddrV2 = deserialize(&Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::I2p(FromHex::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap())); + + // Invalid I2P, with bogus length. + assert!(deserialize::(&Vec::from_hex("050300").unwrap()).is_err()); + + // Valid CJDNS. + let ip: AddrV2 = deserialize(&Vec::from_hex("0610fc000001000200030004000500060007").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap())); + + // Invalid CJDNS, incorrect marker + assert!(deserialize::(&Vec::from_hex("0610fd000001000200030004000500060007").unwrap()).is_err()); + + // Invalid CJDNS, with bogus length. + assert!(deserialize::(&Vec::from_hex("060100").unwrap()).is_err()); + + // Unknown, with extreme length. + assert!(deserialize::(&Vec::from_hex("aafe0000000201020304050607").unwrap()).is_err()); + + // Unknown, with reasonable length. + let ip: AddrV2 = deserialize(&Vec::from_hex("aa0401020304").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::Unknown(170, Vec::from_hex("01020304").unwrap())); + + // Unknown, with zero length. + let ip: AddrV2 = deserialize(&Vec::from_hex("aa00").unwrap()).unwrap(); + assert_eq!(ip, AddrV2::Unknown(170, vec![])); + } + + #[test] + fn addrv2message_test() { + let raw = Vec::from_hex("0261bc6649019902abab208d79627683fd4804010409090909208d").unwrap(); + let addresses: Vec = deserialize(&raw).unwrap(); + + assert_eq!(addresses, vec![ + AddrV2Message{services: ServiceFlags::NETWORK, time: 0x4966bc61, port: 8333, addr: AddrV2::Unknown(153, Vec::from_hex("abab").unwrap())}, + AddrV2Message{services: ServiceFlags::NETWORK_LIMITED | ServiceFlags::WITNESS | ServiceFlags::COMPACT_FILTERS, time: 0x83766279, port: 8333, addr: AddrV2::Ipv4(Ipv4Addr::new(9, 9, 9, 9))}, + ]); + + assert_eq!(serialize(&addresses), raw); + } +} diff --git a/src/network/message.rs b/src/network/message.rs index 455515ea..eda4ff53 100644 --- a/src/network/message.rs +++ b/src/network/message.rs @@ -25,7 +25,7 @@ use std::io::Cursor; use blockdata::block; use blockdata::transaction; -use network::address::Address; +use network::address::{Address, AddrV2Message}; use network::message_network; use network::message_blockdata; use network::message_filter; @@ -159,6 +159,10 @@ pub enum NetworkMessage { FeeFilter(i64), /// `wtxidrelay` WtxidRelay, + /// `addrv2` + AddrV2(Vec), + /// `sendaddrv2` + SendAddrV2, } impl NetworkMessage { @@ -191,6 +195,8 @@ impl NetworkMessage { NetworkMessage::Reject(_) => "reject", NetworkMessage::FeeFilter(_) => "feefilter", NetworkMessage::WtxidRelay => "wtxidrelay", + NetworkMessage::AddrV2(_) => "addrv2", + NetworkMessage::SendAddrV2 => "sendaddrv2", } } @@ -260,11 +266,13 @@ impl Encodable for RawNetworkMessage { NetworkMessage::Alert(ref dat) => serialize(dat), NetworkMessage::Reject(ref dat) => serialize(dat), NetworkMessage::FeeFilter(ref data) => serialize(data), + NetworkMessage::AddrV2(ref dat) => serialize(dat), NetworkMessage::Verack | NetworkMessage::SendHeaders | NetworkMessage::MemPool | NetworkMessage::GetAddr | NetworkMessage::WtxidRelay => vec![], + | NetworkMessage::SendAddrV2 => vec![], }).consensus_encode(&mut s)?; Ok(len) } @@ -329,6 +337,8 @@ impl Decodable for RawNetworkMessage { "alert" => NetworkMessage::Alert(Decodable::consensus_decode(&mut mem_d)?), "feefilter" => NetworkMessage::FeeFilter(Decodable::consensus_decode(&mut mem_d)?), "wtxidrelay" => NetworkMessage::WtxidRelay, + "addrv2" => NetworkMessage::AddrV2(Decodable::consensus_decode(&mut mem_d)?), + "sendaddrv2" => NetworkMessage::SendAddrV2, _ => return Err(encode::Error::UnrecognizedNetworkCommand(cmd.into_owned())), }; Ok(RawNetworkMessage { @@ -341,13 +351,14 @@ impl Decodable for RawNetworkMessage { #[cfg(test)] mod test { use std::io; + use std::net::Ipv4Addr; use super::{RawNetworkMessage, NetworkMessage, CommandString}; use network::constants::ServiceFlags; use consensus::encode::{Encodable, deserialize, deserialize_partial, serialize}; use hashes::hex::FromHex; use hashes::sha256d::Hash; use hashes::Hash as HashTrait; - use network::address::Address; + use network::address::{Address, AddrV2, AddrV2Message}; use super::message_network::{Reject, RejectReason, VersionMessage}; use network::message_blockdata::{Inventory, GetBlocksMessage, GetHeadersMessage}; use blockdata::block::{Block, BlockHeader}; @@ -393,6 +404,8 @@ mod test { NetworkMessage::Reject(Reject{message: "Test reject".into(), ccode: RejectReason::Duplicate, reason: "Cause".into(), hash: hash([255u8; 32])}), NetworkMessage::FeeFilter(1000), NetworkMessage::WtxidRelay, + NetworkMessage::AddrV2(vec![AddrV2Message{ addr: AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1)), port: 0, services: ServiceFlags::NONE, time: 0 }]), + NetworkMessage::SendAddrV2, ]; for msg in msgs {