use crate::{iolib::IoError, service::ProtocolVersion, ProtocolId};
use rlp::{self, Decodable, DecoderError, Encodable, Rlp, RlpStream};
use std::{fmt, io, net};
use thiserror::Error;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum DisconnectReason {
DisconnectRequested,
UselessPeer,
WrongEndpointInfo,
IpLimited,
UpdateNodeIdFailed,
Blacklisted,
Custom(String),
Unknown,
}
impl DisconnectReason {
fn code(&self) -> u8 {
match self {
DisconnectReason::DisconnectRequested => 0,
DisconnectReason::UselessPeer => 1,
DisconnectReason::WrongEndpointInfo => 2,
DisconnectReason::IpLimited => 3,
DisconnectReason::UpdateNodeIdFailed => 4,
DisconnectReason::Blacklisted => 5,
DisconnectReason::Custom(_) => 100,
DisconnectReason::Unknown => 0xff,
}
}
}
impl Encodable for DisconnectReason {
fn rlp_append(&self, s: &mut RlpStream) {
let mut raw = vec![self.code()];
if let DisconnectReason::Custom(msg) = self {
raw.extend(msg.bytes());
}
s.append_raw(&raw[..], raw.len());
}
}
impl Decodable for DisconnectReason {
fn decode(rlp: &Rlp) -> std::result::Result<Self, DecoderError> {
let raw = rlp.as_raw().to_vec();
if raw.is_empty() {
return Err(DecoderError::RlpIsTooShort);
}
match raw[0] {
0 => Ok(DisconnectReason::DisconnectRequested),
1 => Ok(DisconnectReason::UselessPeer),
2 => Ok(DisconnectReason::WrongEndpointInfo),
3 => Ok(DisconnectReason::IpLimited),
4 => Ok(DisconnectReason::UpdateNodeIdFailed),
5 => Ok(DisconnectReason::Blacklisted),
100 => match std::str::from_utf8(&raw[1..]) {
Err(_) => {
Err(DecoderError::Custom("Unable to decode message part"))
}
Ok(msg) => Ok(DisconnectReason::Custom(msg.to_owned())),
},
_ => Ok(DisconnectReason::Unknown),
}
}
}
impl fmt::Display for DisconnectReason {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let msg = match *self {
DisconnectReason::DisconnectRequested => "disconnect requested",
DisconnectReason::UselessPeer => "useless peer",
DisconnectReason::WrongEndpointInfo => "wrong node id",
DisconnectReason::IpLimited => "IP limited",
DisconnectReason::UpdateNodeIdFailed => "Update node id failed",
DisconnectReason::Blacklisted => "blacklisted",
DisconnectReason::Custom(ref msg) => &msg[..],
DisconnectReason::Unknown => "unknown",
};
f.write_str(msg)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ThrottlingReason {
QueueFull,
Throttled,
PacketThrottled(&'static str),
}
impl fmt::Display for ThrottlingReason {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
ThrottlingReason::QueueFull => {
f.write_str("egress queue capacity reached")
}
ThrottlingReason::Throttled => f.write_str("egress throttled"),
ThrottlingReason::PacketThrottled(name) => {
let msg = format!("packet {} throttled", name);
f.write_str(msg.as_str())
}
}
}
}
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
SocketIo(#[from] IoError),
#[error("Failed to parse network address")]
AddressParse,
#[error("Failed to resolve network address {}", .0.as_ref().map_or("".to_string(), |e| e.to_string()))]
AddressResolve(Option<io::Error>),
#[error("Authentication failure")]
Auth,
#[error("Bad protocol")]
BadProtocol,
#[error("Bad socket address")]
BadAddr,
#[error("Decoder error: reason={0}")]
Decoder(String),
#[error("Expired message")]
Expired,
#[error("Peer disconnected: {0}")]
Disconnect(DisconnectReason),
#[error("Invalid node id")]
InvalidNodeId,
#[error("Packet is too large")]
OversizedPacket,
#[error("Unexpected IO error: {0}")]
Io(io::Error),
#[error("Received message is deprecated. Protocol {protocol:?}, message id {msg_id}, \
min_supported_version {min_supported_version}")]
MessageDeprecated {
protocol: ProtocolId,
msg_id: u16,
min_supported_version: ProtocolVersion,
},
#[error("We are trying to send unsupported message to peer. Protocol {protocol:?},\
message id {msg_id}, peer_protocol_version {peer_protocol_version:?}, min_supported_version {min_supported_version:?}")]
SendUnsupportedMessage {
protocol: ProtocolId,
msg_id: u16,
peer_protocol_version: Option<ProtocolVersion>,
min_supported_version: Option<ProtocolVersion>,
},
#[error("throttling failure: {0}")]
Throttling(ThrottlingReason),
#[error("{0}")]
Msg(String),
}
impl From<io::Error> for Error {
fn from(err: io::Error) -> Self { Error::Io(err) }
}
impl From<rlp::DecoderError> for Error {
fn from(err: rlp::DecoderError) -> Self {
Error::Decoder(format!("{}", err)).into()
}
}
impl From<crate::keylib::Error> for Error {
fn from(_err: crate::keylib::Error) -> Self { Error::Auth.into() }
}
impl From<crate::keylib::crypto::Error> for Error {
fn from(_err: crate::keylib::crypto::Error) -> Self { Error::Auth.into() }
}
impl From<net::AddrParseError> for Error {
fn from(_err: net::AddrParseError) -> Self { Error::BadAddr.into() }
}
impl From<&str> for Error {
fn from(error: &str) -> Self { Error::Msg(error.to_string()) }
}
#[cfg(test)]
mod tests {
use super::DisconnectReason::{self, *};
use rlp::{decode, encode};
fn check_rlp(r: DisconnectReason) {
assert_eq!(decode::<DisconnectReason>(&encode(&r)).unwrap(), r);
}
#[test]
fn test_disconnect_reason_rlp() {
check_rlp(DisconnectRequested);
check_rlp(UselessPeer);
check_rlp(WrongEndpointInfo);
check_rlp(IpLimited);
check_rlp(UpdateNodeIdFailed);
check_rlp(Unknown);
check_rlp(Custom("".to_owned()));
check_rlp(Custom("test test test".to_owned()));
}
}