From c88f5f4cf1ad223fe4753f081a88d3ca31f44d63 Mon Sep 17 00:00:00 2001 From: Anton Kaliaev Date: Mon, 28 Mar 2022 16:04:41 +0400 Subject: [PATCH] add x-webrtc protocol --- Cargo.toml | 1 + src/protocol.rs | 137 +++++++++++++++--------- tests/lib.rs | 274 ++++++++++++++++++++++++++++++++++-------------- 3 files changed, 286 insertions(+), 126 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4ce2c75..7d65f75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ serde = "1.0.70" static_assertions = "1.1" unsigned-varint = "0.7" url = { version = "2.1.0", optional = true, default-features = false } +hex = "0.4" [dev-dependencies] bincode = "1" diff --git a/src/protocol.rs b/src/protocol.rs index e67d5e6..ee39177 100644 --- a/src/protocol.rs +++ b/src/protocol.rs @@ -1,7 +1,9 @@ +use crate::onion_addr::Onion3Addr; +use crate::{Error, Result}; use arrayref::array_ref; use byteorder::{BigEndian, ByteOrder, ReadBytesExt, WriteBytesExt}; -use crate::{Result, Error}; use data_encoding::BASE32; +use hex; use multihash::Multihash; use std::{ borrow::Cow, @@ -9,10 +11,9 @@ use std::{ fmt, io::{Cursor, Write}, net::{IpAddr, Ipv4Addr, Ipv6Addr}, - str::{self, FromStr} + str::{self, FromStr}, }; -use unsigned_varint::{encode, decode}; -use crate::onion_addr::Onion3Addr; +use unsigned_varint::{decode, encode}; // All the values are obtained by converting hexadecimal protocol codes to u32. // Protocols as well as their corresponding codes are defined in @@ -43,9 +44,10 @@ const UDT: u32 = 301; const UNIX: u32 = 400; const UTP: u32 = 302; const WS: u32 = 477; -const WS_WITH_PATH: u32 = 4770; // Note: not standard +const WS_WITH_PATH: u32 = 4770; // Note: not standard const WSS: u32 = 478; -const WSS_WITH_PATH: u32 = 4780; // Note: not standard +const WSS_WITH_PATH: u32 = 4780; // Note: not standard +const XWEBRTC: u32 = 277; const PATH_SEGMENT_ENCODE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS .add(b'%') @@ -79,6 +81,7 @@ pub enum Protocol<'a> { Ip6(Ipv6Addr), P2pWebRtcDirect, P2pWebRtcStar, + XWebRTC(Cow<'a, [u8; 32]>), P2pWebSocketStar, /// Contains the "port" to contact. Similar to TCP or UDP, 0 means "assign me a port". Memory(u64), @@ -107,7 +110,7 @@ impl<'a> Protocol<'a> { /// that iteration has finished whenever appropriate. pub fn from_str_parts(mut iter: I) -> Result where - I: Iterator + I: Iterator, { match iter.next().ok_or(Error::InvalidProtocolString)? { "ip4" => { @@ -164,16 +167,16 @@ impl<'a> Protocol<'a> { } "http" => Ok(Protocol::Http), "https" => Ok(Protocol::Https), - "onion" => - iter.next() - .ok_or(Error::InvalidProtocolString) - .and_then(|s| read_onion(&s.to_uppercase())) - .map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)), - "onion3" => - iter.next() - .ok_or(Error::InvalidProtocolString) - .and_then(|s| read_onion3(&s.to_uppercase())) - .map(|(a, p)| Protocol::Onion3((a, p).into())), + "onion" => iter + .next() + .ok_or(Error::InvalidProtocolString) + .and_then(|s| read_onion(&s.to_uppercase())) + .map(|(a, p)| Protocol::Onion(Cow::Owned(a), p)), + "onion3" => iter + .next() + .ok_or(Error::InvalidProtocolString) + .and_then(|s| read_onion3(&s.to_uppercase())) + .map(|(a, p)| Protocol::Onion3((a, p).into())), "quic" => Ok(Protocol::Quic), "ws" => Ok(Protocol::Ws(Cow::Borrowed("/"))), "wss" => Ok(Protocol::Wss(Cow::Borrowed("/"))), @@ -195,7 +198,13 @@ impl<'a> Protocol<'a> { let s = iter.next().ok_or(Error::InvalidProtocolString)?; Ok(Protocol::Memory(s.parse()?)) } - unknown => Err(Error::UnknownProtocolString(unknown.to_string())) + "x-webrtc" => { + let s = iter.next().ok_or(Error::InvalidProtocolString)?; + let mut buf = [0; 32]; + hex::decode_to_slice(s, &mut buf).map_err(|_| Error::InvalidProtocolString)?; + Ok(Protocol::XWebRTC(Cow::Owned(buf))) + } + unknown => Err(Error::UnknownProtocolString(unknown.to_string())), } } @@ -204,7 +213,7 @@ impl<'a> Protocol<'a> { pub fn from_bytes(input: &'a [u8]) -> Result<(Self, &'a [u8])> { fn split_at(n: usize, input: &[u8]) -> Result<(&[u8], &[u8])> { if input.len() < n { - return Err(Error::DataLessThanLen) + return Err(Error::DataLessThanLen); } Ok(input.split_at(n)) } @@ -234,13 +243,19 @@ impl<'a> Protocol<'a> { DNSADDR => { let (n, input) = decode::usize(input)?; let (data, rest) = split_at(n, input)?; - Ok((Protocol::Dnsaddr(Cow::Borrowed(str::from_utf8(data)?)), rest)) + Ok(( + Protocol::Dnsaddr(Cow::Borrowed(str::from_utf8(data)?)), + rest, + )) } HTTP => Ok((Protocol::Http, input)), HTTPS => Ok((Protocol::Https, input)), IP4 => { let (data, rest) = split_at(4, input)?; - Ok((Protocol::Ip4(Ipv4Addr::new(data[0], data[1], data[2], data[3])), rest)) + Ok(( + Protocol::Ip4(Ipv4Addr::new(data[0], data[1], data[2], data[3])), + rest, + )) } IP6 => { let (data, rest) = split_at(16, input)?; @@ -251,14 +266,9 @@ impl<'a> Protocol<'a> { *x = rdr.read_u16::()?; } - let addr = Ipv6Addr::new(seg[0], - seg[1], - seg[2], - seg[3], - seg[4], - seg[5], - seg[6], - seg[7]); + let addr = Ipv6Addr::new( + seg[0], seg[1], seg[2], seg[3], seg[4], seg[5], seg[6], seg[7], + ); Ok((Protocol::Ip6(addr), rest)) } @@ -273,13 +283,19 @@ impl<'a> Protocol<'a> { } ONION => { let (data, rest) = split_at(12, input)?; - let port = BigEndian::read_u16(&data[10 ..]); - Ok((Protocol::Onion(Cow::Borrowed(array_ref!(data, 0, 10)), port), rest)) + let port = BigEndian::read_u16(&data[10..]); + Ok(( + Protocol::Onion(Cow::Borrowed(array_ref!(data, 0, 10)), port), + rest, + )) } ONION3 => { let (data, rest) = split_at(37, input)?; - let port = BigEndian::read_u16(&data[35 ..]); - Ok((Protocol::Onion3((array_ref!(data, 0, 35), port).into()), rest)) + let port = BigEndian::read_u16(&data[35..]); + Ok(( + Protocol::Onion3((array_ref!(data, 0, 35), port).into()), + rest, + )) } P2P => { let (n, input) = decode::usize(input)?; @@ -326,7 +342,14 @@ impl<'a> Protocol<'a> { let (data, rest) = split_at(n, input)?; Ok((Protocol::Wss(Cow::Borrowed(str::from_utf8(data)?)), rest)) } - _ => Err(Error::UnknownProtocolId(id)) + XWEBRTC => { + let (data, rest) = split_at(32, input)?; + Ok(( + Protocol::XWebRTC(Cow::Borrowed(array_ref!(data, 0, 32))), + rest, + )) + } + _ => Err(Error::UnknownProtocolId(id)), } } @@ -419,14 +442,14 @@ impl<'a> Protocol<'a> { let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(&bytes)? - }, + } Protocol::Wss(ref s) if s == "/" => w.write_all(encode::u32(WSS, &mut buf))?, Protocol::Wss(s) => { w.write_all(encode::u32(WSS_WITH_PATH, &mut buf))?; let bytes = s.as_bytes(); w.write_all(encode::usize(bytes.len(), &mut encode::usize_buffer()))?; w.write_all(&bytes)? - }, + } Protocol::P2pWebSocketStar => w.write_all(encode::u32(P2P_WEBSOCKET_STAR, &mut buf))?, Protocol::P2pWebRtcStar => w.write_all(encode::u32(P2P_WEBRTC_STAR, &mut buf))?, Protocol::P2pWebRtcDirect => w.write_all(encode::u32(P2P_WEBRTC_DIRECT, &mut buf))?, @@ -435,6 +458,10 @@ impl<'a> Protocol<'a> { w.write_all(encode::u32(MEMORY, &mut buf))?; w.write_u64::(*port)? } + Protocol::XWebRTC(fingerprint) => { + w.write_all(encode::u32(XWEBRTC, &mut buf))?; + w.write_all(fingerprint.as_ref())? + } } Ok(()) } @@ -470,6 +497,7 @@ impl<'a> Protocol<'a> { Utp => Utp, Ws(cow) => Ws(Cow::Owned(cow.into_owned())), Wss(cow) => Wss(Cow::Owned(cow.into_owned())), + XWebRTC(fingerprint) => XWebRTC(Cow::Owned(fingerprint.into_owned())), } } } @@ -495,7 +523,7 @@ impl<'a> fmt::Display for Protocol<'a> { let s = BASE32.encode(addr.as_ref()); write!(f, "/onion/{}:{}", s.to_lowercase(), port) } - Onion3(addr ) => { + Onion3(addr) => { let s = BASE32.encode(addr.hash()); write!(f, "/onion3/{}:{}", s.to_lowercase(), addr.port()) } @@ -511,14 +539,20 @@ impl<'a> fmt::Display for Protocol<'a> { Utp => f.write_str("/utp"), Ws(ref s) if s == "/" => f.write_str("/ws"), Ws(s) => { - let encoded = percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET); + let encoded = + percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET); write!(f, "/x-parity-ws/{}", encoded) - }, + } Wss(ref s) if s == "/" => f.write_str("/wss"), Wss(s) => { - let encoded = percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET); + let encoded = + percent_encoding::percent_encode(s.as_bytes(), PATH_SEGMENT_ENCODE_SET); write!(f, "/x-parity-wss/{}", encoded) - }, + } + XWebRTC(fingerprint) => { + let s = hex::encode(fingerprint.as_ref()); + write!(f, "/x-webrtc/{}", s.to_uppercase()) + } } } } @@ -555,11 +589,12 @@ macro_rules! read_onion_impl { // address part (without ".onion") let b32 = parts.next().ok_or(Error::InvalidMultiaddr)?; if b32.len() != $encoded_len { - return Err(Error::InvalidMultiaddr) + return Err(Error::InvalidMultiaddr); } // port number - let port = parts.next() + let port = parts + .next() .ok_or(Error::InvalidMultiaddr) .and_then(|p| str::parse(p).map_err(From::from))?; @@ -570,19 +605,25 @@ macro_rules! read_onion_impl { // nothing else expected if parts.next().is_some() { - return Err(Error::InvalidMultiaddr) + return Err(Error::InvalidMultiaddr); } - if $len != BASE32.decode_len(b32.len()).map_err(|_| Error::InvalidMultiaddr)? { - return Err(Error::InvalidMultiaddr) + if $len + != BASE32 + .decode_len(b32.len()) + .map_err(|_| Error::InvalidMultiaddr)? + { + return Err(Error::InvalidMultiaddr); } let mut buf = [0u8; $len]; - BASE32.decode_mut(b32.as_bytes(), &mut buf).map_err(|_| Error::InvalidMultiaddr)?; + BASE32 + .decode_mut(b32.as_bytes(), &mut buf) + .map_err(|_| Error::InvalidMultiaddr)?; Ok((buf, port)) } - } + }; } // Parse a version 2 onion address and return its binary representation. diff --git a/tests/lib.rs b/tests/lib.rs index 39ed765..eee6a75 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -1,13 +1,14 @@ use data_encoding::HEXUPPER; -use multihash::Multihash; +use hex; use multiaddr::*; +use multihash::Multihash; use quickcheck::{Arbitrary, Gen, QuickCheck}; use std::{ borrow::Cow, convert::{TryFrom, TryInto}, - iter::{FromIterator, self}, + iter::{self, FromIterator}, net::{Ipv4Addr, Ipv6Addr}, - str::FromStr + str::FromStr, }; // Property tests @@ -37,7 +38,9 @@ fn byteswriter() { for p in b.0.iter() { x = x.with(p) } - x.iter().zip(a.0.iter().chain(b.0.iter())).all(|(x, y)| x == y) + x.iter() + .zip(a.0.iter().chain(b.0.iter())) + .all(|(x, y)| x == y) } QuickCheck::new().quickcheck(prop as fn(Ma, Ma) -> bool) } @@ -58,7 +61,7 @@ fn push_pop_identity() { fn ends_with() { fn prop(Ma(m): Ma) { let n = m.iter().count(); - for i in 0 .. n { + for i in 0..n { let suffix = m.iter().skip(i).collect::(); assert!(m.ends_with(&suffix)); } @@ -66,16 +69,14 @@ fn ends_with() { QuickCheck::new().quickcheck(prop as fn(_)) } - // Arbitrary impls - #[derive(PartialEq, Eq, Clone, Hash, Debug)] struct Ma(Multiaddr); impl Arbitrary for Ma { fn arbitrary(g: &mut G) -> Self { - let iter = (0 .. g.next_u32() % 128).map(|_| Proto::arbitrary(g).0); + let iter = (0..g.next_u32() % 128).map(|_| Proto::arbitrary(g).0); Ma(Multiaddr::from_iter(iter)) } } @@ -86,21 +87,24 @@ struct Proto(Protocol<'static>); impl Arbitrary for Proto { fn arbitrary(g: &mut G) -> Self { use Protocol::*; - match u8::arbitrary(g) % 26 { // TODO: Add Protocol::Quic - 0 => Proto(Dccp(Arbitrary::arbitrary(g))), - 1 => Proto(Dns(Cow::Owned(SubString::arbitrary(g).0))), - 2 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))), - 3 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))), - 4 => Proto(Http), - 5 => Proto(Https), - 6 => Proto(Ip4(Ipv4Addr::arbitrary(g))), - 7 => Proto(Ip6(Ipv6Addr::arbitrary(g))), - 8 => Proto(P2pWebRtcDirect), - 9 => Proto(P2pWebRtcStar), + match u8::arbitrary(g) % 26 { + // TODO: Add Protocol::Quic + 0 => Proto(Dccp(Arbitrary::arbitrary(g))), + 1 => Proto(Dns(Cow::Owned(SubString::arbitrary(g).0))), + 2 => Proto(Dns4(Cow::Owned(SubString::arbitrary(g).0))), + 3 => Proto(Dns6(Cow::Owned(SubString::arbitrary(g).0))), + 4 => Proto(Http), + 5 => Proto(Https), + 6 => Proto(Ip4(Ipv4Addr::arbitrary(g))), + 7 => Proto(Ip6(Ipv6Addr::arbitrary(g))), + 8 => Proto(P2pWebRtcDirect), + 9 => Proto(P2pWebRtcStar), 10 => Proto(P2pWebSocketStar), 11 => Proto(Memory(Arbitrary::arbitrary(g))), // TODO: impl Arbitrary for Multihash: - 12 => Proto(P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))), + 12 => Proto(P2p(multihash( + "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + ))), 13 => Proto(P2pCircuit), 14 => Proto(Quic), 15 => Proto(Sctp(Arbitrary::arbitrary(g))), @@ -112,24 +116,23 @@ impl Arbitrary for Proto { 21 => Proto(Ws("/".into())), 22 => Proto(Wss("/".into())), 23 => { - let a = iter::repeat_with(|| u8::arbitrary(g)) .take(10) .collect::>() .try_into() .unwrap(); Proto(Onion(Cow::Owned(a), std::cmp::max(1, u16::arbitrary(g)))) - }, + } 24 => { - let a: [u8;35] = iter::repeat_with(|| u8::arbitrary(g)) + let a: [u8; 35] = iter::repeat_with(|| u8::arbitrary(g)) .take(35) .collect::>() .try_into() .unwrap(); Proto(Onion3((a, std::cmp::max(1, u16::arbitrary(g))).into())) - }, + } 25 => Proto(Tls), - _ => panic!("outside range") + _ => panic!("outside range"), } } } @@ -145,22 +148,29 @@ impl Arbitrary for SubString { } } - // other unit tests - fn ma_valid(source: &str, target: &str, protocols: Vec>) { let parsed = source.parse::().unwrap(); assert_eq!(HEXUPPER.encode(&parsed.to_vec()[..]), target); assert_eq!(parsed.iter().collect::>(), protocols); assert_eq!(source.parse::().unwrap().to_string(), source); - assert_eq!(Multiaddr::try_from(HEXUPPER.decode(target.as_bytes()).unwrap()).unwrap(), parsed); + assert_eq!( + Multiaddr::try_from(HEXUPPER.decode(target.as_bytes()).unwrap()).unwrap(), + parsed + ); } fn multihash(s: &str) -> Multihash { Multihash::from_bytes(&bs58::decode(s).into_vec().unwrap()).unwrap() } +fn hex_to_cow<'a>(s: &str) -> Cow<'a, [u8; 32]> { + let mut buf = [0; 32]; + hex::decode_to_slice(s, &mut buf).unwrap(); + Cow::Owned(buf) +} + #[test] fn multiaddr_eq() { let m1 = "/ip4/127.0.0.1/udp/1234".parse::().unwrap(); @@ -180,12 +190,26 @@ fn construct_success() { let local: Ipv4Addr = "127.0.0.1".parse().unwrap(); let addr6: Ipv6Addr = "2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095".parse().unwrap(); - ma_valid("/ip4/1.2.3.4", "0401020304", vec![Ip4("1.2.3.4".parse().unwrap())]); - ma_valid("/ip4/0.0.0.0", "0400000000", vec![Ip4("0.0.0.0".parse().unwrap())]); - ma_valid("/ip6/::1", "2900000000000000000000000000000001", vec![Ip6("::1".parse().unwrap())]); - ma_valid("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", - "29260100094F819700803ECA6566E80C21", - vec![Ip6("2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap())]); + ma_valid( + "/ip4/1.2.3.4", + "0401020304", + vec![Ip4("1.2.3.4".parse().unwrap())], + ); + ma_valid( + "/ip4/0.0.0.0", + "0400000000", + vec![Ip4("0.0.0.0".parse().unwrap())], + ); + ma_valid( + "/ip6/::1", + "2900000000000000000000000000000001", + vec![Ip6("::1".parse().unwrap())], + ); + ma_valid( + "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21", + "29260100094F819700803ECA6566E80C21", + vec![Ip6("2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap())], + ); ma_valid("/udp/0", "91020000", vec![Udp(0)]); ma_valid("/tcp/0", "060000", vec![Tcp(0)]); ma_valid("/sctp/0", "84010000", vec![Sctp(0)]); @@ -194,24 +218,58 @@ fn construct_success() { ma_valid("/sctp/1234", "840104D2", vec![Sctp(1234)]); ma_valid("/udp/65535", "9102FFFF", vec![Udp(65535)]); ma_valid("/tcp/65535", "06FFFF", vec![Tcp(65535)]); - ma_valid("/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", - "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", - vec![P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); - ma_valid("/udp/1234/sctp/1234", "910204D2840104D2", vec![Udp(1234), Sctp(1234)]); + ma_valid( + "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", + vec![P2p(multihash( + "QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + ))], + ); + ma_valid( + "/udp/1234/sctp/1234", + "910204D2840104D2", + vec![Udp(1234), Sctp(1234)], + ); ma_valid("/udp/1234/udt", "910204D2AD02", vec![Udp(1234), Udt]); ma_valid("/udp/1234/utp", "910204D2AE02", vec![Udp(1234), Utp]); ma_valid("/tcp/1234/http", "0604D2E003", vec![Tcp(1234), Http]); - ma_valid("/tcp/1234/tls/http", "0604D2C003E003", vec![Tcp(1234), Tls, Http]); + ma_valid( + "/tcp/1234/tls/http", + "0604D2C003E003", + vec![Tcp(1234), Tls, Http], + ); ma_valid("/tcp/1234/https", "0604D2BB03", vec![Tcp(1234), Https]); - ma_valid("/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", - "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", - vec![P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234)]); - ma_valid("/ip4/127.0.0.1/udp/1234", "047F000001910204D2", vec![Ip4(local.clone()), Udp(1234)]); - ma_valid("/ip4/127.0.0.1/udp/0", "047F00000191020000", vec![Ip4(local.clone()), Udp(0)]); - ma_valid("/ip4/127.0.0.1/tcp/1234", "047F0000010604D2", vec![Ip4(local.clone()), Tcp(1234)]); - ma_valid("/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", - "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", - vec![Ip4(local.clone()), P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC"))]); + ma_valid( + "/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", + "A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", + vec![ + P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), + Tcp(1234), + ], + ); + ma_valid( + "/ip4/127.0.0.1/udp/1234", + "047F000001910204D2", + vec![Ip4(local.clone()), Udp(1234)], + ); + ma_valid( + "/ip4/127.0.0.1/udp/0", + "047F00000191020000", + vec![Ip4(local.clone()), Udp(0)], + ); + ma_valid( + "/ip4/127.0.0.1/tcp/1234", + "047F0000010604D2", + vec![Ip4(local.clone()), Tcp(1234)], + ); + ma_valid( + "/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B", + vec![ + Ip4(local.clone()), + P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), + ], + ); ma_valid("/ip4/127.0.0.1/p2p/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC/tcp/1234", "047F000001A503221220D52EBB89D85B02A284948203A62FF28389C57C9F42BEEC4EC20DB76A68911C0B0604D2", vec![Ip4(local.clone()), P2p(multihash("QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC")), Tcp(1234)]); @@ -237,26 +295,56 @@ fn construct_success() { ma_valid( "/onion/aaimaq4ygg2iegci:80", "BC030010C0439831B48218480050", - vec![Onion(Cow::Owned([0, 16, 192, 67, 152, 49, 180, 130, 24, 72]), 80)], + vec![Onion( + Cow::Owned([0, 16, 192, 67, 152, 49, 180, 130, 24, 72]), + 80, + )], ); ma_valid( "/onion3/vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd:1234", "BD03ADADEC040BE047F9658668B11A504F3155001F231A37F54C4476C07FB4CC139ED7E30304D2", - vec![Onion3(([173, 173, 236, 4, 11, 224, 71, 249, 101, 134, 104, 177, 26, 80, 79, 49, 85, 0, 31, 35, 26, 55, 245, 76, 68, 118, 192, 127, 180, 204, 19, 158, 215, 227, 3], 1234).into())], + vec![Onion3( + ( + [ + 173, 173, 236, 4, 11, 224, 71, 249, 101, 134, 104, 177, 26, 80, 79, 49, 85, 0, + 31, 35, 26, 55, 245, 76, 68, 118, 192, 127, 180, 204, 19, 158, 215, 227, 3, + ], + 1234, + ) + .into(), + )], ); ma_valid( "/dnsaddr/sjc-1.bootstrap.libp2p.io", "3819736A632D312E626F6F7473747261702E6C69627032702E696F", - vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io"))] + vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io"))], ); ma_valid( "/dnsaddr/sjc-1.bootstrap.libp2p.io/tcp/1234/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", "3819736A632D312E626F6F7473747261702E6C69627032702E696F0604D2A50322122006B3608AA000274049EB28AD8E793A26FF6FAB281A7D3BD77CD18EB745DFAABB", vec![Dnsaddr(Cow::Borrowed("sjc-1.bootstrap.libp2p.io")), Tcp(1234), P2p(multihash("QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN"))] ); - ma_valid("/ip4/127.0.0.1/tcp/127/ws", "047F00000106007FDD03", vec![Ip4(local.clone()),Tcp(127),Ws("/".into())] ); - ma_valid("/ip4/127.0.0.1/tcp/127/tls", "047F00000106007FC003", vec![Ip4(local.clone()),Tcp(127),Tls] ); - ma_valid("/ip4/127.0.0.1/tcp/127/tls/ws", "047F00000106007FC003DD03", vec![Ip4(local.clone()),Tcp(127),Tls,Ws("/".into())] ); + ma_valid( + "/ip4/127.0.0.1/tcp/127/ws", + "047F00000106007FDD03", + vec![Ip4(local.clone()), Tcp(127), Ws("/".into())], + ); + ma_valid( + "/ip4/127.0.0.1/tcp/127/tls", + "047F00000106007FC003", + vec![Ip4(local.clone()), Tcp(127), Tls], + ); + ma_valid( + "/ip4/127.0.0.1/tcp/127/tls/ws", + "047F00000106007FC003DD03", + vec![Ip4(local.clone()), Tcp(127), Tls, Ws("/".into())], + ); + + ma_valid( + "/ip4/127.0.0.1/tcp/127/x-webrtc/ACD1E533EC271FCDE0275947F4D62A2B2331FF10C9DDE0298EB7B399B4BFF60B", + "047F00000106007F9502ACD1E533EC271FCDE0275947F4D62A2B2331FF10C9DDE0298EB7B399B4BFF60B", + vec![Ip4(local.clone()), Tcp(127), XWebRTC(hex_to_cow("ACD1E533EC271FCDE0275947F4D62A2B2331FF10C9DDE0298EB7B399B4BFF60B"))], + ); } #[test] @@ -292,28 +380,54 @@ fn construct_fail() { "/ip4/127.0.0.1/tcp", "/ip4/127.0.0.1/p2p", "/ip4/127.0.0.1/p2p/tcp", - "/p2p-circuit/50" + "/p2p-circuit/50", + "/x-webrtc/ACD1", ]; for address in &addresses { - assert!(address.parse::().is_err(), "{}", address.to_string()); + assert!( + address.parse::().is_err(), + "{}", + address.to_string() + ); } } #[test] fn to_multiaddr() { - assert_eq!(Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)), "/ip4/127.0.0.1".parse().unwrap()); - assert_eq!(Multiaddr::from(Ipv6Addr::new(0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21)), - "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap()); - assert_eq!(Multiaddr::try_from("/ip4/127.0.0.1/tcp/1234".to_string()).unwrap(), - "/ip4/127.0.0.1/tcp/1234".parse::().unwrap()); - assert_eq!(Multiaddr::try_from("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21").unwrap(), - "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse::().unwrap()); - assert_eq!(Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)).with(Protocol::Tcp(1234)), - "/ip4/127.0.0.1/tcp/1234".parse::().unwrap()); - assert_eq!(Multiaddr::from(Ipv6Addr::new(0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21)) - .with(Protocol::Tcp(1234)), - "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/tcp/1234".parse::().unwrap()); + assert_eq!( + Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)), + "/ip4/127.0.0.1".parse().unwrap() + ); + assert_eq!( + Multiaddr::from(Ipv6Addr::new( + 0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21 + )), + "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21".parse().unwrap() + ); + assert_eq!( + Multiaddr::try_from("/ip4/127.0.0.1/tcp/1234".to_string()).unwrap(), + "/ip4/127.0.0.1/tcp/1234".parse::().unwrap() + ); + assert_eq!( + Multiaddr::try_from("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21").unwrap(), + "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21" + .parse::() + .unwrap() + ); + assert_eq!( + Multiaddr::from(Ipv4Addr::new(127, 0, 0, 1)).with(Protocol::Tcp(1234)), + "/ip4/127.0.0.1/tcp/1234".parse::().unwrap() + ); + assert_eq!( + Multiaddr::from(Ipv6Addr::new( + 0x2601, 0x9, 0x4f81, 0x9700, 0x803e, 0xca65, 0x66e8, 0xc21 + )) + .with(Protocol::Tcp(1234)), + "/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21/tcp/1234" + .parse::() + .unwrap() + ); } #[test] @@ -322,23 +436,24 @@ fn from_bytes_fail() { assert!(Multiaddr::try_from(bytes).is_err()); } - #[test] fn ser_and_deser_json() { - let addr : Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap(); + let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap(); let serialized = serde_json::to_string(&addr).unwrap(); assert_eq!(serialized, "\"/ip4/0.0.0.0/tcp/0/tls\""); let deserialized: Multiaddr = serde_json::from_str(&serialized).unwrap(); assert_eq!(addr, deserialized); } - #[test] fn ser_and_deser_bincode() { - let addr : Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap(); + let addr: Multiaddr = "/ip4/0.0.0.0/tcp/0/tls".parse::().unwrap(); let serialized = bincode::serialize(&addr).unwrap(); // compact addressing - assert_eq!(serialized, vec![10, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 6, 0, 0, 192, 3]); + assert_eq!( + serialized, + vec![10, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 6, 0, 0, 192, 3] + ); let deserialized: Multiaddr = bincode::deserialize(&serialized).unwrap(); assert_eq!(addr, deserialized); } @@ -359,7 +474,7 @@ fn append() { fn replace_ip_addr(a: &Multiaddr, p: Protocol<'_>) -> Option { a.replace(0, move |x| match x { Protocol::Ip4(_) | Protocol::Ip6(_) => Some(p), - _ => None + _ => None, }) } @@ -381,7 +496,10 @@ fn replace_ip6_with_ip4() { fn replace_ip4_with_ip6() { let server = multiaddr!(Ip4(Ipv4Addr::LOCALHOST), Tcp(10000u16)); let result = replace_ip_addr(&server, "2001:db8::1".parse::().unwrap().into()); - assert_eq!(result.unwrap(), "/ip6/2001:db8::1/tcp/10000".parse::().unwrap()) + assert_eq!( + result.unwrap(), + "/ip6/2001:db8::1/tcp/10000".parse::().unwrap() + ) } #[test] @@ -391,8 +509,8 @@ fn unknown_protocol_string() { Err(e) => match e { crate::Error::UnknownProtocolString(protocol) => { assert_eq!(protocol, "unknown") - }, - _ => assert!(false, "The UnknownProtocolString error should be caused") - } + } + _ => assert!(false, "The UnknownProtocolString error should be caused"), + }, } }