Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add noise protocol #53

Merged
merged 2 commits into from
May 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{net, fmt, error, io, num, str, string};
use std::{error, fmt, io, net, num, str, string};
use unsigned_varint::decode;

pub type Result<T> = ::std::result::Result<T, Error>;
Expand All @@ -25,7 +25,9 @@ impl fmt::Display for Error {
Error::InvalidUvar(e) => write!(f, "failed to decode unsigned varint: {}", e),
Error::ParsingError(e) => write!(f, "failed to parse: {}", e),
Error::UnknownProtocolId(id) => write!(f, "unknown protocol id: {}", id),
Error::UnknownProtocolString(string) => write!(f, "unknown protocol string: {}", string),
Error::UnknownProtocolString(string) => {
write!(f, "unknown protocol string: {}", string)
}
}
}
}
Expand Down
57 changes: 35 additions & 22 deletions src/from_url.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,18 +57,21 @@ fn from_url_inner(url: &str, lossy: bool) -> std::result::Result<Multiaddr, From
// Note: if you add support for a new scheme, please update the documentation as well.
"ws" | "wss" | "http" | "https" => from_url_inner_http_ws(url, lossy),
"unix" => from_url_inner_path(url, lossy),
_ => Err(FromUrlErr::UnsupportedScheme)
_ => Err(FromUrlErr::UnsupportedScheme),
}
}

