#![recursion_limit="128"]
extern crate tetsy_crypto as crypto;
extern crate vapcore_io as io;
extern crate vapory_types;
extern crate tetsy_rlp;
extern crate ipnetwork;
extern crate tetsy_snappy as snappy;
extern crate libc;
extern crate semver;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[cfg(test)] #[macro_use]
extern crate assert_matches;
extern crate derive_more;
#[macro_use]
extern crate lazy_static;
pub mod client_version;
mod connection_filter;
mod error;
pub use connection_filter::{ConnectionFilter, ConnectionDirection};
pub use io::TimerToken;
pub use error::{Error, DisconnectReason};
use client_version::ClientVersion;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::net::{SocketAddr, SocketAddrV4, Ipv4Addr};
use std::str::{self, FromStr};
use std::sync::Arc;
use std::time::Duration;
use ipnetwork::{IpNetwork, IpNetworkError};
use crypto::publickey::Secret;
use vapory_types::H512;
use tetsy_rlp::{Decodable, DecoderError, Rlp};
pub type PacketId = u8;
pub type ProtocolId = [u8; 3];
pub type NodeId = H512;
pub type PeerId = usize;
#[derive(Clone)]
pub enum NetworkIoMessage {
AddHandler {
handler: Arc<dyn NetworkProtocolHandler + Sync>,
protocol: ProtocolId,
versions: Vec<(u8, u8)>,
},
AddTimer {
protocol: ProtocolId,
token: TimerToken,
delay: Duration,
},
InitPublicInterface,
Disconnect(PeerId),
DisablePeer(PeerId),
NetworkStarted(String),
}
#[derive(Debug, Clone)]
pub struct SessionInfo {
pub id: Option<NodeId>,
pub client_version: ClientVersion,
pub protocol_version: u32,
pub capabilities: Vec<SessionCapabilityInfo>,
pub peer_capabilities: Vec<PeerCapabilityInfo>,
pub ping: Option<Duration>,
pub originated: bool,
pub remote_address: String,
pub local_address: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PeerCapabilityInfo {
pub protocol: ProtocolId,
pub version: u8,
}
impl Decodable for PeerCapabilityInfo {
fn decode(rlp: &Rlp) -> Result<Self, DecoderError> {
let p: Vec<u8> = rlp.val_at(0)?;
if p.len() != 3 {
return Err(DecoderError::Custom("Invalid subprotocol string length. Should be 3"));
}
let mut p2: ProtocolId = [0u8; 3];
p2.clone_from_slice(&p);
Ok(PeerCapabilityInfo {
protocol: p2,
version: rlp.val_at(1)?
})
}
}
impl ToString for PeerCapabilityInfo {
fn to_string(&self) -> String {
format!("{}/{}", str::from_utf8(&self.protocol[..]).unwrap_or("???"), self.version)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct SessionCapabilityInfo {
pub protocol: [u8; 3],
pub version: u8,
pub packet_count: u8,
pub id_offset: u8,
}
impl PartialOrd for SessionCapabilityInfo {
fn partial_cmp(&self, other: &SessionCapabilityInfo) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for SessionCapabilityInfo {
fn cmp(&self, b: &SessionCapabilityInfo) -> Ordering {
if self.protocol != b.protocol {
return self.protocol.cmp(&b.protocol);
}
self.version.cmp(&b.version)
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum NatType {
Nothing,
Any,
UPnP,
NatPMP,
}
#[derive(Debug, PartialEq, Clone)]
pub struct NetworkConfiguration {
pub config_path: Option<String>,
pub net_config_path: Option<String>,
pub listen_address: Option<SocketAddr>,
pub public_address: Option<SocketAddr>,
pub udp_port: Option<u16>,
pub nat_enabled: bool,
pub nat_type: NatType,
pub discovery_enabled: bool,
pub boot_nodes: Vec<String>,
pub use_secret: Option<Secret>,
pub min_peers: u32,
pub max_peers: u32,
pub max_handshakes: u32,
pub reserved_protocols: HashMap<ProtocolId, u32>,
pub reserved_nodes: Vec<String>,
pub non_reserved_mode: NonReservedPeerMode,
pub ip_filter: IpFilter,
pub client_version: String,
}
impl Default for NetworkConfiguration {
fn default() -> Self {
NetworkConfiguration::new()
}
}
impl NetworkConfiguration {
pub fn new() -> Self {
NetworkConfiguration {
config_path: None,
net_config_path: None,
listen_address: None,
public_address: None,
udp_port: None,
nat_enabled: true,
nat_type: NatType::Any,
discovery_enabled: true,
boot_nodes: Vec::new(),
use_secret: None,
min_peers: 25,
max_peers: 50,
max_handshakes: 64,
reserved_protocols: HashMap::new(),
ip_filter: IpFilter::default(),
reserved_nodes: Vec::new(),
non_reserved_mode: NonReservedPeerMode::Accept,
client_version: "Tetsy-network".into(),
}
}
pub fn new_with_port(port: u16) -> NetworkConfiguration {
let mut config = NetworkConfiguration::new();
config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), port)));
config
}
pub fn new_local() -> NetworkConfiguration {
let mut config = NetworkConfiguration::new();
config.listen_address = Some(SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0)));
config.nat_enabled = false;
config
}
}
pub trait NetworkContext {
fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error>;
fn disable_peer(&self, peer: PeerId);
fn disconnect_peer(&self, peer: PeerId);
fn is_expired(&self) -> bool;
fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error>;
fn peer_client_version(&self, peer: PeerId) -> ClientVersion;
fn session_info(&self, peer: PeerId) -> Option<SessionInfo>;
fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option<u8>;
fn subprotocol_name(&self) -> ProtocolId;
fn is_reserved_peer(&self, peer: PeerId) -> bool;
fn payload_soft_limit(&self) -> usize;
}
impl<'a, T> NetworkContext for &'a T where T: ?Sized + NetworkContext {
fn send(&self, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).send(peer, packet_id, data)
}
fn send_protocol(&self, protocol: ProtocolId, peer: PeerId, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).send_protocol(protocol, peer, packet_id, data)
}
fn respond(&self, packet_id: PacketId, data: Vec<u8>) -> Result<(), Error> {
(**self).respond(packet_id, data)
}
fn disable_peer(&self, peer: PeerId) {
(**self).disable_peer(peer)
}
fn disconnect_peer(&self, peer: PeerId) {
(**self).disconnect_peer(peer)
}
fn is_expired(&self) -> bool {
(**self).is_expired()
}
fn register_timer(&self, token: TimerToken, delay: Duration) -> Result<(), Error> {
(**self).register_timer(token, delay)
}
fn peer_client_version(&self, peer: PeerId) -> ClientVersion {
(**self).peer_client_version(peer)
}
fn session_info(&self, peer: PeerId) -> Option<SessionInfo> {
(**self).session_info(peer)
}
fn protocol_version(&self, protocol: ProtocolId, peer: PeerId) -> Option<u8> {
(**self).protocol_version(protocol, peer)
}
fn subprotocol_name(&self) -> ProtocolId {
(**self).subprotocol_name()
}
fn is_reserved_peer(&self, peer: PeerId) -> bool {
(**self).is_reserved_peer(peer)
}
fn payload_soft_limit(&self) -> usize {
(**self).payload_soft_limit()
}
}
pub trait NetworkProtocolHandler: Sync + Send {
fn initialize(&self, _io: &dyn NetworkContext) {}
fn read(&self, io: &dyn NetworkContext, peer: &PeerId, packet_id: u8, data: &[u8]);
fn connected(&self, io: &dyn NetworkContext, peer: &PeerId);
fn disconnected(&self, io: &dyn NetworkContext, peer: &PeerId);
fn timeout(&self, _io: &dyn NetworkContext, _timer: TimerToken) {}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum NonReservedPeerMode {
Accept,
Deny,
}
impl NonReservedPeerMode {
pub fn parse(s: &str) -> Option<Self> {
match s {
"accept" => Some(NonReservedPeerMode::Accept),
"deny" => Some(NonReservedPeerMode::Deny),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
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 {
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)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum AllowIP {
All,
Private,
Public,
None,
}