Merge branch 'master' into split

This commit is contained in:
debris
2017-07-29 23:44:55 +02:00
19 changed files with 582 additions and 152 deletions

View File

@@ -30,7 +30,7 @@ use node_table::*;
use error::NetworkError;
use io::{StreamToken, IoContext};
use ethkey::{Secret, KeyPair, sign, recover};
use AllowIP;
use IpFilter;
use PROTOCOL_VERSION;
@@ -99,7 +99,7 @@ pub struct Discovery {
send_queue: VecDeque<Datagramm>,
check_timestamps: bool,
adding_nodes: Vec<NodeEntry>,
allow_ips: AllowIP,
ip_filter: IpFilter,
}
pub struct TableUpdates {
@@ -108,7 +108,7 @@ pub struct TableUpdates {
}
impl Discovery {
pub fn new(key: &KeyPair, listen: SocketAddr, public: NodeEndpoint, token: StreamToken, allow_ips: AllowIP) -> Discovery {
pub fn new(key: &KeyPair, listen: SocketAddr, public: NodeEndpoint, token: StreamToken, ip_filter: IpFilter) -> Discovery {
let socket = UdpSocket::bind(&listen).expect("Error binding UDP socket");
Discovery {
id: key.public().clone(),
@@ -124,7 +124,7 @@ impl Discovery {
send_queue: VecDeque::new(),
check_timestamps: true,
adding_nodes: Vec::new(),
allow_ips: allow_ips,
ip_filter: ip_filter,
}
}
@@ -400,7 +400,7 @@ impl Discovery {
}
fn is_allowed(&self, entry: &NodeEntry) -> bool {
entry.endpoint.is_allowed(self.allow_ips) && entry.id != self.id
entry.endpoint.is_allowed(&self.ip_filter) && entry.id != self.id
}
fn on_ping(&mut self, rlp: &UntrustedRlp, node: &NodeId, from: &SocketAddr) -> Result<Option<TableUpdates>, NetworkError> {
@@ -561,7 +561,6 @@ mod tests {
use std::str::FromStr;
use rustc_hex::FromHex;
use ethkey::{Random, Generator};
use AllowIP;
#[test]
fn find_node() {
@@ -586,8 +585,8 @@ mod tests {
let key2 = Random.generate().unwrap();
let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40444").unwrap(), udp_port: 40444 };
let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 };
let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0, AllowIP::All);
let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0, AllowIP::All);
let mut discovery1 = Discovery::new(&key1, ep1.address.clone(), ep1.clone(), 0, IpFilter::default());
let mut discovery2 = Discovery::new(&key2, ep2.address.clone(), ep2.clone(), 0, IpFilter::default());
let node1 = Node::from_str("enode://a979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7770").unwrap();
let node2 = Node::from_str("enode://b979fb575495b8d6db44f750317d0f4622bf4c2aa3365d6af7c284339968eef29b69ad0dce72a4d8db5ebb4968de0e3bec910127f134779fbcb0cb6d3331163c@127.0.0.1:7771").unwrap();
@@ -619,7 +618,7 @@ mod tests {
fn removes_expired() {
let key = Random.generate().unwrap();
let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40446").unwrap(), udp_port: 40447 };
let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, AllowIP::All);
let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, IpFilter::default());
for _ in 0..1200 {
discovery.add_node(NodeEntry { id: NodeId::random(), endpoint: ep.clone() });
}
@@ -648,7 +647,7 @@ mod tests {
fn packets() {
let key = Random.generate().unwrap();
let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 };
let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, AllowIP::All);
let mut discovery = Discovery::new(&key, ep.address.clone(), ep.clone(), 0, IpFilter::default());
discovery.check_timestamps = false;
let from = SocketAddr::from_str("99.99.99.99:40445").unwrap();

View File

@@ -35,7 +35,7 @@ use rlp::*;
use session::{Session, SessionInfo, SessionData};
use error::*;
use io::*;
use {NetworkProtocolHandler, NonReservedPeerMode, AllowIP, PROTOCOL_VERSION};
use {NetworkProtocolHandler, NonReservedPeerMode, PROTOCOL_VERSION, IpFilter};
use node_table::*;
use stats::NetworkStats;
use discovery::{Discovery, TableUpdates, NodeEntry};
@@ -106,7 +106,7 @@ pub struct NetworkConfiguration {
/// The non-reserved peer mode.
pub non_reserved_mode: NonReservedPeerMode,
/// IP filter
pub allow_ips: AllowIP,
pub ip_filter: IpFilter,
}
impl Default for NetworkConfiguration {
@@ -132,7 +132,7 @@ impl NetworkConfiguration {
max_peers: 50,
max_handshakes: 64,
reserved_protocols: HashMap::new(),
allow_ips: AllowIP::All,
ip_filter: IpFilter::default(),
reserved_nodes: Vec::new(),
non_reserved_mode: NonReservedPeerMode::Accept,
}
@@ -566,7 +566,7 @@ impl Host {
}
let local_endpoint = self.info.read().local_endpoint.clone();
let public_address = self.info.read().config.public_address.clone();
let allow_ips = self.info.read().config.allow_ips;
let allow_ips = self.info.read().config.ip_filter.clone();
let public_endpoint = match public_address {
None => {
let public_address = select_public_address(local_endpoint.address.port());
@@ -660,7 +660,7 @@ impl Host {
}
let config = &info.config;
(config.min_peers, config.non_reserved_mode == NonReservedPeerMode::Deny, config.max_handshakes as usize, config.allow_ips, info.id().clone())
(config.min_peers, config.non_reserved_mode == NonReservedPeerMode::Deny, config.max_handshakes as usize, config.ip_filter.clone(), info.id().clone())
};
let session_count = self.session_count();

View File

@@ -21,55 +21,193 @@ use std::io;
use igd::{PortMappingProtocol, search_gateway_from_timeout};
use std::time::Duration;
use node_table::{NodeEndpoint};
use ipnetwork::{IpNetwork};
/// Socket address extension for rustc beta. To be replaces with now unstable API
pub trait SocketAddrExt {
/// Returns true for the special 'unspecified' address 0.0.0.0.
fn is_unspecified_s(&self) -> bool;
/// Returns true if the address appears to be globally routable.
fn is_global_s(&self) -> bool;
// Ipv4 specific
fn is_shared_space(&self) -> bool { false }
fn is_special_purpose(&self) -> bool { false }
fn is_benchmarking(&self) -> bool { false }
fn is_future_use(&self) -> bool { false }
// Ipv6 specific
fn is_unique_local_s(&self) -> bool { false }
fn is_unicast_link_local_s(&self) -> bool { false }
fn is_documentation_s(&self) -> bool { false }
fn is_global_multicast(&self) -> bool { false }
fn is_other_multicast(&self) -> bool { false }
fn is_reserved(&self) -> bool;
fn is_usable_public(&self) -> bool;
fn is_usable_private(&self) -> bool;
fn is_within(&self, ipnet: &IpNetwork) -> bool;
}
impl SocketAddrExt for Ipv4Addr {
fn is_unspecified_s(&self) -> bool {
self.octets() == [0, 0, 0, 0]
fn is_global_s(&self) -> bool {
!self.is_private() &&
!self.is_loopback() &&
!self.is_link_local() &&
!self.is_broadcast() &&
!self.is_documentation()
}
fn is_global_s(&self) -> bool {
!self.is_private() && !self.is_loopback() && !self.is_link_local() &&
!self.is_broadcast() && !self.is_documentation()
// Used for communications between a service provider and its subscribers when using a carrier-grade NAT
// see: https://en.wikipedia.org/wiki/Reserved_IP_addresses
fn is_shared_space(&self) -> bool {
*self >= Ipv4Addr::new(100, 64, 0, 0) &&
*self <= Ipv4Addr::new(100, 127, 255, 255)
}
// Used for the IANA IPv4 Special Purpose Address Registry
// see: https://en.wikipedia.org/wiki/Reserved_IP_addresses
fn is_special_purpose(&self) -> bool {
*self >= Ipv4Addr::new(192, 0, 0, 0) &&
*self <= Ipv4Addr::new(192, 0, 0, 255)
}
// Used for testing of inter-network communications between two separate subnets
// see: https://en.wikipedia.org/wiki/Reserved_IP_addresses
fn is_benchmarking(&self) -> bool {
*self >= Ipv4Addr::new(198, 18, 0, 0) &&
*self <= Ipv4Addr::new(198, 19, 255, 255)
}
// Reserved for future use
// see: https://en.wikipedia.org/wiki/Reserved_IP_addresses
fn is_future_use(&self) -> bool {
*self >= Ipv4Addr::new(240, 0, 0, 0) &&
*self <= Ipv4Addr::new(255, 255, 255, 254)
}
fn is_reserved(&self) -> bool {
self.is_unspecified() ||
self.is_loopback() ||
self.is_link_local() ||
self.is_broadcast() ||
self.is_documentation() ||
self.is_multicast() ||
self.is_shared_space() ||
self.is_special_purpose() ||
self.is_benchmarking() ||
self.is_future_use()
}
fn is_usable_public(&self) -> bool {
!self.is_reserved() &&
!self.is_private()
}
fn is_usable_private(&self) -> bool {
self.is_private()
}
fn is_within(&self, ipnet: &IpNetwork) -> bool {
match ipnet {
&IpNetwork::V4(ipnet) => ipnet.contains(*self),
_ => false
}
}
}
impl SocketAddrExt for Ipv6Addr {
fn is_unspecified_s(&self) -> bool {
self.segments() == [0, 0, 0, 0, 0, 0, 0, 0]
fn is_global_s(&self) -> bool {
self.is_global_multicast() ||
(!self.is_loopback() &&
!self.is_unique_local_s() &&
!self.is_unicast_link_local_s() &&
!self.is_documentation_s() &&
!self.is_other_multicast())
}
fn is_global_s(&self) -> bool {
if self.is_multicast() {
self.segments()[0] & 0x000f == 14
} else {
!self.is_loopback() && !((self.segments()[0] & 0xffc0) == 0xfe80) &&
!((self.segments()[0] & 0xffc0) == 0xfec0) && !((self.segments()[0] & 0xfe00) == 0xfc00)
// unique local address (fc00::/7).
fn is_unique_local_s(&self) -> bool {
(self.segments()[0] & 0xfe00) == 0xfc00
}
// unicast and link-local (fe80::/10).
fn is_unicast_link_local_s(&self) -> bool {
(self.segments()[0] & 0xffc0) == 0xfe80
}
// reserved for documentation (2001:db8::/32).
fn is_documentation_s(&self) -> bool {
(self.segments()[0] == 0x2001) && (self.segments()[1] == 0xdb8)
}
fn is_global_multicast(&self) -> bool {
self.segments()[0] & 0x000f == 14
}
fn is_other_multicast(&self) -> bool {
self.is_multicast() && !self.is_global_multicast()
}
fn is_reserved(&self) -> bool {
self.is_unspecified() ||
self.is_loopback() ||
self.is_unicast_link_local_s() ||
self.is_documentation_s() ||
self.is_other_multicast()
}
fn is_usable_public(&self) -> bool {
!self.is_reserved() &&
!self.is_unique_local_s()
}
fn is_usable_private(&self) -> bool {
self.is_unique_local_s()
}
fn is_within(&self, ipnet: &IpNetwork) -> bool {
match ipnet {
&IpNetwork::V6(ipnet) => ipnet.contains(*self),
_ => false
}
}
}
impl SocketAddrExt for IpAddr {
fn is_unspecified_s(&self) -> bool {
match *self {
IpAddr::V4(ref ip) => ip.is_unspecified_s(),
IpAddr::V6(ref ip) => ip.is_unspecified_s(),
}
}
fn is_global_s(&self) -> bool {
match *self {
IpAddr::V4(ref ip) => ip.is_global_s(),
IpAddr::V6(ref ip) => ip.is_global_s(),
}
}
fn is_reserved(&self) -> bool {
match *self {
IpAddr::V4(ref ip) => ip.is_reserved(),
IpAddr::V6(ref ip) => ip.is_reserved(),
}
}
fn is_usable_public(&self) -> bool {
match *self {
IpAddr::V4(ref ip) => ip.is_usable_public(),
IpAddr::V6(ref ip) => ip.is_usable_public(),
}
}
fn is_usable_private(&self) -> bool {
match *self {
IpAddr::V4(ref ip) => ip.is_usable_private(),
IpAddr::V6(ref ip) => ip.is_usable_private(),
}
}
fn is_within(&self, ipnet: &IpNetwork) -> bool {
match *self {
IpAddr::V4(ref ip) => ip.is_within(ipnet),
IpAddr::V6(ref ip) => ip.is_within(ipnet)
}
}
}
#[cfg(not(windows))]
@@ -79,7 +217,7 @@ mod getinterfaces {
use libc::{getifaddrs, freeifaddrs, ifaddrs, sockaddr, sockaddr_in, sockaddr_in6};
use std::net::{Ipv4Addr, Ipv6Addr, IpAddr};
fn convert_sockaddr (sa: *mut sockaddr) -> Option<IpAddr> {
fn convert_sockaddr(sa: *mut sockaddr) -> Option<IpAddr> {
if sa == ptr::null_mut() { return None; }
let (addr, _) = match unsafe { *sa }.sa_family as i32 {
@@ -115,7 +253,7 @@ mod getinterfaces {
Some(addr)
}
fn convert_ifaddrs (ifa: *mut ifaddrs) -> Option<IpAddr> {
fn convert_ifaddrs(ifa: *mut ifaddrs) -> Option<IpAddr> {
let ifa = unsafe { &mut *ifa };
convert_sockaddr(ifa.ifa_addr)
}
@@ -158,16 +296,16 @@ pub fn select_public_address(port: u16) -> SocketAddr {
Ok(list) => {
//prefer IPV4 bindings
for addr in &list { //TODO: use better criteria than just the first in the list
match *addr {
IpAddr::V4(a) if !a.is_unspecified_s() && !a.is_loopback() && !a.is_link_local() => {
match addr {
&IpAddr::V4(a) if !a.is_reserved() => {
return SocketAddr::V4(SocketAddrV4::new(a, port));
},
_ => {},
}
}
for addr in list {
for addr in &list {
match addr {
IpAddr::V6(a) if !a.is_unspecified_s() && !a.is_loopback() => {
&IpAddr::V6(a) if !a.is_reserved() => {
return SocketAddr::V6(SocketAddrV6::new(a, port, 0, 0));
},
_ => {},
@@ -227,7 +365,6 @@ fn can_map_external_address_or_fail() {
#[test]
fn ipv4_properties() {
#![cfg_attr(feature="dev", allow(too_many_arguments))]
fn check(octets: &[u8; 4], unspec: bool, loopback: bool,
private: bool, link_local: bool, global: bool,
@@ -235,7 +372,7 @@ fn ipv4_properties() {
let ip = Ipv4Addr::new(octets[0], octets[1], octets[2], octets[3]);
assert_eq!(octets, &ip.octets());
assert_eq!(ip.is_unspecified_s(), unspec);
assert_eq!(ip.is_unspecified(), unspec);
assert_eq!(ip.is_loopback(), loopback);
assert_eq!(ip.is_private(), private);
assert_eq!(ip.is_link_local(), link_local);
@@ -264,13 +401,131 @@ fn ipv4_properties() {
check(&[255, 255, 255, 255], false, false, false, false, false, false, true, false);
}
#[test]
fn ipv4_shared_space() {
assert!(!Ipv4Addr::new(100, 63, 255, 255).is_shared_space());
assert!(Ipv4Addr::new(100, 64, 0, 0).is_shared_space());
assert!(Ipv4Addr::new(100, 127, 255, 255).is_shared_space());
assert!(!Ipv4Addr::new(100, 128, 0, 0).is_shared_space());
}
#[test]
fn ipv4_special_purpose() {
assert!(!Ipv4Addr::new(191, 255, 255, 255).is_special_purpose());
assert!(Ipv4Addr::new(192, 0, 0, 0).is_special_purpose());
assert!(Ipv4Addr::new(192, 0, 0, 255).is_special_purpose());
assert!(!Ipv4Addr::new(192, 0, 1, 255).is_special_purpose());
}
#[test]
fn ipv4_benchmarking() {
assert!(!Ipv4Addr::new(198, 17, 255, 255).is_benchmarking());
assert!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking());
assert!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking());
assert!(!Ipv4Addr::new(198, 20, 0, 0).is_benchmarking());
}
#[test]
fn ipv4_future_use() {
assert!(!Ipv4Addr::new(239, 255, 255, 255).is_future_use());
assert!(Ipv4Addr::new(240, 0, 0, 0).is_future_use());
assert!(Ipv4Addr::new(255, 255, 255, 254).is_future_use());
assert!(!Ipv4Addr::new(255, 255, 255, 255).is_future_use());
}
#[test]
fn ipv4_usable_public() {
assert!(!Ipv4Addr::new(0,0,0,0).is_usable_public()); // unspecified
assert!(Ipv4Addr::new(0,0,0,1).is_usable_public());
assert!(Ipv4Addr::new(9,255,255,255).is_usable_public());
assert!(!Ipv4Addr::new(10,0,0,0).is_usable_public()); // private intra-network
assert!(!Ipv4Addr::new(10,255,255,255).is_usable_public()); // private intra-network
assert!(Ipv4Addr::new(11,0,0,0).is_usable_public());
assert!(Ipv4Addr::new(100, 63, 255, 255).is_usable_public());
assert!(!Ipv4Addr::new(100, 64, 0, 0).is_usable_public()); // shared space
assert!(!Ipv4Addr::new(100, 127, 255, 255).is_usable_public()); // shared space
assert!(Ipv4Addr::new(100, 128, 0, 0).is_usable_public());
assert!(Ipv4Addr::new(126,255,255,255).is_usable_public());
assert!(!Ipv4Addr::new(127,0,0,0).is_usable_public()); // loopback
assert!(!Ipv4Addr::new(127,255,255,255).is_usable_public()); // loopback
assert!(Ipv4Addr::new(128,0,0,0).is_usable_public());
assert!(Ipv4Addr::new(169,253,255,255).is_usable_public());
assert!(!Ipv4Addr::new(169,254,0,0).is_usable_public()); // link-local
assert!(!Ipv4Addr::new(169,254,255,255).is_usable_public()); // link-local
assert!(Ipv4Addr::new(169,255,0,0).is_usable_public());
assert!(Ipv4Addr::new(172,15,255,255).is_usable_public());
assert!(!Ipv4Addr::new(172,16,0,0).is_usable_public()); // private intra-network
assert!(!Ipv4Addr::new(172,31,255,255).is_usable_public()); // private intra-network
assert!(Ipv4Addr::new(172,32,255,255).is_usable_public());
assert!(Ipv4Addr::new(191,255,255,255).is_usable_public());
assert!(!Ipv4Addr::new(192,0,0,0).is_usable_public()); // special purpose
assert!(!Ipv4Addr::new(192,0,0,255).is_usable_public()); // special purpose
assert!(Ipv4Addr::new(192,0,1,0).is_usable_public());
assert!(Ipv4Addr::new(192,0,1,255).is_usable_public());
assert!(!Ipv4Addr::new(192,0,2,0).is_usable_public()); // documentation
assert!(!Ipv4Addr::new(192,0,2,255).is_usable_public()); // documentation
assert!(Ipv4Addr::new(192,0,3,0).is_usable_public());
assert!(Ipv4Addr::new(192,167,255,255).is_usable_public());
assert!(!Ipv4Addr::new(192,168,0,0).is_usable_public()); // private intra-network
assert!(!Ipv4Addr::new(192,168,255,255).is_usable_public()); // private intra-network
assert!(Ipv4Addr::new(192,169,0,0).is_usable_public());
assert!(Ipv4Addr::new(198,17,255,255).is_usable_public());
assert!(!Ipv4Addr::new(198,18,0,0).is_usable_public()); // benchmarking
assert!(!Ipv4Addr::new(198,19,255,255).is_usable_public()); // benchmarking
assert!(Ipv4Addr::new(198,20,0,0).is_usable_public());
assert!(Ipv4Addr::new(198,51,99,255).is_usable_public());
assert!(!Ipv4Addr::new(198,51,100,0).is_usable_public()); // documentation
assert!(!Ipv4Addr::new(198,51,100,255).is_usable_public()); // documentation
assert!(Ipv4Addr::new(198,51,101,0).is_usable_public());
assert!(Ipv4Addr::new(203,0,112,255).is_usable_public());
assert!(!Ipv4Addr::new(203,0,113,0).is_usable_public()); // documentation
assert!(!Ipv4Addr::new(203,0,113,255).is_usable_public()); // documentation
assert!(Ipv4Addr::new(203,0,114,0).is_usable_public());
assert!(Ipv4Addr::new(223,255,255,255).is_usable_public());
assert!(!Ipv4Addr::new(224,0,0,0).is_usable_public()); // multicast
assert!(!Ipv4Addr::new(239, 255, 255, 255).is_usable_public()); // multicast
assert!(!Ipv4Addr::new(240, 0, 0, 0).is_usable_public()); // future use
assert!(!Ipv4Addr::new(255, 255, 255, 254).is_usable_public()); // future use
assert!(!Ipv4Addr::new(255, 255, 255, 255).is_usable_public()); // limited broadcast
}
#[test]
fn ipv4_usable_private() {
assert!(!Ipv4Addr::new(9,255,255,255).is_usable_private());
assert!(Ipv4Addr::new(10,0,0,0).is_usable_private()); // private intra-network
assert!(Ipv4Addr::new(10,255,255,255).is_usable_private()); // private intra-network
assert!(!Ipv4Addr::new(11,0,0,0).is_usable_private());
assert!(!Ipv4Addr::new(172,15,255,255).is_usable_private());
assert!(Ipv4Addr::new(172,16,0,0).is_usable_private()); // private intra-network
assert!(Ipv4Addr::new(172,31,255,255).is_usable_private()); // private intra-network
assert!(!Ipv4Addr::new(172,32,255,255).is_usable_private());
assert!(!Ipv4Addr::new(192,167,255,255).is_usable_private());
assert!(Ipv4Addr::new(192,168,0,0).is_usable_private()); // private intra-network
assert!(Ipv4Addr::new(192,168,255,255).is_usable_private()); // private intra-network
assert!(!Ipv4Addr::new(192,169,0,0).is_usable_private());
}
#[test]
fn ipv6_properties() {
fn check(str_addr: &str, unspec: bool, loopback: bool, global: bool) {
let ip: Ipv6Addr = str_addr.parse().unwrap();
assert_eq!(str_addr, ip.to_string());
assert_eq!(ip.is_unspecified_s(), unspec);
assert_eq!(ip.is_unspecified(), unspec);
assert_eq!(ip.is_loopback(), loopback);
assert_eq!(ip.is_global_s(), global);
}
@@ -279,3 +534,5 @@ fn ipv6_properties() {
check("::", true, false, true);
check("::1", false, true, false);
}

View File

@@ -77,6 +77,7 @@ extern crate rlp;
extern crate bytes;
extern crate path;
extern crate ethcore_logger;
extern crate ipnetwork;
#[macro_use]
extern crate log;
@@ -106,6 +107,8 @@ pub use session::SessionInfo;
pub use io::TimerToken;
pub use node_table::{is_valid_node_url, NodeId};
use ipnetwork::{IpNetwork, IpNetworkError};
use std::str::FromStr;
const PROTOCOL_VERSION: u32 = 4;
@@ -145,8 +148,49 @@ impl NonReservedPeerMode {
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "ipc", binary)]
pub struct IpFilter {
pub predefined: AllowIP,
pub custom_allow: Vec<IpNetwork>,
pub custom_block: Vec<IpNetwork>,
}
impl Default for IpFilter {
fn default() -> Self {
IpFilter {
predefined: AllowIP::All,
custom_allow: vec![],
custom_block: vec![],
}
}
}
impl IpFilter {
/// Attempt to parse the peer mode from a string.
pub fn parse(s: &str) -> Result<IpFilter, IpNetworkError> {
let mut filter = IpFilter::default();
for f in s.split_whitespace() {
match f {
"all" => filter.predefined = AllowIP::All,
"private" => filter.predefined = AllowIP::Private,
"public" => filter.predefined = AllowIP::Public,
"none" => filter.predefined = AllowIP::None,
custom => {
if custom.starts_with("-") {
filter.custom_block.push(IpNetwork::from_str(&custom.to_owned().split_off(1))?)
} else {
filter.custom_allow.push(IpNetwork::from_str(custom)?)
}
}
}
}
Ok(filter)
}
}
/// IP fiter
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AllowIP {
/// Connect to any address
All,
@@ -154,5 +198,7 @@ pub enum AllowIP {
Private,
/// Connect to public network only
Public,
/// Block all addresses
None,
}

View File

@@ -30,7 +30,7 @@ use util::UtilError;
use rlp::*;
use time::Tm;
use error::NetworkError;
use AllowIP;
use {AllowIP, IpFilter};
use discovery::{TableUpdates, NodeEntry};
use ip_utils::*;
pub use rustc_serialize::json::Json;
@@ -55,11 +55,21 @@ impl NodeEndpoint {
}
}
pub fn is_allowed(&self, filter: AllowIP) -> bool {
pub fn is_allowed(&self, filter: &IpFilter) -> bool {
(self.is_allowed_by_predefined(&filter.predefined) || filter.custom_allow.iter().any(|ipnet| {
self.address.ip().is_within(ipnet)
}))
&& !filter.custom_block.iter().any(|ipnet| {
self.address.ip().is_within(ipnet)
})
}
pub fn is_allowed_by_predefined(&self, filter: &AllowIP) -> bool {
match filter {
AllowIP::All => true,
AllowIP::Private => !self.address.ip().is_global_s(),
AllowIP::Public => self.address.ip().is_global_s(),
&AllowIP::All => true,
&AllowIP::Private => self.address.ip().is_usable_private(),
&AllowIP::Public => self.address.ip().is_usable_public(),
&AllowIP::None => false,
}
}
@@ -101,8 +111,8 @@ impl NodeEndpoint {
pub fn is_valid(&self) -> bool {
self.udp_port != 0 && self.address.port() != 0 &&
match self.address {
SocketAddr::V4(a) => !a.ip().is_unspecified_s(),
SocketAddr::V6(a) => !a.ip().is_unspecified_s()
SocketAddr::V4(a) => !a.ip().is_unspecified(),
SocketAddr::V6(a) => !a.ip().is_unspecified()
}
}
}
@@ -219,8 +229,8 @@ impl NodeTable {
}
/// Returns node ids sorted by number of failures
pub fn nodes(&self, filter: AllowIP) -> Vec<NodeId> {
let mut refs: Vec<&Node> = self.nodes.values().filter(|n| !self.useless_nodes.contains(&n.id) && n.endpoint.is_allowed(filter)).collect();
pub fn nodes(&self, filter: IpFilter) -> Vec<NodeId> {
let mut refs: Vec<&Node> = self.nodes.values().filter(|n| !self.useless_nodes.contains(&n.id) && n.endpoint.is_allowed(&filter)).collect();
refs.sort_by(|a, b| a.failures.cmp(&b.failures));
refs.iter().map(|n| n.id.clone()).collect()
}
@@ -283,7 +293,7 @@ impl NodeTable {
let mut json = String::new();
json.push_str("{\n");
json.push_str("\"nodes\": [\n");
let node_ids = self.nodes(AllowIP::All);
let node_ids = self.nodes(IpFilter::default());
for i in 0 .. node_ids.len() {
let node = self.nodes.get(&node_ids[i]).expect("self.nodes() only returns node IDs from self.nodes");
json.push_str(&format!("\t{{ \"url\": \"{}\", \"failures\": {} }}{}\n", node, node.failures, if i == node_ids.len() - 1 {""} else {","}))
@@ -366,7 +376,7 @@ mod tests {
use util::H512;
use std::str::FromStr;
use devtools::*;
use AllowIP;
use ipnetwork::IpNetwork;
#[test]
fn endpoint_parse() {
@@ -412,7 +422,7 @@ mod tests {
table.note_failure(&id1);
table.note_failure(&id2);
let r = table.nodes(AllowIP::All);
let r = table.nodes(IpFilter::default());
assert_eq!(r[0][..], id3[..]);
assert_eq!(r[1][..], id2[..]);
assert_eq!(r[2][..], id1[..]);
@@ -434,9 +444,55 @@ mod tests {
{
let table = NodeTable::new(Some(temp_path.as_path().to_str().unwrap().to_owned()));
let r = table.nodes(AllowIP::All);
let r = table.nodes(IpFilter::default());
assert_eq!(r[0][..], id1[..]);
assert_eq!(r[1][..], id2[..]);
}
}
#[test]
fn custom_allow() {
let filter = IpFilter {
predefined: AllowIP::None,
custom_allow: vec![IpNetwork::from_str(&"10.0.0.0/8").unwrap(), IpNetwork::from_str(&"1.0.0.0/8").unwrap()],
custom_block: vec![],
};
assert!(!NodeEndpoint::from_str("123.99.55.44:7770").unwrap().is_allowed(&filter));
assert!(NodeEndpoint::from_str("10.0.0.1:7770").unwrap().is_allowed(&filter));
assert!(NodeEndpoint::from_str("1.0.0.55:5550").unwrap().is_allowed(&filter));
}
#[test]
fn custom_block() {
let filter = IpFilter {
predefined: AllowIP::All,
custom_allow: vec![],
custom_block: vec![IpNetwork::from_str(&"10.0.0.0/8").unwrap(), IpNetwork::from_str(&"1.0.0.0/8").unwrap()],
};
assert!(NodeEndpoint::from_str("123.99.55.44:7770").unwrap().is_allowed(&filter));
assert!(!NodeEndpoint::from_str("10.0.0.1:7770").unwrap().is_allowed(&filter));
assert!(!NodeEndpoint::from_str("1.0.0.55:5550").unwrap().is_allowed(&filter));
}
#[test]
fn custom_allow_ipv6() {
let filter = IpFilter {
predefined: AllowIP::None,
custom_allow: vec![IpNetwork::from_str(&"fc00::/8").unwrap()],
custom_block: vec![],
};
assert!(NodeEndpoint::from_str("[fc00::]:5550").unwrap().is_allowed(&filter));
assert!(!NodeEndpoint::from_str("[fd00::]:5550").unwrap().is_allowed(&filter));
}
#[test]
fn custom_block_ipv6() {
let filter = IpFilter {
predefined: AllowIP::All,
custom_allow: vec![],
custom_block: vec![IpNetwork::from_str(&"fc00::/8").unwrap()],
};
assert!(!NodeEndpoint::from_str("[fc00::]:5550").unwrap().is_allowed(&filter));
assert!(NodeEndpoint::from_str("[fd00::]:5550").unwrap().is_allowed(&filter));
}
}