Skip to content
This repository has been archived by the owner on Nov 6, 2020. It is now read-only.

Handle socket address parsing errors #8545

Merged
merged 7 commits into from
May 9, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions util/network-devp2p/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ error-chain = { version = "0.11", default-features = false }

[dev-dependencies]
tempdir = "0.3"
assert_matches = "1.2"

[features]
default = []
2 changes: 2 additions & 0 deletions util/network-devp2p/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ extern crate serde_derive;

#[cfg(test)]
extern crate tempdir;
#[cfg(test)] #[macro_use]
extern crate assert_matches;

mod host;
mod connection;
Expand Down
92 changes: 88 additions & 4 deletions util/network-devp2p/src/node_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use network::{Error, ErrorKind, AllowIP, IpFilter};
use discovery::{TableUpdates, NodeEntry};
use ip_utils::*;
use serde_json;
use std::io;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please place imports in order, ideally in line 23, together with other std imports

tip: if you are using vim, you can easily sort everything by typing :sort when in visual mode :)


/// Node public key
pub type NodeId = H512;
Expand Down Expand Up @@ -122,18 +123,31 @@ impl FromStr for NodeEndpoint {
address: a,
udp_port: a.port()
}),
Ok(_) => Err(ErrorKind::AddressResolve(None).into()),
Err(e) => Err(ErrorKind::AddressResolve(Some(e)).into())
Ok(None) => {
// REVIEW: how is this case possible? I think we can remove this case.
Err(ErrorKind::AddressResolve(None).into())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we shouldn't remote it, cause we cannot prove that std api, will not change underlaying implementation.

anyway, I think it's also more idiomatic to use bail! macro in cases like this one, e.g.

Ok(None) => bail!(ErrorKind::AddressResolve(None)),

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on this a little? Not sure I understand what you mean by "shouldn't remote it". Thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I think you meant to type "shouldn't REMOVE" it. Now I get it!

},
Err(e) => {
match e.kind() {
io::ErrorKind::InvalidInput => {
Err(ErrorKind::AddressParse(Some(e)).into())
},
_ => {
Err(e.into())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be transformed into Error::Io(...). Is it desired?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about we return Err(ErrorKind::AddressParse()) for all error cases here? In light of your other comment below and https://doc.rust-lang.org/src/std/net/addr.rs.html#891 it seems kind of silly to cover other cases (there aren't any, to_socket_addrs always only return InvalidInput). This match arm would become:

Err(e) => Err(ErrorKind::AddressParse())

Thoughts?

}
}
}
}
}
}

#[derive(PartialEq, Eq, Copy, Clone)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum PeerType {
_Required,
Optional
}

#[derive(Debug)]
pub struct Node {
pub id: NodeId,
pub endpoint: NodeEndpoint,
Expand Down Expand Up @@ -442,11 +456,56 @@ mod tests {
assert!(endpoint.is_ok());
let v4 = match endpoint.unwrap().address {
SocketAddr::V4(v4address) => v4address,
_ => panic!("should ve v4 address")
_ => panic!("should be v4 address")
};
assert_eq!(SocketAddrV4::new(Ipv4Addr::new(123, 99, 55, 44), 7770), v4);
}

#[test]
fn endpoint_parse_empty_ip_string_returns_error() {
let endpoint = NodeEndpoint::from_str("");
assert!(endpoint.is_err());
assert_matches!(
endpoint.unwrap_err().kind(),
// TODO: after 1.27 is stable, remove the `&`s and `ref`s, see https://github.com/rust-lang/rust/pull/49394
&ErrorKind::AddressParse(ref io_err) => {
if let &Some(ref e) = io_err {
assert_eq!(e.to_string(), "invalid socket address");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if io_err is None? This assertion will not catch that

}
}
);
}

#[test]
fn endpoint_parse_invalid_ip_string_returns_error() {
let endpoint = NodeEndpoint::from_str("beef");
assert!(endpoint.is_err());
assert_matches!(
endpoint.unwrap_err().kind(),
&ErrorKind::AddressParse(ref io_err) => {
if let &Some(ref e) = io_err {
assert_eq!(e.to_string(), "invalid socket address");
}
}
);
}

#[test]
fn endpoint_parse_valid_ip_without_port_returns_error() {
let endpoint = NodeEndpoint::from_str("123.123.123.123");
assert!(endpoint.is_err());
assert_matches!(
endpoint.unwrap_err().kind(),
&ErrorKind::AddressParse(ref io_err) => {
if let &Some(ref e) = io_err {
assert_eq!(e.to_string(), "invalid socket address");
}
}
);
let endpoint = NodeEndpoint::from_str("123.123.123.123:123");
assert!(endpoint.is_ok())
}

#[test]
fn node_parse() {
assert!(validate_node_url("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").is_none());
Expand All @@ -463,6 +522,31 @@ mod tests {
node.id);
}

#[test]
fn node_parse_fails_for_invalid_urls() {
let node = Node::from_str("foo");
assert!(node.is_err());
assert_matches!(
node.unwrap_err().kind(),
&ErrorKind::AddressParse(ref io_err) => {
if let &Some(ref e) = io_err {
assert_eq!(e.to_string(), "invalid socket address");
}
}
);

let node = Node::from_str("enode://foo@bar");
assert!(node.is_err());
assert_matches!(
node.unwrap_err().kind(),
&ErrorKind::AddressParse(ref io_err) => {
if let &Some(ref e) = io_err {
assert_eq!(e.to_string(), "invalid port value");
}
}
);
}

#[test]
fn table_failure_percentage_order() {
let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@22.99.55.44:7770").unwrap();
Expand Down
13 changes: 12 additions & 1 deletion util/network/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,16 @@ error_chain! {
foreign_links {
SocketIo(IoError) #[doc = "Socket IO error."];
Io(io::Error) #[doc = "Error concerning the Rust standard library's IO subsystem."];
AddressParse(net::AddrParseError) #[doc = "Error concerning the network address parsing subsystem."];
Decompression(snappy::InvalidInput) #[doc = "Decompression error."];
}

errors {
#[doc = "Error concerning the network address parsing subsystem."]
AddressParse(err: Option<io::Error>) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if it's worth having Option<io::Error> here. Maybe it should be just AddressParse() ? io:Error is always the same https://doc.rust-lang.org/src/std/net/addr.rs.html#891

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The io::Error is always of the io::ErrorKind::InvalidInput kind but the message string changes. That said I had the same thought but thought I'd leave it similar to the foreign linked error type we had before.

description("Failed to parse network address"),
display("Failed to parse network address {}", err.as_ref().map_or("".to_string(), |e| e.to_string())),
}

#[doc = "Error concerning the network address resolution subsystem."]
AddressResolve(err: Option<io::Error>) {
description("Failed to resolve network address"),
Expand Down Expand Up @@ -157,6 +162,12 @@ impl From<crypto::Error> for Error {
}
}

impl From<net::AddrParseError> for Error {
fn from(_err: net::AddrParseError) -> Self {
ErrorKind::AddressParse(None).into()
}
}

#[test]
fn test_errors() {
assert_eq!(DisconnectReason::ClientQuit, DisconnectReason::from_u8(8));
Expand Down