/// Called when `url.scheme()` is an Internet-like URL.
fn from_url_inner_http_ws(url: url::Url, lossy: bool) -> std::result::Result<Multiaddr, FromUrlErr> {
fn from_url_inner_http_ws(
url: url::Url,
lossy: bool,
) -> std::result::Result<Multiaddr, FromUrlErr> {
let (protocol, lost_path, default_port) = match url.scheme() {
"ws" => (Protocol::Ws(url.path().to_owned().into()), false, 80),
"wss" => (Protocol::Wss(url.path().to_owned().into()), false, 443),
"http" => (Protocol::Http, true, 80),
"https" => (Protocol::Https, true, 443),
_ => unreachable!("We only call this function for one of the given schemes; qed")
_ => unreachable!("We only call this function for one of the given schemes; qed"),
};

let port = Protocol::Tcp(url.port().unwrap_or(default_port));
Expand All @@ -82,12 +85,13 @@ fn from_url_inner_http_ws(url: url::Url, lossy: bool) -> std::result::Result<Mul
return Err(FromUrlErr::BadUrl);
};

if !lossy && (
!url.username().is_empty() ||
url.password().is_some() ||
(lost_path && url.path() != "/" && !url.path().is_empty()) ||
url.query().is_some() || url.fragment().is_some()
) {
if !lossy
&& (!url.username().is_empty()
|| url.password().is_some()
|| (lost_path && url.path() != "/" && !url.path().is_empty())
|| url.query().is_some()
|| url.fragment().is_some())
{
return Err(FromUrlErr::InformationLoss);
}

Expand All @@ -101,15 +105,15 @@ fn from_url_inner_http_ws(url: url::Url, lossy: bool) -> std::result::Result<Mul
fn from_url_inner_path(url: url::Url, lossy: bool) -> std::result::Result<Multiaddr, FromUrlErr> {
let protocol = match url.scheme() {
"unix" => Protocol::Unix(url.path().to_owned().into()),
_ => unreachable!("We only call this function for one of the given schemes; qed")
_ => unreachable!("We only call this function for one of the given schemes; qed"),
};

if !lossy && (
!url.username().is_empty() ||
url.password().is_some() ||
url.query().is_some() ||
url.fragment().is_some()
) {
if !lossy
&& (!url.username().is_empty()
|| url.password().is_some()
|| url.query().is_some()
|| url.fragment().is_some())
{
return Err(FromUrlErr::InformationLoss);
}

Expand Down Expand Up @@ -137,16 +141,15 @@ impl fmt::Display for FromUrlErr {
}
}

impl error::Error for FromUrlErr {
}
impl error::Error for FromUrlErr {}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn parse_garbage_doesnt_panic() {
for _ in 0 .. 50 {
for _ in 0..50 {
let url = (0..16).map(|_| rand::random::<u8>()).collect::<Vec<_>>();
let url = String::from_utf8_lossy(&url);
assert!(from_url(&url).is_err());
Expand Down Expand Up @@ -223,7 +226,7 @@ mod tests {
fn wrong_scheme() {
match from_url("foo://127.0.0.1") {
Err(FromUrlErr::UnsupportedScheme) => {}
_ => panic!()
_ => panic!(),
}
}

Expand Down Expand Up @@ -271,13 +274,23 @@ mod tests {
#[test]
fn ws_path() {
let addr = from_url("ws://1.2.3.4:1000/foo/bar").unwrap();
assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/x-parity-ws/%2ffoo%2fbar".parse().unwrap());
assert_eq!(
addr,
"/ip4/1.2.3.4/tcp/1000/x-parity-ws/%2ffoo%2fbar"
.parse()
.unwrap()
);

let addr = from_url("ws://1.2.3.4:1000/").unwrap();
assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/ws".parse().unwrap());

let addr = from_url("wss://1.2.3.4:1000/foo/bar").unwrap();
assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/x-parity-wss/%2ffoo%2fbar".parse().unwrap());
assert_eq!(
addr,
"/ip4/1.2.3.4/tcp/1000/x-parity-wss/%2ffoo%2fbar"
.parse()
.unwrap()
);

let addr = from_url("wss://1.2.3.4:1000").unwrap();
assert_eq!(addr, "/ip4/1.2.3.4/tcp/1000/wss".parse().unwrap());
Expand Down
110 changes: 68 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
///! Implementation of [multiaddr](https://github.com/jbenet/multiaddr) in Rust.

///! Implementation of [multiaddr](https://github.com/multiformats/multiaddr) in Rust.
pub use multihash;

mod protocol;
mod onion_addr;
mod errors;
mod onion_addr;
mod protocol;

#[cfg(feature = "url")]
mod from_url;

pub use self::errors::{Error, Result};
pub use self::onion_addr::Onion3Addr;
pub use self::protocol::Protocol;
use serde::{
Deserialize,
Deserializer,
Serialize,
Serializer,
de::{self, Error as DeserializerError}
de::{self, Error as DeserializerError},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::{
convert::TryFrom,
fmt,
io,
fmt, io,
iter::FromIterator,
net::{IpAddr, Ipv4Addr, Ipv6Addr},
result::Result as StdResult,
str::FromStr,
sync::Arc
sync::Arc,
};
pub use self::errors::{Result, Error};
pub use self::protocol::Protocol;
pub use self::onion_addr::Onion3Addr;

#[cfg(feature = "url")]
pub use self::from_url::{FromUrlErr, from_url, from_url_lossy};
pub use self::from_url::{from_url, from_url_lossy, FromUrlErr};

static_assertions::const_assert! {
// This check is most certainly overkill right now, but done here
Expand All @@ -42,17 +37,23 @@ static_assertions::const_assert! {
/// Representation of a Multiaddr.
#[allow(clippy::rc_buffer)]
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Multiaddr { bytes: Arc<Vec<u8>> }
pub struct Multiaddr {
bytes: Arc<Vec<u8>>,
}

impl Multiaddr {
/// Create a new, empty multiaddress.
pub fn empty() -> Self {
Self { bytes: Arc::new(Vec::new()) }
Self {
bytes: Arc::new(Vec::new()),
}
}

/// Create a new, empty multiaddress with the given capacity.
pub fn with_capacity(n: usize) -> Self {
Self { bytes: Arc::new(Vec::with_capacity(n)) }
Self {
bytes: Arc::new(Vec::with_capacity(n)),
}
}

/// Return the length in bytes of this multiaddress.
Expand Down Expand Up @@ -85,7 +86,8 @@ impl Multiaddr {
pub fn push(&mut self, p: Protocol<'_>) {
let mut w = io::Cursor::<&mut Vec<u8>>::new(Arc::make_mut(&mut self.bytes));
w.set_position(w.get_ref().len() as u64);
p.write_bytes(&mut w).expect("Writing to a `io::Cursor<&mut Vec<u8>>` never fails.")
p.write_bytes(&mut w)
.expect("Writing to a `io::Cursor<&mut Vec<u8>>` never fails.")
}

/// Pops the last `Protocol` of this multiaddr, or `None` if the multiaddr is empty.
Expand All @@ -101,12 +103,12 @@ impl Multiaddr {
pub fn pop<'a>(&mut self) -> Option<Protocol<'a>> {
let mut slice = &self.bytes[..]; // the remaining multiaddr slice
if slice.is_empty() {
return None
return None;
}
let protocol = loop {
let (p, s) = Protocol::from_bytes(slice).expect("`slice` is a valid `Protocol`.");
if s.is_empty() {
break p.acquire()
break p.acquire();
}
slice = s
};
Expand All @@ -119,7 +121,8 @@ impl Multiaddr {
pub fn with(mut self, p: Protocol<'_>) -> Self {
let mut w = io::Cursor::<&mut Vec<u8>>::new(Arc::make_mut(&mut self.bytes));
w.set_position(w.get_ref().len() as u64);
p.write_bytes(&mut w).expect("Writing to a `io::Cursor<&mut Vec<u8>>` never fails.");
p.write_bytes(&mut w)
.expect("Writing to a `io::Cursor<&mut Vec<u8>>` never fails.");
self
}

Expand Down Expand Up @@ -153,7 +156,7 @@ impl Multiaddr {
/// updated `Protocol` at position `at` will be returned.
pub fn replace<'a, F>(&self, at: usize, by: F) -> Option<Multiaddr>
where
F: FnOnce(&Protocol<'_>) -> Option<Protocol<'a>>
F: FnOnce(&Protocol<'_>) -> Option<Protocol<'a>>,
{
let mut address = Multiaddr::with_capacity(self.len());
let mut fun = Some(by);
Expand All @@ -165,24 +168,28 @@ impl Multiaddr {
if let Some(q) = f(&p) {
address = address.with(q);
replaced = true;
continue
continue;
}
return None
return None;
}
address = address.with(p)
}

if replaced { Some(address) } else { None }
if replaced {
Some(address)
} else {
None
}
}

/// Checks whether the given `Multiaddr` is a suffix of this `Multiaddr`.
pub fn ends_with(&self, other: &Multiaddr) -> bool {
let n = self.bytes.len();
let m = other.bytes.len();
if n < m {
return false
return false;
}
self.bytes[(n - m) ..] == other.bytes[..]
self.bytes[(n - m)..] == other.bytes[..]
}
}

Expand Down Expand Up @@ -234,9 +241,12 @@ impl<'a> FromIterator<Protocol<'a>> for Multiaddr {
{
let mut writer = Vec::new();
for cmp in iter {
cmp.write_bytes(&mut writer).expect("Writing to a `Vec` never fails.");
cmp.write_bytes(&mut writer)
.expect("Writing to a `Vec` never fails.");
}
Multiaddr {
bytes: Arc::new(writer),
}
Multiaddr { bytes: Arc::new(writer) }
}
}

Expand All @@ -249,15 +259,18 @@ impl FromStr for Multiaddr {

if Some("") != parts.next() {
// A multiaddr must start with `/`
return Err(Error::InvalidMultiaddr)
return Err(Error::InvalidMultiaddr);
}

while parts.peek().is_some() {
let p = Protocol::from_str_parts(&mut parts)?;
p.write_bytes(&mut writer).expect("Writing to a `Vec` never fails.");
p.write_bytes(&mut writer)
.expect("Writing to a `Vec` never fails.");
}

Ok(Multiaddr { bytes: Arc::new(writer) })
Ok(Multiaddr {
bytes: Arc::new(writer),
})
}
}

Expand All @@ -283,7 +296,8 @@ impl<'a> Iterator for Iter<'a> {
impl<'a> From<Protocol<'a>> for Multiaddr {
fn from(p: Protocol<'a>) -> Multiaddr {
let mut w = Vec::new();
p.write_bytes(&mut w).expect("Writing to a `Vec` never fails.");
p.write_bytes(&mut w)
.expect("Writing to a `Vec` never fails.");
Multiaddr { bytes: Arc::new(w) }
}
}
Expand All @@ -292,7 +306,7 @@ impl From<IpAddr> for Multiaddr {
fn from(v: IpAddr) -> Multiaddr {
match v {
IpAddr::V4(a) => a.into(),
IpAddr::V6(a) => a.into()
IpAddr::V6(a) => a.into(),
}
}
}
Expand Down Expand Up @@ -357,17 +371,25 @@ impl<'de> Deserialize<'de> for Multiaddr {
where
D: Deserializer<'de>,
{
struct Visitor { is_human_readable: bool }
struct Visitor {
is_human_readable: bool,
}

impl<'de> de::Visitor<'de> for Visitor {
type Value = Multiaddr;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("multiaddress")
}
fn visit_seq<A: de::SeqAccess<'de>>(self, mut seq: A) -> StdResult<Self::Value, A::Error> {
let mut buf: Vec<u8> = Vec::with_capacity(std::cmp::min(seq.size_hint().unwrap_or(0), 4096));
while let Some(e) = seq.next_element()? { buf.push(e); }
fn visit_seq<A: de::SeqAccess<'de>>(
self,
mut seq: A,
) -> StdResult<Self::Value, A::Error> {
let mut buf: Vec<u8> =
Vec::with_capacity(std::cmp::min(seq.size_hint().unwrap_or(0), 4096));
while let Some(e) = seq.next_element()? {
buf.push(e);
}
if self.is_human_readable {
let s = String::from_utf8(buf).map_err(DeserializerError::custom)?;
s.parse().map_err(DeserializerError::custom)
Expand Down Expand Up @@ -396,9 +418,13 @@ impl<'de> Deserialize<'de> for Multiaddr {
}

if deserializer.is_human_readable() {
deserializer.deserialize_str(Visitor { is_human_readable: true })
deserializer.deserialize_str(Visitor {
is_human_readable: true,
})
} else {
deserializer.deserialize_bytes(Visitor { is_human_readable: false })
deserializer.deserialize_bytes(Visitor {
is_human_readable: false,
})
}
}
}
Expand Down
Loading